From 5b3a059b672be039d360a7fcb84054eb9c489f38 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Tue, 21 Jun 2011 02:45:59 -0400 Subject: started working on comment upvote --- askbot/skins/default/media/images/go-up-grey.png | Bin 0 -> 563 bytes askbot/skins/default/media/images/go-up-orange.png | Bin 0 -> 586 bytes askbot/skins/default/media/js/post.js | 11 +++++++++ askbot/skins/default/media/style/style.css | 27 ++++++++++++++++++++- askbot/skins/default/templates/macros.html | 14 +++++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 askbot/skins/default/media/images/go-up-grey.png create mode 100644 askbot/skins/default/media/images/go-up-orange.png diff --git a/askbot/skins/default/media/images/go-up-grey.png b/askbot/skins/default/media/images/go-up-grey.png new file mode 100644 index 00000000..763bb799 Binary files /dev/null and b/askbot/skins/default/media/images/go-up-grey.png differ diff --git a/askbot/skins/default/media/images/go-up-orange.png b/askbot/skins/default/media/images/go-up-orange.png new file mode 100644 index 00000000..eca3579d Binary files /dev/null and b/askbot/skins/default/media/images/go-up-orange.png differ diff --git a/askbot/skins/default/media/js/post.js b/askbot/skins/default/media/js/post.js index a228e9ce..ee6703d1 100644 --- a/askbot/skins/default/media/js/post.js +++ b/askbot/skins/default/media/js/post.js @@ -1059,6 +1059,17 @@ Comment.prototype.decorate = function(element){ this._edit_link.decorate(edit_link); } + var comment_vote = this._element.find('.upvote'); + var height = this._element.height(); + this._element.find('.comment-votes').height(height); + this._element.mouseenter(function(){ + comment_vote.addClass('hover'); + comment_vo + }); + this._element.mouseleave(function(){ + comment_vote.removeClass('hover'); + }); + this._blank = false; }; diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css index ffa15009..c1474432 100755 --- a/askbot/skins/default/media/style/style.css +++ b/askbot/skins/default/media/style/style.css @@ -958,6 +958,32 @@ a:hover.medal { clear: both; } +.comments .content { + width: 650px; + float: right; +} + +.comments div.comment { + min-height: 25px; +} + +div.comment .comment-votes { + position:absolute; + width: 20px; + height: 25px; + margin-left: -20px; +} + +div.comment .comment-votes .upvote.hover { + background: url(../images/go-up-grey.png) no-repeat; + padding-left: 16px; +} + +div.comment .comment-votes .upvote:hover { + background: url(../images/go-up-orange.png) no-repeat; + padding-left: 16px; +} + .comments div.controls { clear: both; background: url(../images/gray-up-arrow-h18px.png) no-repeat; @@ -1782,7 +1808,6 @@ button::-moz-focus-inner { margin: 0; color: #444; padding: 2px 3px 5px 3px; - width: 670px; overflow: auto; } diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index 969052ba..45605d9c 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -450,9 +450,23 @@ poor design of the data or methods on data objects #} {%- endmacro -%} +{%- macro comment_votes(comment = None, user = None) -%} +
+ {% if comment.score > 0 %} + + {{comment.score}} + + {% else %} + + + {% endif %} +
+{%- endmacro -%} + {%- macro comment_list(comments = None, user = None) -%} {% for comment in comments %}
+ {{ comment_votes(comment = comment, user = user) }} {{comment.html}} - Date: Wed, 22 Jun 2011 03:36:10 -0400 Subject: almost done with comment voting --- askbot/forms.py | 18 +++ .../management/commands/load_stackexchange.py | 2 +- askbot/models/__init__.py | 6 +- askbot/models/content.py | 28 +++- askbot/models/meta.py | 9 ++ askbot/skins/default/media/js/post.js | 159 +++++++++++++++++++-- askbot/skins/default/templates/macros.html | 12 +- askbot/skins/default/templates/question.html | 1 + askbot/tests/db_api_tests.py | 10 ++ askbot/urls.py | 5 + askbot/views/commands.py | 17 +++ askbot/views/writers.py | 5 +- 12 files changed, 248 insertions(+), 24 deletions(-) diff --git a/askbot/forms.py b/askbot/forms.py index 9e4f4c14..2ab67213 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -647,6 +647,24 @@ class AnswerForm(forms.Form): return self.fields['email_notify'].initial = False +class VoteForm(forms.Form): + """form used in ajax vote view (only comment_upvote so far) + """ + post_id = forms.IntegerField() + cancel_vote = forms.CharField()#char because it is 'true' or 'false' as string + + def clean_cancel_vote(self): + val = self.cleaned_data['cancel_vote'] + if val == 'true': + result = True + elif val == 'false': + result = False + else: + del self.cleaned_data['cancel_vote'] + raise forms.ValidationError('either "true" or "false" strings expected') + self.cleaned_data['cancel_vote'] = result + return self.cleaned_data['cancel_vote'] + class CloseForm(forms.Form): reason = forms.ChoiceField(choices=const.CLOSE_REASONS) diff --git a/askbot/importers/stackexchange/management/commands/load_stackexchange.py b/askbot/importers/stackexchange/management/commands/load_stackexchange.py index 47e920e2..ddac764e 100644 --- a/askbot/importers/stackexchange/management/commands/load_stackexchange.py +++ b/askbot/importers/stackexchange/management/commands/load_stackexchange.py @@ -6,7 +6,7 @@ import zipfile from django.core.management.base import BaseCommand, CommandError import askbot.importers.stackexchange.parse_models as se_parser from xml.etree import ElementTree as et -from django.db import models, transaction +from django.db import models#, transaction #from askbot.utils import dummy_transaction as transaction import askbot.models as askbot import askbot.deps.django_authopenid.models as askbot_openid diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 1e33ecb8..4cf713b2 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -1786,7 +1786,8 @@ def user_is_following_question(user, question): def upvote(self, post, timestamp=None, cancel=False): return _process_vote( - self,post, + self, + post, timestamp=timestamp, cancel=cancel, vote_type=Vote.VOTE_UP @@ -1794,7 +1795,8 @@ def upvote(self, post, timestamp=None, cancel=False): def downvote(self, post, timestamp=None, cancel=False): return _process_vote( - self,post, + self, + post, timestamp=timestamp, cancel=cancel, vote_type=Vote.VOTE_DOWN diff --git a/askbot/models/content.py b/askbot/models/content.py index 84bd2421..c1760593 100644 --- a/askbot/models/content.py +++ b/askbot/models/content.py @@ -1,8 +1,10 @@ import datetime from django.contrib.auth.models import User from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils import html as html_utils +from django.utils.datastructures import SortedDict from askbot import const from askbot.models.meta import Comment, Vote from askbot.models.user import EmailFeedSetting @@ -46,9 +48,29 @@ class Content(models.Model): abstract = True app_label = 'askbot' - def get_comments(self): - comments = self.comments.all().order_by('id') - return comments + def get_comments(self, visitor = None): + """returns comments for a post, annotated with + ``upvoted_by_user`` parameter, if visitor is logged in + otherwise, returns query set for all comments to a given post + """ + if visitor.is_anonymous(): + return self.comments.all().order_by('id') + else: + comment_content_type = ContentType.objects.get_for_model(Comment) + #a fancy query to annotate comments with the visitor votes + comments = self.comments.extra( + select = SortedDict([ + ( + 'upvoted_by_user', + 'SELECT COUNT(*) from vote, comment ' + 'WHERE vote.user_id = %s AND ' + 'vote.content_type_id = %s AND ' + 'vote.object_id = comment.id', + ) + ]), + select_params = (visitor.id, comment_content_type.id) + ).order_by('id') + return comments #todo: maybe remove this wnen post models are unified def get_text(self): diff --git a/askbot/models/meta.py b/askbot/models/meta.py index d7cf4b7e..0db36ab3 100644 --- a/askbot/models/meta.py +++ b/askbot/models/meta.py @@ -296,6 +296,15 @@ class Comment(base.MetaContent, base.UserContent): def get_latest_revision_number(self): return 1 + def is_upvoted_by(self, user): + content_type = ContentType.objects.get_for_model(self) + what_to_count = { + 'user': user, + 'object_id': self.id, + 'content_type': self.content_type + } + return Vote.objects.count(**what_to_count) > 0 + def is_last(self): """True if there are no newer comments on the related parent object diff --git a/askbot/skins/default/media/js/post.js b/askbot/skins/default/media/js/post.js index ee6703d1..e836ac32 100644 --- a/askbot/skins/default/media/js/post.js +++ b/askbot/skins/default/media/js/post.js @@ -141,7 +141,136 @@ var CPValidator = function(){ }; }(); +/** + * @enum {number} + */ +var VoteType = { + UP: 0, + DOWN: 1, +}; + +/** + * @constructor + * @extends {SimpleControl} + * @param {VoteType} vote_type + * @param {string} vote_url + */ +var CommentVoteButton = function(vote_type, vote_url){ + SimpleControl.call(this); + /** + * @type {boolean} + */ + this._voted = false; + /** + * @type {number} + */ + this._score = 0; + /** + * @type {VoteType} + */ + this._vote_type = vote_type; + /** + * @type {string} + */ + this._vote_url = vote_url; + /** + * @type {?number} + */ + this._post_id; +}; +inherits(CommentVoteButton, SimpleControl); +/** + * @param {number} + */ +CommentVoteButton.prototype.setCommentId = function(post_id){ + this._post_id = post_id; +}; +/** + * a hack. this method is to be called before CommentVoteButton.getElement() + */ +CommentVoteButton.prototype.setCommentElement = function(celem){ + this._comment_element = celem; +}; +/** + * @param {number} score + */ +CommentVoteButton.prototype.setScore = function(score){ + this._score = score; + if (this._element){ + this._element.html(score); + } +}; +/** + * @param {boolean} voted + */ +CommentVoteButton.prototype.setVoted = function(voted){ + this._voted = voted; +}; +CommentVoteButton.prototype.getVoteHandler = function(){ + var me = this; + return function(){ + var voted = me._voted; + var post_id = me._post_id; + var url = me._vote_url; + var data = { + cancel_vote: voted ? true:false, + post_id: post_id + }; + $.ajax({ + type: 'POST', + data: data, + dataType: 'json', + url: url, + cache: false, + success: function(data){ + me.setScore(data['score']); + me.setVoted(true); + }, + error: function(xhr, textStatus, exception) { + showMessage(me.getElement(), xhr.responseText, 'after'); + } + }); + }; +}; + +CommentVoteButton.prototype.decorate = function(element){ + this._element = element; + + this.setHandler(this.getVoteHandler()); + + var comment_vote = this._element.find('.upvote'); + if (this._element.parent()){ + var height = this._element.parent().height(); + this._element.height(height); + } + var element = this._element; + this._comment_element.mouseenter(function(){ + elment.addClass('hover'); + }); + this._comment_element.mouseleave(function(){ + element.removeClass('hover'); + }); + +}; + +CommentVoteButton.prototype.createDom = function(){ + this._element = this.makeElement('span'); + if (this._vote_type === VoteType.UP){ + this._element.addClass('upvote'); + } else if (this._vote_type === VoteType.DOWN){ + this._element.addClass('downvote'); + } + if (this._voted){ + this._element.addClass('voted'); + } + this.decorate(this._element); +}; + +/** + * legacy Vote class + * handles all sorts of vote-like operations + */ var Vote = function(){ // All actions are related to a question var questionId; @@ -1059,16 +1188,11 @@ Comment.prototype.decorate = function(element){ this._edit_link.decorate(edit_link); } - var comment_vote = this._element.find('.upvote'); - var height = this._element.height(); - this._element.find('.comment-votes').height(height); - this._element.mouseenter(function(){ - comment_vote.addClass('hover'); - comment_vo - }); - this._element.mouseleave(function(){ - comment_vote.removeClass('hover'); - }); + var url = askbot['urls']['upvote_comment']; + var vote = new CommentVoteButton(VoteType.UP, url); + vote.setCommentId(comment_id); + vote.setCommentElement(this._element); + vote.decorate(this._element.find('.comment-votes .upvote')); this._blank = false; }; @@ -1108,6 +1232,21 @@ Comment.prototype.setContent = function(data){ this._element.attr('class', 'comment'); this._element.attr('id', 'comment-' + this._data['id']); + var votes = this.makeElement('div'); + votes.addClass('comment-votes'); + + var vote_url = askbot['urls']['upvote_comment']; + var vote = new CommentVoteButton(VoteType.UP, vote_url); + if (this._data['upvoted_by_user']){ + vote.setVoted(true); + } + vote.setScore(this._data['score']); + vote.setCommentId(this._data['id']); + vote.setCommentElement(this._element); + votes.append(vote.getElement()); + + this._element.append(votes); + this._element.append(this._data['html']); this._element.append(' - '); diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index 45605d9c..5e401cb8 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -450,10 +450,10 @@ poor design of the data or methods on data objects #}
{%- endmacro -%} -{%- macro comment_votes(comment = None, user = None) -%} +{%- macro comment_votes(comment = None) -%}
{% if comment.score > 0 %} - + {{comment.score}} {% else %} @@ -466,7 +466,7 @@ poor design of the data or methods on data objects #} {%- macro comment_list(comments = None, user = None) -%} {% for comment in comments %} diff --git a/askbot/skins/default/templates/question.html b/askbot/skins/default/templates/question.html index d0eadaef..343648ac 100644 --- a/askbot/skins/default/templates/question.html +++ b/askbot/skins/default/templates/question.html @@ -447,6 +447,7 @@ askbot['urls']['user_signin'] = '{{ settings.LOGIN_URL }}'; askbot['urls']['vote_url_template'] = scriptUrl + '{% trans %}questions/{% endtrans %}{{ "{{QuestionID}}/" }}{% trans %}vote/{% endtrans %}'; askbot['urls']['swap_question_with_answer'] = '{% url swap_question_with_answer %}'; + askbot['urls']['upvote_comment'] = '{% url upvote_comment %}'; askbot['messages']['addComment'] = '{% trans %}add comment{% endtrans %}'; {% if settings.SAVE_COMMENT_ON_ENTER %} askbot['settings']['saveCommentOnEnter'] = true; diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py index 6473845f..2bb39084 100644 --- a/askbot/tests/db_api_tests.py +++ b/askbot/tests/db_api_tests.py @@ -38,6 +38,16 @@ class DBApiTests(AskbotTestCase): self.assertTrue(post.deleted_by == None) self.assertTrue(post.deleted_at == None) + def test_get_question_comments(self): + comment = self.user.post_comment( + parent_post = self.question, + body_text = 'lalalalalalalalal hahahah' + ) + self.other_user.upvote(comment) + comments = self.question.get_comments(visitor = self.other_user) + self.assertEquals(len(comments), 1) + self.assertEquals(comments[0].upvoted_by_user, True) + def test_flag_question(self): self.user.set_status('m') self.user.flag_post(self.question) diff --git a/askbot/urls.py b/askbot/urls.py index f6c5e937..05bb2db3 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -106,6 +106,11 @@ urlpatterns = patterns('', kwargs = {'object_name': 'Question'}, name='question_revisions' ), + url(#ajax only + r'^comment/upvote/$', + views.commands.upvote_comment, + name = 'upvote_comment' + ), url(#ajax only r'^post_comments/$', views.writers.post_comments, diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 5b7e8f18..d6fe4759 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -536,6 +536,23 @@ def swap_question_with_answer(request): } raise Http404 +@decorators.ajax_only +@decorators.post_only +def upvote_comment(request): + import pdb + pdb.set_trace() + if request.user.is_anonymous(): + raise exceptions.PermissionDenied(_('Please sign in to vote')) + form = forms.VoteForm(request.POST) + if form.is_valid(): + comment_id = form.cleaned_data['post_id'] + cancel_vote = form.cleaned_data['cancel_vote'] + comment = models.Comment.objects.get(id = comment_id) + request.user.upvote(comment, cancel = cancel_vote) + else: + raise ValueError + return {'score': comment.score} + #askbot-user communication system def read_message(request):#marks message a read if request.method == "POST": diff --git a/askbot/views/writers.py b/askbot/views/writers.py index bd65fbcd..8ec85bad 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -509,7 +509,7 @@ def answer(request, id):#process a new answer def __generate_comments_json(obj, user):#non-view generates json data for the post comments """non-view generates json data for the post comments """ - comments = obj.comments.all().order_by('id') + comments = obj.get_comments(visitor = user) # {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null} json_comments = [] for comment in comments: @@ -543,7 +543,7 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po data = simplejson.dumps(json_comments) return HttpResponse(data, mimetype="application/json") -def post_comments(request):#non-view generic ajax handler to load comments to an object +def post_comments(request):#generic ajax handler to load comments to an object # only support get post comments by ajax now user = request.user if request.is_ajax(): @@ -604,6 +604,7 @@ def edit_comment(request): 'user_id': comment.user.id, 'is_deletable': is_deletable, 'is_editable': is_editable, + 'voted': comment.is_upvoted_by(request.user), } else: raise exceptions.PermissionDenied( -- cgit v1.2.3-1-g7c22 From e29674c17e846b227d44d62b64f3f2c47c81c488 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Wed, 22 Jun 2011 22:47:44 -0400 Subject: voting for comments works --- askbot/models/__init__.py | 7 ++- askbot/skins/default/media/js/post.js | 93 ++++++++++-------------------- askbot/skins/default/media/style/style.css | 25 +++++--- askbot/skins/default/templates/macros.html | 8 +-- askbot/tests/db_api_tests.py | 42 ++++++++++---- askbot/views/commands.py | 9 +-- askbot/views/writers.py | 7 ++- 7 files changed, 100 insertions(+), 91 deletions(-) diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 4cf713b2..66992dad 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -336,7 +336,12 @@ def user_assert_can_vote_for_post( :param:post can be instance of question or answer """ - if self == post.author: + #todo: after unifying models this if else will go away + if isinstance(post, Comment): + post_author = post.user + else: + post_author = post.author + if self == post_author: raise django_exceptions.PermissionDenied(_('cannot vote for own posts')) blocked_error_message = _( diff --git a/askbot/skins/default/media/js/post.js b/askbot/skins/default/media/js/post.js index e836ac32..5fbf0ad8 100644 --- a/askbot/skins/default/media/js/post.js +++ b/askbot/skins/default/media/js/post.js @@ -141,22 +141,17 @@ var CPValidator = function(){ }; }(); -/** - * @enum {number} - */ -var VoteType = { - UP: 0, - DOWN: 1, -}; - /** * @constructor * @extends {SimpleControl} - * @param {VoteType} vote_type - * @param {string} vote_url + * @param {Comment} comment to upvote */ -var CommentVoteButton = function(vote_type, vote_url){ +var CommentVoteButton = function(comment){ SimpleControl.call(this); + /** + * @param {Comment} + */ + this._comment = comment; /** * @type {boolean} */ @@ -165,32 +160,8 @@ var CommentVoteButton = function(vote_type, vote_url){ * @type {number} */ this._score = 0; - /** - * @type {VoteType} - */ - this._vote_type = vote_type; - /** - * @type {string} - */ - this._vote_url = vote_url; - /** - * @type {?number} - */ - this._post_id; }; inherits(CommentVoteButton, SimpleControl); -/** - * @param {number} - */ -CommentVoteButton.prototype.setCommentId = function(post_id){ - this._post_id = post_id; -}; -/** - * a hack. this method is to be called before CommentVoteButton.getElement() - */ -CommentVoteButton.prototype.setCommentElement = function(celem){ - this._comment_element = celem; -}; /** * @param {number} score */ @@ -205,14 +176,17 @@ CommentVoteButton.prototype.setScore = function(score){ */ CommentVoteButton.prototype.setVoted = function(voted){ this._voted = voted; + if (this._element){ + this._element.addClass('upvoted'); + } }; CommentVoteButton.prototype.getVoteHandler = function(){ var me = this; + var comment = this._comment; return function(){ var voted = me._voted; - var post_id = me._post_id; - var url = me._vote_url; + var post_id = me._comment.getId(); var data = { cancel_vote: voted ? true:false, post_id: post_id @@ -221,14 +195,14 @@ CommentVoteButton.prototype.getVoteHandler = function(){ type: 'POST', data: data, dataType: 'json', - url: url, + url: askbot['urls']['upvote_comment'], cache: false, success: function(data){ me.setScore(data['score']); me.setVoted(true); }, error: function(xhr, textStatus, exception) { - showMessage(me.getElement(), xhr.responseText, 'after'); + showMessage(comment.getElement(), xhr.responseText, 'after'); } }); }; @@ -236,33 +210,34 @@ CommentVoteButton.prototype.getVoteHandler = function(){ CommentVoteButton.prototype.decorate = function(element){ this._element = element; - this.setHandler(this.getVoteHandler()); - var comment_vote = this._element.find('.upvote'); - if (this._element.parent()){ - var height = this._element.parent().height(); - this._element.height(height); - } var element = this._element; - this._comment_element.mouseenter(function(){ - elment.addClass('hover'); + var comment = this._comment; + /* can't call comment.getElement() here due + * an issue in the getElement() of comment + * so use an "illegal" access to comment._element here + */ + comment._element.mouseenter(function(){ + //outside height may not be known + var height = comment.getElement().height(); + element.height(height); + element.addClass('hover'); }); - this._comment_element.mouseleave(function(){ + comment._element.mouseleave(function(){ element.removeClass('hover'); }); }; CommentVoteButton.prototype.createDom = function(){ - this._element = this.makeElement('span'); - if (this._vote_type === VoteType.UP){ - this._element.addClass('upvote'); - } else if (this._vote_type === VoteType.DOWN){ - this._element.addClass('downvote'); + this._element = this.makeElement('div'); + if (this._score > 0){ + this._element.html(this._score); } + this._element.addClass('upvote'); if (this._voted){ - this._element.addClass('voted'); + this._element.addClass('upvoted'); } this.decorate(this._element); }; @@ -1188,10 +1163,7 @@ Comment.prototype.decorate = function(element){ this._edit_link.decorate(edit_link); } - var url = askbot['urls']['upvote_comment']; - var vote = new CommentVoteButton(VoteType.UP, url); - vote.setCommentId(comment_id); - vote.setCommentElement(this._element); + var vote = new CommentVoteButton(this); vote.decorate(this._element.find('.comment-votes .upvote')); this._blank = false; @@ -1235,14 +1207,11 @@ Comment.prototype.setContent = function(data){ var votes = this.makeElement('div'); votes.addClass('comment-votes'); - var vote_url = askbot['urls']['upvote_comment']; - var vote = new CommentVoteButton(VoteType.UP, vote_url); + var vote = new CommentVoteButton(this); if (this._data['upvoted_by_user']){ vote.setVoted(true); } vote.setScore(this._data['score']); - vote.setCommentId(this._data['id']); - vote.setCommentElement(this._element); votes.append(vote.getElement()); this._element.append(votes); diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css index c1474432..85bc8801 100755 --- a/askbot/skins/default/media/style/style.css +++ b/askbot/skins/default/media/style/style.css @@ -968,20 +968,29 @@ a:hover.medal { } div.comment .comment-votes { - position:absolute; + position: absolute; width: 20px; - height: 25px; - margin-left: -20px; + margin: -2px 0 0 -20px; } -div.comment .comment-votes .upvote.hover { +div.comment .upvote { + width: 20px; + height: 20px; + padding: 3px 0 0 3px; + font-weight: bold; + color: #777; +} + +div.comment .upvote.upvoted { + color: #d64000; +} + +div.comment .upvote.hover { background: url(../images/go-up-grey.png) no-repeat; - padding-left: 16px; } -div.comment .comment-votes .upvote:hover { +div.comment .upvote:hover { background: url(../images/go-up-orange.png) no-repeat; - padding-left: 16px; } .comments div.controls { @@ -1807,7 +1816,7 @@ button::-moz-focus-inner { border-top: 1px dotted #ccccce; margin: 0; color: #444; - padding: 2px 3px 5px 3px; + padding: 5px 3px 5px 3px; overflow: auto; } diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index 5e401cb8..23177c0b 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -453,12 +453,12 @@ poor design of the data or methods on data objects #} {%- macro comment_votes(comment = None) -%}
{% if comment.score > 0 %} - +
{{comment.score}} - +
{% else %} - - +
+
{% endif %}
{%- endmacro -%} diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py index 2bb39084..18fc174b 100644 --- a/askbot/tests/db_api_tests.py +++ b/askbot/tests/db_api_tests.py @@ -3,6 +3,7 @@ functions that happen on behalf of users e.g. ``some_user.do_something(...)`` """ +from django.core import exceptions from askbot.tests.utils import AskbotTestCase from askbot import models from askbot import const @@ -38,16 +39,6 @@ class DBApiTests(AskbotTestCase): self.assertTrue(post.deleted_by == None) self.assertTrue(post.deleted_at == None) - def test_get_question_comments(self): - comment = self.user.post_comment( - parent_post = self.question, - body_text = 'lalalalalalalalal hahahah' - ) - self.other_user.upvote(comment) - comments = self.question.get_comments(visitor = self.other_user) - self.assertEquals(len(comments), 1) - self.assertEquals(comments[0].upvoted_by_user, True) - def test_flag_question(self): self.user.set_status('m') self.user.flag_post(self.question) @@ -355,3 +346,34 @@ class GlobalTagSubscriberGetterTests(AskbotTestCase): expected_subscribers = set([self.u2,]), reason = 'bad' ) + +class CommentTests(AskbotTestCase): + """unfortunately, not very useful tests, + as assertions of type "user can" are not inside + the User.upvote() function + todo: refactor vote processing code + """ + def setUp(self): + self.create_user() + self.create_user(username = 'other_user') + self.question = self.post_question() + self.now = datetime.datetime.now() + self.comment = self.user.post_comment( + parent_post = self.question, + body_text = 'lalalalalalalalal hahahah' + ) + + def test_other_user_can_upvote_comment(self): + self.other_user.upvote(self.comment) + comments = self.question.get_comments(visitor = self.other_user) + self.assertEquals(len(comments), 1) + self.assertEquals(comments[0].upvoted_by_user, True) + + + def test_other_user_can_cancel_upvote(self): + self.test_other_user_can_upvote_comment() + comment = models.Comment.objects.get(id = self.comment.id) + self.assertEquals(comment.score, 1) + self.other_user.upvote(comment, cancel = True) + comment = models.Comment.objects.get(id = self.comment.id) + self.assertEquals(comment.score, 0) diff --git a/askbot/views/commands.py b/askbot/views/commands.py index d6fe4759..04d4ef1b 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -35,7 +35,6 @@ def process_vote(user = None, vote_direction = None, post = None): also in the future make keys in response data be more meaningful right now they are kind of cryptic - "status", "count" """ - if user.is_anonymous(): raise exceptions.PermissionDenied(_('anonymous users cannot vote')) @@ -539,8 +538,6 @@ def swap_question_with_answer(request): @decorators.ajax_only @decorators.post_only def upvote_comment(request): - import pdb - pdb.set_trace() if request.user.is_anonymous(): raise exceptions.PermissionDenied(_('Please sign in to vote')) form = forms.VoteForm(request.POST) @@ -548,7 +545,11 @@ def upvote_comment(request): comment_id = form.cleaned_data['post_id'] cancel_vote = form.cleaned_data['cancel_vote'] comment = models.Comment.objects.get(id = comment_id) - request.user.upvote(comment, cancel = cancel_vote) + process_vote( + post = comment, + vote_direction = 'up', + user = request.user + ) else: raise ValueError return {'score': comment.score} diff --git a/askbot/views/writers.py b/askbot/views/writers.py index 8ec85bad..c5a69c1d 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -529,7 +529,7 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po comment_owner = comment.get_owner() - json_comments.append({'id' : comment.id, + comment_data = {'id' : comment.id, 'object_id': obj.id, 'comment_age': diff_date(comment.added_at), 'html': comment.html, @@ -538,7 +538,10 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po 'user_id': comment_owner.id, 'is_deletable': is_deletable, 'is_editable': is_editable, - }) + 'score': comment.score, + 'upvoted_by_user': getattr(comment, 'upvoted_by_user', False) + } + json_comments.append(comment_data) data = simplejson.dumps(json_comments) return HttpResponse(data, mimetype="application/json") -- cgit v1.2.3-1-g7c22