# HG changeset patch # User Brian Neal # Date 1246752220 0 # Node ID 374b24dd2f9a79cda256816da4cc56292860b52b # Parent df56795771a63416f608b13e7ef3b0cb3a80e94a First checkin of forums. Have noticed cascading delete behavior. Will try to prevent this next. diff -r df56795771a6 -r 374b24dd2f9a gpp/forums/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/__init__.py Sun Jul 05 00:03:40 2009 +0000 @@ -0,0 +1,1 @@ +import signals diff -r df56795771a6 -r 374b24dd2f9a gpp/forums/admin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/admin.py Sun Jul 05 00:03:40 2009 +0000 @@ -0,0 +1,42 @@ +""" +This file contains the admin definitions for the forums application. +""" +from django.contrib import admin + +from forums.models import Category +from forums.models import Forum +from forums.models import Topic +from forums.models import Post + + +class CategoryAdmin(admin.ModelAdmin): + list_display = ('name', 'position', ) + + +class ForumAdmin(admin.ModelAdmin): + list_display = ('name', 'category', 'position', 'topic_count', 'post_count') + prepopulated_fields = { 'slug': ('name', ) } + raw_id_fields = ('last_post', ) + +class TopicAdmin(admin.ModelAdmin): + list_display = ('name', 'forum', 'creation_date', 'user', 'sticky', 'locked', + 'post_count') + raw_id_fields = ('user', 'last_post', ) + search_fields = ('name', ) + date_hierarchy = 'creation_date' + list_filter = ('creation_date', 'update_date', ) + + +class PostAdmin(admin.ModelAdmin): + list_display = ('topic', 'user', 'creation_date', 'update_date', 'summary') + raw_id_fields = ('topic', 'user', ) + exclude = ('html', ) + search_fields = ('body', ) + date_hierarchy = 'creation_date' + list_filter = ('creation_date', 'update_date', ) + + +admin.site.register(Category, CategoryAdmin) +admin.site.register(Forum, ForumAdmin) +admin.site.register(Topic, TopicAdmin) +admin.site.register(Post, PostAdmin) diff -r df56795771a6 -r 374b24dd2f9a gpp/forums/models.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/models.py Sun Jul 05 00:03:40 2009 +0000 @@ -0,0 +1,128 @@ +""" +Models for the forums application. +""" +from django.db import models +from django.contrib.auth.models import User, Group +from django.template.loader import render_to_string + + +class Category(models.Model): + name = models.CharField(max_length=80) + slug = models.SlugField(max_length=80) + position = models.IntegerField(blank=True, default=0) + groups = models.ManyToManyField(Group, blank=True, null=True, + help_text="If groups are assigned to this category, only members" \ + " of those groups can view this category.") + + class Meta: + ordering = ('position', ) + verbose_name_plural = 'Categories' + + def __unicode__(self): + return self.name + + +class Forum(models.Model): + category = models.ForeignKey(Category, related_name='forums') + name = models.CharField(max_length=80) + slug = models.SlugField(max_length=80) + description = models.TextField(blank=True, default='') + position = models.IntegerField(blank=True, default=0) + moderators = models.ManyToManyField(Group, blank=True, null=True) + + # denormalized fields to reduce database hits + topic_count = models.IntegerField(blank=True, default=0) + post_count = models.IntegerField(blank=True, default=0) + last_post = models.OneToOneField('Post', blank=True, null=True, + related_name='parent_forum') + + class Meta: + ordering = ('position', ) + + def __unicode__(self): + return self.name + + def topic_count_update(self): + """Call to notify the forum that its topic count has been updated.""" + self.topic_count = Topic.objects.filter(forum=self).count() + + def post_count_update(self): + """Call to notify the forum that its post count has been updated.""" + my_posts = Post.objects.filter(topic__forum=self) + self.post_count = my_posts.count() + if self.post_count > 0: + self.last_post = my_posts[self.post_count - 1] + else: + self.last_post = None + + +class Topic(models.Model): + forum = models.ForeignKey(Forum, related_name='topics') + name = models.CharField(max_length=255) + creation_date = models.DateTimeField(auto_now_add=True) + user = models.ForeignKey(User) + view_count = models.IntegerField(blank=True, default=0) + sticky = models.BooleanField(blank=True, default=False) + locked = models.BooleanField(blank=True, default=False) + + # denormalized fields to reduce database hits + post_count = models.IntegerField(blank=True, default=0) + update_date = models.DateTimeField(auto_now=True) + last_post = models.OneToOneField('Post', blank=True, null=True, + related_name='parent_topic') + + class Meta: + ordering = ('-sticky', '-update_date', ) + + def __unicode__(self): + return self.name + + def post_count_update(self): + """ + Call this function to notify the topic instance that its post count + has changed. + """ + my_posts = Post.objects.filter(topic=self) + self.post_count = my_posts.count() + if self.post_count > 0: + self.last_post = my_posts[self.post_count - 1] + self.update_date = self.last_post.creation_date + else: + self.last_post = None + self.update_date = self.creation_date + + +class Post(models.Model): + topic = models.ForeignKey(Topic, related_name='posts') + user = models.ForeignKey(User, related_name='posts') + creation_date = models.DateTimeField(auto_now_add=True) + update_date = models.DateTimeField(auto_now=True) + body = models.TextField() + html = models.TextField() + user_ip = models.IPAddressField(blank=True, default='') + + class Meta: + ordering = ('creation_date', ) + + def summary(self): + LIMIT = 50 + if len(self.body) < LIMIT: + return self.body + return self.body[:LIMIT] + '...' + + def __unicode__(self): + return self.summary() + + def save(self, *args, **kwargs): + html = render_to_string('forums/post.html', {'data': self.body}) + self.html = html.strip() + super(Post, self).save(*args, **kwargs) + + def delete(self, *args, **kwargs): + first_post_id = self.topic.posts.all()[0].id + super(Post, self).delete(*args, **kwargs) + if self.id == first_post_id: + self.topic.delete() + +# TODO: A "read" table +# TODO: A flagged post table diff -r df56795771a6 -r 374b24dd2f9a gpp/forums/signals.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/signals.py Sun Jul 05 00:03:40 2009 +0000 @@ -0,0 +1,52 @@ +""" +Signal handlers for the forums application. +""" +from django.db.models.signals import post_save +from django.db.models.signals import post_delete +from models import Topic +from models import Post + + +def on_topic_save(sender, **kwargs): + if kwargs['created']: + topic = kwargs['instance'] + topic.forum.topic_count_update() + topic.forum.save() + + +def on_topic_delete(sender, **kwargs): + topic = kwargs['instance'] + topic.forum.topic_count_update() + topic.forum.save() + + +def on_post_save(sender, **kwargs): + if kwargs['created']: + post = kwargs['instance'] + + # update the topic + post.topic.post_count_update() + post.topic.save() + + # update the forum + post.topic.forum.post_count_update() + post.topic.forum.save() + + +def on_post_delete(sender, **kwargs): + post = kwargs['instance'] + + # update the topic + post.topic.post_count_update() + post.topic.save() + + # update the forum + post.topic.forum.post_count_update() + post.topic.forum.save() + + +post_save.connect(on_topic_save, sender=Topic) +post_delete.connect(on_topic_delete, sender=Topic) + +post_save.connect(on_post_save, sender=Post) +post_delete.connect(on_post_delete, sender=Post) diff -r df56795771a6 -r 374b24dd2f9a gpp/forums/views.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/views.py Sun Jul 05 00:03:40 2009 +0000 @@ -0,0 +1,1 @@ +# Create your views here. diff -r df56795771a6 -r 374b24dd2f9a gpp/settings.py --- a/gpp/settings.py Wed Jul 01 20:02:14 2009 +0000 +++ b/gpp/settings.py Sun Jul 05 00:03:40 2009 +0000 @@ -114,6 +114,7 @@ 'core', 'donations', 'downloads', + 'forums', 'gcalendar', 'irc', 'membermap', diff -r df56795771a6 -r 374b24dd2f9a gpp/templates/forums/post.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/templates/forums/post.html Sun Jul 05 00:03:40 2009 +0000 @@ -0,0 +1,2 @@ +{% load markup %} +{{ data|markdown:"safe" }}