changeset 75:374b24dd2f9a

First checkin of forums. Have noticed cascading delete behavior. Will try to prevent this next.
author Brian Neal <bgneal@gmail.com>
date Sun, 05 Jul 2009 00:03:40 +0000
parents df56795771a6
children eecbd00e092a
files gpp/forums/__init__.py gpp/forums/admin.py gpp/forums/models.py gpp/forums/signals.py gpp/forums/views.py gpp/settings.py gpp/templates/forums/post.html
diffstat 7 files changed, 227 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /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
--- /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)
--- /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
--- /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)
--- /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.
--- 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',
--- /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" }}