diff options
-rw-r--r-- | forum/urls.py | 10 | ||||
-rw-r--r-- | forum/views/__init__.py | 1 | ||||
-rw-r--r-- | forum/views/commands.py | 280 | ||||
-rw-r--r-- | forum/views/content.py | 271 |
4 files changed, 286 insertions, 276 deletions
diff --git a/forum/urls.py b/forum/urls.py index fe335538..f5cc561f 100644 --- a/forum/urls.py +++ b/forum/urls.py @@ -42,10 +42,10 @@ urlpatterns = patterns('', url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('close/')), app.content.close, name='close'), url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('reopen/')), app.content.reopen, name='reopen'), url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('answer/')), app.content.answer, name='answer'), - url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.content.vote, name='vote'), + url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.commands.vote, name='vote'), url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.content.question_revisions, name='question_revisions'), url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.content.question_comments, name='question_comments'), - url(r'^%s$' % _('command/'), app.content.ajax_command, name='call_ajax'), + url(r'^%s$' % _('command/'), app.commands.ajax_command, name='call_ajax'), url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('questions/'), _('comments/'),_('delete/')), \ app.content.delete_comment, kwargs={'commented_object_type':'question'},\ @@ -59,15 +59,15 @@ urlpatterns = patterns('', url(r'^%s$' % _('tags/'), app.content.tags, name='tags'), url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.content.tag, name='tag_questions'), - url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.content.mark_tag, \ + url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.commands.mark_tag, \ kwargs={'reason':'good','action':'add'}, \ name='mark_interesting_tag'), - url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')), app.content.mark_tag, \ + url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')), app.commands.mark_tag, \ kwargs={'reason':'bad','action':'add'}, \ name='mark_ignored_tag'), - url(r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'), app.content.mark_tag, \ + url(r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'), app.commands.mark_tag, \ kwargs={'action':'remove'}, \ name='mark_ignored_tag'), diff --git a/forum/views/__init__.py b/forum/views/__init__.py index 7fdb6f61..a8e179b4 100644 --- a/forum/views/__init__.py +++ b/forum/views/__init__.py @@ -1,4 +1,5 @@ import content +import commands import users import meta import books diff --git a/forum/views/commands.py b/forum/views/commands.py new file mode 100644 index 00000000..9ad07a2c --- /dev/null +++ b/forum/views/commands.py @@ -0,0 +1,280 @@ +import datetime +from django.conf import settings +from django.utils import simplejson +from django.http import HttpResponse +from django.shortcuts import get_object_or_404 +from django.utils.translation import ugettext as _ +from forum.models import * +from forum import auth +from utils.decorators import ajax_method, ajax_login_required +import logging + +def vote(request, id):#refactor - pretty incomprehensible view used by various ajax calls +#issues: this subroutine is too long, contains many magic numbers and other issues +#it's called "vote" but many actions processed here have nothing to do with voting + """ + vote_type: + acceptAnswer : 0, + questionUpVote : 1, + questionDownVote : 2, + favorite : 4, + answerUpVote: 5, + answerDownVote:6, + offensiveQuestion : 7, + offensiveAnswer:8, + removeQuestion: 9, + removeAnswer:10 + questionSubscribeUpdates:11 + questionUnSubscribeUpdates:12 + + accept answer code: + response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default + response_data['success'] = 0, failed 1, Success - by default + response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel) + + vote code: + allowed = -3, Don't have enough votes left + -2, Don't have enough reputation score + -1, Vote his own post + 0, no allowed - Anonymous + 1, Allowed - by default + status = 0, By default + 1, Cancel + 2, Vote is too old to be canceled + + offensive code: + allowed = -3, Don't have enough flags left + -2, Don't have enough reputation score to do this + 0, not allowed + 1, allowed + status = 0, by default + 1, can't do it again + """ + response_data = { + "allowed": 1, + "success": 1, + "status" : 0, + "count" : 0, + "message" : '' + } + + def __can_vote(vote_score, user):#refactor - belongs to auth.py + if vote_score == 1:#refactor magic number + return auth.can_vote_up(request.user) + else: + return auth.can_vote_down(request.user) + + try: + if not request.user.is_authenticated(): + response_data['allowed'] = 0 + response_data['success'] = 0 + + elif request.is_ajax() and request.method == 'POST': + question = get_object_or_404(Question, id=id) + vote_type = request.POST.get('type') + + #accept answer + if vote_type == '0': + answer_id = request.POST.get('postId') + answer = get_object_or_404(Answer, id=answer_id) + # 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) + else: + response_data['allowed'] = 0 + response_data['success'] = 0 + # favorite + elif vote_type == '4': + has_favorited = False + fav_questions = FavoriteQuestion.objects.filter(question=question) + # if the same question has been favorited before, then delete it + if fav_questions is not None: + for item in fav_questions: + if item.user == request.user: + item.delete() + response_data['status'] = 1 + response_data['count'] = len(fav_questions) - 1 + if response_data['count'] < 0: + response_data['count'] = 0 + has_favorited = True + # if above deletion has not been executed, just insert a new favorite question + if not has_favorited: + new_item = FavoriteQuestion(question=question, user=request.user) + new_item.save() + response_data['count'] = FavoriteQuestion.objects.filter(question=question).count() + Question.objects.update_favorite_count(question) + + elif vote_type in ['1', '2', '5', '6']: + post_id = id + post = question + vote_score = 1 + if vote_type in ['5', '6']: + answer_id = request.POST.get('postId') + answer = get_object_or_404(Answer, id=answer_id) + post_id = answer_id + post = answer + if vote_type in ['2', '6']: + vote_score = -1 + + if post.author == request.user: + response_data['allowed'] = -1 + elif not __can_vote(vote_score, request.user): + response_data['allowed'] = -2 + elif post.votes.filter(user=request.user).count() > 0: + vote = post.votes.filter(user=request.user)[0] + # unvote should be less than certain time + if (datetime.datetime.now().day - vote.voted_at.day) >= auth.VOTE_RULES['scope_deny_unvote_days']: + response_data['status'] = 2 + else: + voted = vote.vote + if voted > 0: + # cancel upvote + auth.onUpVotedCanceled(vote, post, request.user) + + else: + # cancel downvote + auth.onDownVotedCanceled(vote, post, request.user) + + response_data['status'] = 1 + response_data['count'] = post.score + elif Vote.objects.get_votes_count_today_from_user(request.user) >= auth.VOTE_RULES['scope_votes_per_user_per_day']: + response_data['allowed'] = -3 + else: + vote = Vote(user=request.user, content_object=post, vote=vote_score, voted_at=datetime.datetime.now()) + if vote_score > 0: + # upvote + auth.onUpVoted(vote, post, request.user) + else: + # downvote + auth.onDownVoted(vote, post, request.user) + + votes_left = auth.VOTE_RULES['scope_votes_per_user_per_day'] - Vote.objects.get_votes_count_today_from_user(request.user) + if votes_left <= auth.VOTE_RULES['scope_warn_votes_left']: + response_data['message'] = u'%s votes left' % votes_left + response_data['count'] = post.score + elif vote_type in ['7', '8']: + post = question + post_id = id + if vote_type == '8': + post_id = request.POST.get('postId') + post = get_object_or_404(Answer, id=post_id) + + if FlaggedItem.objects.get_flagged_items_count_today(request.user) >= auth.VOTE_RULES['scope_flags_per_user_per_day']: + response_data['allowed'] = -3 + elif not auth.can_flag_offensive(request.user): + response_data['allowed'] = -2 + elif post.flagged_items.filter(user=request.user).count() > 0: + response_data['status'] = 1 + else: + item = FlaggedItem(user=request.user, content_object=post, flagged_at=datetime.datetime.now()) + auth.onFlaggedItem(item, post, request.user) + response_data['count'] = post.offensive_flag_count + # send signal when question or answer be marked offensive + mark_offensive.send(sender=post.__class__, instance=post, mark_by=request.user) + elif vote_type in ['9', '10']: + 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 not auth.can_delete_post(request.user, post): + response_data['allowed'] = -2 + elif post.deleted == True: + logging.debug('debug restoring post in view') + auth.onDeleteCanceled(post, request.user) + response_data['status'] = 1 + else: + auth.onDeleted(post, request.user) + delete_post_or_answer.send(sender=post.__class__, instance=post, delete_by=request.user) + elif vote_type == '11':#subscribe q updates + user = request.user + if user.is_authenticated(): + if user not in question.followed_by.all(): + question.followed_by.add(user) + if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False: + response_data['message'] = \ + _('subscription saved, %(email)s needs validation, see %(details_url)s') \ + % {'email':user.email,'details_url':reverse('faq') + '#validate'} + feed_setting = EmailFeedSetting.objects.get(subscriber=user,feed_type='q_sel') + if feed_setting.frequency == 'n': + feed_setting.frequency = 'd' + feed_setting.save() + if 'message' in response_data: + response_data['message'] += '<br/>' + response_data['message'] = _('email update frequency has been set to daily') + #response_data['status'] = 1 + #responst_data['allowed'] = 1 + else: + pass + #response_data['status'] = 0 + #response_data['allowed'] = 0 + elif vote_type == '12':#unsubscribe q updates + user = request.user + if user.is_authenticated(): + if user in question.followed_by.all(): + question.followed_by.remove(user) + else: + response_data['success'] = 0 + response_data['message'] = u'Request mode is not supported. Please try again.' + + data = simplejson.dumps(response_data) + + except Exception, e: + response_data['message'] = str(e) + data = simplejson.dumps(response_data) + return HttpResponse(data, mimetype="application/json") + +#internally grouped views - used by the tagging system +@ajax_login_required +def mark_tag(request, tag=None, **kwargs):#tagging system + action = kwargs['action'] + ts = MarkedTag.objects.filter(user=request.user, tag__name=tag) + if action == 'remove': + logging.debug('deleting tag %s' % tag) + ts.delete() + else: + reason = kwargs['reason'] + if len(ts) == 0: + try: + t = Tag.objects.get(name=tag) + mt = MarkedTag(user=request.user, reason=reason, tag=t) + mt.save() + except: + pass + else: + ts.update(reason=reason) + return HttpResponse(simplejson.dumps(''), mimetype="application/json") + +@ajax_login_required +def ajax_toggle_ignored_questions(request):#ajax tagging and tag-filtering system + if request.user.hide_ignored_questions: + new_hide_setting = False + else: + new_hide_setting = True + request.user.hide_ignored_questions = new_hide_setting + request.user.save() + +@ajax_method +def ajax_command(request):#refactor? view processing ajax commands - note "vote" and view others do it too + if 'command' not in request.POST: + return HttpResponseForbidden(mimetype="application/json") + if request.POST['command'] == 'toggle-ignored-questions': + return ajax_toggle_ignored_questions(request) + diff --git a/forum/views/content.py b/forum/views/content.py index 3fd3017d..5ac6164e 100644 --- a/forum/views/content.py +++ b/forum/views/content.py @@ -1016,7 +1016,6 @@ def edit_answer(request, id): 'form': form, }, context_instance=RequestContext(request)) - def answer(request, id):#process a new answer question = get_object_or_404(Question, id=id) if request.method == "POST": @@ -1122,273 +1121,3 @@ def delete_comment(request, object_id='', comment_id='', commented_object_type=N user = request.user return __generate_comments_json(obj, commented_object_type, user) raise PermissionDenied() - - -def vote(request, id):#refactor - pretty incomprehensible view used by various ajax calls -#issues: this subroutine is too long, contains many magic numbers and other issues -#it's called "vote" but many actions processed here have nothing to do with voting - """ - vote_type: - acceptAnswer : 0, - questionUpVote : 1, - questionDownVote : 2, - favorite : 4, - answerUpVote: 5, - answerDownVote:6, - offensiveQuestion : 7, - offensiveAnswer:8, - removeQuestion: 9, - removeAnswer:10 - questionSubscribeUpdates:11 - - accept answer code: - response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default - response_data['success'] = 0, failed 1, Success - by default - response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel) - - vote code: - allowed = -3, Don't have enough votes left - -2, Don't have enough reputation score - -1, Vote his own post - 0, no allowed - Anonymous - 1, Allowed - by default - status = 0, By default - 1, Cancel - 2, Vote is too old to be canceled - - offensive code: - allowed = -3, Don't have enough flags left - -2, Don't have enough reputation score to do this - 0, not allowed - 1, allowed - status = 0, by default - 1, can't do it again - """ - response_data = { - "allowed": 1, - "success": 1, - "status" : 0, - "count" : 0, - "message" : '' - } - - def __can_vote(vote_score, user):#refactor - belongs to auth.py - if vote_score == 1:#refactor magic number - return auth.can_vote_up(request.user) - else: - return auth.can_vote_down(request.user) - - try: - if not request.user.is_authenticated(): - response_data['allowed'] = 0 - response_data['success'] = 0 - - elif request.is_ajax() and request.method == 'POST': - question = get_object_or_404(Question, id=id) - vote_type = request.POST.get('type') - - #accept answer - if vote_type == '0': - answer_id = request.POST.get('postId') - answer = get_object_or_404(Answer, id=answer_id) - # 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: - 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: - 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) - onAnswerAccept(answer, request.user) - else: - response_data['allowed'] = 0 - response_data['success'] = 0 - # favorite - elif vote_type == '4': - has_favorited = False - fav_questions = FavoriteQuestion.objects.filter(question=question) - # if the same question has been favorited before, then delete it - if fav_questions is not None: - for item in fav_questions: - if item.user == request.user: - item.delete() - response_data['status'] = 1 - response_data['count'] = len(fav_questions) - 1 - if response_data['count'] < 0: - response_data['count'] = 0 - has_favorited = True - # if above deletion has not been executed, just insert a new favorite question - if not has_favorited: - new_item = FavoriteQuestion(question=question, user=request.user) - new_item.save() - response_data['count'] = FavoriteQuestion.objects.filter(question=question).count() - Question.objects.update_favorite_count(question) - - elif vote_type in ['1', '2', '5', '6']: - post_id = id - post = question - vote_score = 1 - if vote_type in ['5', '6']: - answer_id = request.POST.get('postId') - answer = get_object_or_404(Answer, id=answer_id) - post_id = answer_id - post = answer - if vote_type in ['2', '6']: - vote_score = -1 - - if post.author == request.user: - response_data['allowed'] = -1 - elif not __can_vote(vote_score, request.user): - response_data['allowed'] = -2 - elif post.votes.filter(user=request.user).count() > 0: - vote = post.votes.filter(user=request.user)[0] - # unvote should be less than certain time - if (datetime.datetime.now().day - vote.voted_at.day) >= VOTE_RULES['scope_deny_unvote_days']: - response_data['status'] = 2 - else: - voted = vote.vote - if voted > 0: - # cancel upvote - onUpVotedCanceled(vote, post, request.user) - - else: - # cancel downvote - onDownVotedCanceled(vote, post, request.user) - - response_data['status'] = 1 - response_data['count'] = post.score - elif Vote.objects.get_votes_count_today_from_user(request.user) >= VOTE_RULES['scope_votes_per_user_per_day']: - response_data['allowed'] = -3 - else: - vote = Vote(user=request.user, content_object=post, vote=vote_score, voted_at=datetime.datetime.now()) - if vote_score > 0: - # upvote - onUpVoted(vote, post, request.user) - else: - # downvote - onDownVoted(vote, post, request.user) - - votes_left = VOTE_RULES['scope_votes_per_user_per_day'] - Vote.objects.get_votes_count_today_from_user(request.user) - if votes_left <= VOTE_RULES['scope_warn_votes_left']: - response_data['message'] = u'%s votes left' % votes_left - response_data['count'] = post.score - elif vote_type in ['7', '8']: - post = question - post_id = id - if vote_type == '8': - post_id = request.POST.get('postId') - post = get_object_or_404(Answer, id=post_id) - - if FlaggedItem.objects.get_flagged_items_count_today(request.user) >= VOTE_RULES['scope_flags_per_user_per_day']: - response_data['allowed'] = -3 - elif not auth.can_flag_offensive(request.user): - response_data['allowed'] = -2 - elif post.flagged_items.filter(user=request.user).count() > 0: - response_data['status'] = 1 - else: - item = FlaggedItem(user=request.user, content_object=post, flagged_at=datetime.datetime.now()) - onFlaggedItem(item, post, request.user) - response_data['count'] = post.offensive_flag_count - # send signal when question or answer be marked offensive - mark_offensive.send(sender=post.__class__, instance=post, mark_by=request.user) - elif vote_type in ['9', '10']: - 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 not auth.can_delete_post(request.user, post): - response_data['allowed'] = -2 - elif post.deleted == True: - logging.debug('debug restoring post in view') - onDeleteCanceled(post, request.user) - response_data['status'] = 1 - else: - onDeleted(post, request.user) - delete_post_or_answer.send(sender=post.__class__, instance=post, delete_by=request.user) - elif vote_type == '11':#subscribe q updates - user = request.user - if user.is_authenticated(): - if user not in question.followed_by.all(): - question.followed_by.add(user) - if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False: - response_data['message'] = \ - _('subscription saved, %(email)s needs validation, see %(details_url)s') \ - % {'email':user.email,'details_url':reverse('faq') + '#validate'} - feed_setting = EmailFeedSetting.objects.get(subscriber=user,feed_type='q_sel') - if feed_setting.frequency == 'n': - feed_setting.frequency = 'd' - feed_setting.save() - if 'message' in response_data: - response_data['message'] += '<br/>' - response_data['message'] = _('email update frequency has been set to daily') - #response_data['status'] = 1 - #responst_data['allowed'] = 1 - else: - pass - #response_data['status'] = 0 - #response_data['allowed'] = 0 - elif vote_type == '12':#unsubscribe q updates - user = request.user - if user.is_authenticated(): - if user in question.followed_by.all(): - question.followed_by.remove(user) - else: - response_data['success'] = 0 - response_data['message'] = u'Request mode is not supported. Please try again.' - - data = simplejson.dumps(response_data) - - except Exception, e: - response_data['message'] = str(e) - data = simplejson.dumps(response_data) - return HttpResponse(data, mimetype="application/json") - -#internally grouped views - used by the tagging system -@ajax_login_required -def mark_tag(request, tag=None, **kwargs):#tagging system - action = kwargs['action'] - ts = MarkedTag.objects.filter(user=request.user, tag__name=tag) - if action == 'remove': - logging.debug('deleting tag %s' % tag) - ts.delete() - else: - reason = kwargs['reason'] - if len(ts) == 0: - try: - t = Tag.objects.get(name=tag) - mt = MarkedTag(user=request.user, reason=reason, tag=t) - mt.save() - except: - pass - else: - ts.update(reason=reason) - return HttpResponse(simplejson.dumps(''), mimetype="application/json") - -@ajax_login_required -def ajax_toggle_ignored_questions(request):#ajax tagging and tag-filtering system - if request.user.hide_ignored_questions: - new_hide_setting = False - else: - new_hide_setting = True - request.user.hide_ignored_questions = new_hide_setting - request.user.save() - -@ajax_method -def ajax_command(request):#refactor? view processing ajax commands - note "vote" and view others do it too - if 'command' not in request.POST: - return HttpResponseForbidden(mimetype="application/json") - if request.POST['command'] == 'toggle-ignored-questions': - return ajax_toggle_ignored_questions(request) - |