From bc1a00485fe64a4aec77c271945543169d4f57fb Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Mon, 19 Jul 2010 22:39:24 -0400 Subject: suspended and blocked users now cannot vote --- askbot/locale/de/LC_MESSAGES/django.mo | Bin 80778 -> 81314 bytes askbot/locale/de/LC_MESSAGES/django.po | 17 ++ askbot/locale/en/LC_MESSAGES/django.mo | Bin 23891 -> 24349 bytes askbot/locale/en/LC_MESSAGES/django.po | 17 ++ askbot/locale/es/LC_MESSAGES/django.mo | Bin 43243 -> 43703 bytes askbot/locale/es/LC_MESSAGES/django.po | 17 ++ askbot/locale/fi/LC_MESSAGES/django.mo | Bin 73972 -> 74438 bytes askbot/locale/fi/LC_MESSAGES/django.po | 17 ++ askbot/locale/ru/LC_MESSAGES/django.mo | Bin 93578 -> 94248 bytes askbot/locale/ru/LC_MESSAGES/django.po | 17 ++ askbot/locale/tr/LC_MESSAGES/django.mo | Bin 52874 -> 53425 bytes askbot/locale/tr/LC_MESSAGES/django.po | 17 ++ askbot/locale/zh-cn/LC_MESSAGES/django.mo | Bin 24271 -> 24695 bytes askbot/locale/zh-cn/LC_MESSAGES/django.po | 17 ++ askbot/models/__init__.py | 107 ++++++++++++- askbot/models/meta.py | 21 +++ askbot/skins/default/media/js/com.cnprog.i18n.js | 41 +---- askbot/skins/default/media/js/com.cnprog.post.js | 52 +++--- askbot/utils/decorators.py | 1 + askbot/views/commands.py | 193 +++++++++++++++-------- 20 files changed, 400 insertions(+), 134 deletions(-) diff --git a/askbot/locale/de/LC_MESSAGES/django.mo b/askbot/locale/de/LC_MESSAGES/django.mo index fd451a6d..8ee39e01 100644 Binary files a/askbot/locale/de/LC_MESSAGES/django.mo and b/askbot/locale/de/LC_MESSAGES/django.mo differ diff --git a/askbot/locale/de/LC_MESSAGES/django.po b/askbot/locale/de/LC_MESSAGES/django.po index b5de1030..1ded3e79 100644 --- a/askbot/locale/de/LC_MESSAGES/django.po +++ b/askbot/locale/de/LC_MESSAGES/django.po @@ -28,6 +28,23 @@ msgstr "Ein Zugang dieses Namens existiert bereits!" msgid "can't have two logins to the same account yet, sorry." msgstr "Zwei Logins für den selben Zugang sind leider noch nicht möglich." +msgid "anonymous users cannot vote" +msgstr "Gastbenutzer können nicht abstimmen" + +#, python-format +msgid ">%(points)s points required to upvote" +msgstr "Positiv bewerten benötigt mindestens %(points)s Punkte " + +#, python-format +msgid ">%(points)s points required to downvote" +msgstr "Negativ bewerten benötigt mindestens %(points)s Punkte" + +msgid "cannot vote for own posts" +msgstr "Über selbst verfaßte Beiträge kann nicht abgestimmt werden" + +msgid "cannot revoke old vote" +msgstr "Bewertung kann nicht mehr zurückgenommen werden" + #: django_authopenid/forms.py:157 msgid "Please enter valid username and password (both are case-sensitive)." msgstr "" diff --git a/askbot/locale/en/LC_MESSAGES/django.mo b/askbot/locale/en/LC_MESSAGES/django.mo index 5b3e4ad5..e80b4343 100644 Binary files a/askbot/locale/en/LC_MESSAGES/django.mo and b/askbot/locale/en/LC_MESSAGES/django.mo differ diff --git a/askbot/locale/en/LC_MESSAGES/django.po b/askbot/locale/en/LC_MESSAGES/django.po index a3dfdafb..d259f4fc 100644 --- a/askbot/locale/en/LC_MESSAGES/django.po +++ b/askbot/locale/en/LC_MESSAGES/django.po @@ -24,6 +24,23 @@ msgstr "" msgid "Account with this name already exists on the forum" msgstr "" +msgid "anonymous users cannot vote" +msgstr "sorry, anonymous users cannot vote " + +#, python-format +msgid ">%(points)s points required to upvote" +msgstr ">%(points)s points required to upvote " + +#, python-format +msgid ">%(points)s points required to downvote" +msgstr ">%(points)s points required to downvote " + +msgid "cannot vote for own posts" +msgstr "sorry, you cannot vote for your own posts" + +msgid "cannot revoke old vote" +msgstr "sorry, but older votes cannot be revoked" + #: django_authopenid/forms.py:136 msgid "can't have two logins to the same account yet, sorry." msgstr "" diff --git a/askbot/locale/es/LC_MESSAGES/django.mo b/askbot/locale/es/LC_MESSAGES/django.mo index 27fb5837..3e6d4406 100644 Binary files a/askbot/locale/es/LC_MESSAGES/django.mo and b/askbot/locale/es/LC_MESSAGES/django.mo differ diff --git a/askbot/locale/es/LC_MESSAGES/django.po b/askbot/locale/es/LC_MESSAGES/django.po index 5d5019d5..857c175e 100644 --- a/askbot/locale/es/LC_MESSAGES/django.po +++ b/askbot/locale/es/LC_MESSAGES/django.po @@ -31,10 +31,27 @@ msgstr "" msgid "can't have two logins to the same account yet, sorry." msgstr "" +msgid "anonymous users cannot vote" +msgstr "usuarios anónimos no pueden votar" + +#, python-format +msgid ">%(points)s points required to upvote" +msgstr ">%(points)s puntos requeridos para votar positivamente " + +#, python-format +msgid ">%(points)s points required to downvote" +msgstr ">%(points)s puntos requeridos para votar negativamente" + #: django_authopenid/forms.py:157 msgid "Please enter valid username and password (both are case-sensitive)." msgstr "Ingrese su nombre de usuario y contraseña (sensible a las mayusculas)" +msgid "cannot vote for own posts" +msgstr "no se puede votar por sus propias publicaciones" + +msgid "cannot revoke old vote" +msgstr "no puede revocar un voto viejo" + #: django_authopenid/forms.py:160 django_authopenid/forms.py:210 msgid "This account is inactive." msgstr "Esta cuenta está inactiva." diff --git a/askbot/locale/fi/LC_MESSAGES/django.mo b/askbot/locale/fi/LC_MESSAGES/django.mo index 4d5d13da..6277ccb6 100644 Binary files a/askbot/locale/fi/LC_MESSAGES/django.mo and b/askbot/locale/fi/LC_MESSAGES/django.mo differ diff --git a/askbot/locale/fi/LC_MESSAGES/django.po b/askbot/locale/fi/LC_MESSAGES/django.po index 16669b1a..58e73cbd 100644 --- a/askbot/locale/fi/LC_MESSAGES/django.po +++ b/askbot/locale/fi/LC_MESSAGES/django.po @@ -34,6 +34,23 @@ msgstr "Kysymys ja kaikki sen vastaukset on poistettu" msgid "The question has been deleted" msgstr "Kysymys on poistettu" +msgid "anonymous users cannot vote" +msgstr "kirjaudu sisään, jotta voit käyttää tätä ominaisuutta" + +#, python-format +msgid ">%(points)s points required to upvote" +msgstr "tarvitset >%(points)s points äänestämiseen " + +#, python-format +msgid ">%(points)s points required to downvote" +msgstr ">%(points)s mainetta tarvitaan" + +msgid "cannot vote for own posts" +msgstr "et voi äänestää omia postauksia" + +msgid "cannot revoke old vote" +msgstr "vanhoja ääniä ei voi muuttaa" + #: feed.py:22 msgid " - " msgstr " - " diff --git a/askbot/locale/ru/LC_MESSAGES/django.mo b/askbot/locale/ru/LC_MESSAGES/django.mo index 8afb2eaf..feb9d081 100644 Binary files a/askbot/locale/ru/LC_MESSAGES/django.mo and b/askbot/locale/ru/LC_MESSAGES/django.mo differ diff --git a/askbot/locale/ru/LC_MESSAGES/django.po b/askbot/locale/ru/LC_MESSAGES/django.po index 886316fa..e189e983 100644 --- a/askbot/locale/ru/LC_MESSAGES/django.po +++ b/askbot/locale/ru/LC_MESSAGES/django.po @@ -29,6 +29,23 @@ msgstr "аккаунт с таким именем уже существует н msgid "can't have two logins to the same account yet, sorry." msgstr "извините, но пока нельзя входить в аккаунт больше чем одним методом" +msgid "anonymous users cannot vote" +msgstr "неавторизированные пользователи не могут голосовать " + +#, python-format +msgid ">%(points)s points required to upvote" +msgstr "для повышения рейтинга требуется минимум %(points)s баллов " + +#, python-format +msgid ">%(points)s points required to downvote" +msgstr "для понижения рейтинга требуется минимум %(points)s баллов" + +msgid "cannot vote for own posts" +msgstr "нельзя голосовать за собственные сообщения" + +msgid "cannot revoke old vote" +msgstr "голос не может быть отозван" + #: django_authopenid/forms.py:158 msgid "Please enter valid username and password (both are case-sensitive)." msgstr "Пожалуйста введите имя пользователя и пароль (с учетом регистра букв)" diff --git a/askbot/locale/tr/LC_MESSAGES/django.mo b/askbot/locale/tr/LC_MESSAGES/django.mo index 0bc6227d..64cd2338 100644 Binary files a/askbot/locale/tr/LC_MESSAGES/django.mo and b/askbot/locale/tr/LC_MESSAGES/django.mo differ diff --git a/askbot/locale/tr/LC_MESSAGES/django.po b/askbot/locale/tr/LC_MESSAGES/django.po index 0bc2ee80..8cfe11d6 100644 --- a/askbot/locale/tr/LC_MESSAGES/django.po +++ b/askbot/locale/tr/LC_MESSAGES/django.po @@ -28,6 +28,23 @@ msgstr "Hesap bu adla zaten forum var" msgid "can't have two logins to the same account yet, sorry." msgstr "Üzgünüm.. Bir hesaba aynı anda iki giriş mümkün değil." +msgid "anonymous users cannot vote" +msgstr "üye girişi yapmadan oy kullanamazsınız" + +#, python-format +msgid ">%(points)s points required to upvote" +msgstr "beğeninizi göstermek için en az %(points)s puan toplamalısınız " + +#, python-format +msgid ">%(points)s points required to downvote" +msgstr "beğenmediğinizi göstermek için en az %(points)s puan toplamalısınız" + +msgid "cannot vote for own posts" +msgstr "kendi yazılarınıza oy veremezsiniz" + +msgid "cannot revoke old vote" +msgstr "verilen bir oyu iptal edemezsiniz" + #: django_authopenid/forms.py:157 msgid "Please enter valid username and password (both are case-sensitive)." msgstr "Lütfen) geçerli kullanıcı adı ve şifre (hem harf duyarlıdır girin." diff --git a/askbot/locale/zh-cn/LC_MESSAGES/django.mo b/askbot/locale/zh-cn/LC_MESSAGES/django.mo index a73a9675..be6432a8 100644 Binary files a/askbot/locale/zh-cn/LC_MESSAGES/django.mo and b/askbot/locale/zh-cn/LC_MESSAGES/django.mo differ diff --git a/askbot/locale/zh-cn/LC_MESSAGES/django.po b/askbot/locale/zh-cn/LC_MESSAGES/django.po index e08a05d2..b23f6ea6 100644 --- a/askbot/locale/zh-cn/LC_MESSAGES/django.po +++ b/askbot/locale/zh-cn/LC_MESSAGES/django.po @@ -19,10 +19,27 @@ msgstr "" msgid "i-names are not supported" msgstr "基本的HTML标签也是支持的" +#, python-format +msgid ">%(points)s points requried to upvote" +msgstr "需要+%(points)s积分才能投支持票。" + #: django_authopenid/forms.py:134 msgid "Account with this name already exists on the forum" msgstr "" +msgid "anonymous users cannot vote" +msgstr "匿名用户不能投票" + +#, python-format +msgid ">%(points)s points required to downvote" +msgstr "需要+%(points)s积分才能投反对票。" + +msgid "cannot vote for own posts" +msgstr "不能给自己的帖子投票" + +msgid "cannot revoke old vote" +msgstr "这个投票已经过时,不能撤销。" + #: django_authopenid/forms.py:135 msgid "can't have two logins to the same account yet, sorry." msgstr "" diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index c4d13b5d..8738ce41 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -29,6 +29,9 @@ from askbot.models.repute import Badge, Award, Repute from askbot import auth from askbot.utils.decorators import auto_now_timestamp +class PermissionError(Exception): + pass + User.add_to_class( 'status', models.CharField( @@ -68,6 +71,95 @@ User.add_to_class('tag_filter_setting', ) User.add_to_class('response_count', models.IntegerField(default=0)) + +def user_assert_can_vote_for_post( + self, + post = None, + direction = None, + ): + """raises PermissionError exception + if user can't in fact upvote + + :param:direction can be 'up' or 'down' + :param:post can be instance of question or answer + """ + if self.is_blocked(): + raise PermissionError( + _( + 'Sorry your account appears to be blocked ' + + 'and you cannot vote - please contact the ' + + 'site administrator to resolve the issue' + ) + ) + if self.is_suspended(): + raise PermissionError( + _( + 'Sorry your account appears to be suspended ' + + 'and you cannot vote - please contact the ' + + 'site administrator to resolve the issue' + ) + ) + if self.is_moderator() or self.is_administrator(): + return + + if direction == 'up': + if self.reputation < askbot_settings.MIN_REP_TO_VOTE_UP: + msg = _(">%(points)s points required to upvote, bitch") % \ + {'points': askbot_settings.MIN_REP_TO_VOTE_UP} + raise PermissionError(msg) + else: + if self.reputation < askbot_settings.MIN_REP_TO_VOTE_DOWN: + msg = _(">%(points)s points required to downvote") % \ + {'points': askbot_settings.MIN_REP_TO_VOTE_DOWN} + raise PermissionError(msg) + + if self == post.author: + raise PermissionError('cannot vote for own posts') + + +def user_get_old_vote_for_post(self, post): + """returns previous vote for this post + by the user or None, if does not exist + + raises assertion_error is number of old votes is > 1 + which is illegal + """ + post_content_type = ContentType.objects.get_for_model(post) + old_votes = Vote.objects.filter( + user = self, + content_type = post_content_type, + object_id = post.id + ) + if len(old_votes) == 0: + return None + else: + assert(len(old_votes) == 1) + + return old_votes[0] + +def user_assert_can_revoke_old_vote(self, vote): + """raises PermissionError if old vote + cannot be revoked due to age of the vote + """ + if (datetime.datetime.now().day - vote.voted_at.day) \ + >= askbot_settings.MAX_DAYS_TO_CANCEL_VOTE: + raise PermissionError(_('cannot revoke old vote')) + +def user_get_unused_votes_today(self): + """returns number of votes that are + still available to the user today + """ + today = datetime.date.today() + one_day_interval = (today, today + datetime.timedelta(1)) + + used_votes = Vote.objects.filter( + user = self, + voted_at__range = one_day_interval + ).count() + + available_votes = askbot_settings.MAX_VOTES_PER_USER_PER_DAY - used_votes + return max(0, available_votes) + def user_post_comment( self, parent_post = None, @@ -417,6 +509,7 @@ def toggle_favorite_question(self, question, timestamp=None, cancel=False): Question.objects.update_favorite_count(question) return result +@auto_now_timestamp def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None): """"private" wrapper function that applies post upvotes/downvotes and cancelations @@ -460,13 +553,17 @@ def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None): if vote_type == Vote.VOTE_UP: if cancel: auth.onUpVotedCanceled(vote, post, user, timestamp) + return None else: auth.onUpVoted(vote, post, user, timestamp) + return vote elif vote_type == Vote.VOTE_DOWN: if cancel: auth.onDownVotedCanceled(vote, post, user, timestamp) + return None else: auth.onDownVoted(vote, post, user, timestamp) + return vote def user_unfollow_question(self, question = None): if self in question.followed_by.all(): @@ -477,7 +574,7 @@ def user_follow_question(self, question = None): question.followed_by.add(self) def upvote(self, post, timestamp=None, cancel=False): - _process_vote( + return _process_vote( self,post, timestamp=timestamp, cancel=cancel, @@ -485,7 +582,7 @@ def upvote(self, post, timestamp=None, cancel=False): ) def downvote(self, post, timestamp=None, cancel=False): - _process_vote( + return _process_vote( self,post, timestamp=timestamp, cancel=cancel, @@ -564,6 +661,10 @@ User.add_to_class('can_moderate_user', user_can_moderate_user) User.add_to_class('moderate_user_reputation', user_moderate_user_reputation) User.add_to_class('set_status', user_set_status) User.add_to_class('get_status_display', user_get_status_display) +User.add_to_class('assert_can_vote_for_post', user_assert_can_vote_for_post) +User.add_to_class('get_old_vote_for_post', user_get_old_vote_for_post) +User.add_to_class('assert_can_revoke_old_vote', user_assert_can_revoke_old_vote) +User.add_to_class('get_unused_votes_today', user_get_unused_votes_today) #todo: move this to askbot/utils ?? def format_instant_notification_body( @@ -997,6 +1098,8 @@ EmailFeedSetting = EmailFeedSetting __all__ = [ 'signals', + 'PermissionError', + 'Question', 'QuestionRevision', 'QuestionView', diff --git a/askbot/models/meta.py b/askbot/models/meta.py index 13ddd803..c10bb8fe 100644 --- a/askbot/models/meta.py +++ b/askbot/models/meta.py @@ -56,6 +56,27 @@ class Vote(base.MetaContent, base.UserContent): assert(vote_type in (self.VOTE_UP, self.VOTE_DOWN)) return self.vote != vote_type + def cancel(self): + """cancel the vote + while taking into account whether vote was up + or down + + return change in score on the post + """ + #importing locally because of circular dependency + from askbot import auth + score_before = self.content_object.score + if self.vote > 0: + # cancel upvote + auth.onUpVotedCanceled(self, self.content_object, self.user) + + else: + # cancel downvote + auth.onDownVotedCanceled(self, self.content_object, self.user) + score_after = self.content_object.score + + return score_after - score_before + class FlaggedItemManager(models.Manager): def get_flagged_items_count_today(self, user): diff --git a/askbot/skins/default/media/js/com.cnprog.i18n.js b/askbot/skins/default/media/js/com.cnprog.i18n.js index a52ab52f..926b42ff 100644 --- a/askbot/skins/default/media/js/com.cnprog.i18n.js +++ b/askbot/skins/default/media/js/com.cnprog.i18n.js @@ -4,13 +4,8 @@ var i18nZh = { 'cannot pick own answer as best':'不能设置自己的回答为最佳答案', 'anonymous users cannot select favorite questions':'匿名用户不能收藏问题,请先', 'please login':'注册或者登录', - 'anonymous users cannot vote':'匿名用户不能投票', - '>15 points requried to upvote':'需要+15积分才能投支持票。', - '>100 points required to downvote':'需要+100积分才能投反对票。', 'please see': '查看', - 'cannot vote for own posts':'不能给自己的帖子投票', 'daily vote cap exhausted':'对不起,您已用完今日所有的投票。', - 'cannot revoke old vote':'这个投票已经过时,不能撤销。', 'please confirm offensive':"确定要归类该帖为广告、人身攻击、恶意言论吗?", 'anonymous users cannot flag offensive posts':'匿名用户不能操作,请先', 'cannot flag message as offensive twice':'不能重复操作。', @@ -58,10 +53,9 @@ var i18nZh = { }; var i18nEn = { + "anonymous users cannot vote": "sorry, anonymous users cannot vote ", 'need >15 points to report spam':'need >15 points to report spam ', - '>15 points requried to upvote':'>15 points required to upvote ', 'tags cannot be empty':'please enter at least one tag', - 'anonymous users cannot vote':'sorry, anonymous users cannot vote ', 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ', 'to comment, need': '(to comment other people\'s posts, karma ', 'please see':'please see ', @@ -71,7 +65,6 @@ var i18nEn = { 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"', 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap', 'cannot pick own answer as best':'sorry, you cannot accept your own answer', - 'cannot revoke old vote':'sorry, older votes cannot be revoked', 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?', 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ', 'confirm delete':'are you sure you want to delete this?', @@ -84,16 +77,13 @@ var i18nEn = { 'content minchars': 'please enter more than {0} characters', 'title minchars':"please enter at least {0} characters", 'characters':'characters left', - 'cannot vote for own posts':'sorry, you cannot vote for your own posts', 'cannot flag message as offensive twice':'cannot flag message as offensive twice ', - '>100 points required to downvote':'>100 points required to downvote ' }; var i18nFi = { + "anonymous users cannot vote": "kirjaudu sisään, jotta voit käyttää tätä ominaisuutta ", 'need >15 points to report spam':'tarvitset >15 pistettä raportoidaksesi roskapostista', - '>15 points requried to upvote':'tarvitset >15 points äänestämiseen ', 'tags cannot be empty':'anna vähintään yksi tagi', - 'anonymous users cannot vote':'kirjaudu sisään, jotta voit käyttää tätä ominaisuutta', 'anonymous users cannot select favorite questions':'kirjaudu sisään, jotta voit käyttää tätä ominaisuutta', 'to comment, need': '(kommentoidaksesi muita ', 'please see':'katso ', @@ -103,7 +93,6 @@ var i18nFi = { 'enter url':'Anna URL-osoite, esim. http://www.example.com \"sivun otsikko\"', 'daily vote cap exhausted':'olet käyttänyt tämän päivän osalta äänesi', 'cannot pick own answer as best':'et voi hyväksyä omaa vastaustasi parhaaksi', - 'cannot revoke old vote':'vanhoja ääniä ei voi muuttaa', 'please confirm offensive':'oletko varma, että tämä on roskaposti, loukkaava tai muuta hyväksymätöntä?', 'flag offensive cap exhausted':'olet käyttänyt tämän päivän merkkausmäärät ', 'confirm delete':'oletko varma, että haluat poistaa tämän?', @@ -116,9 +105,7 @@ var i18nFi = { 'content minchars': 'syötä vähintään {0} merkkiä', 'title minchars':"syötä vähintään {0} merkkiä", 'characters':'merkkiä jäljellä', - 'cannot vote for own posts':'et voi äänestää omia postauksia', 'cannot flag message as offensive twice':'ei voi merkata kahta kertaa ', - '>100 points required to downvote':'>100 mainetta tarvitaan', 'questions/' : 'kysymykset/', 'answers/' : 'vastukset/', 'comments/' : 'kommentit/', @@ -133,17 +120,13 @@ var i18nFi = { }; var i18nTr = { + "anonymous users cannot vote":"üye girişi yapmadan oy kullanamazsınız", 'insufficient privilege':'buna yetkiniz yoktur', 'cannot pick own answer as best':'en cevap olarak kendi cevabınızı seçemezsiniz', 'anonymous users cannot select favorite questions':'üye girişi yapmadan favori seçemezsiniz', 'please login':'lütfen üye girişi yapınız', - 'anonymous users cannot vote':'üye girişi yapmadan oy kullanamazsınız', - '>15 points requried to upvote': 'beğeninizi göstermek için en az 15 puan toplamalısınız', - '>100 points required to downvote':'beğenmediğinizi göstermek için en az 100 puan toplamalısınız', 'please see': 'lütfen bakın', - 'cannot vote for own posts':'kendi yazılarınıza oy veremezsiniz', 'daily vote cap exhausted':'bugünlük oy verme kotanız doldu', - 'cannot revoke old vote':'verilen bir oyu iptal edemezsiniz', 'please confirm offensive':"şikayetinizi onaylayın", 'anonymous users cannot flag offensive posts':'üye girişi yapmadan şikayet gönderemezsiniz', 'cannot flag message as offensive twice':'şikayet mesajı olarak iki kez işaretlemelisiniz', @@ -202,17 +185,13 @@ var i18nTr = { }; var i18nEs = { + "anonymous users cannot vote":"usuarios anónimos no pueden votar", 'insufficient privilege':'privilegio insuficiente', 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor', 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar', 'please login':'por favor inicie sesión', - 'anonymous users cannot vote':'usuarios anónimos no pueden votar', - '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente', - '>100 points required to downvote':'>100 puntos requeridos para votar negativamente', 'please see': 'por favor vea', - 'cannot vote for own posts':'no se puede votar por sus propias publicaciones', 'daily vote cap exhausted':'cuota de votos diarios excedida', - 'cannot revoke old vote':'no puede revocar un voto viejo', 'please confirm offensive':"por favor confirme ofensiva", 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas', 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces', @@ -271,21 +250,17 @@ var i18nEs = { }; var i18nDe = { - '>100 points required to downvote': 'Negativ bewerten benötigt mindestens 100 Punkte', - '>15 points requried to upvote': 'Positiv bewerten benötigt mindestens 15 Punkte', + "anonymous users cannot vote":"Gastbenutzer können nicht abstimmen ", 'add a comment': 'Kommentar hinzufügen', 'add comment': 'OK', 'anonymous users cannot delete/undelete': 'Gastbenutzer können Beiträge nicht löschen oder wiederherstellen', 'anonymous users cannot flag offensive posts': 'Gastbenutzer können Beiträge nicht melden', 'anonymous users cannot select favorite questions': 'Gastbenutzer können keine Fragen als Favoriten markieren', - 'anonymous users cannot vote': 'Gastbenutzer können nicht abstimmen', 'bold': 'Fett', 'bulleted list': 'Liste', 'can write': 'Noch ', 'cannot flag message as offensive twice': 'Beiträge können nicht doppelt gemeldet werden', 'cannot pick own answer as best': 'Eigene Antworten können nicht als die korrekte akzeptiert werden', - 'cannot revoke old vote': 'Bewertung kann nicht mehr zurückgenommen werden', - 'cannot vote for own posts': 'Über selbst verfaßte Beiträge kann nicht abgestimmt werden', 'characters': 'Zeichen', 'click to close': 'Schließen mit Klick', 'comments': 'Kommentare', @@ -341,22 +316,18 @@ var i18nDe = { var i18nRu = { - '>100 points required to downvote': 'для понижения рейтинга требуется минимум 100 баллов', - '>15 points requried to upvote': 'для повышения рейтинга требуется минимум 15 баллов', + "anonymous users cannot vote": "неавторизированные пользователи не могут голосовать ", 'add a comment': 'добавить комментарий', 'add comment': 'добавить комментарий', 'anonymous users cannot delete/undelete': 'неавторизированные пользователи не могут восстанавливать и удалять сообщения', 'anonymous users cannot flag offensive posts': 'неавторизированные пользователи не могут пожаловаться на сообщение', 'anonymous users cannot select favorite questions': 'неавторизированные пользователи не могут добавлять вопросы в закладки', - 'anonymous users cannot vote': 'неавторизированные пользователи не могут голосовать', 'answers/': 'ответы/', 'bold': 'жирный', 'bulleted list': 'список', 'can write': 'пишите', 'cannot flag message as offensive twice': 'нельзя отметить сообщение как спам два раза', 'cannot pick own answer as best': 'нельзя выбрать собственный ответ в качестве лучшего', - 'cannot revoke old vote': 'голос не может быть отозван', - 'cannot vote for own posts': 'нельзя голосовать за собственные сообщения', 'characters': 'символы', 'click to close': 'нажмите, что бы закрыть', 'comments': 'комментарии', diff --git a/askbot/skins/default/media/js/com.cnprog.post.js b/askbot/skins/default/media/js/com.cnprog.post.js index 7cc14e67..90956d9e 100755 --- a/askbot/skins/default/media/js/com.cnprog.post.js +++ b/askbot/skins/default/media/js/com.cnprog.post.js @@ -57,16 +57,13 @@ var Vote = function(){ var pleaseLogin = "" - + $.i18n._('please login') + ""; + + $.i18n._('please login') + ""; var pleaseSeeFAQ = $.i18n._('please see') + "faq"; var favoriteAnonymousMessage = $.i18n._('anonymous users cannot select favorite questions'); var voteAnonymousMessage = $.i18n._('anonymous users cannot vote') + pleaseLogin; - var upVoteRequiredScoreMessage = $.i18n._('>15 points requried to upvote') + pleaseSeeFAQ; - var downVoteRequiredScoreMessage = $.i18n._('>100 points required to downvote') + pleaseSeeFAQ; - var voteOwnDeniedMessage = $.i18n._('cannot vote for own posts'); - var voteRequiredMoreVotes = $.i18n._('daily vote cap exhausted') + pleaseSeeFAQ; + //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; @@ -235,6 +232,7 @@ var Vote = function(){ }; var submit = function(object, voteType, callback) { + //this function submits votes $.ajax({ type: "POST", cache: false, @@ -305,27 +303,25 @@ var Vote = function(){ }; var callback_vote = function(object, voteType, data){ - if(data.allowed == "0" && data.success == "0"){ - showMessage(object, voteAnonymousMessage.replace("{{QuestionID}}", questionId)); - } - else if (data.allowed == "-3"){ - showMessage(object, voteRequiredMoreVotes); + if (data.success == '0'){ + showMessage(object, data.message); + return; } - else if (data.allowed == "-2"){ - if (voteType == VoteType.questionUpVote || voteType == VoteType.answerUpVote){ - showMessage(object, upVoteRequiredScoreMessage); + else { + if (data.status == '1'){ + setVoteImage(voteType, true, object); } - else if (voteType == VoteType.questionDownVote || voteType == VoteType.answerDownVote){ - showMessage(object, downVoteRequiredScoreMessage); + else { + setVoteImage(voteType, false, object); } + setVoteNumber(object, data.count); + if (data.message.length > 0){ + showMessage(object, data.message); + } + return; } - else if (data.allowed == "-1"){ - showMessage(object, voteOwnDeniedMessage); - } - else if (data.status == "2"){ - showMessage(object, voteDenyCancelMessage); - } - else if (data.status == "1"){ + //may need to take a look at this again + if (data.status == "1"){ setVoteImage(voteType, true, object); setVoteNumber(object, data.count); } @@ -389,12 +385,12 @@ var Vote = function(){ bindEvents(); }, - // Accept answer public function + //accept answer accept: function(object){ postId = object.attr("id").substring(imgIdPrefixAccept.length); submit(object, VoteType.acceptAnswer, callback_accept); }, - + //mark question as favorite favorite: function(object){ if (!currentUserId || currentUserId.toUpperCase() == "NONE"){ showMessage(object, favoriteAnonymousMessage.replace("{{QuestionID}}", questionId)); @@ -405,9 +401,9 @@ var Vote = function(){ vote: function(object, voteType){ if (!currentUserId || currentUserId.toUpperCase() == "NONE"){ - showMessage(object, voteAnonymousMessage.replace("{{QuestionID}}", questionId).replace("{{questionSlug}}", questionSlug)); - return false; + showMessage($(object), voteAnonymousMessage); } + // up and downvote processor if (voteType == VoteType.answerUpVote){ postId = object.attr("id").substring(imgIdPrefixAnswerVoteup.length); } @@ -417,7 +413,7 @@ var Vote = function(){ submit(object, voteType, callback_vote); }, - + //flag offensive offensive: function(object, voteType){ if (!currentUserId || currentUserId.toUpperCase() == "NONE"){ showMessage($(object), offensiveAnonymousMessage.replace("{{QuestionID}}", questionId)); @@ -428,7 +424,7 @@ var Vote = function(){ submit(object, voteType, callback_offensive); } }, - + //delete question or answer (comments are deleted separately) remove: function(object, voteType){ if (!currentUserId || currentUserId.toUpperCase() == "NONE"){ showMessage($(object), removeAnonymousMessage.replace("{{QuestionID}}", questionId)); diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py index 3813d358..8c88e426 100644 --- a/askbot/utils/decorators.py +++ b/askbot/utils/decorators.py @@ -1,6 +1,7 @@ import hotshot import time import os +import datetime import functools from django.conf import settings from django.http import HttpResponse, HttpResponseForbidden, Http404 diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 1d9f9173..0ddfc36e 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -23,6 +23,61 @@ from django.contrib.auth.decorators import login_required from askbot.utils.decorators import ajax_method, ajax_login_required import logging +def process_vote(user = None, vote_direction = None, post = None): + """function (non-view) that actually processes user votes + - i.e. up- or down- votes + + in the future this needs to be converted into a real view function + for that url and javascript will need to be adjusted + + 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 PermissionError(_('anonymous users cannot vote')) + + user.assert_can_vote_for_post( + post = post, + direction = vote_direction + ) + + vote = user.get_old_vote_for_post(post) + response_data = {} + if vote != None: + user.assert_can_revoke_old_vote(vote) + score_delta = vote.cancel() + response_data['count'] = post.score + score_delta + response_data['status'] = 1 #this means "cancel" + + else: + #this is a new vote + votes_left = user.get_unused_votes_today() + if votes_left <= 0: + raise PermissionError( + _('Sorry you ran out of votes for today') + ) + + votes_left -= 1 + if votes_left <= \ + askbot_settings.VOTES_LEFT_WARNING_THRESHOLD: + msg = _('You have %(votes_left)s votes left for today') \ + % {'votes_left': votes_left } + response_data['message'] = msg + + if vote_direction == 'up': + vote = user.upvote(post = post) + else: + vote = user.downvote(post = post) + + response_data['count'] = post.score + response_data['status'] = 0 #this means "not cancel", normal operation + + response_data['success'] = 1 + + return response_data + + def vote(request, id): """ todo: this subroutine needs serious refactoring it's too long and is hard to understand @@ -44,7 +99,7 @@ def vote(request, id): accept answer code: response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default response_data['success'] = 0, failed 1, Success - by default - response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel) + response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel) vote code: allowed = -3, Don't have enough votes left @@ -72,23 +127,81 @@ def vote(request, id): "message" : '' } - def __can_vote(vote_score, user):#refactor - belongs to auth.py - if vote_score == 1:#refactor magic number - return auth.can_vote_up(request.user) + try: + if request.is_ajax() and request.method == 'POST': + vote_type = request.POST.get('type') else: - return auth.can_vote_down(request.user) + raise Exception(_('Sorry, something is not right here...')) - try: - if not request.user.is_authenticated(): - response_data['allowed'] = 0 - response_data['success'] = 0 + if vote_type == '0': + if request.user.is_authenticated(): + answer_id = request.POST.get('postId') + answer = get_object_or_404(Answer, id=answer_id) + question = answer.question + # 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: + raise Exception( + 'Sorry, only question owner can accept the answer' + ) + else: + raise Exception( + _('Sorry, but anonymous users cannot accept answers') + ) + + elif vote_type in ('1', '2', '5', '6'):#Q&A up/down votes + + ############################### + # all this can be avoided with + # better query parameters + vote_direction = 'up' + if vote_type in ('2','6'): + vote_direction = 'down' + + if vote_type in ('5', '6'): + #todo: fix this weirdness - why postId here + #and not with question? + id = request.POST.get('postId') + post = get_object_or_404(Answer, id=id) + else: + post = get_object_or_404(Question, id=id) + # + ###################### + + response_data = process_vote( + user = request.user, + vote_direction = vote_direction, + post = post + ) elif request.is_ajax() and request.method == 'POST': + + if not request.user.is_authenticated(): + response_data['allowed'] = 0 + response_data['success'] = 0 + question = get_object_or_404(Question, id=id) vote_type = request.POST.get('type') #accept answer - if vote_type == '0': + 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 @@ -122,65 +235,6 @@ def vote(request, id): ).count() if fave == False: response_data['status'] = 1 - - elif vote_type in ['1', '2', '5', '6']: - post_id = id - post = question - vote_score = 1 - if vote_type in ['5', '6']: - answer_id = request.POST.get('postId') - answer = get_object_or_404(Answer, id=answer_id) - post_id = answer_id - post = answer - if vote_type in ['2', '6']: - vote_score = -1 - - if post.author == request.user: - response_data['allowed'] = -1 - elif not __can_vote(vote_score, request.user): - response_data['allowed'] = -2 - elif post.votes.filter(user=request.user).count() > 0: - #todo: I think we have a bug here - #we need to instead select vote on that particular post - #not just the latest vote, although it is a good shortcut. - #The problem is that this vote is deleted in one of - #the on...Canceled() functions - vote = post.votes.filter(user=request.user)[0] - # get latest vote by the current user - # unvote should be less than certain time - if (datetime.datetime.now().day - vote.voted_at.day) \ - >= askbot_settings.MAX_DAYS_TO_CANCEL_VOTE: - response_data['status'] = 2 - else: - voted = vote.vote - if voted > 0: - # cancel upvote - auth.onUpVotedCanceled(vote, post, request.user) - - else: - # cancel downvote - auth.onDownVotedCanceled(vote, post, request.user) - - response_data['status'] = 1 - response_data['count'] = post.score - elif Vote.objects.get_votes_count_today_from_user(request.user)\ - >= askbot_settings.MAX_VOTES_PER_USER_PER_DAY: - response_data['allowed'] = -3 - else: - vote = Vote(user=request.user, content_object=post, vote=vote_score, voted_at=datetime.datetime.now()) - if vote_score > 0: - # upvote - auth.onUpVoted(vote, post, request.user) - else: - # downvote - auth.onDownVoted(vote, post, request.user) - - votes_left = askbot_settings.MAX_VOTES_PER_USER_PER_DAY \ - - Vote.objects.get_votes_count_today_from_user(request.user) - if votes_left <= \ - askbot_settings.VOTES_LEFT_WARNING_THRESHOLD: - response_data['message'] = u'%s votes left' % votes_left - response_data['count'] = post.score elif vote_type in ['7', '8']: post = question post_id = id @@ -249,6 +303,7 @@ def vote(request, id): except Exception, e: response_data['message'] = str(e) + response_data['success'] = 0 data = simplejson.dumps(response_data) return HttpResponse(data, mimetype="application/json") -- cgit v1.2.3-1-g7c22