2009-01-05 14:30:08 +02:00
from datetime import datetime
import os
import os . path
from django . db import models
from django . contrib . auth . models import User , Group
from django . core . urlresolvers import reverse
from django . utils . html import escape , strip_tags
from django . conf import settings
from django . utils . translation import ugettext_lazy as _
#from django.contrib.markup.templatetags.markup import markdown
from markdown import Markdown
2009-03-31 00:03:16 +03:00
from forum . markups import mypostmarkup
from forum . fields import AutoOneToOneField , ExtendedImageField
from forum . util import urlize , smiles
from forum import settings as forum_settings
2009-01-05 14:30:08 +02:00
TZ_CHOICES = [ ( float ( x [ 0 ] ) , x [ 1 ] ) for x in (
( - 12 , ' -12 ' ) , ( - 11 , ' -11 ' ) , ( - 10 , ' -10 ' ) , ( - 9.5 , ' -09.5 ' ) , ( - 9 , ' -09 ' ) ,
( - 8.5 , ' -08.5 ' ) , ( - 8 , ' -08 PST ' ) , ( - 7 , ' -07 MST ' ) , ( - 6 , ' -06 CST ' ) ,
( - 5 , ' -05 EST ' ) , ( - 4 , ' -04 AST ' ) , ( - 3.5 , ' -03.5 ' ) , ( - 3 , ' -03 ADT ' ) ,
( - 2 , ' -02 ' ) , ( - 1 , ' -01 ' ) , ( 0 , ' 00 GMT ' ) , ( 1 , ' +01 CET ' ) , ( 2 , ' +02 ' ) ,
( 3 , ' +03 ' ) , ( 3.5 , ' +03.5 ' ) , ( 4 , ' +04 ' ) , ( 4.5 , ' +04.5 ' ) , ( 5 , ' +05 ' ) ,
( 5.5 , ' +05.5 ' ) , ( 6 , ' +06 ' ) , ( 6.5 , ' +06.5 ' ) , ( 7 , ' +07 ' ) , ( 8 , ' +08 ' ) ,
( 9 , ' +09 ' ) , ( 9.5 , ' +09.5 ' ) , ( 10 , ' +10 ' ) , ( 10.5 , ' +10.5 ' ) , ( 11 , ' +11 ' ) ,
( 11.5 , ' +11.5 ' ) , ( 12 , ' +12 ' ) , ( 13 , ' +13 ' ) , ( 14 , ' +14 ' ) ,
) ]
SIGN_CHOICES = (
( 1 , ' PLUS ' ) ,
( - 1 , ' MINUS ' ) ,
)
PRIVACY_CHOICES = (
( 0 , _ ( u ' Display your e-mail address. ' ) ) ,
( 1 , _ ( u ' Hide your e-mail address but allow form e-mail. ' ) ) ,
( 2 , _ ( u ' Hide your e-mail address and disallow form e-mail. ' ) ) ,
)
MARKUP_CHOICES = (
( ' bbcode ' , ' bbcode ' ) ,
( ' markdown ' , ' markdown ' ) ,
)
path = settings . PROJECT_ROOT + settings . MEDIA_URL + ' forum/themes/ '
THEME_CHOICES = [ ( theme , theme ) for theme in os . listdir ( path )
if os . path . isdir ( path + theme ) ]
class Category ( models . Model ) :
name = models . CharField ( _ ( ' Name ' ) , max_length = 80 )
2009-04-07 17:46:22 +03:00
groups = models . ManyToManyField ( Group , blank = True , null = True , verbose_name = _ ( ' Groups ' ) , help_text = _ ( ' Only users from these groups can see this category ' ) )
2009-01-05 14:30:08 +02:00
position = models . IntegerField ( _ ( ' Position ' ) , blank = True , default = 0 )
class Meta :
ordering = [ ' position ' ]
verbose_name = _ ( ' Category ' )
verbose_name_plural = _ ( ' Categories ' )
def __unicode__ ( self ) :
return self . name
def forum_count ( self ) :
return self . forums . all ( ) . count ( )
def get_absolute_url ( self ) :
return reverse ( ' category ' , args = [ self . id ] )
@property
def topics ( self ) :
return Topic . objects . filter ( forum__category = self ) . select_related ( )
@property
def posts ( self ) :
return Post . objects . filter ( topic__forum__category = self ) . select_related ( )
2009-04-07 17:46:22 +03:00
def has_access ( self , user ) :
2009-04-07 18:43:04 +03:00
if self . groups . count ( ) > 0 :
try :
self . groups . get ( user = user )
except Group . DoesNotExist :
return False
return True
2009-01-05 14:30:08 +02:00
class Forum ( models . Model ) :
category = models . ForeignKey ( Category , related_name = ' forums ' , verbose_name = _ ( ' Category ' ) )
name = models . CharField ( _ ( ' Name ' ) , max_length = 80 )
position = models . IntegerField ( _ ( ' Position ' ) , blank = True , default = 0 )
description = models . TextField ( _ ( ' Description ' ) , blank = True , default = ' ' )
moderators = models . ManyToManyField ( User , blank = True , null = True , verbose_name = _ ( ' Moderators ' ) )
2009-04-07 17:46:22 +03:00
updated = models . DateTimeField ( _ ( ' Updated ' ) , default = datetime . now )
2009-01-05 14:30:08 +02:00
post_count = models . IntegerField ( _ ( ' Post count ' ) , blank = True , default = 0 )
class Meta :
ordering = [ ' position ' ]
verbose_name = _ ( ' Forum ' )
verbose_name_plural = _ ( ' Forums ' )
def __unicode__ ( self ) :
return self . name
def topic_count ( self ) :
return self . topics . all ( ) . count ( )
def get_absolute_url ( self ) :
return reverse ( ' forum ' , args = [ self . id ] )
@property
def posts ( self ) :
return Post . objects . filter ( topic__forum = self ) . select_related ( )
@property
def last_post ( self ) :
posts = self . posts . order_by ( ' -created ' ) . select_related ( )
try :
return posts [ 0 ]
except IndexError :
return None
class Topic ( models . Model ) :
forum = models . ForeignKey ( Forum , related_name = ' topics ' , verbose_name = _ ( ' Forum ' ) )
name = models . CharField ( _ ( ' Subject ' ) , max_length = 255 )
created = models . DateTimeField ( _ ( ' Created ' ) , null = True )
updated = models . DateTimeField ( _ ( ' Updated ' ) , null = True )
user = models . ForeignKey ( User , verbose_name = _ ( ' User ' ) )
views = models . IntegerField ( _ ( ' Views count ' ) , blank = True , default = 0 )
sticky = models . BooleanField ( _ ( ' Sticky ' ) , blank = True , default = False )
closed = models . BooleanField ( _ ( ' Closed ' ) , blank = True , default = False )
2009-01-14 17:31:10 +02:00
subscribers = models . ManyToManyField ( User , related_name = ' subscriptions ' , verbose_name = _ ( ' Subscribers ' ) , blank = True )
2009-01-05 14:30:08 +02:00
post_count = models . IntegerField ( _ ( ' Post count ' ) , blank = True , default = 0 )
class Meta :
2009-01-22 14:38:28 +02:00
ordering = [ ' -updated ' ]
2009-01-05 14:30:08 +02:00
verbose_name = _ ( ' Topic ' )
verbose_name_plural = _ ( ' Topics ' )
def __unicode__ ( self ) :
return self . name
@property
def head ( self ) :
return self . posts . all ( ) . order_by ( ' created ' ) . select_related ( ) [ 0 ]
@property
def last_post ( self ) :
return self . posts . all ( ) . order_by ( ' -created ' ) . select_related ( ) [ 0 ]
def reply_count ( self ) :
return self . post_count - 1
def get_absolute_url ( self ) :
return reverse ( ' topic ' , args = [ self . id ] )
def save ( self , * args , * * kwargs ) :
if self . id is None :
self . created = datetime . now ( )
super ( Topic , self ) . save ( * args , * * kwargs )
def update_read ( self , user ) :
read , new = Read . objects . get_or_create ( user = user , topic = self )
if not new :
read . time = datetime . now ( )
read . save ( )
#def has_unreads(self, user):
#try:
#read = Read.objects.get(user=user, topic=self)
#except Read.DoesNotExist:
#return True
#else:
#return self.updated > read.time
class Post ( models . Model ) :
topic = models . ForeignKey ( Topic , related_name = ' posts ' , verbose_name = _ ( ' Topic ' ) )
user = models . ForeignKey ( User , related_name = ' posts ' , verbose_name = _ ( ' User ' ) )
created = models . DateTimeField ( _ ( ' Created ' ) , blank = True )
updated = models . DateTimeField ( _ ( ' Updated ' ) , blank = True , null = True )
2009-01-17 17:56:19 +02:00
markup = models . CharField ( _ ( ' Markup ' ) , max_length = 15 , default = forum_settings . DEFAULT_MARKUP , choices = MARKUP_CHOICES )
2009-01-05 14:30:08 +02:00
body = models . TextField ( _ ( ' Message ' ) )
body_html = models . TextField ( _ ( ' HTML version ' ) )
body_text = models . TextField ( _ ( ' Text version ' ) )
user_ip = models . IPAddressField ( _ ( ' User IP ' ) , blank = True , default = ' ' )
class Meta :
ordering = [ ' created ' ]
verbose_name = _ ( ' Post ' )
verbose_name_plural = _ ( ' Posts ' )
def summary ( self ) :
LIMIT = 50
tail = len ( self . body ) > LIMIT and ' ... ' or ' '
return self . body [ : LIMIT ] + tail
__unicode__ = summary
def save ( self , * args , * * kwargs ) :
if self . created is None :
self . created = datetime . now ( )
if self . markup == ' bbcode ' :
self . body_html = mypostmarkup . markup ( self . body , auto_urls = False )
elif self . markup == ' markdown ' :
self . body_html = unicode ( Markdown ( self . body , safe_mode = ' escape ' ) )
#self.body_html = markdown(self.body, 'safe')
else :
raise Exception ( ' Invalid markup property: %s ' % self . markup )
self . body_text = strip_tags ( self . body_html )
self . body_html = urlize ( self . body_html )
2009-01-17 14:42:12 +02:00
self . body_html = smiles ( self . body_html )
2009-01-05 14:30:08 +02:00
new = self . id is None
if new :
2009-01-22 14:38:28 +02:00
#new post created
super ( Post , self ) . save ( * args , * * kwargs )
2009-01-05 14:30:08 +02:00
self . topic . updated = datetime . now ( )
2009-01-22 14:38:28 +02:00
self . topic . post_count = Post . objects . filter ( topic = self . topic ) . count ( )
2009-01-05 14:30:08 +02:00
self . topic . save ( )
self . topic . forum . updated = self . topic . updated
2009-01-22 14:38:28 +02:00
self . topic . forum . post_count = Post . objects . filter ( topic__forum = self . topic . forum ) . count ( )
2009-01-05 14:30:08 +02:00
self . topic . forum . save ( )
2009-01-22 14:38:28 +02:00
else :
#edit post
super ( Post , self ) . save ( * args , * * kwargs )
2009-01-05 14:30:08 +02:00
def get_absolute_url ( self ) :
return reverse ( ' post ' , args = [ self . id ] )
def delete ( self , * args , * * kwargs ) :
self_id = self . id
head_post_id = self . topic . posts . order_by ( ' created ' ) [ 0 ] . id
super ( Post , self ) . delete ( * args , * * kwargs )
2009-01-22 14:38:28 +02:00
self . topic . post_count = Post . objects . filter ( topic = self . topic ) . count ( )
2009-01-14 17:31:10 +02:00
self . topic . save ( )
2009-01-22 14:38:28 +02:00
self . topic . forum . post_count = Post . objects . filter ( topic__forum = self . topic . forum ) . count ( )
2009-01-14 17:31:10 +02:00
self . topic . forum . save ( )
2009-01-22 14:38:28 +02:00
2009-01-05 14:30:08 +02:00
if self_id == head_post_id :
self . topic . delete ( )
class Reputation ( models . Model ) :
from_user = models . ForeignKey ( User , related_name = ' reputations_from ' , verbose_name = _ ( ' From ' ) )
to_user = models . ForeignKey ( User , related_name = ' reputations_to ' , verbose_name = _ ( ' To ' ) )
topic = models . ForeignKey ( Topic , related_name = ' topic ' , verbose_name = _ ( ' Topic ' ) )
time = models . DateTimeField ( _ ( ' Time ' ) , blank = True )
sign = models . IntegerField ( _ ( ' Sign ' ) , choices = SIGN_CHOICES , default = 0 )
reason = models . TextField ( _ ( ' Reason ' ) , blank = True , default = ' ' , max_length = ' 1000 ' )
class Meta :
verbose_name = _ ( ' Reputation ' )
verbose_name_plural = _ ( ' Reputations ' )
def __unicode__ ( self ) :
return u ' T[ %d ], FU[ %d ], TU[ %d ]: %s ' % ( self . topic . id , self . from_user . id , self . to_user . id , unicode ( self . time ) )
class Profile ( models . Model ) :
user = AutoOneToOneField ( User , related_name = ' forum_profile ' , verbose_name = _ ( ' User ' ) )
#group = models.ForeignKey(Group, verbose_name=_('Group'), default='Member')
status = models . CharField ( _ ( ' Status ' ) , max_length = 30 , blank = True , default = ' ' )
site = models . URLField ( _ ( ' Site ' ) , verify_exists = False , blank = True , default = ' ' )
jabber = models . CharField ( _ ( ' Jabber ' ) , max_length = 80 , blank = True , default = ' ' )
icq = models . CharField ( _ ( ' ICQ ' ) , max_length = 12 , blank = True , default = ' ' )
msn = models . CharField ( _ ( ' MSN ' ) , max_length = 80 , blank = True , default = ' ' )
aim = models . CharField ( _ ( ' AIM ' ) , max_length = 80 , blank = True , default = ' ' )
yahoo = models . CharField ( _ ( ' Yahoo ' ) , max_length = 80 , blank = True , default = ' ' )
location = models . CharField ( _ ( ' Location ' ) , max_length = 30 , blank = True , default = ' ' )
2009-01-17 17:56:19 +02:00
signature = models . TextField ( _ ( ' Signature ' ) , blank = True , default = ' ' , max_length = forum_settings . SIGNATURE_MAX_LENGTH )
time_zone = models . FloatField ( _ ( ' Time zone ' ) , choices = TZ_CHOICES , default = float ( forum_settings . DEFAULT_TIME_ZONE ) )
2009-03-30 19:25:28 +03:00
language = models . CharField ( _ ( ' Language ' ) , max_length = 3 , default = ' ' , choices = settings . LANGUAGES )
2009-01-17 17:56:19 +02:00
avatar = ExtendedImageField ( _ ( ' Avatar ' ) , blank = True , default = ' ' , upload_to = forum_settings . AVATARS_UPLOAD_TO , width = forum_settings . AVATAR_WIDTH , height = forum_settings . AVATAR_HEIGHT )
2009-01-05 14:30:08 +02:00
theme = models . CharField ( _ ( ' Theme ' ) , choices = THEME_CHOICES , max_length = 80 , default = ' ' )
show_avatar = models . BooleanField ( _ ( ' Show avatar ' ) , blank = True , default = True )
show_signatures = models . BooleanField ( _ ( ' Show signatures ' ) , blank = True , default = True )
privacy_permission = models . IntegerField ( _ ( ' Privacy permission ' ) , choices = PRIVACY_CHOICES , default = 1 )
2009-01-17 17:56:19 +02:00
markup = models . CharField ( _ ( ' Default markup ' ) , max_length = 15 , default = forum_settings . DEFAULT_MARKUP , choices = MARKUP_CHOICES )
2009-01-19 21:23:53 +02:00
post_count = models . IntegerField ( _ ( ' Post count ' ) , blank = True , default = 0 )
2009-01-05 14:30:08 +02:00
class Meta :
verbose_name = _ ( ' Profile ' )
verbose_name_plural = _ ( ' Profiles ' )
2009-01-22 14:38:28 +02:00
2009-01-05 14:30:08 +02:00
def last_post ( self ) :
posts = Post . objects . filter ( user = self . user ) . order_by ( ' -created ' ) . select_related ( )
if posts . count ( ) :
return posts [ 0 ] . created
else :
return None
def reply_total ( self ) :
total = 0
for reputation in Reputation . objects . filter ( to_user = self . user ) . select_related ( ) :
total + = reputation . sign
return total
def reply_count_minus ( self ) :
return Reputation . objects . filter ( to_user = self . user , sign = - 1 ) . select_related ( ) . count ( )
def reply_count_plus ( self ) :
return Reputation . objects . filter ( to_user = self . user , sign = 1 ) . select_related ( ) . count ( )
class Read ( models . Model ) :
"""
For each topic that user has entered the time
is logged to this model .
"""
user = models . ForeignKey ( User , verbose_name = _ ( ' User ' ) )
topic = models . ForeignKey ( Topic , verbose_name = _ ( ' Topic ' ) )
time = models . DateTimeField ( _ ( ' Time ' ) , blank = True )
class Meta :
unique_together = [ ' user ' , ' topic ' ]
verbose_name = _ ( ' Read ' )
verbose_name_plural = _ ( ' Reads ' )
def save ( self , * args , * * kwargs ) :
if self . time is None :
self . time = datetime . now ( )
super ( Read , self ) . save ( * args , * * kwargs )
def __unicode__ ( self ) :
return u ' T[ %d ], U[ %d ]: %s ' % ( self . topic . id , self . user . id , unicode ( self . time ) )
class Report ( models . Model ) :
reported_by = models . ForeignKey ( User , related_name = ' reported_by ' , verbose_name = _ ( ' Reported by ' ) )
post = models . ForeignKey ( Post , verbose_name = _ ( ' Post ' ) )
zapped = models . BooleanField ( _ ( ' Zapped ' ) , blank = True , default = False )
zapped_by = models . ForeignKey ( User , related_name = ' zapped_by ' , blank = True , null = True , verbose_name = _ ( ' Zapped by ' ) )
created = models . DateTimeField ( _ ( ' Created ' ) , blank = True )
reason = models . TextField ( _ ( ' Reason ' ) , blank = True , default = ' ' , max_length = ' 1000 ' )
class Meta :
verbose_name = _ ( ' Report ' )
verbose_name_plural = _ ( ' Reports ' )
def __unicode__ ( self ) :
return u ' %s %s ' % ( self . reported_by , self . zapped )
class PrivateMessage ( models . Model ) :
dst_user = models . ForeignKey ( User , verbose_name = _ ( ' Recipient ' ) , related_name = ' dst_users ' )
src_user = models . ForeignKey ( User , verbose_name = _ ( ' Author ' ) , related_name = ' src_users ' )
read = models . BooleanField ( _ ( ' Read ' ) , blank = True , default = False )
created = models . DateTimeField ( _ ( ' Created ' ) , blank = True )
2009-01-17 17:56:19 +02:00
markup = models . CharField ( _ ( ' Markup ' ) , max_length = 15 , default = forum_settings . DEFAULT_MARKUP , choices = MARKUP_CHOICES )
2009-01-05 14:30:08 +02:00
subject = models . CharField ( _ ( ' Subject ' ) , max_length = 255 )
body = models . TextField ( _ ( ' Message ' ) )
body_html = models . TextField ( _ ( ' HTML version ' ) )
body_text = models . TextField ( _ ( ' Text version ' ) )
class Meta :
ordering = [ ' -created ' ]
verbose_name = _ ( ' Private message ' )
verbose_name_plural = _ ( ' Private messages ' )
2009-01-19 19:50:01 +02:00
# TODO: summary and part of the save method is the same as in the Post model
2009-01-05 14:30:08 +02:00
# move to common functions
def summary ( self ) :
LIMIT = 50
tail = len ( self . body ) > LIMIT and ' ... ' or ' '
return self . body [ : LIMIT ] + tail
def __unicode__ ( self ) :
return self . subject
def save ( self , * args , * * kwargs ) :
if self . created is None :
self . created = datetime . now ( )
if self . markup == ' bbcode ' :
self . body_html = mypostmarkup . markup ( self . body , auto_urls = False )
elif self . markup == ' markdown ' :
self . body_html = unicode ( Markdown ( self . body , safe_mode = ' escape ' ) )
#self.body_html = markdown(self.body, 'safe')
else :
raise Exception ( ' Invalid markup property: %s ' % self . markup )
self . body_text = strip_tags ( self . body_html )
self . body_html = urlize ( self . body_html )
2009-01-17 14:42:12 +02:00
self . body_html = smiles ( self . body_html )
2009-01-05 14:30:08 +02:00
new = self . id is None
super ( PrivateMessage , self ) . save ( * args , * * kwargs )
2009-01-19 19:50:01 +02:00
def get_absolute_url ( self ) :
return reverse ( ' forum_show_pm ' , args = [ self . id ] )
2009-04-08 20:43:29 +03:00
class Ban ( models . Model ) :
user = models . ForeignKey ( User , verbose_name = _ ( ' Banned user ' ) , related_name = ' ban_users ' )
ban_start = models . DateTimeField ( _ ( ' Ban start ' ) , default = datetime . now )
ban_end = models . DateTimeField ( _ ( ' Ban end ' ) , blank = True , null = True )
reason = models . TextField ( _ ( ' Reason ' ) )
class Meta :
verbose_name = _ ( ' Ban ' )
verbose_name_plural = _ ( ' Bans ' )
def save ( self , * args , * * kwargs ) :
self . user . is_active = False
self . user . save ( )
super ( Ban , self ) . save ( * args , * * kwargs )
def __unicode__ ( self ) :
return self . user