bgneal@75: """
bgneal@75: Models for the forums application.
bgneal@75: """
bgneal@75: from django.db import models
bgneal@75: from django.contrib.auth.models import User, Group
bgneal@75: from django.template.loader import render_to_string
bgneal@75: 
bgneal@75: 
bgneal@75: class Category(models.Model):
bgneal@75:     name = models.CharField(max_length=80)
bgneal@75:     slug = models.SlugField(max_length=80)
bgneal@75:     position = models.IntegerField(blank=True, default=0)
bgneal@75:     groups = models.ManyToManyField(Group, blank=True, null=True,
bgneal@75:         help_text="If groups are assigned to this category, only members" \
bgneal@75:                     " of those groups can view this category.")
bgneal@75: 
bgneal@75:     class Meta:
bgneal@75:         ordering = ('position', )
bgneal@75:         verbose_name_plural = 'Categories'
bgneal@75: 
bgneal@75:     def __unicode__(self):
bgneal@75:         return self.name
bgneal@75: 
bgneal@75: 
bgneal@75: class Forum(models.Model):
bgneal@75:     category = models.ForeignKey(Category, related_name='forums')
bgneal@75:     name = models.CharField(max_length=80)
bgneal@75:     slug = models.SlugField(max_length=80)
bgneal@75:     description = models.TextField(blank=True, default='')
bgneal@75:     position = models.IntegerField(blank=True, default=0)
bgneal@75:     moderators = models.ManyToManyField(Group, blank=True, null=True)
bgneal@75: 
bgneal@75:     # denormalized fields to reduce database hits
bgneal@75:     topic_count = models.IntegerField(blank=True, default=0)
bgneal@75:     post_count = models.IntegerField(blank=True, default=0)
bgneal@75:     last_post = models.OneToOneField('Post', blank=True, null=True,
bgneal@75:         related_name='parent_forum')
bgneal@75: 
bgneal@75:     class Meta:
bgneal@75:         ordering = ('position', )
bgneal@75: 
bgneal@75:     def __unicode__(self):
bgneal@75:         return self.name
bgneal@75: 
bgneal@81:     @models.permalink
bgneal@81:     def get_absolute_url(self):
bgneal@81:         return ('forums-forum_index', [self.slug])
bgneal@81: 
bgneal@75:     def topic_count_update(self):
bgneal@75:         """Call to notify the forum that its topic count has been updated."""
bgneal@75:         self.topic_count = Topic.objects.filter(forum=self).count()
bgneal@75: 
bgneal@75:     def post_count_update(self):
bgneal@75:         """Call to notify the forum that its post count has been updated."""
bgneal@75:         my_posts = Post.objects.filter(topic__forum=self)
bgneal@75:         self.post_count = my_posts.count()
bgneal@75:         if self.post_count > 0:
bgneal@75:             self.last_post = my_posts[self.post_count - 1]
bgneal@75:         else:
bgneal@75:             self.last_post = None
bgneal@75: 
bgneal@75: 
bgneal@75: class Topic(models.Model):
bgneal@75:     forum = models.ForeignKey(Forum, related_name='topics')
bgneal@75:     name = models.CharField(max_length=255)
bgneal@75:     creation_date = models.DateTimeField(auto_now_add=True)
bgneal@75:     user = models.ForeignKey(User)
bgneal@75:     view_count = models.IntegerField(blank=True, default=0)
bgneal@75:     sticky = models.BooleanField(blank=True, default=False)
bgneal@75:     locked = models.BooleanField(blank=True, default=False)
bgneal@75: 
bgneal@75:     # denormalized fields to reduce database hits
bgneal@75:     post_count = models.IntegerField(blank=True, default=0)
bgneal@75:     update_date = models.DateTimeField(auto_now=True)
bgneal@75:     last_post = models.OneToOneField('Post', blank=True, null=True,
bgneal@75:         related_name='parent_topic')
bgneal@75: 
bgneal@75:     class Meta:
bgneal@75:         ordering = ('-sticky', '-update_date', )
bgneal@75: 
bgneal@75:     def __unicode__(self):
bgneal@75:         return self.name
bgneal@75: 
bgneal@82:     @models.permalink
bgneal@82:     def get_absolute_url(self):
bgneal@82:         return ('forums-topic_index', [self.pk])
bgneal@82: 
bgneal@75:     def post_count_update(self):
bgneal@75:         """
bgneal@75:         Call this function to notify the topic instance that its post count
bgneal@75:         has changed.
bgneal@75:         """
bgneal@75:         my_posts = Post.objects.filter(topic=self)
bgneal@75:         self.post_count = my_posts.count()
bgneal@75:         if self.post_count > 0:
bgneal@75:             self.last_post = my_posts[self.post_count - 1]
bgneal@75:             self.update_date = self.last_post.creation_date
bgneal@75:         else:
bgneal@75:             self.last_post = None
bgneal@75:             self.update_date = self.creation_date
bgneal@75: 
bgneal@83:     def reply_count(self):
bgneal@83:         """
bgneal@83:         Returns the number of replies to a topic. The first post
bgneal@83:         doesn't count as a reply.
bgneal@83:         """
bgneal@83:         if self.post_count > 1:
bgneal@83:             return self.post_count - 1
bgneal@83:         return 0
bgneal@83: 
bgneal@75: 
bgneal@75: class Post(models.Model):
bgneal@75:     topic = models.ForeignKey(Topic, related_name='posts')
bgneal@75:     user = models.ForeignKey(User, related_name='posts')
bgneal@75:     creation_date = models.DateTimeField(auto_now_add=True)
bgneal@75:     update_date = models.DateTimeField(auto_now=True)
bgneal@75:     body = models.TextField()
bgneal@75:     html = models.TextField()
bgneal@83:     user_ip = models.IPAddressField(blank=True, default='', null=True)
bgneal@75: 
bgneal@75:     class Meta:
bgneal@97:         ordering = ('creation_date', )
bgneal@75: 
bgneal@91:     @models.permalink
bgneal@91:     def get_absolute_url(self):
bgneal@91:         return ('forums-goto_post', [self.pk])
bgneal@91: 
bgneal@75:     def summary(self):
bgneal@75:         LIMIT = 50
bgneal@75:         if len(self.body) < LIMIT:
bgneal@75:             return self.body
bgneal@75:         return self.body[:LIMIT] + '...'
bgneal@75: 
bgneal@75:     def __unicode__(self):
bgneal@75:         return self.summary()
bgneal@75: 
bgneal@75:     def save(self, *args, **kwargs):
bgneal@75:         html = render_to_string('forums/post.html', {'data': self.body})
bgneal@75:         self.html = html.strip()
bgneal@75:         super(Post, self).save(*args, **kwargs)
bgneal@75: 
bgneal@75:     def delete(self, *args, **kwargs):
bgneal@75:         first_post_id = self.topic.posts.all()[0].id
bgneal@75:         super(Post, self).delete(*args, **kwargs)
bgneal@75:         if self.id == first_post_id:
bgneal@75:             self.topic.delete()
bgneal@75: 
bgneal@98: 
bgneal@98: class FlaggedPost(models.Model):
bgneal@98:     """This model represents a user flagging a post as inappropriate."""
bgneal@98:     user = models.ForeignKey(User)
bgneal@98:     post = models.ForeignKey(Post)
bgneal@98:     flag_date = models.DateTimeField(auto_now_add=True)
bgneal@98: 
bgneal@98:     def __unicode__(self):
bgneal@98:         return u'Post ID %s flagged by %s' % (self.post.id, self.user.username)
bgneal@98: 
bgneal@98:     class Meta:
bgneal@98:         ordering = ('flag_date', )
bgneal@98: 
bgneal@98:     def get_post_url(self):
bgneal@98:         return '<a href="%s">Post</a>' % self.post.get_absolute_url()
bgneal@98:     get_post_url.allow_tags = True
bgneal@98: 
bgneal@75: # TODO: A "read" table