From: Matthijs Kooijman Date: Wed, 29 Oct 2008 10:31:19 +0000 (+0100) Subject: Improve influence comment handling. X-Git-Url: https://git.stderr.nl/gitweb?a=commitdiff_plain;h=435a62554852c2863d2247c04df4342e002576fc;p=matthijs%2Fprojects%2Fxerxes.git Improve influence comment handling. This adds a number of form classes for commenting, which allow to choose between public or private comments and can prevent the choice of markup. Using a wrapper function, the right class can be selected depending on the logged in user and the comment to reply to. Each comment to display is annotated with the reply form to reply to it, so we no longer need changes in threaded_comments to support quoted replies. Additionally, a new view is added that handles the selection of the right form class when saving a comment. --- diff --git a/influences/views.py b/influences/views.py index 2dbd5c9..ca1a1e1 100644 --- a/influences/views.py +++ b/influences/views.py @@ -1,19 +1,70 @@ +from django import forms from django.contrib.auth.decorators import login_required from django.shortcuts import render_to_response from django.shortcuts import get_object_or_404 from django.template import RequestContext from django.utils.translation import ugettext as _ from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect, HttpResponseForbidden from django.views.generic.list_detail import object_detail, object_list from threadedcomments.models import ThreadedComment from threadedcomments.forms import ThreadedCommentForm +from threadedcomments.views import comment from xerxes.influences.models import Character from xerxes.influences.models import Influence from xerxes.tools.forms import ContextModelForm - +# +# A few comment form classes, to handle the various cases (staff/no staff, +# reply to public/private post) +# +# It is probably possible to reduce this mess a bit using metaclasses, but I +# didn't get this to work yet. +# +class InfluenceCommentForm(ThreadedCommentForm): + def __init__(self, *args, **kwargs): + super(InfluenceCommentForm, self).__init__(*args, **kwargs) + + class Meta(ThreadedCommentForm.Meta): + exclude = ('markup', ) + +class AdminInfluenceCommentForm(ThreadedCommentForm): + def __init__(self, *args, **kwargs): + super(AdminInfluenceCommentForm, self).__init__(*args, **kwargs) + + class Meta(ThreadedCommentForm.Meta): + fields = ThreadedCommentForm.Meta.fields + ('is_public',) + +class AdminPrivateInfluenceCommentForm(ThreadedCommentForm): + def __init__(self, *args, **kwargs): + super(AdminPrivateInfluenceCommentForm, self).__init__(*args, **kwargs) + self.instance.is_public = False + +def get_influence_comment_form(is_staff, reply_to): + """ Gets the form class that a user can use to reply to the given comment. + reply_to can be None, in which case the form class for a new comment is + returned. """ + allow_markup = allow_private = is_staff + if reply_to: + allow_public = reply_to.is_public + else: + allow_public = True + return _get_influence_comment_form(allow_markup, allow_public, allow_private) + +def _get_influence_comment_form(allow_markup, allow_public, allow_private): + """ Internal wrapper that selects the right form class depending on the + given options. Should not be called directly, call + get_influence_comment_form instead. """ + if allow_markup and allow_public and allow_private: + return AdminInfluenceCommentForm + elif allow_markup and not allow_public and allow_private: + return AdminPrivateInfluenceCommentForm + elif not allow_markup and allow_public and not allow_private: + return InfluenceCommentForm + else: + raise Exception("Unsupported configuration") class InfluenceForm(ContextModelForm): class Meta: @@ -101,6 +152,10 @@ def influence_list(request): @login_required def influence_detail(request, object_id): + + def quote_reply(comment): + return "\n".join(["> " + l for l in comment.comment.split("\n")]) + o = Influence.objects.get(pk=object_id) # Don't show other player's influences if (o.character.player != request.user): @@ -112,12 +167,30 @@ def influence_detail(request, object_id): comments = ThreadedComment.objects.get_tree(o) else: comments = ThreadedComment.public.get_tree(o) + + # Annotate each comment with a proper reply form + for comment in comments: + initial = { 'comment' : quote_reply(comment) } + comment.reply_form = get_influence_comment_form(request.user.is_staff, comment)(initial=initial) context = { 'object' : o, 'comments' : comments, - 'comment_form' : ThreadedCommentForm(), + 'comment_form' : get_influence_comment_form(request.user.is_staff, None) } return render_to_response('influences/influence_detail.html', context, RequestContext(request)) +@login_required +def influence_comment(request, edit_id=None, *args, **kwargs): + # Add the content_type, since we don't put in in the url explicitly + kwargs['content_type'] = ContentType.objects.get_for_model(Influence).id + # Find the comment to which we're replying, so we can get the right form for it. + if edit_id: + reply_to = get_object_or_404(ThreadedComment, id=edit_id) + else: + reply_to = None + # Find the right form class + kwargs['form_class'] = get_influence_comment_form(request.user.is_staff, reply_to) + return comment(request, edit_id=edit_id, *args, **kwargs) + # vim: set sts=4 sw=4 expandtab: diff --git a/templates/influences/influence_detail_block.html b/templates/influences/influence_detail_block.html index ea6603d..d60ac14 100644 --- a/templates/influences/influence_detail_block.html +++ b/templates/influences/influence_detail_block.html @@ -33,10 +33,10 @@

-
+

{% trans "Cancel reply" %}

- {{ comment.get_reply_form.as_table }} + {{ comment.reply_form.as_table }}
@@ -51,7 +51,7 @@

{% trans "Add comment" %}

-
+

Cancel comment

{{ comment_form.as_table }} diff --git a/urls.py b/urls.py index 1b4e400..6265def 100644 --- a/urls.py +++ b/urls.py @@ -16,6 +16,8 @@ urlpatterns = patterns('', url(r'^influences/$', 'xerxes.influences.views.index', name='influences_index'), url(r'^influences/influence/$', 'xerxes.influences.views.influence_list', name='influences_influence_list'), url(r'^influences/influence/(?P\d+)/$', 'xerxes.influences.views.influence_detail', name='influences_influence_detail'), + url(r'^influences/influence/(?P\d+)/comment/$', 'xerxes.influences.views.influence_comment', name='influences_influence_comment'), + url(r'^influences/influence/(?P\d+)/comment/(?P\d+)/$', 'xerxes.influences.views.influence_comment', name='influences_influence_comment_parent'), url(r'^influences/influence/add/(\d+)/$', 'xerxes.influences.views.add_influence', name='influences_add_influence_for_character'), url(r'^influences/influence/add/$', 'xerxes.influences.views.add_influence', name='influences_add_influence'),