summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/auth.py35
-rw-r--r--askbot/exceptions.py14
-rw-r--r--askbot/locale/en/LC_MESSAGES/django.mobin24826 -> 25398 bytes
-rw-r--r--askbot/locale/en/LC_MESSAGES/django.po18
-rw-r--r--askbot/models/__init__.py87
-rw-r--r--askbot/models/signals.py2
-rwxr-xr-xaskbot/skins/default/media/js/com.cnprog.post.js123
-rwxr-xr-xaskbot/skins/default/media/style/style.css33
-rw-r--r--askbot/skins/default/templates/question.html32
-rw-r--r--askbot/templatetags/extra_filters.py65
-rw-r--r--askbot/tests/__init__.py1
-rw-r--r--askbot/tests/db_api_tests.py29
-rw-r--r--askbot/tests/permission_assertion_tests.py277
-rw-r--r--askbot/tests/utils.py113
-rw-r--r--askbot/utils/decorators.py14
-rw-r--r--askbot/views/commands.py59
-rw-r--r--askbot/views/writers.py100
17 files changed, 768 insertions, 234 deletions
diff --git a/askbot/auth.py b/askbot/auth.py
index 6a92b005..1ab24f52 100644
--- a/askbot/auth.py
+++ b/askbot/auth.py
@@ -18,26 +18,6 @@ import logging
from askbot.conf import settings as askbot_settings
-def can_flag_offensive(user):
- """Determines if a User can flag Questions and Answers as offensive."""
- return user.is_authenticated() and (
- user.reputation >= askbot_settings.MIN_REP_TO_FLAG_OFFENSIVE or
- user.is_superuser)
-
-def can_add_comments(user, subject):
- """Determines if a User can add comments to Questions and Answers."""
- if user.is_authenticated():
- if user.id == subject.author.id:
- return True
- if user.reputation >= askbot_settings.MIN_REP_TO_LEAVE_COMMENTS:
- return True
- if user.is_superuser:
- return True
- if isinstance(subject, Answer):
- if subject.question.author.id == user.id:
- return True
- return False
-
def can_retag_questions(user):
"""Determines if a User can retag Questions."""
if user.is_authenticated():
@@ -65,18 +45,11 @@ def can_edit_post(user, post):
return True
return False
-def can_delete_comment(user, comment):
- """Determines if a User can delete the given Comment."""
- return user.is_authenticated() and (
- user.id == comment.user_id or
- user.reputation >= askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS or
- user.is_superuser)
-
def can_view_offensive_flags(user):
"""Determines if a User can view offensive flag counts."""
return user.is_authenticated() and (
user.reputation >= askbot_settings.MIN_REP_TO_VIEW_OFFENSIVE_FLAGS or
- user.is_superuser)
+ user.is_superuser or user.is_moderator())
def can_close_question(user, question):
"""Determines if a User can close the given Question."""
@@ -150,7 +123,7 @@ def onFlaggedItem(item, post, user, timestamp=None):
post.author.reputation = calculate_reputation(
post.author.reputation,
- askbot_settings.REP_LOSS_FOR_RECEIVING_DOWNVOTE
+ askbot_settings.REP_LOSS_FOR_RECEIVING_FLAG
)
post.author.save()
@@ -160,7 +133,7 @@ def onFlaggedItem(item, post, user, timestamp=None):
reputation = Repute(
user=post.author,
- negative=askbot_settings.REP_LOSS_FOR_RECEIVING_DOWNVOTE,
+ negative=askbot_settings.REP_LOSS_FOR_RECEIVING_FLAG,
question=question, reputed_at=timestamp,
reputation_type=-4,
reputation=post.author.reputation
@@ -212,7 +185,7 @@ def onFlaggedItem(item, post, user, timestamp=None):
#post.deleted_at = timestamp
#post.deleted_by = Admin
post.save()
- signals.mark_offensive.send(
+ signals.flag_offensive.send(
sender=post.__class__,
instance=post,
mark_by=user
diff --git a/askbot/exceptions.py b/askbot/exceptions.py
new file mode 100644
index 00000000..0e7cb712
--- /dev/null
+++ b/askbot/exceptions.py
@@ -0,0 +1,14 @@
+from django.core import exceptions
+
+class InsufficientReputation(exceptions.PermissionDenied):
+ """exception class to indicate that permission
+ was denied due to insufficient reputation
+ """
+ pass
+
+class DuplicateCommand(exceptions.PermissionDenied):
+ """exception class to indicate that something
+ that can happen only once was attempted for the second time
+ """
+ pass
+
diff --git a/askbot/locale/en/LC_MESSAGES/django.mo b/askbot/locale/en/LC_MESSAGES/django.mo
index 21a76706..25f26273 100644
--- a/askbot/locale/en/LC_MESSAGES/django.mo
+++ b/askbot/locale/en/LC_MESSAGES/django.mo
Binary files differ
diff --git a/askbot/locale/en/LC_MESSAGES/django.po b/askbot/locale/en/LC_MESSAGES/django.po
index 4442a2e6..94b64b78 100644
--- a/askbot/locale/en/LC_MESSAGES/django.po
+++ b/askbot/locale/en/LC_MESSAGES/django.po
@@ -27,6 +27,24 @@ msgstr ""
msgid "anonymous users cannot vote"
msgstr "sorry, anonymous users cannot vote "
+msgid "cannot flag message as offensive twice"
+msgstr "You have flagged this question before and "
+"cannot do it more than once"
+
+msgid "blocked users cannot flag posts"
+msgstr "Sorry, since your account is blocked "
+"you cannot flag posts as offensive"
+
+#, python-format
+msgid "need > %(min_rep)s points to flag spam"
+msgstr "Sorry, to flag posts as offensive a minimum "
+"reputation of %(min_rep)s is required"
+
+#, python-format
+msgid "%(max_flags_per_day)s exceeded"
+msgstr "Sorry, you have exhausted the maximum number "
+"of %(max_flags_per_day)s offensive flags per day."
+
msgid "blocked users cannot post"
msgstr "Sorry, your account appears to be blocked and you "
"cannot make new posts until this issue is resolved. "
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index e75593b5..a59448c1 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -14,7 +14,8 @@ from django.utils.safestring import mark_safe
from django.db import models
from django.conf import settings as django_settings
from django.contrib.contenttypes.models import ContentType
-from django.core import exceptions
+from django.core import exceptions as django_exceptions
+from askbot import exceptions as askbot_exceptions
from askbot import const
from askbot.conf import settings as askbot_settings
from askbot.models.question import Question, QuestionRevision
@@ -33,12 +34,6 @@ from askbot.startup_tests import run_startup_tests
run_startup_tests()
-class InsufficientReputation(exceptions.PermissionDenied):
- """exception class to indicate that permission
- was denied due to insufficient reputation
- """
- pass
-
User.add_to_class(
'status',
models.CharField(
@@ -126,10 +121,10 @@ def _assert_user_can(
return
elif low_rep_error_message and user.reputation < min_rep_setting:
error_message = low_rep_error_message % {'min_rep': min_rep_setting}
- raise InsufficientReputation(error_message)
+ raise askbot_exceptions.InsufficientReputation(error_message)
else:
return
- raise exceptions.PermissionDenied(error_message)
+ raise django_exceptions.PermissionDenied(error_message)
def user_assert_can_vote_for_post(
@@ -145,7 +140,7 @@ def user_assert_can_vote_for_post(
"""
if self == post.author:
- raise exceptions.PermissionDenied('cannot vote for own posts')
+ raise django_exceptions.PermissionDenied('cannot vote for own posts')
blocked_error_message = _(
'Sorry your account appears to be blocked ' +
@@ -243,7 +238,7 @@ def user_assert_can_post_comment(self, parent_post = None):
min_rep_setting = askbot_settings.MIN_REP_TO_LEAVE_COMMENTS,
low_rep_error_message = low_rep_error_message,
)
- except InsufficientReputation, e:
+ except askbot_exceptions.InsufficientReputation, e:
if isinstance(parent_post, Answer):
if self == parent_post.question.author:
return
@@ -344,21 +339,21 @@ def user_assert_can_close_question(self, question = None):
)
-def user_assert_can_flag_offensive(self):
+def user_assert_can_flag_offensive(self, post = None):
- blocked_error_message = _(
- 'Sorry, since your account is blocked ' + \
- 'you cannot flag posts as offensive'
- )
- suspended_error_message = _(
- 'Sorry, since your account is suspended ' + \
- 'you cannot flag posts as offensive'
- )
- low_rep_error_message = _(
- 'Sorry, to flag posts as offensive a minimum ' + \
- 'reputation of %(min_rep)s is required'
- ) % \
- {'min_rep': askbot_settings.MIN_REP_TO_FLAG_OFFENSIVE}
+ assert(post is not None)
+
+ double_flagging_error_message = _('cannot flag message as offensive twice')
+
+ if post.flagged_items.filter(user = self).count() > 0:
+ raise askbot_exceptions.DuplicateCommand(double_flagging_error_message)
+
+ blocked_error_message = _('blocked users cannot flag posts')
+
+ suspended_error_message = _('suspended users cannot flag posts')
+
+ low_rep_error_message = _('need > %(min_rep)s points to flag spam') % \
+ {'min_rep': askbot_settings.MIN_REP_TO_FLAG_OFFENSIVE}
min_rep_setting = askbot_settings.MIN_REP_TO_FLAG_OFFENSIVE
_assert_user_can(
@@ -369,6 +364,21 @@ def user_assert_can_flag_offensive(self):
low_rep_error_message = low_rep_error_message,
min_rep_setting = min_rep_setting
)
+ #one extra assertion
+ if self.is_administrator() or self.is_moderator():
+ return
+ else:
+ flag_count_today = FlaggedItem.objects.get_flagged_items_count_today(
+ self
+ )
+ if flag_count_today >= askbot_settings.MAX_FLAGS_PER_USER_PER_DAY:
+ flags_exceeded_error_message = _(
+ '%(max_flags_per_day)s exceeded'
+ ) % {
+ 'max_flags_per_day': \
+ askbot_settings.MAX_FLAGS_PER_USER_PER_DAY
+ }
+ raise django_exceptions.PermissionDenied(flags_exceeded_error_message)
def user_assert_can_retag_questions(self):
@@ -432,7 +442,7 @@ def user_assert_can_revoke_old_vote(self, vote):
"""
if (datetime.datetime.now().day - vote.voted_at.day) \
>= askbot_settings.MAX_DAYS_TO_CANCEL_VOTE:
- raise exceptions.PermissionDenied(_('cannot revoke old vote'))
+ raise django_exceptions.PermissionDenied(_('cannot revoke old vote'))
def user_get_unused_votes_today(self):
"""returns number of votes that are
@@ -757,7 +767,6 @@ def user_get_status_display(self, soft = False):
elif soft == True:
return _('Registered User')
elif self.is_watched():
- print 'watched'
return _('Watched User')
elif self.is_approved():
return _('Approved User')
@@ -936,18 +945,18 @@ def accept_answer(self, answer, timestamp=None, cancel=False):
else:
auth.onAnswerAccept(answer, self, timestamp=timestamp)
+@auto_now_timestamp
def flag_post(user, post, timestamp=None, cancel=False):
if cancel:#todo: can't unflag?
return
- if post.flagged_items.filter(user=user).count() > 0:
- return
- else:
- flag = FlaggedItem(
- user = user,
- content_object = post,
- flagged_at = timestamp,
- )
- auth.onFlaggedItem(flag, post, user, timestamp=timestamp)
+
+ user.assert_can_flag_offensive(post = post)
+ flag = FlaggedItem(
+ user = user,
+ content_object = post,
+ flagged_at = timestamp,
+ )
+ auth.onFlaggedItem(flag, post, user, timestamp=timestamp)
def user_increment_response_count(user):
"""increment response counter for user
@@ -1316,7 +1325,7 @@ def record_delete_question(instance, delete_by, **kwargs):
#no need to set receiving user here
activity.save()
-def record_mark_offensive(instance, mark_by, **kwargs):
+def record_flag_offensive(instance, mark_by, **kwargs):
activity = Activity(
user=mark_by,
active_at=datetime.datetime.now(),
@@ -1415,8 +1424,8 @@ django_signals.post_delete.connect(record_cancel_vote, sender=Vote)
#change this to real m2m_changed with Django1.2
signals.delete_question_or_answer.connect(record_delete_question, sender=Question)
signals.delete_question_or_answer.connect(record_delete_question, sender=Answer)
-signals.mark_offensive.connect(record_mark_offensive, sender=Question)
-signals.mark_offensive.connect(record_mark_offensive, sender=Answer)
+signals.flag_offensive.connect(record_flag_offensive, sender=Question)
+signals.flag_offensive.connect(record_flag_offensive, sender=Answer)
signals.tags_updated.connect(record_update_tags, sender=Question)
signals.user_updated.connect(record_user_full_updated, sender=User)
signals.user_logged_in.connect(post_stored_anonymous_content)
diff --git a/askbot/models/signals.py b/askbot/models/signals.py
index 1c5324c3..825f72e7 100644
--- a/askbot/models/signals.py
+++ b/askbot/models/signals.py
@@ -9,7 +9,7 @@ edit_question_or_answer = django.dispatch.Signal(
delete_question_or_answer = django.dispatch.Signal(
providing_args=['instance', 'deleted_by']
)
-mark_offensive = django.dispatch.Signal(providing_args=['instance', 'mark_by'])
+flag_offensive = django.dispatch.Signal(providing_args=['instance', 'mark_by'])
user_updated = django.dispatch.Signal(providing_args=['instance', 'updated_by'])
#todo: move this to authentication app
user_logged_in = django.dispatch.Signal(providing_args=['session'])
diff --git a/askbot/skins/default/media/js/com.cnprog.post.js b/askbot/skins/default/media/js/com.cnprog.post.js
index 90956d9e..b82be4ee 100755
--- a/askbot/skins/default/media/js/com.cnprog.post.js
+++ b/askbot/skins/default/media/js/com.cnprog.post.js
@@ -61,17 +61,14 @@ var Vote = function(){
var pleaseSeeFAQ = $.i18n._('please see') + "<a href='" + scriptUrl + $.i18n._("faq/") + "'>faq</a>";
- var favoriteAnonymousMessage = $.i18n._('anonymous users cannot select favorite questions');
+ var favoriteAnonymousMessage = $.i18n._('anonymous users cannot select favorite questions') + pleaseLogin;
var voteAnonymousMessage = $.i18n._('anonymous users cannot vote') + pleaseLogin;
//there were a couple of more messages...
var voteDenyCancelMessage = $.i18n._('cannot revoke old vote') + pleaseSeeFAQ;
var offensiveConfirmation = $.i18n._('please confirm offensive');
var offensiveAnonymousMessage = $.i18n._('anonymous users cannot flag offensive posts') + pleaseLogin;
- var offensiveTwiceMessage = $.i18n._('cannot flag message as offensive twice') + pleaseSeeFAQ;
- var offensiveNoFlagsLeftMessage = $.i18n._('flag offensive cap exhausted') + pleaseSeeFAQ;
- var offensiveNoPermissionMessage = $.i18n._('need >15 points to report spam') + pleaseSeeFAQ;
var removeConfirmation = $.i18n._('confirm delete');
- var removeAnonymousMessage = $.i18n._('anonymous users cannot delete/undelete');
+ var removeAnonymousMessage = $.i18n._('anonymous users cannot delete/undelete') + pleaseLogin;
var recoveredMessage = $.i18n._('post recovered');
var deletedMessage = $.i18n._('post deleted');
@@ -280,7 +277,15 @@ var Vote = function(){
var callback_favorite = function(object, voteType, data){
if(data.allowed == "0" && data.success == "0"){
- showMessage(object, favoriteAnonymousMessage.replace("{{QuestionID}}", questionId));
+ showMessage(
+ object,
+ favoriteAnonymousMessage.replace(
+ '{{QuestionID}}',
+ questionId).replace(
+ '{{questionSlug}}',
+ ''
+ )
+ );
}
else if(data.status == "1"){
object.attr("src", mediaUrl("media/images/vote-favorite-off.png"));
@@ -335,27 +340,33 @@ var Vote = function(){
};
var callback_offensive = function(object, voteType, data){
- object = $(object);
- if (data.allowed == "0" && data.success == "0"){
- showMessage(object, offensiveAnonymousMessage.replace("{{QuestionID}}", questionId));
- }
- else if (data.allowed == "-3"){
- showMessage(object, offensiveNoFlagsLeftMessage);
- }
- else if (data.allowed == "-2"){
- showMessage(object, offensiveNoPermissionMessage);
- }
- else if (data.status == "1"){
- showMessage(object, offensiveTwiceMessage);
- }
- else if (data.success == "1"){
+ //todo: transfer proper translations of these from i18n.js
+ //to django.po files
+ //_('anonymous users cannot flag offensive posts') + pleaseLogin;
+ //_('flag offensive cap exhausted') + pleaseSeeFAQ;
+ //_('need >15 points to report spam') + pleaseSeeFAQ;
+ //_('cannot flag message as offensive twice') + pleaseSeeFAQ;
+ if (data.success == "1"){
$(object).children('span[class=darkred]').text("("+ data.count +")");
}
+ else {
+ object = $(object);
+ showMessage(object, data.message)
+ }
};
var callback_remove = function(object, voteType, data){
if (data.allowed == "0" && data.success == "0"){
- showMessage(object, removeAnonymousMessage.replace("{{QuestionID}}", questionId));
+ showMessage(
+ object,
+ removeAnonymousMessage.replace(
+ "{{QuestionID}}",
+ questionId
+ ).replace(
+ '{{questionSlug}}',
+ ''
+ )
+ );
}
else if (data.success == "1"){
if (voteType == VoteType.removeQuestion){
@@ -393,7 +404,16 @@ var Vote = function(){
//mark question as favorite
favorite: function(object){
if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
- showMessage(object, favoriteAnonymousMessage.replace("{{QuestionID}}", questionId));
+ showMessage(
+ object,
+ favoriteAnonymousMessage.replace(
+ "{{QuestionID}}",
+ questionId
+ ).replace(
+ '{{questionSlug}}',
+ questionSlug
+ )
+ );
return false;
}
submit(object, VoteType.favorite, callback_favorite);
@@ -401,7 +421,16 @@ var Vote = function(){
vote: function(object, voteType){
if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
- showMessage($(object), voteAnonymousMessage);
+ showMessage(
+ $(object),
+ voteAnonymousMessage.replace(
+ "{{QuestionID}}",
+ questionId
+ ).replace(
+ '{{questionSlug}}',
+ questionSlug
+ )
+ );
}
// up and downvote processor
if (voteType == VoteType.answerUpVote){
@@ -416,7 +445,16 @@ var Vote = function(){
//flag offensive
offensive: function(object, voteType){
if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
- showMessage($(object), offensiveAnonymousMessage.replace("{{QuestionID}}", questionId));
+ showMessage(
+ $(object),
+ offensiveAnonymousMessage.replace(
+ "{{QuestionID}}",
+ questionId
+ ).replace(
+ '{{questionSlug}}',
+ questionSlug
+ )
+ );
return false;
}
if (confirm(offensiveConfirmation)){
@@ -427,7 +465,16 @@ var Vote = function(){
//delete question or answer (comments are deleted separately)
remove: function(object, voteType){
if (!currentUserId || currentUserId.toUpperCase() == "NONE"){
- showMessage($(object), removeAnonymousMessage.replace("{{QuestionID}}", questionId));
+ showMessage(
+ $(object),
+ removeAnonymousMessage.replace(
+ '{{QuestionID}}',
+ questionId
+ ).replace(
+ '{{questionSlug}}',
+ questionSlug
+ )
+ );
return false;
}
bits = object.id.split('-');
@@ -590,9 +637,9 @@ function createComments(type) {
commentsFactory[objectType].updateTextCounter(textarea);
enableSubmitButton(formSelector);
},
- error: function(res, textStatus, errorThrown) {
+ error: function(xhr, textStatus, errorThrown) {
removeLoader();
- showMessage(formSelector, res.responseText);
+ showMessage($(formSelector), xhr.responseText);
enableSubmitButton(formSelector);
}
});
@@ -610,6 +657,7 @@ function createComments(type) {
var cBox = $("[id^='comments-container-" + objectType + "']");
cBox.each( function(i){
var post_id = $(this).attr('id').replace('comments-container-' + objectType + '-', '');
+
$(this).children().each(
function(i){
var comment_id = $(this).attr('id').replace('comment-','');
@@ -664,10 +712,19 @@ function createComments(type) {
deleteComment: function(jImg, id, deleteUrl) {
if (confirm($.i18n._('confirm delete comment'))) {
jImg.hide();
- $.post(deleteUrl, { dataNeeded: "forIIS7" }, function(json) {
- var par = jImg.parent();
- par.remove();
- }, "json");
+ $.ajax({
+ type: 'POST',
+ url: deleteUrl,
+ data: { dataNeeded: "forIIS7" },
+ success: function(json, textStatus, xhr) {
+ var par = jImg.parent();
+ par.remove();
+ },
+ error: function(xhr, textStatus, exception) {
+ showMessage(jImg, xhr.responseText);
+ },
+ dataType: "json"
+ });
}
},
@@ -676,8 +733,8 @@ function createComments(type) {
var color = length > 270 ? "#f00" : length > 200 ? "#f60" : "#999";
var jSpan = $(textarea).siblings("span.text-counter");
jSpan.html($.i18n._('can write') +
- (300 - length) + ' ' +
- $.i18n._('characters')).css("color", color);
+ (300 - length) + ' ' +
+ $.i18n._('characters')).css("color", color);
}
};
}
diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css
index b2e74d37..44276798 100755
--- a/askbot/skins/default/media/style/style.css
+++ b/askbot/skins/default/media/style/style.css
@@ -1227,22 +1227,6 @@ a:hover.medal {
color: #777;
}
-.vote-notification {
- z-index: 1;
- cursor: pointer;
- display: none;
- position: absolute;
- padding: 15px;
- color: White;
- background-color: darkred;
- text-align: center;
-}
-
-.vote-notification a {
- color: White;
- text-decoration: underline;
-}
-
.offensive-flag a {
color: #777;
padding: 3px;
@@ -2677,3 +2661,20 @@ p.signup_p {
img.gravatar {
margin:2px;
}
+
+.vote-notification {
+ z-index: 1;
+ cursor: pointer;
+ display: none;
+ position: absolute;
+ padding: 15px;
+ color: white;
+ background-color: darkred;
+ text-align: center;
+}
+
+.vote-notification a {
+ color: white;
+ text-decoration: underline;
+}
+
diff --git a/askbot/skins/default/templates/question.html b/askbot/skins/default/templates/question.html
index abcf5585..3cb7fb9c 100644
--- a/askbot/skins/default/templates/question.html
+++ b/askbot/skins/default/templates/question.html
@@ -153,12 +153,12 @@
{% endif %}
{% endif %}
{% separator %}
- {% if request.user|can_flag_offensive %}
+ {% if request.user|can_flag_offensive:question %}
<span id="question-offensive-flag-{{ question.id }}" class="offensive-flag"
title="{% trans "report as offensive (i.e containing spam, advertising, malicious text, etc.)" %}">
<a>{% trans "flag offensive" %}</a>
- {% if request.user|can_view_offensive_flags and question.offensive_flag_count %}
- <span class="darkred">({{ question.offensive_flag_count }})</span>
+ {% if request.user|can_view_offensive_flags %}
+ <span class="darkred">{% if question.offensive_flag_count %}({{ question.offensive_flag_count }}){% endif %}</span>
{% endif %}
</span>
{% endif %}
@@ -178,7 +178,7 @@
{{comment.html|safe}}
- <a class="comment-user" href="{{comment.user.get_profile_url}}">{{comment.user}}</a>
{% spaceless %}
- <span class="comment-age">({% diff_date comment.added_at %})</span>
+ <span class="comment-age">&nbsp;({% diff_date comment.added_at %})</span>
{% if request.user|can_delete_comment:comment %}
<img class="delete-icon"
src="{% media "/media/images/close-small.png" %}"
@@ -189,14 +189,14 @@
{% endfor %}
</div>
<div class="post-comments" style="margin-bottom:20px">
- <input id="can-post-comments-question-{{question.id}}" type="hidden" value="{{ request.user|can_add_comments:question }}"/>
- {% if request.user|can_add_comments:question or question.comment_count > 5 %}
+ <input id="can-post-comments-question-{{question.id}}" type="hidden" value="{{ request.user|can_post_comment:question }}"/>
+ {% if request.user|can_post_comment:question or question.comment_count > 5 %}
<a id="comments-link-question-{{question.id}}" class="comments-link">
- {% if request.user|can_add_comments:question %}
+ {% if request.user|can_post_comment:question %}
{% trans "add comment" %}
{% endif %}
{% if question.comment_count > 5 %}
- {% if request.user|can_add_comments:question %}/
+ {% if request.user|can_post_comment:question %}/
{% blocktrans count question.get_comments|slice:"5:"|length as counter %}see <strong>{{counter}}</strong> more{% plural %}see <strong>{{counter}}</strong> more{% endblocktrans %}
{% else %}
{% blocktrans count question.get_comments|slice:"5:"|length as counter %}see <strong>{{counter}}</strong> more comment{% plural %}see <strong>{{counter}}</strong> more comments
@@ -290,12 +290,12 @@
<span class="action-link"><a href="{% url edit_answer answer.id %}">{% trans 'edit' %}</a></span>
{% endif %}
{% separator %}
- {% if request.user|can_flag_offensive %}
+ {% if request.user|can_flag_offensive:answer %}
<span id="answer-offensive-flag-{{ answer.id }}" class="offensive-flag"
title="{% trans "report as offensive (i.e containing spam, advertising, malicious text, etc.)" %}">
<a>{% trans "flag offensive" %}</a>
- {% if request.user|can_view_offensive_flags and answer.offensive_flag_count %}
- <span class="darkred">({{ answer.offensive_flag_count }})</span>
+ {% if request.user|can_view_offensive_flags %}
+ <span class="darkred">{% if answer.offensive_flag_count %}({{ answer.offensive_flag_count }}){% endif %}</span>
{% endif %}
</span>
{% endif %}
@@ -320,7 +320,7 @@
{{comment.html|safe}}
- <a class="comment-user" href="{{comment.user.get_profile_url}}">{{comment.user}}</a>
{% spaceless %}
- <span class="comment-age">({% diff_date comment.added_at %})</span>
+ <span class="comment-age">&nbsp;({% diff_date comment.added_at %})</span>
{% if request.user|can_delete_comment:comment %}
<img class="delete-icon"
src="{% media "/media/images/close-small.png" %}"
@@ -331,14 +331,14 @@
{% endfor %}
</div>
<div class="post-comments" style="margin-bottom:20px">
- <input id="can-post-comments-answer-{{answer.id}}" type="hidden" value="{{ request.user|can_add_comments:answer}}"/>
- {% if request.user|can_add_comments:answer or answer.comment_count > 5 %}
+ <input id="can-post-comments-answer-{{answer.id}}" type="hidden" value="{{ request.user|can_post_comment:answer}}"/>
+ {% if request.user|can_post_comment:answer or answer.comment_count > 5 %}
<a id="comments-link-answer-{{answer.id}}" class="comments-link">
- {% if request.user|can_add_comments:answer %}
+ {% if request.user|can_post_comment:answer %}
{% trans "add comment" %}
{% endif %}
{% if answer.comment_count > 5 %}
- {% if request.user|can_add_comments:answer %}/
+ {% if request.user|can_post_comment:answer %}/
{% blocktrans count answer.get_comments|slice:"5:"|length as counter %}see <strong>{{counter}}</strong> more{% plural %}see <strong>{{counter}}</strong> more{% endblocktrans %}
{% else %}
{% blocktrans count answer.get_comments|slice:"5:"|length as counter %}see <strong>{{counter}}</strong> more comment{% plural %} see <strong>{{counter}}</strong> more comments{% endblocktrans %}
diff --git a/askbot/templatetags/extra_filters.py b/askbot/templatetags/extra_filters.py
index a8884895..383c8f7c 100644
--- a/askbot/templatetags/extra_filters.py
+++ b/askbot/templatetags/extra_filters.py
@@ -1,5 +1,6 @@
from django import template
-from django.core import exceptions
+from django.core import exceptions as django_exceptions
+from askbot import exceptions as askbot_exceptions
from askbot import auth
from askbot import models
from askbot.deps.grapefruit import Color
@@ -13,19 +14,57 @@ register = template.Library()
def collapse(input):
return ' '.join(input.split())
+def make_test_from_permission_assertion(
+ assertion_name = None,
+ allowed_exception = None
+ ):
+ """a decorator-like function that will create a True/False test from
+ permission assertion
+ """
+ def test_function(user, post):
+ if user.is_anonymous():
+ return False
+
+ assertion = getattr(user, assertion_name)
+ if allowed_exception:
+ try:
+ assertion(post)
+ return True
+ except allowed_exception:
+ return True
+ except django_exceptions.PermissionDenied:
+ return False
+ else:
+ try:
+ assertion(post)
+ return True
+ except django_exceptions.PermissionDenied:
+ return False
+
+ return test_function
+
+
@register.filter
def can_moderate_user(user, other_user):
if user.is_authenticated() and user.can_moderate_user(other_user):
return True
return False
-@register.filter
-def can_flag_offensive(user):
- return auth.can_flag_offensive(user)
+can_flag_offensive = make_test_from_permission_assertion(
+ assertion_name = 'assert_can_flag_offensive',
+ allowed_exception = askbot_exceptions.DuplicateCommand
+ )
+register.filter('can_flag_offensive', can_flag_offensive)
-@register.filter
-def can_add_comments(user, subject):
- return auth.can_add_comments(user, subject)
+can_post_comment = make_test_from_permission_assertion(
+ assertion_name = 'assert_can_post_comment'
+ )
+register.filter('can_post_comment', can_post_comment)
+
+can_delete_comment = make_test_from_permission_assertion(
+ assertion_name = 'assert_can_delete_comment'
+ )
+register.filter('can_delete_comment', can_delete_comment)
@register.filter
def can_retag_questions(user):
@@ -36,16 +75,6 @@ def can_edit_post(user, post):
return auth.can_edit_post(user, post)
@register.filter
-def can_delete_comment(user, comment):
- if user.is_anonymous():
- return False
- try:
- user.assert_can_delete_comment(comment)
- return True
- except exceptions.PermissionDenied:
- return False
-
-@register.filter
def can_view_offensive_flags(user):
return auth.can_view_offensive_flags(user)
@@ -78,7 +107,7 @@ def can_delete_post(user, post):
return True
else:
return False
- except exceptions.PermissionDenied:
+ except django_exceptions.PermissionDenied:
return False
@register.filter
diff --git a/askbot/tests/__init__.py b/askbot/tests/__init__.py
index bc1b9431..329a5271 100644
--- a/askbot/tests/__init__.py
+++ b/askbot/tests/__init__.py
@@ -2,3 +2,4 @@ from askbot.tests.email_alert_tests import *
from askbot.tests.on_screen_notification_tests import *
from askbot.tests.page_load_tests import *
from askbot.tests.permission_assertion_tests import *
+from askbot.tests.db_api_tests import *
diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py
new file mode 100644
index 00000000..8aa7fa78
--- /dev/null
+++ b/askbot/tests/db_api_tests.py
@@ -0,0 +1,29 @@
+"""Tests database api - the basic data entry
+functions that happen on behalf of users
+
+e.g. ``some_user.do_something(...)``
+"""
+from askbot.tests.utils import AskbotTestCase
+
+class DBApiTests(AskbotTestCase):
+
+ def test_flag_question(self):
+ self.create_user()
+ question = self.post_question()
+ self.user.set_status('m')
+ self.user.flag_post(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)
+ self.user.set_status('m')
+ self.user.flag_post(answer)
+ self.assertEquals(
+ len(self.user.flaggeditems.all()),
+ 1
+ )
diff --git a/askbot/tests/permission_assertion_tests.py b/askbot/tests/permission_assertion_tests.py
index dd84a85b..dfef0ca9 100644
--- a/askbot/tests/permission_assertion_tests.py
+++ b/askbot/tests/permission_assertion_tests.py
@@ -3,6 +3,7 @@ from django.core import exceptions
from askbot.tests import utils
from askbot.conf import settings as askbot_settings
from askbot import models
+from askbot.templatetags import extra_filters as template_filters
class PermissionAssertionTestCase(TestCase):
"""base TestCase class for permission
@@ -44,6 +45,180 @@ class PermissionAssertionTestCase(TestCase):
body_text = 'test answer'
)
+class FlagOffensivePermissionAssertionTests(PermissionAssertionTestCase):
+
+ def extraSetUp(self):
+ self.min_rep = askbot_settings.MIN_REP_TO_FLAG_OFFENSIVE
+ self.question = self.post_question()
+ self.answer = self.post_answer(question = self.question)
+
+ def assert_user_cannot_flag(self):
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.flag_post,
+ post = self.question
+ )
+ self.assertFalse(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.question
+ )
+ )
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.flag_post,
+ post = self.answer
+ )
+ self.assertFalse(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.answer
+ )
+ )
+
+ def assert_user_can_flag(self):
+ self.user.flag_post(post = self.question)
+ self.assertTrue(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.question
+ )
+ )
+ self.user.flag_post(post = self.answer)
+ self.assertTrue(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.answer
+ )
+ )
+
+ def setup_high_rep(self):
+ #there is a catch - assert_user_can_flag
+ #flags twice and each time user reputation
+ #suffers a hit, so test may actually fail
+ #set amply high reputation
+ extra_rep = -100 * askbot_settings.REP_LOSS_FOR_RECEIVING_FLAG
+ #NB: REP_LOSS is negative
+ self.user.reputation = self.min_rep + extra_rep
+ self.user.save()
+
+ def test_high_rep_user_cannot_exceed_max_flags_per_day(self):
+ max_flags = askbot_settings.MAX_FLAGS_PER_USER_PER_DAY
+ other_user = self.create_other_user()
+ other_user.reputation = self.min_rep
+ for i in range(max_flags):
+ question = self.post_question()
+ other_user.flag_post(question)
+ question = self.post_question()
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ other_user.flag_post,
+ question
+ )
+
+ def test_admin_has_no_limit_for_flags_per_day(self):
+ max_flags = askbot_settings.MAX_FLAGS_PER_USER_PER_DAY
+ other_user = self.create_other_user()
+ other_user.is_superuser = True
+ for i in range(max_flags + 1):
+ question = self.post_question()
+ other_user.flag_post(question)
+
+ def test_moderator_has_no_limit_for_flags_per_day(self):
+ max_flags = askbot_settings.MAX_FLAGS_PER_USER_PER_DAY
+ other_user = self.create_other_user()
+ other_user.set_status('m')
+ for i in range(max_flags + 1):
+ question = self.post_question()
+ other_user.flag_post(question)
+
+ def test_low_rep_user_cannot_flag(self):
+ assert(self.user.reputation < self.min_rep)
+ self.assert_user_cannot_flag()
+
+ def test_high_rep_blocked_or_suspended_user_cannot_flag(self):
+ self.setup_high_rep()
+ self.user.set_status('b')
+ self.assert_user_cannot_flag()
+ self.user.set_status('s')
+ self.assert_user_cannot_flag()
+
+ def test_high_rep_user_can_flag(self):
+ self.setup_high_rep()
+ self.assert_user_can_flag()
+
+ def test_low_rep_moderator_can_flag(self):
+ assert(self.user.reputation < self.min_rep)
+ self.user.set_status('m')
+ self.assert_user_can_flag()
+
+ def low_rep_administrator_can_flag(self):
+ assert(self.user.reputation < self.min_rep)
+ self.user.is_superuser = True
+ self.assert_user_can_flag()
+
+ def test_superuser_cannot_flag_question_twice(self):
+ self.user.is_superuser = True
+ self.user.flag_post(post = self.question)
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.flag_post,
+ post = self.question
+ )
+ #here is a deviation - the link will still be shown
+ #in templates
+ self.assertTrue(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.question
+ )
+ )
+
+ def test_superuser_cannot_flag_answer_twice(self):
+ self.user.is_superuser = True
+ self.user.flag_post(post = self.answer)
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.flag_post,
+ post = self.answer
+ )
+ self.assertTrue(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.answer
+ )
+ )
+
+ def test_high_rep_user_cannot_flag_question_twice(self):
+ self.user.reputation = self.min_rep
+ self.user.flag_post(post = self.question)
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.flag_post,
+ post = self.question
+ )
+ self.assertTrue(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.question
+ )
+ )
+
+ def test_high_rep_user_cannot_flag_answer_twice(self):
+ self.user.reputation = self.min_rep
+ self.user.flag_post(post = self.answer)
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.flag_post,
+ post = self.answer
+ )
+ self.assertTrue(
+ template_filters.can_flag_offensive(
+ self.user,
+ self.answer
+ )
+ )
+
class CommentPermissionAssertionTests(PermissionAssertionTestCase):
@@ -61,6 +236,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
parent_post = question,
body_text = 'test comment'
)
+ self.assertFalse(
+ template_filters.can_post_comment(
+ self.user,
+ question
+ )
+ )
def test_blocked_user_cannot_comment_own_answer(self):
question = self.post_question()
@@ -74,6 +255,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
parent_post = answer,
body_text = 'test comment'
)
+ self.assertFalse(
+ template_filters.can_post_comment(
+ self.user,
+ answer
+ )
+ )
def test_blocked_user_cannot_delete_own_comment(self):
question = self.post_question()
@@ -87,6 +274,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
self.user.delete_post,
post = comment
)
+ self.assertFalse(
+ template_filters.can_delete_comment(
+ self.user,
+ comment
+ )
+ )
def test_low_rep_user_cannot_delete_others_comment(self):
question = self.post_question()
@@ -103,6 +296,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
self.other_user.delete_post,
post = comment
)
+ self.assertFalse(
+ template_filters.can_delete_comment(
+ self.other_user,
+ comment
+ )
+ )
def test_high_rep_user_can_delete_comment(self):
question = self.post_question()
@@ -114,6 +313,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS
self.other_user.delete_comment(comment)
+ self.assertTrue(
+ template_filters.can_delete_comment(
+ self.other_user,
+ comment
+ )
+ )
def test_low_rep_user_can_delete_own_comment(self):
question = self.post_question()
@@ -130,6 +335,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS
)
self.user.delete_comment(comment)
+ self.assertTrue(
+ template_filters.can_delete_comment(
+ self.user,
+ comment
+ )
+ )
def test_moderator_can_delete_comment(self):
question = self.post_question()
@@ -139,6 +350,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
)
self.other_user.set_status('m')
self.other_user.delete_comment(comment)
+ self.assertTrue(
+ template_filters.can_delete_comment(
+ self.other_user,
+ comment
+ )
+ )
def test_admin_can_delete_comment(self):
question = self.post_question()
@@ -148,6 +365,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
)
self.other_user.is_superuser = True
self.other_user.delete_comment(comment)
+ self.assertTrue(
+ template_filters.can_delete_comment(
+ self.other_user,
+ comment
+ )
+ )
def test_high_rep_suspended_user_cannot_delete_others_comment(self):
question = self.post_question()
@@ -163,6 +386,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
self.other_user.delete_post,
post = comment
)
+ self.assertFalse(
+ template_filters.can_delete_comment(
+ self.other_user,
+ comment
+ )
+ )
def test_suspended_user_can_delete_own_comment(self):
question = self.post_question()
@@ -172,6 +401,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
)
self.user.set_status('s')
self.user.delete_comment(comment)
+ self.assertTrue(
+ template_filters.can_delete_comment(
+ self.user,
+ comment
+ )
+ )
def test_low_rep_user_cannot_comment_others(self):
question = self.post_question(
@@ -184,6 +419,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
parent_post = question,
body_text = 'test comment'
)
+ self.assertFalse(
+ template_filters.can_post_comment(
+ self.user,
+ question
+ )
+ )
def test_low_rep_user_can_comment_others_answer_to_own_question(self):
question = self.post_question()
@@ -197,6 +438,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
body_text = 'test comment'
)
self.assertTrue(isinstance(comment, models.Comment))
+ self.assertTrue(
+ template_filters.can_post_comment(
+ self.user,
+ answer
+ )
+ )
def test_high_rep_user_can_comment(self):
question = self.post_question(
@@ -208,6 +455,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
body_text = 'test comment'
)
self.assertTrue(isinstance(comment, models.Comment))
+ self.assertTrue(
+ template_filters.can_post_comment(
+ self.user,
+ question
+ )
+ )
def test_suspended_user_cannot_comment_others_question(self):
question = self.post_question(author = self.other_user)
@@ -218,6 +471,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
parent_post = question,
body_text = 'test comment'
)
+ self.assertFalse(
+ template_filters.can_post_comment(
+ self.user,
+ question
+ )
+ )
def test_suspended_user_can_comment_own_question(self):
question = self.post_question()
@@ -227,6 +486,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
body_text = 'test comment'
)
self.assertTrue(isinstance(comment, models.Comment))
+ self.assertTrue(
+ template_filters.can_post_comment(
+ self.user,
+ question
+ )
+ )
def test_low_rep_admin_can_comment_others_question(self):
question = self.post_question()
@@ -238,6 +503,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
body_text = 'test comment'
)
self.assertTrue(isinstance(comment, models.Comment))
+ self.assertTrue(
+ template_filters.can_post_comment(
+ self.other_user,
+ question
+ )
+ )
def test_low_rep_moderator_can_comment_others_question(self):
question = self.post_question()
@@ -249,6 +520,12 @@ class CommentPermissionAssertionTests(PermissionAssertionTestCase):
body_text = 'test comment'
)
self.assertTrue(isinstance(comment, models.Comment))
+ self.assertTrue(
+ template_filters.can_post_comment(
+ self.other_user,
+ question
+ )
+ )
#def user_assert_can_post_comment(self, parent_post):
#def user_assert_can_delete_comment(self, comment = None):
diff --git a/askbot/tests/utils.py b/askbot/tests/utils.py
index 46d8451a..b8f81717 100644
--- a/askbot/tests/utils.py
+++ b/askbot/tests/utils.py
@@ -1,5 +1,6 @@
"""utility functions used by Askbot test cases
"""
+from django.test import TestCase
from askbot import models
def create_user(
@@ -28,3 +29,115 @@ def create_user(
feed.save()
return user
+
+class AskbotTestCase(TestCase):
+ """adds some askbot-specific methods
+ to django TestCase class
+ """
+
+ def create_user(
+ self,
+ username = 'user',
+ email = None,
+ notification_schedule = None,
+ date_joined = None,
+ status = 'a'
+ ):
+ """creates user with username, etc and
+ makes the result accessible as
+
+ self.<username>
+
+ newly created user object is also returned
+ """
+ assert(username is not None)
+ assert(not hasattr(self, username))
+
+ if email is None:
+ email = username + '@example.com'
+
+ user_object = create_user(
+ username = username,
+ email = email,
+ notification_schedule = notification_schedule,
+ date_joined = date_joined,
+ status = status
+ )
+
+ setattr(self, username, user_object)
+
+ return user_object
+
+ def post_question(
+ self,
+ user = None,
+ title = 'test question title',
+ body_text = 'test question body text',
+ tags = 'test',
+ wiki = False,
+ follow = False,
+ timestamp = None
+ ):
+ """posts and returns question on behalf
+ of user. If user is not given, it will be self.user
+
+ if follow is True, question is followed by the poster
+ """
+
+ if user is None:
+ user = self.user
+
+ question = user.post_question(
+ title = title,
+ body_text = body_text,
+ tags = tags,
+ wiki = wiki,
+ timestamp = timestamp
+ )
+
+ if follow:
+ user.follow_question(question)
+
+ return question
+
+ def post_answer(
+ self,
+ user = None,
+ question = None,
+ body_text = 'test answer text',
+ follow = False,
+ wiki = False,
+ timestamp = None
+ ):
+
+ if user is None:
+ user = self.user
+ return user.post_answer(
+ question = question,
+ body_text = body_text,
+ follow = follow,
+ wiki = wiki,
+ timestamp = timestamp
+ )
+
+ def post_comment(
+ self,
+ user = None,
+ parent_post = None,
+ body_text = 'test comment text',
+ timestamp = None
+ ):
+ """posts and returns a comment to parent post, uses
+ now timestamp if not given, dummy body_text
+ author is required
+ """
+ if user is None:
+ user = self.user
+
+ comment = user.post_comment(
+ parent_post = parent_post,
+ body_text = body_text,
+ timestamp = timestamp,
+ )
+
+ return comment
diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py
index 8c88e426..db000d2f 100644
--- a/askbot/utils/decorators.py
+++ b/askbot/utils/decorators.py
@@ -6,6 +6,8 @@ import functools
from django.conf import settings
from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.utils import simplejson
+from askbot import exceptions as askbot_exceptions
+from django.core import exceptions as django_exceptions
def auto_now_timestamp(func):
"""decorator that will automatically set
@@ -15,16 +17,15 @@ def auto_now_timestamp(func):
"""
@functools.wraps(func)
def decorated_func(*arg, **kwarg):
- if 'timestamp' in kwarg:
- if kwarg['timestamp'] is None:
- kwarg['timestamp'] = datetime.datetime.now()
- return func(*arg, **kwarg)
- else:
- raise ValueError('timestamp argument is required')
+ timestamp = kwarg.get('timestamp', None)
+ if timestamp is None:
+ kwarg['timestamp'] = datetime.datetime.now()
+ return func(*arg, **kwarg)
return decorated_func
def ajax_login_required(view_func):
+ @functools.wraps(view_func)
def wrap(request,*args,**kwargs):
if request.user.is_authenticated():
return view_func(request,*args,**kwargs)
@@ -35,6 +36,7 @@ def ajax_login_required(view_func):
def ajax_method(view_func):
+ @functools.wraps(view_func)
def wrap(request,*args,**kwargs):
if not request.is_ajax():
raise Http404
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index ca48c9bb..ba5c4bd1 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -192,6 +192,20 @@ def vote(request, id):
post = post
)
+ 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)
+
+ request.user.flag_post(post)
+
+ response_data['count'] = post.offensive_flag_count
+ response_data['success'] = 1
+
elif request.is_ajax() and request.method == 'POST':
if not request.user.is_authenticated():
@@ -202,33 +216,7 @@ def vote(request, id):
vote_type = request.POST.get('type')
#accept answer
- if vote_type == '00':
- 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':
+ if vote_type == '4':
has_favorited = False
fave = request.user.toggle_favorite_question(question)
response_data['count'] = FavoriteQuestion.objects.filter(
@@ -236,23 +224,6 @@ def vote(request, id):
).count()
if fave == False:
response_data['status'] = 1
- 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) >= askbot_settings.MAX_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
elif vote_type in ['9', '10']:
#delete question or answer
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index 3ef14c05..65e58352 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -354,29 +354,35 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po
json_comments = []
from askbot.templatetags.extra_tags import diff_date
for comment in comments:
- comment_user = comment.user
- delete_url = ""
- if user != None and auth.can_delete_comment(user, comment):
- #/posts/392845/comments/219852/delete
- #todo translate this url
- if isinstance(comment.content_object, models.Answer):
- delete_comment_view = 'delete_answer_comment'
- elif isinstance(comment.content_object, models.Question):
- delete_comment_view = 'delete_question_comment'
- delete_url = reverse(
- delete_comment_view,
- kwargs = {
- 'object_id': obj.id,
- 'comment_id': comment.id
- }
- )
- json_comments.append({"id" : comment.id,
- "object_id" : obj.id,
- "comment_age" : diff_date(comment.added_at),
- "text" : comment.html,
- "user_display_name" : comment_user.username,
- "user_url" : comment_user.get_profile_url(),
- "delete_url" : delete_url
+
+ if user != None and user.is_authenticated():
+ try:
+ user.assert_can_delete_comment(comment)
+ #/posts/392845/comments/219852/delete
+ #todo translate this url
+ if isinstance(comment.content_object, models.Answer):
+ delete_comment_view = 'delete_answer_comment'
+ elif isinstance(comment.content_object, models.Question):
+ delete_comment_view = 'delete_question_comment'
+ delete_url = reverse(
+ delete_comment_view,
+ kwargs = {
+ 'object_id': obj.id,
+ 'comment_id': comment.id
+ }
+ )
+ except exceptions.PermissionDenied:
+ delete_url = ''
+ else:
+ delete_url = ''
+
+ json_comments.append({'id' : comment.id,
+ 'object_id' : obj.id,
+ 'comment_age' : diff_date(comment.added_at),
+ 'text' : comment.html,
+ 'user_display_name' : comment.get_owner().username,
+ 'user_url' : comment.get_owner().get_profile_url(),
+ 'delete_url' : delete_url
})
data = simplejson.dumps(json_comments)
@@ -400,6 +406,12 @@ def __comments(request, obj):#non-view generic ajax handler to load comments to
response = __generate_comments_json(obj, user)
elif request.method == "POST":
try:
+ if user.is_anonymous():
+ msg = _('Sorry, you appear to be logged out and ' + \
+ 'cannot post comments. Please ' + \
+ '<a href="%(sign_in_url)s">sign in</a>.') % \
+ {'sign_in_url': reverse('user_signin')}
+ raise exceptions.PermissionDenied(msg)
user.post_comment(
parent_post = obj,
body_text = request.POST.get('comment')
@@ -411,21 +423,49 @@ def __comments(request, obj):#non-view generic ajax handler to load comments to
mimetype="application/json"
)
return response
+ else:
+ raise Http404
+
+def delete_comment(
+ request,
+ object_id='',
+ comment_id='',
+ commented_object_type=None):
+ """ajax handler to delete comment
+ """
-def delete_comment(request, object_id='', comment_id='', commented_object_type=None):#ajax handler to delete comment
commented_object = None
if commented_object_type == 'question':
commented_object = models.Question
elif commented_object_type == 'answer':
commented_object = models.Answer
- if request.is_ajax():
- comment = get_object_or_404(models.Comment, id=comment_id)
- if auth.can_delete_comment(request.user, comment):
+ try:
+ if request.user.is_anonymous():
+ msg = _('Sorry, you appear to be logged out and ' + \
+ 'cannot delete comments. Please ' + \
+ '<a href="%(sign_in_url)s">sign in</a>.') % \
+ {'sign_in_url': reverse('user_signin')}
+ raise exceptions.PermissionDenied(msg)
+ if request.is_ajax():
+ comment = get_object_or_404(models.Comment, id=comment_id)
+
+ request.user.assert_can_delete_comment(comment)
+
obj = get_object_or_404(commented_object, id=object_id)
+ #todo: are the removed comments actually deleted?
obj.comments.remove(comment)
+ #attn: recalc denormalized field
obj.comment_count = obj.comment_count - 1
obj.save()
- user = request.user
- return __generate_comments_json(obj, user)
- raise exceptions.PermissionDenied()
+
+ return __generate_comments_json(obj, request.user)
+
+ raise exceptions.PermissionDenied(
+ _('sorry, we seem to have some technical difficulties')
+ )
+ except exceptions.PermissionDenied, e:
+ return HttpResponseForbidden(
+ str(e),
+ mimetype = 'application/json'
+ )