Просмотр исходного кода

Asynchronus post voting + ajax helpers

Ralfp 12 лет назад
Родитель
Сommit
41a42d2b8e

+ 11 - 0
misago/apps/errors.py

@@ -1,4 +1,6 @@
 from django.template import RequestContext
+from django.utils.translation import ugettext as _
+from misago.utils.views import json_response
 
 def error_not_implemented(request, *args, **kwargs):
     """Generic "NOT IMPLEMENTED!" Error"""
@@ -6,6 +8,13 @@ def error_not_implemented(request, *args, **kwargs):
 
 
 def error_view(request, error, message):
+    if request.is_ajax():
+        if not message:
+            if error == 404:
+                message = _("Requested page could not be loaded.")
+            if error == 403:
+                message = _("You don't have permission to see requested page.")
+        return json_response(request, status=error, message=message)
     response = request.theme.render_to_response(('error%s.html' % error),
                                                 {
                                                  'message': message,
@@ -28,6 +37,8 @@ def error404(request, message=None):
 def error_banned(request, user=None, ban=None):
     if not ban:
         ban = request.ban
+    if request.is_ajax():
+        return json_response(request, status=403, message=_("You are banned."))
     response = request.theme.render_to_response('error403_banned.html',
                                                 {
                                                  'banned_user': user,

+ 11 - 3
misago/apps/threadtype/jumps.py

@@ -9,6 +9,7 @@ from misago.messages import Message
 from misago.models import Forum, Thread, Post, Karma, WatchedThread
 from misago.readstrackers import ThreadsTracker
 from misago.utils.pagination import make_pagination
+from misago.utils.views import json_response
 from misago.apps.threadtype.base import ViewBase
 
 class JumpView(ViewBase):
@@ -40,9 +41,9 @@ class JumpView(ViewBase):
         except (Thread.DoesNotExist, Post.DoesNotExist):
             return error404(self.request)
         except ACLError403 as e:
-            return error403(request, e.message)
+            return error403(request, e)
         except ACLError404 as e:
-            return error404(request, e.message)
+            return error404(request, e)
 
 
 class LastReplyBaseView(JumpView):
@@ -192,7 +193,6 @@ class UpvotePostBaseView(JumpView):
             vote.ip = request.session.get_ip(request)
             vote.agent = request.META.get('HTTP_USER_AGENT')
             self.make_vote(request, vote)
-            request.messages.set_flash(Message(_('Your vote has been saved.')), 'success', 'threads_%s' % self.post.pk)
             if vote.pk:
                 vote.save(force_update=True)
             else:
@@ -219,6 +219,14 @@ class UpvotePostBaseView(JumpView):
             request.user.save(force_update=True)
             if self.post.user_id:
                 self.post.user.save(force_update=True)
+            if request.is_ajax():
+                return json_response(request, {
+                                               'score_total': self.post.upvotes - self.post.downvotes,
+                                               'score_upvotes': self.post.upvotes,
+                                               'score_downvotes': self.post.downvotes,
+                                               'user_vote': vote.score,
+                                              })
+            request.messages.set_flash(Message(_('Your vote has been saved.')), 'success', 'threads_%s' % self.post.pk)
             return self.redirect_to_post(self.post)
         return view(self.request)
     

+ 7 - 6
misago/utils/views.py

@@ -9,13 +9,14 @@ def redirect_message(request, message, type='info', owner=None):
     return redirect(reverse('index'))
 
 
+def json_response(request, json={}, status=200, message=None):
+    json.update({'code': status, 'message': unicode(message)})
+    response = json_dumps(json, sort_keys=True,  ensure_ascii=False)
+    return HttpResponse(response, content_type='application/json', status=status)
+
+
 def ajax_response(request, template=None, macro=None, vars={}, json={}, status=200, message=None):
     html = ''
     if macro:
         html = request.theme.macro(template, macro, vars, context_instance=RequestContext(request));
-    response = json_dumps(dict(json.items() + {
-                                       'code': status,
-                                       'message': message,
-                                       'html': html
-                                       }.items()), sort_keys=True,  ensure_ascii=False)
-    return HttpResponse(response, content_type='application/json', status=status)
+    return json_response(request, json.update({'html': html}), status, message)

+ 3 - 2
static/cranefly/css/cranefly.css

@@ -1099,8 +1099,9 @@ a.btn-link:hover,a.btn-link:active,a.btn-link:focus{opacity:0.9;filter:alpha(opa
 .thread-body .post-wrapper .post-body .post-content .post-footer .post-rating span.post-like{color:#46a546;}
 .thread-body .post-wrapper .post-body .post-content .post-footer .post-rating span.post-hate{color:#cf402e;}
 .thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form{float:left;margin:0px;padding:0px;}.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link{float:right;margin:0px;margin-left:3.5px;opacity:1;filter:alpha(opacity=100);padding:0px;color:#999999;font-weight:normal;}.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link:hover,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link:active,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link:focus{text-decoration:underline;}
-.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-like:hover,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-like:active,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-like:focus{color:#46a546;}
-.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-hate:hover,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-hate:active,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-hate:focus{color:#cf402e;}
+.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-like:hover,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-like:active,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-like:focus,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-like:disabled{color:#46a546;}
+.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-hate:hover,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-hate:active,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-hate:focus,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link.post-hate:disabled{color:#cf402e;}
+.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link:disabled:hover,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link:disabled:active,.thread-body .post-wrapper .post-body .post-content .post-footer .post-rating form .btn-link:disabled:focus{text-decoration:none;}
 .thread-body .post-wrapper .post-body .post-content .post-footer .post-actions{border-left:1px dotted #e7e7e7;float:right;padding:7px 14px;color:#999999;}.thread-body .post-wrapper .post-body .post-content .post-footer .post-actions a,.thread-body .post-wrapper .post-body .post-content .post-footer .post-actions span,.thread-body .post-wrapper .post-body .post-content .post-footer .post-actions form{float:left;overflow:auto;}
 .thread-body .post-wrapper .post-body .post-content .post-footer .post-actions form{margin:0px;padding:0px;}
 .thread-body .post-wrapper .post-body .post-content .post-footer .post-actions a{margin-left:14px;color:#999999;}.thread-body .post-wrapper .post-body .post-content .post-footer .post-actions a:hover,.thread-body .post-wrapper .post-body .post-content .post-footer .post-actions a a:active{color:#333333;}

+ 8 - 2
static/cranefly/css/cranefly/thread.less

@@ -233,16 +233,22 @@
                 }
 
                 &.post-like {
-                  &:hover, &:active, &:focus {
+                  &:hover, &:active, &:focus, &:disabled {
                     color: @green;
                   }
                 }
 
                 &.post-hate {
-                  &:hover, &:active, &:focus {
+                  &:hover, &:active, &:focus, &:disabled {
                     color: @red;
                   }
                 }
+
+                &:disabled {
+                  &:hover, &:active, &:focus {
+                    text-decoration: none;
+                  }
+                }
               }
             }
           }

+ 50 - 1
static/cranefly/js/cranefly.js

@@ -143,4 +143,53 @@ function link2player(link) {
 
 	// No link
 	return false;
-}
+}
+
+// Ajax errors handler
+$(document).ajaxError(function(event, jqXHR, settings) {
+	var responseJSON = jQuery.parseJSON(jqXHR.responseText);
+	if (responseJSON.message) {
+		alert(responseJSON.message);
+	}
+});
+
+// Ajax: Post votes
+$(function() {
+	$('.post-rating-actions').each(function() {
+		var action_parent = this;
+		var csrf_token = $(this).find('input[name="_csrf_token"]').val();
+		$(this).find('form').submit(function() {
+			var form = this;
+			$.post(this.action, {'_csrf_token': csrf_token}, "json").done(function(data, textStatus, jqXHR) {
+				// Reset stuff and set classess
+				$(action_parent).find('.post-score').removeClass('post-score-good post-score-bad');
+				if (data.score_total > 0) {
+					$(action_parent).find('.post-score-total').addClass('post-score-good');
+				} else if (data.score_total < 0) {
+					$(action_parent).find('.post-score-total').addClass('post-score-bad');
+				} 
+				if (data.score_upvotes > 0) {
+					$(action_parent).find('.post-score-upvotes').addClass('post-score-good');
+				}
+				if (data.score_downvotes > 0) {
+					$(action_parent).find('.post-score-downvotes').addClass('post-score-bad');
+				}
+
+				// Set votes
+				$(action_parent).find('.post-score-total').text(data.score_total);
+				$(action_parent).find('.post-score-upvotes').text(data.score_upvotes);
+				$(action_parent).find('.post-score-downvotes').text(data.score_downvotes);
+
+				// Disable and enable forms
+				if (data.user_vote == 1) {
+					$(action_parent).find('.form-upvote button').attr("disabled", "disabled");
+					$(action_parent).find('.form-downvote button').removeAttr("disabled");
+				} else {
+					$(action_parent).find('.form-upvote button').removeAttr("disabled");
+					$(action_parent).find('.form-downvote button').attr("disabled", "disabled");
+				}
+			}).error();
+			return false;
+		});
+	});
+});

+ 32 - 38
templates/cranefly/threads/thread.html

@@ -242,47 +242,41 @@
           </div>
           <div class="post-footer">{% filter trim %}
             {% if acl.threads.can_see_post_score(forum) %}
-            <div class="post-rating">
-              {% if acl.threads.can_see_post_score(forum) == 1 %}
-              <span class="post-score{% if (post.upvotes - post.downvotes) > 0 %} post-score-good{% elif (post.upvotes - post.downvotes) < 0 %} post-score-bad{% endif %}">{{ post.upvotes - post.downvotes }}</span>
-              {% elif acl.threads.can_see_post_score(forum) == 2%}
-              <span class="post-score{% if post.upvotes %} post-score-good{% endif %}">{{ post.upvotes }}</span>
-              {% endif %}
-              {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_upvote_posts(forum) %}
-              {% if post.karma_vote and post.karma_vote.score > 0 %}
-              <span class="post-like">{% trans %}Like{% endtrans %}</span>
-              {% else %}
-              <form action="{% url 'post_upvote' thread=thread.pk, slug=thread.slug, post=post.pk %}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link post-like">{% trans %}Like{% endtrans %}</button>
-              </form>
-              {% endif %}
-              {% else %}
-              <span class="post-{% if post.upvotes %}like{% else %}neutral{% endif %}">{% trans %}Likes{% endtrans %}</span>
-              {% endif %}
-            {% if acl.threads.can_see_post_score(forum) == 2 %}
-            </div>
-            <div class="post-rating">
-              <span class="post-score{% if post.downvotes %} post-score-bad{% endif %}">{{ post.downvotes }}</span>
-            {% endif %}
-              {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_downvote_posts(forum) %}
-              {% if post.karma_vote and post.karma_vote.score < 0 %}
-              <span class="post-hate">{% trans %}Hate{% endtrans %}</span>
-              {% else %}
-              <form action="{% url 'post_downvote' thread=thread.pk, slug=thread.slug, post=post.pk %}" class="form-inline" method="post">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                <button type="submit" class="btn btn-link post-hate">{% trans %}Hate{% endtrans %}</button>
-              </form>
+            <div{% if user.is_authenticated() and user.pk != post.user_id %} class="post-rating-actions"{% endif %}>
+              <div class="post-rating">
+                {% if acl.threads.can_see_post_score(forum) == 1 %}
+                <span class="post-score post-score-total{% if (post.upvotes - post.downvotes) > 0 %} post-score-good{% elif (post.upvotes - post.downvotes) < 0 %} post-score-bad{% endif %}">{{ post.upvotes - post.downvotes }}</span>
+                {% elif acl.threads.can_see_post_score(forum) == 2%}
+                <span class="post-score post-score-upvotes{% if post.upvotes %} post-score-good{% endif %}">{{ post.upvotes }}</span>
+                {% endif %}
+                {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_upvote_posts(forum) %}
+                <form action="{% url 'post_upvote' thread=thread.pk, slug=thread.slug, post=post.pk %}" class="form-inline form-upvote" method="post">
+                  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                  <button type="submit" class="btn btn-link post-like"{% if post.karma_vote and post.karma_vote.score > 0 %} disabled="disabled"{% endif %}>{% trans %}Like{% endtrans %}</button>
+                </form>
+                {% else %}
+                <span class="post-{% if post.upvotes %}like{% else %}neutral{% endif %}">{% trans %}Likes{% endtrans %}</span>
+                {% endif %}
+              {% if acl.threads.can_see_post_score(forum) == 2 %}
+              </div>
+              <div class="post-rating">
+                <span class="post-score post-score-downvotes{% if post.downvotes %} post-score-bad{% endif %}">{{ post.downvotes }}</span>
               {% endif %}
-              {% elif acl.threads.can_see_post_score(forum) == 2 %}
-              <span class="post-{% if post.downvotes %}hate{% else %}neutral{% endif %}">{% trans %}Hates{% endtrans %}</span>
+                {% if user.is_authenticated() and user.pk != post.user_id and acl.threads.can_downvote_posts(forum) %}
+                <form action="{% url 'post_downvote' thread=thread.pk, slug=thread.slug, post=post.pk %}" class="form-inline form-downvote" method="post">
+                  <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+                  <button type="submit" class="btn btn-link post-hate"{% if post.karma_vote and post.karma_vote.score < 0 %} disabled="disabled"{% endif %}>{% trans %}Hate{% endtrans %}</button>
+                </form>
+                {% elif acl.threads.can_see_post_score(forum) == 2 %}
+                <span class="post-{% if post.downvotes %}hate{% else %}neutral{% endif %}">{% trans %}Hates{% endtrans %}</span>
+                {% endif %}
+              </div>
+              {% if acl.threads.can_see_post_votes(forum) %}
+              <div class="post-rating">
+                <a href="{% url 'post_votes' thread=thread.pk, slug=thread.slug, post=post.pk %}">{% trans %}Show Votes{% endtrans %}</a>
+              </div>
               {% endif %}
             </div>
-            {% if acl.threads.can_see_post_votes(forum) %}
-            <div class="post-rating">
-              <a href="{% url 'post_votes' thread=thread.pk, slug=thread.slug, post=post.pk %}">{% trans %}Show Votes{% endtrans %}</a>
-            </div>
-            {% endif %}
             {% endif %}
 
             {% if user.is_authenticated() %}