summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-08-07 22:26:22 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-08-07 22:26:22 -0400
commit774ef8a673e7135ca0cd7f4f7c1099971aaa07ba (patch)
treea550b17303acce7fe1893b29780ad7d34aac36b2
parent72da3d527c454bd3e3fee4a94370c6b78289f938 (diff)
downloadaskbot-774ef8a673e7135ca0cd7f4f7c1099971aaa07ba.tar.gz
askbot-774ef8a673e7135ca0cd7f4f7c1099971aaa07ba.tar.bz2
askbot-774ef8a673e7135ca0cd7f4f7c1099971aaa07ba.zip
put reopening questions under moderation rules
-rw-r--r--askbot/auth.py9
-rw-r--r--askbot/bin/test_permission_assertions9
-rw-r--r--askbot/models/__init__.py103
-rw-r--r--askbot/skins/default/templates/reopen.html26
-rw-r--r--askbot/templatetags/extra_filters.py9
-rw-r--r--askbot/tests/permission_assertion_tests.py72
-rw-r--r--askbot/views/commands.py34
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" />&nbsp;
<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