summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-08-07 00:14:28 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-08-07 00:14:28 -0400
commit72da3d527c454bd3e3fee4a94370c6b78289f938 (patch)
treea4a22224295a4d63fa8f25ae9dca25545e603c88
parentd99947cc63fdd650434e5dbfe0462cd8a2a28233 (diff)
downloadaskbot-72da3d527c454bd3e3fee4a94370c6b78289f938.tar.gz
askbot-72da3d527c454bd3e3fee4a94370c6b78289f938.tar.bz2
askbot-72da3d527c454bd3e3fee4a94370c6b78289f938.zip
moderation rules apply to post deletion, added new boolean setting to always show all UI functions
-rw-r--r--askbot/auth.py8
-rw-r--r--askbot/conf/skin_general_settings.py22
-rw-r--r--askbot/models/__init__.py75
-rwxr-xr-xaskbot/skins/default/media/js/com.cnprog.post.js20
-rw-r--r--askbot/templatetags/extra_filters.py32
-rw-r--r--askbot/tests/permission_assertion_tests.py150
-rw-r--r--askbot/views/commands.py36
7 files changed, 269 insertions, 74 deletions
diff --git a/askbot/auth.py b/askbot/auth.py
index fb1832ce..3e24d113 100644
--- a/askbot/auth.py
+++ b/askbot/auth.py
@@ -418,14 +418,12 @@ def onDeleteCanceled(post, user, timestamp=None):
post.deleted_by = None
post.deleted_at = None
post.save()
- logging.debug('now restoring something')
if isinstance(post, Answer):
- logging.debug(
- 'updated answer count on undelete, have %d' \
- % post.question.answer_count
- )
Question.objects.update_answer_count(post.question)
elif isinstance(post, Question):
+ #todo: make sure that these tags actually exist
+ #some may have since been deleted for good
+ #or merged into others
for tag in list(post.tags.all()):
if tag.used_count == 1 and tag.deleted:
tag.deleted = False
diff --git a/askbot/conf/skin_general_settings.py b/askbot/conf/skin_general_settings.py
index b96f3424..56102453 100644
--- a/askbot/conf/skin_general_settings.py
+++ b/askbot/conf/skin_general_settings.py
@@ -2,16 +2,33 @@
General skin settings
"""
from askbot.conf.settings_wrapper import settings
-from askbot.deps.livesettings import ConfigurationGroup, StringValue, IntegerValue
+from askbot.deps.livesettings import ConfigurationGroup
+from askbot.deps.livesettings import StringValue, IntegerValue, BooleanValue
from django.utils.translation import ugettext as _
from askbot.skins.utils import get_skin_choices
GENERAL_SKIN_SETTINGS = ConfigurationGroup(
'GENERAL_SKIN_SETTINGS',
- _('Skin: general settings'),
+ _('Skin and User Interface settings'),
)
settings.register(
+ BooleanValue(
+ GENERAL_SKIN_SETTINGS,
+ 'ALWAYS_SHOW_ALL_UI_FUNCTIONS',
+ default = False,
+ description = _('Show all UI functions to all users'),
+ help_text = _(
+ 'If checked, all forum functions '
+ 'will be shown to users, regardless of their '
+ 'reputation. However to use those functions, '
+ 'moderation rules, reputation and other limits '
+ 'will still apply.'
+ )
+ )
+)
+
+settings.register(
StringValue(
GENERAL_SKIN_SETTINGS,
'ASKBOT_DEFAULT_SKIN',
@@ -35,4 +52,3 @@ settings.register(
)
)
)
-
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index e4e1b2b9..f16fa744 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -8,6 +8,7 @@ from askbot.search.indexer import create_fulltext_indexes
from django.db.models import signals as django_signals
from django.template import loader, Context
from django.utils.translation import ugettext as _
+from django.utils.translation import ungettext
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
from django.utils.safestring import mark_safe
@@ -275,9 +276,56 @@ def user_assert_can_edit_post(self, post = None):
min_rep_setting = min_rep_setting
)
-
def user_assert_can_delete_post(self, post = None):
- #todo: move here advanced logic to authorize deletion
+ if isinstance(post, Question):
+ self.assert_can_delete_question(question = post)
+ elif isinstance(post, Answer):
+ self.assert_can_delete_answer(answer = post)
+ elif isinstance(post, Comment):
+ self.assert_can_delete_comment(comment = post)
+
+def user_assert_can_restore_post(self, post = None):
+ """can_restore_rule is the same as can_delete
+ """
+ self.assert_can_delete_post(post = post)
+
+def user_assert_can_delete_question(self, question = None):
+ """rules are the same as to delete answer,
+ except if question has answers already, when owner
+ cannot delete unless s/he is and adinistrator or moderator
+ """
+
+ #cheating here. can_delete_answer wants argument named
+ #"question", so the argument name is skipped
+ self.assert_can_delete_answer(question)
+ if self == question.get_owner():
+ #if there are answers by other people,
+ #then deny, unless user in admin or moderator
+ answer_count = question.answers.exclude(
+ author = self,
+ ).exclude(
+ score__lte = 0
+ ).count()
+
+ if answer_count > 0:
+ if self.is_administrator() or self.is_moderator():
+ return
+ else:
+ msg = ungettext(
+ 'Sorry, cannot delete your question since it ' + \
+ 'has an upvoted answer posted by someone else',
+ 'Sorry, cannot delete your question since it ' + \
+ 'has some upvoted answers posted by other users',
+ answer_count
+ )
+ raise django_exceptions.PermissionDenied(msg)
+
+
+def user_assert_can_delete_answer(self, answer = None):
+ """intentionally use "post" word in the messages
+ instead of "answer", because this logic also applies to
+ assert on deleting question (in addition to some special rules)
+ """
blocked_error_message = _(
'Sorry, since your account is blocked ' + \
'you cannot delete posts'
@@ -295,7 +343,7 @@ def user_assert_can_delete_post(self, post = None):
_assert_user_can(
user = self,
- post = post,
+ post = answer,
owner_can = True,
blocked_error_message = blocked_error_message,
suspended_error_message = suspended_error_message,
@@ -304,14 +352,6 @@ def user_assert_can_delete_post(self, post = None):
)
-def user_assert_can_delete_question(self, question = None):
- self.assert_can_delete_post(post = question)
-
-
-def user_assert_can_delete_answer(self, answer = None):
- self.assert_can_delete_post(post = answer)
-
-
def user_assert_can_close_question(self, question = None):
assert(isinstance(question, Question) == True)
blocked_error_message = _(
@@ -556,6 +596,17 @@ def user_delete_post(
else:
raise TypeError('either Comment, Question or Answer expected')
+def user_restore_post(
+ self,
+ post = None,
+ timestamp = None
+ ):
+ self.assert_can_restore_post(post)
+ if isinstance(post, Question) or isinstance(post, Answer):
+ auth.onDeleteCanceled(self, post, timestamp)
+ else:
+ raise NotImplementedError()
+
def user_post_question(
self,
title = None,
@@ -1040,6 +1091,7 @@ User.add_to_class('get_unused_votes_today', user_get_unused_votes_today)
User.add_to_class('delete_comment', user_delete_comment)
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)
#assertions
@@ -1055,6 +1107,7 @@ 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
User.add_to_class('assert_can_delete_post', user_assert_can_delete_post)
+User.add_to_class('assert_can_restore_post', user_assert_can_restore_post)
User.add_to_class('assert_can_delete_comment', user_assert_can_delete_comment)
User.add_to_class('assert_can_delete_answer', user_assert_can_delete_answer)
User.add_to_class('assert_can_delete_question', user_assert_can_delete_question)
diff --git a/askbot/skins/default/media/js/com.cnprog.post.js b/askbot/skins/default/media/js/com.cnprog.post.js
index b82be4ee..5ad07f5d 100755
--- a/askbot/skins/default/media/js/com.cnprog.post.js
+++ b/askbot/skins/default/media/js/com.cnprog.post.js
@@ -356,19 +356,7 @@ var Vote = function(){
};
var callback_remove = function(object, voteType, data){
- if (data.allowed == "0" && data.success == "0"){
- showMessage(
- object,
- removeAnonymousMessage.replace(
- "{{QuestionID}}",
- questionId
- ).replace(
- '{{questionSlug}}',
- ''
- )
- );
- }
- else if (data.success == "1"){
+ if (data.success == "1"){
if (voteType == VoteType.removeQuestion){
window.location.href = scriptUrl + $.i18n._("questions/");
}
@@ -384,7 +372,10 @@ var Vote = function(){
showMessage(object, recoveredMessage);
}
}
- }
+ }
+ else {
+ showMessage(object, data.message)
+ }
};
return {
@@ -721,6 +712,7 @@ function createComments(type) {
par.remove();
},
error: function(xhr, textStatus, exception) {
+ jImg.show();
showMessage(jImg, xhr.responseText);
},
dataType: "json"
diff --git a/askbot/templatetags/extra_filters.py b/askbot/templatetags/extra_filters.py
index 688c539e..6c52be64 100644
--- a/askbot/templatetags/extra_filters.py
+++ b/askbot/templatetags/extra_filters.py
@@ -24,6 +24,10 @@ def make_template_filter_from_permission_assertion(
permission assertion
"""
def filter_function(user, post):
+
+ if askbot_settings.ALWAYS_SHOW_ALL_UI_FUNCTIONS:
+ return True
+
if user.is_anonymous():
return False
@@ -64,14 +68,20 @@ can_post_comment = make_template_filter_from_permission_assertion(
filter_name = 'can_post_comment'
)
+can_close_question = make_template_filter_from_permission_assertion(
+ assertion_name = 'assert_can_close_question',
+ filter_name = 'can_close_question'
+ )
+
can_delete_comment = make_template_filter_from_permission_assertion(
assertion_name = 'assert_can_delete_comment',
filter_name = 'can_delete_comment'
)
-can_close_question = make_template_filter_from_permission_assertion(
- assertion_name = 'assert_can_close_question',
- filter_name = 'can_close_question'
+#this works for questions, answers and comments
+can_delete_post = make_template_filter_from_permission_assertion(
+ assertion_name = 'assert_can_delete_post',
+ filter_name = 'can_delete_post'
)
@register.filter
@@ -119,22 +129,6 @@ def can_reopen_question(user, question):
return auth.can_reopen_question(user, question)
@register.filter
-def can_delete_post(user, post):
- if user.is_anonymous():
- return False
- try:
- if isinstance(post, models.Question):
- user.assert_can_delete_question(question = post)
- return True
- elif isinstance(post, models.Answer):
- user.assert_can_delete_answer(answer = post)
- return True
- else:
- return False
- except django_exceptions.PermissionDenied:
- return False
-
-@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 884f3f0e..171d30c3 100644
--- a/askbot/tests/permission_assertion_tests.py
+++ b/askbot/tests/permission_assertion_tests.py
@@ -153,6 +153,156 @@ class SeeOffensiveFlagsPermissionAssertionTests(utils.AskbotTestCase):
)
)
+class DeleteAnswerPermissionAssertionTests(utils.AskbotTestCase):
+
+ def setUp(self):
+ self.create_user()
+ self.create_user(username = 'other_user')
+ self.question = self.post_question()
+ self.min_rep = askbot_settings.MIN_REP_TO_DELETE_OTHERS_POSTS
+
+ def post_answer(self, user = None):
+ if user is None:
+ user = self.user
+ self.answer = super(
+ DeleteAnswerPermissionAssertionTests,
+ self
+ ).post_answer(
+ question = self.question,
+ user = user
+ )
+
+ def assert_can_delete(self):
+ self.user.assert_can_delete_answer(self.answer)
+
+ def assert_cannot_delete(self):
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.assert_can_delete_answer,
+ answer = self.answer
+ )
+
+ def test_low_rep_user_cannot_delete(self):
+ self.post_answer(user = self.other_user)
+ assert(self.user.reputation < self.min_rep)
+ self.assert_cannot_delete()
+
+ def test_high_rep_user_can_delete(self):
+ self.post_answer(user = self.other_user)
+ self.user.reputation = self.min_rep
+ self.assert_can_delete()
+
+ def test_low_rep_owner_can_delete(self):
+ self.post_answer()
+ assert(self.user.reputation < self.min_rep)
+ self.assert_can_delete()
+
+ def test_suspended_owner_can_delete(self):
+ self.post_answer()
+ assert(self.user.reputation < self.min_rep)
+ self.user.set_status('s')
+ self.assert_can_delete()
+
+ def test_blocked_owner_cannot_delete(self):
+ self.post_answer()
+ assert(self.user.reputation < self.min_rep)
+ self.user.set_status('b')
+ self.assert_cannot_delete()
+
+ def test_blocked_user_cannot_delete(self):
+ self.post_answer(user = self.other_user)
+ self.user.set_status('b')
+ self.assert_cannot_delete()
+
+ def test_high_rep_blocked_owner_cannot_delete(self):
+ self.post_answer()
+ self.user.set_status('b')
+ self.user.reputation = 100000
+ self.assert_cannot_delete()
+
+ def test_low_rep_admin_can_delete(self):
+ self.post_answer(user = self.other_user)
+ self.user.is_superuser = True
+ assert(self.user.reputation < self.min_rep)
+ self.assert_can_delete()
+
+ def test_low_rep_moderator_can_delete(self):
+ self.post_answer(user = self.other_user)
+ self.user.set_status('m')
+ assert(self.user.reputation < self.min_rep)
+ self.assert_can_delete()
+
+class DeleteQuestionPermissionAssertionTests(utils.AskbotTestCase):
+ """These specifically test cases where user is
+ owner of the question
+
+ all other cases are the same as DeleteAnswer...
+ """
+
+ def setUp(self):
+ self.create_user()
+ self.create_user(username = 'other_user')
+ self.question = self.post_question()
+
+ def assert_can_delete(self):
+ self.user.assert_can_delete_question(
+ question = self.question
+ )
+
+ def assert_cannot_delete(self):
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.assert_can_delete_question,
+ question = self.question
+ )
+
+ def upvote_answer(self, answer = None, user = None):
+ if user is None:
+ user = self.user
+ user.reputation = askbot_settings.MIN_REP_TO_VOTE_UP
+ user.upvote(answer)
+
+ def test_owner_can_delete_question_with_nonvoted_answer_by_other(self):
+ self.post_answer(
+ user = self.other_user,
+ question = self.question
+ )
+ self.assert_can_delete()
+
+ def test_owner_can_delete_question_with_upvoted_answer_posted_by_self(self):
+ answer = self.post_answer(
+ user = self.user,
+ question = self.question
+ )
+ self.upvote_answer(
+ answer = answer,
+ user = self.other_user
+ )
+ self.assert_can_delete()
+
+ def test_owner_cannot_delete_question_with_upvoted_answer_posted_by_other(self):
+ answer = self.post_answer(
+ user = self.other_user,
+ question = self.question
+ )
+ self.upvote_answer(
+ answer = answer,
+ user = self.user
+ )
+ self.assert_cannot_delete()
+
+ def test_owner_can_delete_question_without_answers(self):
+ self.assert_can_delete()
+
+ def test_moderator_can_delete_question_with_upvoted_answer_by_other(self):
+ self.user.set_status('m')
+ answer = self.post_answer(
+ user = self.other_user,
+ question = self.question
+ )
+ self.user.upvote(answer)
+ self.assert_can_delete()
+
class CloseQuestionPermissionAssertionTests(utils.AskbotTestCase):
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 81a93be7..d3b6dc15 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -195,17 +195,28 @@ def vote(request, id):
elif vote_type in ['7', '8']:
#flag question or answer
if vote_type == '7':
- post_id = id
post = get_object_or_404(Question, id=id)
if vote_type == '8':
- post_id = request.POST.get('postId')
- post = get_object_or_404(Answer, id=post_id)
+ id = request.POST.get('postId')
+ post = get_object_or_404(Answer, id=id)
request.user.flag_post(post)
response_data['count'] = post.offensive_flag_count
response_data['success'] = 1
+ elif vote_type in ['9', '10']:
+ #delete question or answer
+ post = get_object_or_404(Question, id = id)
+ if vote_type == '10':
+ id = request.POST.get('postId')
+ post = get_object_or_404(Answer, id = id)
+
+ if post.deleted == True:
+ request.user.restore_post(post = post)
+ else:
+ request.user.delete_post(post = post)
+
elif request.is_ajax() and request.method == 'POST':
if not request.user.is_authenticated():
@@ -225,25 +236,6 @@ def vote(request, id):
if fave == False:
response_data['status'] = 1
- elif vote_type in ['9', '10']:
- #delete question or answer
- post = question
- post_id = id
- if vote_type == '10':
- post_id = request.POST.get('postId')
- post = get_object_or_404(Answer, id=post_id)
-
- if post.deleted == True:
- logging.debug('debug restoring post in view')
- auth.onDeleteCanceled(post, request.user)
- response_data['status'] = 1
- else:
- try:
- request.user.delete_post(post = post)
- except exceptions.PermissionDenied, e:
- response_data['allowed'] = -2
- request.user.message_set.create(message = str(e))
-
elif vote_type == '11':#subscribe q updates
user = request.user
if user.is_authenticated():