From ee57d8981757732a83bce1fe96a20c505fa65390 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Mon, 20 Feb 2012 23:19:45 -0300 Subject: optimized the question view for the output time --- askbot/models/__init__.py | 13 +++ askbot/models/post.py | 2 +- askbot/skins/common/media/js/post.js | 88 ++++++++++++++++++-- .../common/templates/question/answer_controls.html | 97 +++++++++++++--------- .../templates/question/answer_vote_buttons.html | 7 +- .../templates/question/question_controls.html | 74 +++++++++-------- askbot/skins/default/media/style/style.less | 6 +- askbot/skins/default/templates/macros.html | 2 +- .../default/templates/question/answer_card.html | 2 +- .../default/templates/question/javascript.html | 1 + .../default/templates/question/question_card.html | 5 +- askbot/tasks.py | 35 +++++++- askbot/urls.py | 5 ++ askbot/views/commands.py | 22 +++++ askbot/views/readers.py | 29 ++----- 15 files changed, 272 insertions(+), 116 deletions(-) diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index db939d1a..ee2a6f4a 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -489,6 +489,18 @@ def user_assert_can_edit_comment(self, comment = None): raise django_exceptions.PermissionDenied(error_message) +def user_can_post_comment(self, parent_post = None): + """a simplified method to test ability to comment + """ + if self.reputation >= askbot_settings.MIN_REP_TO_LEAVE_COMMENTS: + return True + if self == parent_post.author: + return True + if self.is_administrator_or_moderator(): + return True + return False + + def user_assert_can_post_comment(self, parent_post = None): """raises exceptions.PermissionDenied if user cannot post comment @@ -2135,6 +2147,7 @@ User.add_to_class('is_following_question', user_is_following_question) User.add_to_class('mark_tags', user_mark_tags) User.add_to_class('update_response_counts', user_update_response_counts) User.add_to_class('can_have_strong_url', user_can_have_strong_url) +User.add_to_class('can_post_comment', user_can_post_comment) User.add_to_class('is_administrator', user_is_administrator) User.add_to_class('is_administrator_or_moderator', user_is_administrator_or_moderator) User.add_to_class('set_admin_status', user_set_admin_status) diff --git a/askbot/models/post.py b/askbot/models/post.py index 5cb9708f..1543a438 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -459,7 +459,7 @@ class Post(models.Model): if self.is_answer(): if not question_post: question_post = self.thread._question_post() - return u'%(base)s%(slug)s?answer=%(id)d#answer-container-%(id)d' % { + return u'%(base)s%(slug)s?answer=%(id)d#post-id-%(id)d' % { 'base': urlresolvers.reverse('question', args=[question_post.id]), 'slug': django_urlquote(slugify(self.thread.title)), 'id': self.id diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js index 2f2fbd75..dc3fbfd7 100644 --- a/askbot/skins/common/media/js/post.js +++ b/askbot/skins/common/media/js/post.js @@ -291,7 +291,7 @@ var Vote = function(){ var postId; var questionAuthorId; var currentUserId; - var answerContainerIdPrefix = 'answer-container-'; + var answerContainerIdPrefix = 'post-id-'; var voteContainerId = 'vote-buttons'; var imgIdPrefixAccept = 'answer-img-accept-'; var classPrefixFollow= 'button follow'; @@ -349,8 +349,8 @@ var Vote = function(){ offensiveAnswer:8, removeOffensiveAnswer:8.5, removeAllOffensiveAnswer:8.6, - removeQuestion: 9, - removeAnswer:10, + removeQuestion: 9,//deprecate + removeAnswer:10,//deprecate questionSubscribeUpdates:11, questionUnsubscribeUpdates:12 }; @@ -520,9 +520,9 @@ var Vote = function(){ Vote.remove_all_offensive(this, VoteType.removeAllOffensiveAnswer); }); - getremoveQuestionLink().unbind('click').click(function(event){ - Vote.remove(this, VoteType.removeQuestion); - }); + //getremoveQuestionLink().unbind('click').click(function(event){ + // Vote.remove(this, VoteType.removeQuestion); + //}); getquestionSubscribeUpdatesCheckbox().unbind('click').click(function(event){ //despeluchar esto @@ -927,7 +927,7 @@ var Vote = function(){ var do_proceed = false; if (postType == 'answer'){ - postNode = $('#answer-container-' + postId); + postNode = $('#post-id-' + postId); } else if (postType == 'question'){ postNode = $('#question-table'); @@ -1126,6 +1126,73 @@ var questionRetagger = function(){ }; }(); +var DeletePostLink = function(){ + SimpleControl.call(this); + this._post_id = null; +}; +inherits(DeletePostLink, SimpleControl); + +DeletePostLink.prototype.setPostId = function(id){ + this._post_id = id; +}; + +DeletePostLink.prototype.getPostId = function(){ + return this._post_id; +}; + +DeletePostLink.prototype.getPostElement = function(){ + return $('#post-id-' + this.getPostId()); +}; + +DeletePostLink.prototype.isPostDeleted = function(){ + return this._post_deleted; +}; + +DeletePostLink.prototype.setPostDeleted = function(is_deleted){ + var post = this.getPostElement(); + if (is_deleted === true){ + post.addClass('deleted'); + this._post_deleted = true; + this.getElement().html(gettext('undelete')); + } else if (is_deleted === false){ + post.removeClass('deleted'); + this._post_deleted = false; + this.getElement().html(gettext('delete')); + } +}; + +DeletePostLink.prototype.getDeleteHandler = function(){ + var me = this; + var post_id = this.getPostId(); + return function(){ + var data = { + 'post_id': me.getPostId(), + //todo rename cancel_vote -> undo + 'cancel_vote': me.isPostDeleted() ? true: false + }; + $.ajax({ + type: 'POST', + data: data, + dataType: 'json', + url: askbot['urls']['delete_post'], + cache: false, + success: function(data){ + if (data['success'] == true){ + me.setPostDeleted(data['is_deleted']); + } else { + showMessage(me.getElement(), data['message']); + } + } + }); + }; +}; + +DeletePostLink.prototype.decorate = function(element){ + this._element = element; + this._post_deleted = this.getPostElement().hasClass('deleted'); + this.setHandler(this.getDeleteHandler()); +} + //constructor for the form var EditCommentForm = function(){ WrappedElement.call(this); @@ -1858,6 +1925,13 @@ $(document).ready(function() { var swapper = new QASwapper(); swapper.decorate($(element)); }); + $('[id^="post-id-"]').each(function(idx, element){ + var deleter = new DeletePostLink(); + //confusingly .question-delete matches the answers too need rename + var post_id = element.id.split('-').pop(); + deleter.setPostId(post_id); + deleter.decorate($(element).find('.question-delete')); + }); questionRetagger.init(); socialSharing.init(); }); diff --git a/askbot/skins/common/templates/question/answer_controls.html b/askbot/skins/common/templates/question/answer_controls.html index bfc36cea..be50d6f4 100644 --- a/askbot/skins/common/templates/question/answer_controls.html +++ b/askbot/skins/common/templates/question/answer_controls.html @@ -1,43 +1,58 @@ -{% set pipe=joiner('|') %} -{{ pipe() }} - - {% trans %}permanent link{% endtrans %} - - - -{% if request.user|can_edit_post(answer) %}{{ pipe() }} - {% trans %}edit{% endtrans %} +{# + {% trans %}swap with question{% endtrans %} +uncomment if needed#} + + + {% trans %}permanent link{% endtrans %} + + +{% if request.user.is_authenticated() and + ( + request.user == answer.author or + request.user.is_administrator_or_moderator() + ) +%} + + {% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %} + + + {% trans %}flag offensive{% endtrans %} + {% if answer.offensive_flag_count > 0 %}({{ answer.offensive_flag_count }}){% endif %} + + +{% if answer.offensive_flag_count > 0 %} + + {% trans %}flag offensive{% endtrans %} ({{ answer.offensive_flag_count }}) + + + + {% trans %}remove flag{% endtrans %} ({{ answer.offensive_flag_count }}) + +{% else %} + + {% trans %}flag offensive{% endtrans %} + {% endif %} -{% if request.user|can_flag_offensive(answer) %}{{ pipe() }} - - {% trans %}flag offensive{% endtrans %} - {% if request.user|can_see_offensive_flags(answer) %} - {% if answer.offensive_flag_count > 0 %}({{ answer.offensive_flag_count }}){% endif %} - {% endif %} - - {% elif request.user|can_remove_flag_offensive(answer)%}{{ pipe() }} - - {% trans %}remove flag{% endtrans %} - {% if request.user|can_see_offensive_flags(answer) %} - {% if answer.offensive_flag_count > 0 %}({{ answer.offensive_flag_count }}){% endif %} - {% endif %} - + + {% trans %}edit{% endtrans %} + {% endif %} -{% if request.user|can_delete_post(answer) %}{{ pipe() }} - {% spaceless %} - - - {% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %} - - {% endspaceless %} -{% endif %} -{% if settings.ALLOW_SWAPPING_QUESTION_WITH_ANSWER and request.user.is_authenticated() and request.user.is_administrator_or_moderator() %}{{ pipe() }} - - {% trans %}swap with question{% endtrans %} - -{% endif %} - diff --git a/askbot/skins/common/templates/question/answer_vote_buttons.html b/askbot/skins/common/templates/question/answer_vote_buttons.html index 68bff3ed..9097fec2 100644 --- a/askbot/skins/common/templates/question/answer_vote_buttons.html +++ b/askbot/skins/common/templates/question/answer_vote_buttons.html @@ -5,7 +5,12 @@ {% else %} src="{{'/images/vote-accepted.png'|media}}" {% endif %} - {% if request.user == question.author or (request.user.is_authenticated() and (request.user.is_moderator() or request.user.is_administrator())) %} + {% if request.user.is_authenticated() and + ( + request.user == question.author or + request.user.is_administrator_or_moderator() + ) + %} alt="{% trans %}mark this answer as correct (click again to undo){% endtrans %}" title="{% trans %}mark this answer as correct (click again to undo){% endtrans %}" {% else %} diff --git a/askbot/skins/common/templates/question/question_controls.html b/askbot/skins/common/templates/question/question_controls.html index 5658d559..4710559d 100644 --- a/askbot/skins/common/templates/question/question_controls.html +++ b/askbot/skins/common/templates/question/question_controls.html @@ -1,39 +1,43 @@ -{% set pipe=joiner('|') %} -{% if request.user|can_edit_post(question) %}{{ pipe() }} - {% trans %}edit{% endtrans %} -{% endif %} -{% if request.user|can_retag_question(question) %}{{ pipe() }} - {% trans %}retag{% endtrans %} - -{% endif %} -{% if thread.closed %} - {% if request.user|can_reopen_question(question) %}{{ pipe() }} +{% if request.user.is_authenticated() and + ( + request.user == question.author or + request.user.is_administrator_or_moderator() + ) +%} + {% if question.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %} + {% if thread.closed %} {% trans %}reopen{% endtrans %} - {% endif %} -{% else %} - {% if request.user|can_close_question(question) %}{{ pipe() }} + {% else %} {% trans %}close{% endtrans %} {% endif %} -{% endif %} -{% if request.user|can_flag_offensive(question) %}{{ pipe() }} - - {% trans %}flag offensive{% endtrans %} - {% if request.user|can_see_offensive_flags(question) %} - {% if question.offensive_flag_count > 0 %}({{ question.offensive_flag_count }}){% endif %} - {% endif %} - - {% elif request.user|can_remove_flag_offensive(question)%}{{ pipe() }} - - {% trans %}remove flag{% endtrans %} - {% if request.user|can_see_offensive_flags(question) %} - {% if question.offensive_flag_count > 0 %}({{ question.offensive_flag_count }}){% endif %} - {% endif %} - -{% endif %} -{% if request.user|can_delete_post(question) %}{{ pipe() }} - {% if question.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %} + {% if question.offensive_flag_count > 0 %} + + {% trans %}flag offensive{% endtrans %} {{ question.offensive_flag_count }}) + + + {% trans %}remove flag{% endtrans %} ({{ question.offensive_flag_count }}) + + {% else %} + + {% trans %}flag offensive{% endtrans %} + + {% endif %} + + {% trans %}retag{% endtrans %} + {% trans %}edit{% endtrans %} {% endif %} diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index 7b564d8a..e8e5a5d8 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -1623,8 +1623,8 @@ ul#related-tags li { margin-bottom:8px; a { - color: #777; - padding: 0px 3px 3px 22px; + color: #777; + padding: 0px 7px 3px 18px; cursor: pointer; border: none; font-size:12px; @@ -1653,7 +1653,7 @@ ul#related-tags li { .post-controls, .answer-controls{ .question-delete{ background: url(../images/delete.png) no-repeat center left; - padding-left:16px; + padding-left:11px; } .question-flag{ background: url(../images/flag.png) no-repeat center left; diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index dcbe7874..af0826ab 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -317,7 +317,7 @@ for the purposes of the AJAX comment editor #} {% endfor %}
- {% set can_post = user|can_post_comment(post) %} + {% set can_post = user.is_authenticated() and user.can_post_comment(post) %} {% if show_post == post and show_comment %} {% if show_comment_position > max_comments %} {{ diff --git a/askbot/skins/default/templates/question/answer_card.html b/askbot/skins/default/templates/question/answer_card.html index 60317559..d71131a8 100644 --- a/askbot/skins/default/templates/question/answer_card.html +++ b/askbot/skins/default/templates/question/answer_card.html @@ -4,7 +4,7 @@ {% endif %}
{# ==== START: question/answer_vote_buttons.html ==== #} diff --git a/askbot/skins/default/templates/question/javascript.html b/askbot/skins/default/templates/question/javascript.html index 9c8e7fc6..a5a53e39 100644 --- a/askbot/skins/default/templates/question/javascript.html +++ b/askbot/skins/default/templates/question/javascript.html @@ -16,6 +16,7 @@ askbot['urls']['user_signin'] = '{{ settings.LOGIN_URL }}'; askbot['urls']['swap_question_with_answer'] = '{% url swap_question_with_answer %}'; askbot['urls']['upvote_comment'] = '{% url upvote_comment %}'; + askbot['urls']['delete_post'] = '{% url delete_post %}'; askbot['messages']['addComment'] = '{% trans %}add comment{% endtrans %}'; {% if settings.SAVE_COMMENT_ON_ENTER %} askbot['settings']['saveCommentOnEnter'] = true; diff --git a/askbot/skins/default/templates/question/question_card.html b/askbot/skins/default/templates/question/question_card.html index ff4ada1d..7077a8d1 100644 --- a/askbot/skins/default/templates/question/question_card.html +++ b/askbot/skins/default/templates/question/question_card.html @@ -1,4 +1,3 @@ -
{# ==== BEGIN: question/question_vote_buttons.html ==== #} {% include "question/question_vote_buttons.html" %} @@ -7,13 +6,13 @@ {% include "question/share_buttons.html" %} {# ==== END: question/share_buttons.html ==== #}
-
+

{{ thread.get_title(question)|escape }}

{% include "question/question_tags.html" %} {# ==== END: question/question_tags.html" #} -
+
{# ==== START: "question/question_author_info.html" #} diff --git a/askbot/tasks.py b/askbot/tasks.py index fefe99f5..634befb9 100644 --- a/askbot/tasks.py +++ b/askbot/tasks.py @@ -22,15 +22,15 @@ import traceback from django.contrib.contenttypes.models import ContentType from celery.decorators import task -from askbot.models import Activity -from askbot.models import User +from askbot.models import Activity, Post, Thread, User from askbot.models import send_instant_notifications_about_activity_in_post +from askbot.models.badges import award_badges_signal # TODO: Make exceptions raised inside record_post_update_celery_task() ... # ... propagate upwards to test runner, if only CELERY_ALWAYS_EAGER = True # (i.e. if Celery tasks are not deferred but executed straight away) -@task(ignore_results = True) +@task(ignore_result = True) def record_post_update_celery_task( post_id, post_content_type_id, @@ -152,3 +152,32 @@ def record_post_update( post = post, recipients = notification_subscribers, ) + +@task(ignore_result = True) +def record_question_visit( + question_post_id = None, + user_id = None, + update_view_count = False): + """celery task which records question visit by a person + updates view counter, if necessary, + and awards the badges associated with the + question visit + """ + #1) maybe update the view count + question_post = Post.objects.get(id = question_post_id) + if update_view_count: + question_post.thread.increase_view_count() + + #2) question view count per user and clear response displays + user = User.objects.get(id = user_id) + if user.is_authenticated(): + #get response notifications + user.visit_question(question_post) + + #3) send award badges signal for any badges + #that are awarded for question views + award_badges_signal.send(None, + event = 'view_question', + actor = user, + context_object = question_post, + ) diff --git a/askbot/urls.py b/askbot/urls.py index 640cb51e..1ab3ea5d 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -129,6 +129,11 @@ urlpatterns = patterns('', views.commands.upvote_comment, name = 'upvote_comment' ), + url(#ajax only + r'^post/delete/$', + views.commands.delete_post, + name = 'delete_post' + ), url(#ajax only r'^post_comments/$', views.writers.post_comments, diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 577946cc..b95143b0 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -596,6 +596,28 @@ def upvote_comment(request): raise ValueError return {'score': comment.score} +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +def delete_post(request): + if request.user.is_anonymous(): + raise exceptions.PermissionDenied(_('Please sign in to delete/restore posts')) + form = forms.VoteForm(request.POST) + if form.is_valid(): + post_id = form.cleaned_data['post_id'] + post = get_object_or_404( + models.Post, + post_type__in = ('question', 'answer'), + id = post_id + ) + if form.cleaned_data['cancel_vote']: + request.user.restore_post(post) + else: + request.user.delete_post(post) + else: + raise ValueError + return {'is_deleted': post.deleted} + #askbot-user communication system @csrf.csrf_exempt def read_message(request):#marks message a read diff --git a/askbot/views/readers.py b/askbot/views/readers.py index fe1185e6..a6f65e28 100644 --- a/askbot/views/readers.py +++ b/askbot/views/readers.py @@ -30,7 +30,6 @@ from askbot.utils.diff import textDiff as htmldiff from askbot.forms import AnswerForm, ShowQuestionForm from askbot import models from askbot import schedules -from askbot.models.badges import award_badges_signal from askbot.models.tag import Tag from askbot import const from askbot.utils import functions @@ -469,11 +468,9 @@ def question(request, id):#refactor - long subroutine. display question body, an last_seen = request.session['question_view_times'].get(question_post.id, None) - updated_when, updated_who = thread.get_last_update_info() - - if updated_who != request.user: + if thread.last_activity_by != request.user: if last_seen: - if last_seen < updated_when: + if last_seen < thread.last_activity_at: update_view_count = True else: update_view_count = True @@ -481,21 +478,13 @@ def question(request, id):#refactor - long subroutine. display question body, an request.session['question_view_times'][question_post.id] = \ datetime.datetime.now() - if update_view_count: - thread.increase_view_count() - - #2) question view count per user and clear response displays - if request.user.is_authenticated(): - #get response notifications - request.user.visit_question(question_post) - - #3) send award badges signal for any badges - #that are awarded for question views - award_badges_signal.send(None, - event = 'view_question', - actor = request.user, - context_object = question_post, - ) + #2) run the slower jobs in a celery task + from askbot import tasks + tasks.record_question_visit.delay( + question_post_id = question_post.id, + user_id = request.user.id, + update_view_count = update_view_count + ) paginator_data = { 'is_paginated' : (objects_list.count > const.ANSWERS_PAGE_SIZE), -- cgit v1.2.3-1-g7c22