# HG changeset patch # User Brian Neal # Date 1253590599 0 # Node ID e94398f5e027a39db8125aace8975f319a014d0a # Parent cb72577785dfe4e2e4aace8fc57bc6f39a765912 Forums: implemented post delete feature. diff -r cb72577785df -r e94398f5e027 gpp/forums/models.py --- a/gpp/forums/models.py Sat Sep 19 21:49:56 2009 +0000 +++ b/gpp/forums/models.py Tue Sep 22 03:36:39 2009 +0000 @@ -108,6 +108,18 @@ else: self.last_post = None + def last_post_pre_delete(self): + """ + Call this function prior to deleting the last post in the forum. + A new last post will be found, if one exists. + This is to avoid the Django cascading delete issue. + """ + try: + self.last_post = \ + Post.objects.filter(topic__forum=self).exclude(pk=self.last_post.pk).latest() + except Post.DoesNotExist: + self.last_post = None + class Topic(models.Model): """ @@ -168,6 +180,18 @@ super(Topic, self).save(*args, **kwargs) + def last_post_pre_delete(self): + """ + Call this function prior to deleting the last post in the topic. + A new last post will be found, if one exists. + This is to avoid the Django cascading delete issue. + """ + try: + self.last_post = \ + Post.objects.filter(topic=self).exclude(pk=self.last_post.pk).latest() + except Post.DoesNotExist: + self.last_post = None + class Post(models.Model): """ @@ -183,6 +207,7 @@ class Meta: ordering = ('creation_date', ) + get_latest_by = 'creation_date' @models.permalink def get_absolute_url(self): diff -r cb72577785df -r e94398f5e027 gpp/forums/urls.py --- a/gpp/forums/urls.py Sat Sep 19 21:49:56 2009 +0000 +++ b/gpp/forums/urls.py Tue Sep 22 03:36:39 2009 +0000 @@ -7,6 +7,7 @@ url(r'^$', 'index', name='forums-index'), url(r'^new-topic-success/(?P\d+)$', 'new_topic_thanks', name='forums-new_topic_thanks'), url(r'^topic/(?P\d+)/$', 'topic_index', name='forums-topic_index'), + url(r'^delete-post/$', 'delete_post', name='forums-delete_post'), url(r'^edit/(?P\d+)/$', 'edit_post', name='forums-edit_post'), url(r'^flag-post/$', 'flag_post', name='forums-flag_post'), url(r'^forum/(?P[\w\d-]+)/$', 'forum_index', name='forums-forum_index'), diff -r cb72577785df -r e94398f5e027 gpp/forums/views.py --- a/gpp/forums/views.py Sat Sep 19 21:49:56 2009 +0000 +++ b/gpp/forums/views.py Tue Sep 22 03:36:39 2009 +0000 @@ -67,7 +67,7 @@ if not forum.category.can_access(request.user): return HttpResponseForbidden() - topics = forum.topics.select_related('last_post', 'last_post__user') + topics = forum.topics.select_related('user', 'last_post', 'last_post__user') paginator = create_topic_paginator(topics) page_num = int(request.GET.get('page', 1)) try: @@ -267,3 +267,79 @@ 'can_moderate': True, }, context_instance=RequestContext(request)) + + +@require_POST +def delete_post(request): + """ + This view function allows superusers and forum moderators to delete posts. + This function is the target of AJAX calls from the client. + """ + if not request.user.is_authenticated(): + return HttpResponseForbidden('Please login to delete a post.') + + id = request.POST.get('id') + if id is None: + return HttpResponseBadRequest('No post id') + + post = get_object_or_404(Post.objects.select_related(), pk=id) + + can_delete = request.user.is_superuser or \ + request.user in post.topic.forum.moderators.all() + + if not can_delete: + return HttpResponseForbidden("You don't have permission to delete that post.") + + if post.topic.post_count == 1 and post == post.topic.last_post: + _delete_topic(post.topic) + else: + _delete_post(post) + + return HttpResponse("The post has been deleted.") + + +def _delete_post(post): + """ + Internal function to delete a single post object. + Decrements the post author's post count. + Adjusts the parent topic and forum's last_post as needed. + """ + # Adjust post creator's post count + profile = post.user.get_profile() + if profile.forum_post_count > 0: + profile.forum_post_count -= 1 + profile.save() + + # If this post is the last_post in a topic, we need to update + # both the topic and parent forum's last post fields. If we don't + # the cascading delete will delete them also! + + topic = post.topic + if topic.last_post == post: + topic.last_post_pre_delete() + topic.save() + + forum = topic.forum + if forum.last_post == post: + forum.last_post_pre_delete() + forum.save() + + # Should be safe to delete the post now: + post.delete() + + +def _delete_topic(topic): + """ + Internal function to delete an entire topic. + Deletes the topic and all posts contained within. + Adjusts the parent forum's last_post as needed. + Note that we don't bother adjusting all the users' + post counts as that doesn't seem to be worth the effort. + """ + if topic.forum.last_post.topic == topic: + topic.forum.last_post_pre_delete() + topic.forum.save() + + # It should be safe to just delete the topic now. This will + # automatically delete all posts in the topic. + topic.delete() diff -r cb72577785df -r e94398f5e027 gpp/templates/forums/display_post.html --- a/gpp/templates/forums/display_post.html Sat Sep 19 21:49:56 2009 +0000 +++ b/gpp/templates/forums/display_post.html Tue Sep 22 03:36:39 2009 +0000 @@ -1,6 +1,6 @@ {% load avatar_tags %} {% load forum_tags %} - + {{ post.user.username }}
@@ -28,7 +28,8 @@ title="Flag this post as spam, abuse, or a violation of site rules."> Flag {% if can_moderate %} - Delete post + Delete post {% endif %} diff -r cb72577785df -r e94398f5e027 gpp/templates/forums/topic.html --- a/gpp/templates/forums/topic.html Sat Sep 19 21:49:56 2009 +0000 +++ b/gpp/templates/forums/topic.html Tue Sep 22 03:36:39 2009 +0000 @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block title %}Forums: {{ topic.name }}{% endblock %} -{% block custom_js %}{% if last_page %}{{ form.media }}{% endif %}{% endblock %} +{% block custom_js %}{{ form.media }}{% endblock %} {% block content %}

Forums: {{ topic.name }}

diff -r cb72577785df -r e94398f5e027 media/js/forums.js --- a/media/js/forums.js Sat Sep 19 21:49:56 2009 +0000 +++ b/media/js/forums.js Tue Sep 22 03:36:39 2009 +0000 @@ -33,7 +33,7 @@ }); $('a.post-flag').click(function () { var id = this.id; - if (id.match(/fp-(\d)/)) { + if (id.match(/fp-(\d+)/)) { id = RegExp.$1; if (confirm('Only flag a post if you feel it is spam, abuse, violates site rules, ' + 'or is not appropriate. ' + @@ -55,5 +55,27 @@ } return false; }); + $('a.post-delete').click(function () { + var id = this.id; + if (id.match(/dp-(\d+)/)) { + id = RegExp.$1; + if (confirm('Are you sure you want to delete this post?')) { + $.ajax({ + url: '/forums/delete-post/', + type: 'POST', + data: {id: id}, + dataType: 'text', + success: function (response, textStatus) { + alert(response); + $('#post-' + id).fadeOut(3000); + }, + error: function (xhr, textStatus, ex) { + alert('Oops, an error occurred: ' + xhr.statusText + ' - ' + xhr.responseText); + } + }); + } + } + return false; + }); $('#id_body').markItUp(mySettings); });