Improve influence comment handling.
authorMatthijs Kooijman <matthijs@stdin.nl>
Wed, 29 Oct 2008 10:31:19 +0000 (11:31 +0100)
committerMatthijs Kooijman <matthijs@stdin.nl>
Wed, 29 Oct 2008 10:31:19 +0000 (11:31 +0100)
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.

influences/views.py
templates/influences/influence_detail_block.html
urls.py

index 2dbd5c990ec692fa3ccf99b454f74fbab3d32492..ca1a1e1e01baffe85c9d2766af6c6bc1b20affff 100644 (file)
@@ -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:
index ea6603d986bfc873da3a3d41134c9591521401e6..d60ac14c1a4a80eaac2c0393eded37dfa213134b 100644 (file)
                        </p>
                        <div id="replyForm{{comment.id}}" class="commentReplyForm">
                                <script type="text/javascript">hideReply('{{comment.id}}');</script>
-                               <form method="post" action="{% get_comment_url object comment %}">
+                               <form method="post" action="{% url influences_influence_comment_parent object.id,comment.id %}">
                                        <p><a href="javascript:hideReply('{{comment.id}}');">{% trans "Cancel reply" %}</a></p>
                                        <table>
-                                       {{ comment.get_reply_form.as_table }}
+                                       {{ comment.reply_form.as_table }}
                                        </table>
                                        <div><input type="submit" value="{% trans "Reply to this comment" %}" /></div>
                                </form>
@@ -51,7 +51,7 @@
 <div id="replyForm">
 <script type="text/javascript">hideReply('')</script>
 <h3>{% trans "Add comment" %}</h3>
-<form method="post" action="{% get_comment_url object %}">
+<form method="post" action="{% url influences_influence_comment object.id %}">
        <p><a href="javascript:hideReply('')">Cancel comment</a></p>
     <table>
     {{ comment_form.as_table }}
diff --git a/urls.py b/urls.py
index 1b4e4004d9086e3732085dbcd3bc60c54ae103ef..6265def1b10eaecf07718362f06c003a4992ef42 100644 (file)
--- 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<object_id>\d+)/$', 'xerxes.influences.views.influence_detail', name='influences_influence_detail'),
+    url(r'^influences/influence/(?P<object_id>\d+)/comment/$', 'xerxes.influences.views.influence_comment', name='influences_influence_comment'),
+    url(r'^influences/influence/(?P<object_id>\d+)/comment/(?P<parent_id>\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'),