From 2d32a08751e699456128489cd2606c4aa015796a Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Mon, 9 Aug 2010 22:08:29 -0400 Subject: accept answer function is now under moderation rules --- askbot/models/__init__.py | 60 +++++++++++++++++++++ askbot/models/answer.py | 1 + askbot/tests/db_api_tests.py | 20 ++++--- askbot/tests/permission_assertion_tests.py | 83 ++++++++++++++++++++++++++++++ askbot/views/commands.py | 27 ++-------- 5 files changed, 163 insertions(+), 28 deletions(-) diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 316e1a77..0b987ea0 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -155,6 +155,37 @@ def _assert_user_can( assert(error_message is not None) raise django_exceptions.PermissionDenied(error_message) +def user_assert_can_unaccept_best_answer(self, answer = None): + assert(isinstance(answer, Answer)) + if self.is_blocked(): + error_message = _( + 'Sorry, you cannot accept or unaccept best answers ' + \ + 'because your account is blocked' + ) + elif self.is_suspended(): + error_message = _( + 'Sorry, you cannot accept or unaccept best answers ' + \ + 'because your account is suspended' + ) + elif self == answer.question.get_owner(): + if self == answer.get_owner(): + error_message = _( + 'Sorry, you cannot accept or unaccept your own answer ' + \ + 'to your own question' + ) + else: + return #assertion success + else: + error_message = _( + 'Sorry, only original author of the question ' + \ + ' - %(username)s - can accept the best answer' + ) % {'username': answer.get_owner().username} + + raise django_exceptions.PermissionDenied(error_message) + +def user_assert_can_accept_best_answer(self, answer = None): + assert(isinstance(answer, Answer)) + self.assert_can_unaccept_best_answer(answer) def user_assert_can_vote_for_post( self, @@ -668,6 +699,26 @@ def user_retag_question( tagnames = tags, ) +@auto_now_timestamp +def user_accept_best_answer(self, answer = None, timestamp = None): + self.assert_can_accept_best_answer(answer) + if answer.accepted == True: + return + + prev_accepted_answers = answer.question.answers.filter(accepted = True) + for prev_answer in prev_accepted_answers: + auth.onAnswerAcceptCanceled(prev_answer, self) + + auth.onAnswerAccept(answer, self) + +@auto_now_timestamp +def user_unaccept_best_answer(self, answer = None, timestamp = None): + self.assert_can_unaccept_best_answer(answer) + if answer.accepted == False: + return + auth.onAnswerAcceptCanceled(answer, self) + +@auto_now_timestamp def user_delete_comment( self, comment = None, @@ -676,6 +727,7 @@ def user_delete_comment( self.assert_can_delete_comment(comment = comment) comment.delete() +@auto_now_timestamp def user_delete_answer( self, answer = None, @@ -685,6 +737,7 @@ def user_delete_answer( #todo - move onDeleted method where appropriate auth.onDeleted(answer, self, timestamp = timestamp) +@auto_now_timestamp def user_delete_question( self, question = None, @@ -1281,6 +1334,8 @@ 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) +User.add_to_class('accept_best_answer', user_accept_best_answer) +User.add_to_class('unaccept_best_answer', user_unaccept_best_answer) #assertions User.add_to_class('assert_can_vote_for_post', user_assert_can_vote_for_post) @@ -1304,6 +1359,11 @@ 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) +User.add_to_class('assert_can_accept_best_answer', user_assert_can_accept_best_answer) +User.add_to_class( + 'assert_can_unaccept_best_answer', + user_assert_can_unaccept_best_answer + ) #todo: move this to askbot/utils ?? def format_instant_notification_body( diff --git a/askbot/models/answer.py b/askbot/models/answer.py index c0107fee..7bb006b1 100644 --- a/askbot/models/answer.py +++ b/askbot/models/answer.py @@ -74,6 +74,7 @@ class AnswerManager(models.Manager): Retrieves visibile answers for the given question. Delete answers are only visibile to the person who deleted them. """ + #todo: there is this odd query used? if user is None or not user.is_authenticated(): return self.filter(question=question, deleted=False) diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py index 8aa7fa78..0dbe4663 100644 --- a/askbot/tests/db_api_tests.py +++ b/askbot/tests/db_api_tests.py @@ -7,23 +7,31 @@ from askbot.tests.utils import AskbotTestCase class DBApiTests(AskbotTestCase): - def test_flag_question(self): + def setUp(self): self.create_user() - question = self.post_question() + self.create_user(username = 'other_user') + self.question = self.post_question() + + def test_flag_question(self): self.user.set_status('m') - self.user.flag_post(question) + self.user.flag_post(self.question) self.assertEquals( len(self.user.flaggeditems.all()), 1 ) def test_flag_answer(self): - self.create_user() - question = self.post_question() - answer = self.post_answer(question = question) + answer = self.post_answer(question = self.question) self.user.set_status('m') self.user.flag_post(answer) self.assertEquals( len(self.user.flaggeditems.all()), 1 ) + + def test_accept_best_answer(self): + answer = self.post_answer( + question = self.question, + user = self.other_user + ) + self.user.accept_best_answer(answer) diff --git a/askbot/tests/permission_assertion_tests.py b/askbot/tests/permission_assertion_tests.py index 65ba6f47..0918e8b5 100644 --- a/askbot/tests/permission_assertion_tests.py +++ b/askbot/tests/permission_assertion_tests.py @@ -1197,6 +1197,89 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase): #def user_assert_can_close_question(self, question = None): #def user_assert_can_retag_questions(self): +class AcceptBestAnswerPermissionAssertionTests(utils.AskbotTestCase): + + def setUp(self): + self.create_user() + self.create_user(username = 'other_user') + self.question = self.post_question() + + def other_post_answer(self): + self.answer = self.post_answer( + question = self.question, + user = self.other_user + ) + + def user_post_answer(self): + self.answer = self.post_answer( + question = self.question, + user = self.user + ) + + def assert_user_can(self, user = None): + if user is None: + user = self.user + user.assert_can_accept_best_answer(self.answer) + + def assert_user_cannot(self, user = None): + if user is None: + user = self.user + self.assertRaises( + exceptions.PermissionDenied, + user.assert_can_accept_best_answer, + answer = self.answer + ) + + def test_question_owner_can_accept_others_answer(self): + self.other_post_answer() + self.assert_user_can() + + def test_suspended_question_owner_cannot_accept_others_answer(self): + self.other_post_answer() + self.user.set_status('s') + self.assert_user_cannot() + + def test_blocked_question_owner_cannot_accept_others_answer(self): + self.other_post_answer() + self.user.set_status('b') + self.assert_user_cannot() + + def test_answer_owner_cannot_accept_answer(self): + self.other_post_answer() + self.assert_user_cannot(user = self.other_user) + + def test_question_and_answer_owner_cannot_accept_answer(self): + self.user_post_answer() + self.assert_user_cannot() + + def test_high_rep_other_user_cannot_accept_answer(self): + self.other_post_answer() + self.create_user(username = 'third_user') + self.third_user.reputation = 1000000 + self.assert_user_cannot(user = self.third_user) + + def test_moderator_cannot_accept_others_answer(self): + self.other_post_answer() + self.create_user(username = 'third_user') + self.third_user.set_status('m') + self.assert_user_cannot(user = self.third_user) + + def test_moderator_cannot_accept_own_answer(self): + self.other_post_answer() + self.other_user.set_status('m') + self.assert_user_cannot(user = self.other_user) + + def test_admin_cannot_accept_others_answer(self): + self.other_post_answer() + self.create_user(username = 'third_user') + self.third_user.is_superuser = True + self.assert_user_cannot(user = self.third_user) + + def test_admin_cannot_accept_own_answer(self): + self.other_post_answer() + self.other_user.is_superuser = True + self.assert_user_cannot(user = self.other_user) + class VotePermissionAssertionTests(PermissionAssertionTestCase): """Tests permission for voting """ diff --git a/askbot/views/commands.py b/askbot/views/commands.py index b52bba1d..ab49dd88 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -138,31 +138,14 @@ def vote(request, id): if vote_type == '0': if request.user.is_authenticated(): answer_id = request.POST.get('postId') - answer = get_object_or_404(Answer, id=answer_id) + answer = get_object_or_404(Answer, id = answer_id) question = answer.question # make sure question author is current user - if question.author == request.user: - # answer user who is also question author is not allow to accept answer - if answer.author == question.author: - response_data['success'] = 0 - response_data['allowed'] = -1 - # check if answer has been accepted already - elif answer.accepted: - auth.onAnswerAcceptCanceled(answer, request.user) - response_data['status'] = 1 - else: - # set other answers in this question not accepted first - for answer_of_question in Answer.objects.get_answers_from_question(question, request.user): - if answer_of_question != answer and answer_of_question.accepted: - auth.onAnswerAcceptCanceled(answer_of_question, request.user) - - #make sure retrieve data again after above author changes, they may have related data - answer = get_object_or_404(Answer, id=answer_id) - auth.onAnswerAccept(answer, request.user) + if answer.accepted: + request.user.unaccept_best_answer(answer) + response_data['status'] = 1 #cancelation else: - raise exceptions.PermissionDenied( - 'Sorry, only question owner can accept the answer' - ) + request.user.accept_best_answer(answer) else: raise exceptions.PermissionDenied( _('Sorry, but anonymous users cannot accept answers') -- cgit v1.2.3-1-g7c22