diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-08-07 22:26:22 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2010-08-07 22:26:22 -0400 |
commit | 774ef8a673e7135ca0cd7f4f7c1099971aaa07ba (patch) | |
tree | a550b17303acce7fe1893b29780ad7d34aac36b2 | |
parent | 72da3d527c454bd3e3fee4a94370c6b78289f938 (diff) | |
download | askbot-774ef8a673e7135ca0cd7f4f7c1099971aaa07ba.tar.gz askbot-774ef8a673e7135ca0cd7f4f7c1099971aaa07ba.tar.bz2 askbot-774ef8a673e7135ca0cd7f4f7c1099971aaa07ba.zip |
put reopening questions under moderation rules
-rw-r--r-- | askbot/auth.py | 9 | ||||
-rw-r--r-- | askbot/bin/test_permission_assertions | 9 | ||||
-rw-r--r-- | askbot/models/__init__.py | 103 | ||||
-rw-r--r-- | askbot/skins/default/templates/reopen.html | 26 | ||||
-rw-r--r-- | askbot/templatetags/extra_filters.py | 9 | ||||
-rw-r--r-- | askbot/tests/permission_assertion_tests.py | 72 | ||||
-rw-r--r-- | askbot/views/commands.py | 34 |
7 files changed, 216 insertions, 46 deletions
diff --git a/askbot/auth.py b/askbot/auth.py index 3e24d113..33caed13 100644 --- a/askbot/auth.py +++ b/askbot/auth.py @@ -71,15 +71,6 @@ def can_accept_answer(user, question, answer): return True return False -# now only support to reopen own question except superuser -def can_reopen_question(user, question): - if user.is_superuser: - return True - if user.is_authenticated() and user.id == question.author_id: - if user.reputation >= askbot_settings.MIN_REP_TO_REOPEN_OWN_QUESTIONS: - return True - return False - def can_view_deleted_post(user, post): return user.is_superuser diff --git a/askbot/bin/test_permission_assertions b/askbot/bin/test_permission_assertions new file mode 100644 index 00000000..f690936e --- /dev/null +++ b/askbot/bin/test_permission_assertions @@ -0,0 +1,9 @@ +python manage.py test askbot.SeeOffensiveFlagsPermissionAssertionTests \ + askbot.DeleteAnswerPermissionAssertionTests \ + askbot.DeleteQuestionPermissionAssertionTests \ + askbot.CloseQuestionPermissionAssertionTests \ + askbot.ReopenQuestionPermissionAssertionTests \ + askbot.FlagOffensivePermissionAssertionTests \ + askbot.CommentPermissionAssertionTests \ + askbot.VotePermissionAssertionTests \ + askbot.UploadPermissionAssertionTests diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index f16fa744..cc68939f 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -98,11 +98,16 @@ def user_get_old_vote_for_post(self, post): def _assert_user_can( user = None, post = None, #related post (may be parent) + admin_or_moderator_required = False, owner_can = False, + suspended_owner_cannot = False, + owner_min_rep_setting = None, blocked_error_message = None, suspended_error_message = None, min_rep_setting = None, low_rep_error_message = None, + owner_low_rep_error_message = None, + general_error_message = None ): """generic helper assert for use in several User.assert_can_XYZ() calls regarding changing content @@ -115,16 +120,39 @@ def _assert_user_can( if blocked_error_message and user.is_blocked(): error_message = blocked_error_message elif post and owner_can and user == post.get_owner(): + if owner_min_rep_setting: + if post.get_owner().reputation < owner_min_rep_setting: + if user.is_moderator() or user.is_administrator(): + return + else: + assert(owner_low_rep_error_message is not None) + raise askbot_exceptions.InsufficientReputation( + owner_low_rep_error_message + ) + if suspended_owner_cannot and user.is_suspended(): + if suspended_error_message: + error_message = suspended_error_message + else: + error_message = general_error_message + assert(error_message is not None) + raise django_exceptions.PermissionDenied(error_message) + else: + return return elif suspended_error_message and user.is_suspended(): error_message = suspended_error_message elif user.is_administrator() or user.is_moderator(): return elif low_rep_error_message and user.reputation < min_rep_setting: - error_message = low_rep_error_message % {'min_rep': min_rep_setting} - raise askbot_exceptions.InsufficientReputation(error_message) + raise askbot_exceptions.InsufficientReputation(low_rep_error_message) else: - return + if admin_or_moderator_required == False: + return + + #if admin or moderator is required, then substitute the message + if admin_or_moderator_required: + error_message = general_error_message + assert(error_message is not None) raise django_exceptions.PermissionDenied(error_message) @@ -226,7 +254,8 @@ def user_assert_can_post_comment(self, parent_post = None): ) low_rep_error_message = _( 'Sorry, to comment any post a minimum reputation of ' + \ - '%(min_rep)s points is required.' + '%(min_rep)s points is required. You can still comment ' +\ + 'your own posts and answers to your questions' ) % {'min_rep': askbot_settings.MIN_REP_TO_LEAVE_COMMENTS} try: @@ -369,26 +398,53 @@ def user_assert_can_close_question(self, question = None): {'min_rep': askbot_settings.MIN_REP_TO_CLOSE_OTHERS_QUESTIONS} min_rep_setting = askbot_settings.MIN_REP_TO_CLOSE_OTHERS_QUESTIONS + owner_min_rep_setting = askbot_settings.MIN_REP_TO_CLOSE_OWN_QUESTIONS + + owner_low_rep_error_message = _( + 'Sorry, to close own question ' + \ + 'a minimum reputation of %(min_rep)s is required' + ) % {'min_rep': owner_min_rep_setting} + _assert_user_can( user = self, post = question, owner_can = True, + suspended_owner_cannot = True, + owner_min_rep_setting = owner_min_rep_setting, blocked_error_message = blocked_error_message, suspended_error_message = suspended_error_message, low_rep_error_message = low_rep_error_message, + owner_low_rep_error_message = owner_low_rep_error_message, min_rep_setting = min_rep_setting ) - #exception - owner needs some rep to do this - if self == question.get_owner(): - if self.is_suspended(): - raise django_exceptions.PermissionDenied(suspended_error_message) - if self.reputation < askbot_settings.MIN_REP_TO_CLOSE_OWN_QUESTIONS: - error_message = _( - 'Sorry, to close own question ' +\ - 'minimum reputation of %(min_rep)s is required' - ) - raise askbot_exceptions.InsufficientReputation(error_message) + +def user_assert_can_reopen_question(self, question = None): + assert(isinstance(question, Question) == True) + + owner_min_rep_setting = askbot_settings.MIN_REP_TO_REOPEN_OWN_QUESTIONS + + general_error_message = _( + 'Sorry, only administrators, moderators ' + \ + 'or post owners with reputation > %(min_rep)s ' + \ + 'can reopen questions.' + ) % {'min_rep': owner_min_rep_setting } + + owner_low_rep_error_message = _( + 'Sorry, to reopen own question ' + \ + 'a minimum reputation of %(min_rep)s is required' + ) % {'min_rep': owner_min_rep_setting} + + _assert_user_can( + user = self, + post = question, + admin_or_moderator_required = True, + owner_can = True, + suspended_owner_cannot = True, + owner_min_rep_setting = owner_min_rep_setting, + owner_low_rep_error_message = owner_low_rep_error_message, + general_error_message = general_error_message + ) def user_assert_can_flag_offensive(self, post = None): @@ -566,18 +622,33 @@ def user_delete_question( #todo - move onDeleted method to a fitting class auth.onDeleted(question, self, timestamp = timestamp) +@auto_now_timestamp def user_close_question( self, question = None, reason = None, + timestamp = None ): self.assert_can_close_question(question) question.closed = True question.closed_by = self - question.closed_at = datetime.datetime.now() + question.closed_at = timestamp question.close_reason = reason question.save() +@auto_now_timestamp +def user_reopen_question( + self, + question = None, + timestamp = None + ): + self.assert_can_reopen_question(question) + question.closed = False + question.closed_by = self + question.closed_at = timestamp + question.close_reason = None + question.save() + def user_delete_post( self, post = None, @@ -1093,6 +1164,7 @@ User.add_to_class('delete_question', user_delete_question) User.add_to_class('delete_answer', user_delete_answer) User.add_to_class('restore_post', user_restore_post) User.add_to_class('close_question', user_close_question) +User.add_to_class('reopen_question', user_reopen_question) #assertions User.add_to_class('assert_can_vote_for_post', user_assert_can_vote_for_post) @@ -1103,6 +1175,7 @@ User.add_to_class('assert_can_post_answer', user_assert_can_post_answer) User.add_to_class('assert_can_post_comment', user_assert_can_post_comment) User.add_to_class('assert_can_edit_post', user_assert_can_edit_post) User.add_to_class('assert_can_close_question', user_assert_can_close_question) +User.add_to_class('assert_can_reopen_question', user_assert_can_reopen_question) User.add_to_class('assert_can_flag_offensive', user_assert_can_flag_offensive) User.add_to_class('assert_can_retag_questions', user_assert_can_retag_questions) #todo: do we need assert_can_delete_post diff --git a/askbot/skins/default/templates/reopen.html b/askbot/skins/default/templates/reopen.html index f60dd507..75bc026a 100644 --- a/askbot/skins/default/templates/reopen.html +++ b/askbot/skins/default/templates/reopen.html @@ -13,18 +13,28 @@ {% endblock %} {% block content %} <div id="main-bar" class="headNormal"> - {% trans "Reopen question" %} + {% trans "Reopen question" %} </div> <div id="main-body" style="width:100%"> - <p>{% trans "Open the previously closed question" %}: <a href="{{ question.get_absolute_url }}"><span class="big">{{ question.get_question_title }}</span></a> - - </p> - <p><strong>{% trans "The question was closed for the following reason " %}"{{ question.get_close_reason_display }}"{% trans "reason - leave blank in english" %} <a href="{{ question.closed_by.get_profile_url }}">{{ question.closed_by.username }}</a> {% trans "on "%} {% diff_date question.closed_at %}<font class="darkred">{% trans "date closed" %}</font> - </strong> + <p>{% trans "Title" %}: + <a href="{{ question.get_absolute_url }}"> + <span class="big">{{ question.get_question_title }}</span> + </a> + </p> + <p>{% blocktrans %}This question has been closed by + <a href="{{closed_by_profile_url}}">{{closed_by_username}}</a> + {% endblocktrans %} + </p> + <p> + {% trans "Close reason:" %} "<strong>{{question.get_close_reason_display}}</strong>". + </p> + <p> + {% trans "When:" %} {% diff_date question.closed_at %} + </p> + <p> + {% trans "Reopen this question?" %} </p> - <form id="fmclose" action="{% url reopen question.id %}" method="post" > - <div id="" style="padding:20px 0 20px 0"> <input type="submit" value="{% trans "Reopen this question" %}" class="submit" /> <input id="btBack" type="button" value="{% trans "Cancel" %}" class="submit" /> diff --git a/askbot/templatetags/extra_filters.py b/askbot/templatetags/extra_filters.py index 6c52be64..6cdd87e3 100644 --- a/askbot/templatetags/extra_filters.py +++ b/askbot/templatetags/extra_filters.py @@ -84,6 +84,11 @@ can_delete_post = make_template_filter_from_permission_assertion( filter_name = 'can_delete_post' ) +can_reopen_question = make_template_filter_from_permission_assertion( + assertion_name = 'assert_can_reopen_question', + filter_name = 'can_reopen_question' + ) + @register.filter def can_retag_questions(user): return auth.can_retag_questions(user) @@ -125,10 +130,6 @@ def can_accept_answer(user, question, answer): return auth.can_accept_answer(user, question, answer) @register.filter -def can_reopen_question(user, question): - return auth.can_reopen_question(user, question) - -@register.filter def can_view_user_edit(request_user, target_user): return auth.can_view_user_edit(request_user, target_user) diff --git a/askbot/tests/permission_assertion_tests.py b/askbot/tests/permission_assertion_tests.py index 171d30c3..d4c34eb6 100644 --- a/askbot/tests/permission_assertion_tests.py +++ b/askbot/tests/permission_assertion_tests.py @@ -400,6 +400,78 @@ class CloseQuestionPermissionAssertionTests(utils.AskbotTestCase): self.assert_cannot_close(user = self.user) +class ReopenQuestionPermissionAssertionTests(utils.AskbotTestCase): + """rules to reo + user = self, + post = question, + admin_or_moderator_required = True, + owner_can = True, + owner_min_rep_setting = owner_min_rep_setting, + owner_low_rep_error_message = owner_low_rep_error_message, + general_error_message = general_error_message + """ + + def setUp(self): + self.min_rep = askbot_settings.MIN_REP_TO_REOPEN_OWN_QUESTIONS + self.create_user() + self.create_user(username = 'other_user') + self.question = self.post_question() + self.user.set_status('m') + self.user.close_question(self.question) + self.user.set_status('a') + + def assert_can_reopen(self, user = None): + if user == None: + user = self.user + + user.assert_can_reopen_question(self.question) + + def assert_cannot_reopen(self, user = None): + if user == None: + user = self.user + + self.assertRaises( + exceptions.PermissionDenied, + user.assert_can_reopen_question, + question = self.question + ) + + + def test_high_rep_nonowner_cannot_reopen(self): + self.other_user.reputation = 1000000 + self.assert_cannot_reopen(user = self.other_user) + + def test_low_rep_admin_can_reopen(self): + self.other_user.is_superuser = True + self.assert_can_reopen(user = self.other_user) + + def test_low_rep_moderator_can_reopen(self): + self.other_user.set_status('m') + self.assert_can_reopen(user = self.other_user) + + def test_low_rep_owner_cannot_reopen(self): + self.assert_cannot_reopen() + + def test_high_rep_owner_can_reopen(self): + self.user.reputation = self.min_rep + self.assert_can_reopen() + + def test_high_rep_suspended_owner_cannot_reopen(self): + self.user.reputation = self.min_rep + self.user.set_status('s') + self.assert_cannot_reopen() + + def test_high_rep_blocked_cannot_reopen(self): + self.other_user.reputation = self.min_rep + self.other_user.set_status('b') + self.assert_cannot_reopen(user = self.other_user) + + def test_high_rep_suspended_cannot_reopen(self): + self.other_user.reputation = self.min_rep + self.other_user.set_status('s') + self.assert_cannot_reopen(user = self.other_user) + + class FlagOffensivePermissionAssertionTests(PermissionAssertionTestCase): def extraSetUp(self): diff --git a/askbot/views/commands.py b/askbot/views/commands.py index d3b6dc15..ac334a7f 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -16,12 +16,13 @@ from django.http import HttpResponseForbidden from django.shortcuts import get_object_or_404, render_to_response from django.utils.translation import ugettext as _ from django.template import RequestContext -from askbot.models import * +from askbot.models import * #todo: clean this up from askbot.forms import CloseForm from askbot import auth from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required from askbot.utils.decorators import ajax_method, ajax_login_required +from askbot.templatetags import extra_filters as template_filters import logging def process_vote(user = None, vote_direction = None, post = None): @@ -352,19 +353,32 @@ def close(request, id):#close question def reopen(request, id):#re-open question """view to initiate and process question close + + this is not an ajax view """ + question = get_object_or_404(Question, id=id) # open question - if not auth.can_reopen_question(request.user, question): - return HttpResponse('Permission denied.') - if request.method == 'POST' : - Question.objects.filter(id=question.id).update(closed=False, - closed_by=None, closed_at=None, close_reason=None) + try: + if request.method == 'POST' : + request.user.reopen_question(question) + return HttpResponseRedirect(question.get_absolute_url()) + else: + request.user.assert_can_reopen_question(question) + closed_by_profile_url = question.closed_by.get_profile_url() + closed_by_username = question.closed_by.username + return render_to_response( + 'reopen.html', + { + 'question' : question, + 'closed_by_profile_url': closed_by_profile_url, + 'closed_by_username': closed_by_username, + }, + context_instance=RequestContext(request) + ) + except exceptions.PermissionDenied, e: + request.user.message_set.create(message = str(e)) return HttpResponseRedirect(question.get_absolute_url()) - else: - return render_to_response('reopen.html', { - 'question' : question, - }, context_instance=RequestContext(request)) #askbot-user communication system def read_message(request):#marks message a read |