diff options
Diffstat (limited to 'askbot')
-rw-r--r-- | askbot/__init__.py | 2 | ||||
-rw-r--r-- | askbot/doc/source/changelog.rst | 1 | ||||
-rw-r--r-- | askbot/doc/source/customizing-style-css-file-in-askbot.rst | 6 | ||||
-rw-r--r-- | askbot/forms.py | 16 | ||||
-rw-r--r-- | askbot/models/__init__.py | 13 | ||||
-rw-r--r-- | askbot/models/post.py | 2 | ||||
-rw-r--r-- | askbot/skins/common/media/js/post.js | 88 | ||||
-rw-r--r-- | askbot/skins/common/templates/question/answer_controls.html | 97 | ||||
-rw-r--r-- | askbot/skins/common/templates/question/answer_vote_buttons.html | 7 | ||||
-rw-r--r-- | askbot/skins/common/templates/question/question_controls.html | 74 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 6 | ||||
-rw-r--r-- | askbot/skins/default/templates/macros.html | 2 | ||||
-rw-r--r-- | askbot/skins/default/templates/question/answer_card.html | 2 | ||||
-rw-r--r-- | askbot/skins/default/templates/question/javascript.html | 1 | ||||
-rw-r--r-- | askbot/skins/default/templates/question/question_card.html | 5 | ||||
-rw-r--r-- | askbot/tasks.py | 35 | ||||
-rw-r--r-- | askbot/urls.py | 5 | ||||
-rw-r--r-- | askbot/views/commands.py | 22 | ||||
-rw-r--r-- | askbot/views/readers.py | 29 |
19 files changed, 296 insertions, 117 deletions
diff --git a/askbot/__init__.py b/askbot/__init__.py index 2989d660..539630d9 100644 --- a/askbot/__init__.py +++ b/askbot/__init__.py @@ -21,7 +21,7 @@ REQUIREMENTS = { 'south': 'South>=0.7.1', 'oauth2': 'oauth2', 'markdown2': 'markdown2', - 'html5lib': 'html5lib', + 'html5lib': 'html5lib==0.90', 'keyedcache': 'django-keyedcache', 'threaded_multihost': 'django-threaded-multihost', 'robots': 'django-robots', diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index 965db53c..54baa18f 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -17,6 +17,7 @@ Development version (not released yet) * Added a management command to build_thread_summary_cache (Evgeny) * Added a management delete_contextless_badge_award_activities (Evgeny) * Fixed a file upload issue in FF and IE found by jerry_gzy (Evgeny) +* Added test on maximum length of title working for utf-8 text (Evgeny) 0.7.39 (Jan 11, 2012) --------------------- diff --git a/askbot/doc/source/customizing-style-css-file-in-askbot.rst b/askbot/doc/source/customizing-style-css-file-in-askbot.rst index 4d64eb81..65b75002 100644 --- a/askbot/doc/source/customizing-style-css-file-in-askbot.rst +++ b/askbot/doc/source/customizing-style-css-file-in-askbot.rst @@ -16,6 +16,12 @@ and some delay in the page load. Please find documentation about the lesscss format elsewhere. +.. note:: + Besides the "official" lesscss compiler, there are other + tools that convert .less files into .css: for example a + `less compiler from codekit (mac) <http://incident57.com/less/>`_ + and a `portable SimpLESS compiler <http://wearekiss.com/simpless>`_. + Compiling lesscss files ======================= diff --git a/askbot/forms.py b/askbot/forms.py index 08645fcd..fcf43814 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -113,6 +113,22 @@ class TitleField(forms.CharField): askbot_settings.MIN_TITLE_LENGTH ) % askbot_settings.MIN_TITLE_LENGTH raise forms.ValidationError(msg) + encoded_value = value.encode('utf-8') + if len(value) == len(encoded_value): + if len(value) > self.max_length: + raise forms.ValidationError( + _( + 'The title is too long, maximum allowed size is ' + '%d characters' + ) % self.max_length + ) + elif encoded_value > self.max_length: + raise forms.ValidationError( + _( + 'The title is too long, maximum allowed size is ' + '%d bytes' + ) % self.max_length + ) return value.strip() # TODO: test me 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('<span class="sep">|</span>') %} -<span class="linksopt">{{ pipe() }} - <a class="permant-link" - href="{{ answer.get_absolute_url(question_post=question) }}" - title="{% trans %}answer permanent link{% endtrans %}"> - {% trans %}permanent link{% endtrans %} - </a> - </span> - -{% if request.user|can_edit_post(answer) %}{{ pipe() }} - <span class="action-link"><a class="question-edit" href="{% url edit_answer answer.id %}">{% trans %}edit{% endtrans %}</a></span> +{#<span class="action-link swap-qa"> + <a id="swap-question-with-answer-{{answer.id}}">{% trans %}swap with question{% endtrans %}</a> +</span>uncomment if needed#} +<span class="action-link"> + <a class="permant-link" + href="{{ answer.get_absolute_url(question_post=question) }}" + title="{% trans %}answer permanent link{% endtrans %}"> + {% trans %}permanent link{% endtrans %} + </a> +</span> +{% if request.user.is_authenticated() and + ( + request.user == answer.author or + request.user.is_administrator_or_moderator() + ) +%} +<span class="action-link delete-post"> + <a class="question-delete" + >{% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a> +</span> +<span + id="answer-offensive-flag-{{ answer.id }}" + class="action-link offensive-flag" + title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" +> + <a class="question-flag">{% trans %}flag offensive{% endtrans %} + <span class="darkred">{% if answer.offensive_flag_count > 0 %}({{ answer.offensive_flag_count }}){% endif %}</span> + </a> +</span> +{% if answer.offensive_flag_count > 0 %} +<span + id="answer-offensive-flag-{{ answer.id }}" + class="action-link offensive-flag" + title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" +> + <a class="question-flag">{% trans %}flag offensive{% endtrans %} ({{ answer.offensive_flag_count }})</a> + </a> +</span> +<span + id="answer-offensive-flag-remove-{{ answer.id }}" + class="action-link offensive-flag" + title="{% trans %}remove offensive flag{% endtrans %}" +> + <a class="question-flag">{% trans %}remove flag{% endtrans %} ({{ answer.offensive_flag_count }})</a> +</span> +{% else %} +<span + id="answer-offensive-flag-{{ answer.id }}" + class="action-link offensive-flag" + title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" +> + <a class="question-flag">{% trans %}flag offensive{% endtrans %}</a> +</span> {% endif %} -{% if request.user|can_flag_offensive(answer) %}{{ pipe() }} - <span id="answer-offensive-flag-{{ answer.id }}" class="offensive-flag" - title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}"> - <a class="question-flag">{% trans %}flag offensive{% endtrans %}</a> - {% if request.user|can_see_offensive_flags(answer) %} - <span class="darkred">{% if answer.offensive_flag_count > 0 %}({{ answer.offensive_flag_count }}){% endif %}</span> - {% endif %} - </span> - {% elif request.user|can_remove_flag_offensive(answer)%}{{ pipe() }} - <span id="answer-offensive-flag-remove-{{ answer.id }}" class="offensive-flag" - title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}"> - <a class="question-flag">{% trans %}remove flag{% endtrans %}</a> - {% if request.user|can_see_offensive_flags(answer) %} - <span class="darkred">{% if answer.offensive_flag_count > 0 %}({{ answer.offensive_flag_count }}){% endif %}</span> - {% endif %} - </span> +<span class="action-link"> + <a class="question-edit" href="{% url edit_answer answer.id %}">{% trans %}edit{% endtrans %}</a> +</span> {% endif %} -{% if request.user|can_delete_post(answer) %}{{ pipe() }} - {% spaceless %} - <span class="action-link"> - <a class="question-delete" id="answer-delete-link-{{answer.id}}"> - {% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a> - </span> - {% endspaceless %} -{% endif %} -{% if settings.ALLOW_SWAPPING_QUESTION_WITH_ANSWER and request.user.is_authenticated() and request.user.is_administrator_or_moderator() %}{{ pipe() }} - <span class="action-link"> - <a id="swap-question-with-answer-{{answer.id}}">{% trans %}swap with question{% endtrans %}</a> - </span> -{% 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('<span class="sep">|</span>') %} -{% if request.user|can_edit_post(question) %}{{ pipe() }} - <a class="question-edit" href="{% url edit_question question.id %}">{% trans %}edit{% endtrans %}</a> -{% endif %} -{% if request.user|can_retag_question(question) %}{{ pipe() }} - <a id="retag" class="question-retag"href="{% url retag_question question.id %}">{% trans %}retag{% endtrans %}</a> - <script type="text/javascript"> - var retagUrl = "{% url retag_question question.id %}"; - </script> -{% 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() + ) +%} + <a + id="question-delete-link-{{question.id}}" + class="question-delete" + >{% if question.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a> + {% if thread.closed %} <a class="question-close" href="{% url reopen question.id %}">{% trans %}reopen{% endtrans %}</a> - {% endif %} -{% else %} - {% if request.user|can_close_question(question) %}{{ pipe() }} + {% else %} <a class="question-close" href="{% url close question.id %}">{% trans %}close{% endtrans %}</a> {% endif %} -{% endif %} -{% if request.user|can_flag_offensive(question) %}{{ pipe() }} - <span id="question-offensive-flag-{{ question.id }}" class="offensive-flag" - title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}"> - <a class="question-flag">{% trans %}flag offensive{% endtrans %}</a> - {% if request.user|can_see_offensive_flags(question) %} - <span class="darkred">{% if question.offensive_flag_count > 0 %}({{ question.offensive_flag_count }}){% endif %}</span> - {% endif %} - </span> - {% elif request.user|can_remove_flag_offensive(question)%}{{ pipe() }} - <span id="question-offensive-flag-remove-{{ question.id }}" class="offensive-flag" - title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}"> - <a class="question-flag">{% trans %}remove flag{% endtrans %}</a> - {% if request.user|can_see_offensive_flags(question) %} - <span class="darkred">{% if question.offensive_flag_count > 0 %}({{ question.offensive_flag_count }}){% endif %}</span> - {% endif %} - </span> -{% endif %} -{% if request.user|can_delete_post(question) %}{{ pipe() }} - <a id="question-delete-link-{{question.id}}" class="question-delete">{% if question.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a> + {% if question.offensive_flag_count > 0 %} + <span + id="question-offensive-flag-{{ question.id }}" class="offensive-flag" + title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" + > + <a class="question-flag">{% trans %}flag offensive{% endtrans %} {{ question.offensive_flag_count }})</a> + </span> + <span + id="question-offensive-flag-remove-{{ question.id }}" + class="offensive-flag" + title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" + > + <a class="question-flag">{% trans %}remove flag{% endtrans %} ({{ question.offensive_flag_count }})</a> + </span> + {% else %} + <span + id="question-offensive-flag-{{ question.id }}" class="offensive-flag" + title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}" + > + <a class="question-flag">{% trans %}flag offensive{% endtrans %}</a> + </span> + {% endif %} + <script type="text/javascript"> + var retagUrl = "{% url retag_question question.id %}"; + </script> + <a id="retag" class="question-retag"href="{% url retag_question question.id %}">{% trans %}retag{% endtrans %}</a> + <a class="question-edit" href="{% url edit_question question.id %}">{% trans %}edit{% endtrans %}</a> {% 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 %} </div> <div class="controls"> - {% 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 @@ <a class="old_answer_id_anchor" name="{{ answer.old_answer_id }}"></a> {% endif %} <div - id="answer-container-{{ answer.id }}" + id="post-id-{{ answer.id }}" class="{{ macros.answer_classes(answer, question) }}"> <div class="vote-buttons"> {# ==== 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 @@ - <div class="vote-buttons"> {# ==== 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 ==== #} </div> -<div class="question-content"> +<div id="post-id-{{question.id}}" class="question-content{% if question.deleted %} deleted{% endif %}"> <h1><a href="{{ question.get_absolute_url() }}">{{ thread.get_title(question)|escape }}</a></h1> {% include "question/question_tags.html" %} {# ==== END: question/question_tags.html" #} - <div id="question-table" {% if question.deleted %}class="deleted"{%endif%}> + <div id="question-table"> <div class="question-body"> <div class="post-update-info-container"> {# ==== 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 @@ -130,6 +130,11 @@ urlpatterns = patterns('', 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, name='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), |