Commit 2c6e925930cab0a22941c76fb1ee5cfef977fa19
1 parent
753af621
Exists in
master
and in
5 other branches
Fixing new post location [Issue: #215]
Showing
6 changed files
with
280 additions
and
35 deletions
Show diff stats
core/static/css/base/amadeus.css
... | ... | @@ -368,4 +368,30 @@ ul, li { |
368 | 368 | /* core/reset_password.html classes*/ |
369 | 369 | .send-reset-email{ |
370 | 370 | float: right; |
371 | +} | |
372 | + | |
373 | +/* forum post loaded */ | |
374 | +@-webkit-keyframes loaded { | |
375 | + 0% { | |
376 | + background-color: Yellow; | |
377 | + opacity: 0.2; | |
378 | + } | |
379 | + 22% { | |
380 | + background-color: Yellow; | |
381 | + opacity: 0.3; | |
382 | + } | |
383 | + 77% { | |
384 | + background-color: Yellow; | |
385 | + opacity: 0.6; | |
386 | + } | |
387 | + 100% { | |
388 | + background-color: White; | |
389 | + } | |
390 | +} | |
391 | + | |
392 | +.loaded { | |
393 | + -webkit-animation-name: loaded; | |
394 | + -webkit-animation-duration: 900ms; | |
395 | + -webkit-animation-iteration-count: 3; | |
396 | + -webkit-animation-timing-function: ease-in-out; | |
371 | 397 | } |
372 | 398 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,156 @@ |
1 | +/*! | |
2 | + * JavaScript Cookie v2.1.3 | |
3 | + * https://github.com/js-cookie/js-cookie | |
4 | + * | |
5 | + * Copyright 2006, 2015 Klaus Hartl & Fagner Brack | |
6 | + * Released under the MIT license | |
7 | + */ | |
8 | +;(function (factory) { | |
9 | + var registeredInModuleLoader = false; | |
10 | + if (typeof define === 'function' && define.amd) { | |
11 | + define(factory); | |
12 | + registeredInModuleLoader = true; | |
13 | + } | |
14 | + if (typeof exports === 'object') { | |
15 | + module.exports = factory(); | |
16 | + registeredInModuleLoader = true; | |
17 | + } | |
18 | + if (!registeredInModuleLoader) { | |
19 | + var OldCookies = window.Cookies; | |
20 | + var api = window.Cookies = factory(); | |
21 | + api.noConflict = function () { | |
22 | + window.Cookies = OldCookies; | |
23 | + return api; | |
24 | + }; | |
25 | + } | |
26 | +}(function () { | |
27 | + function extend () { | |
28 | + var i = 0; | |
29 | + var result = {}; | |
30 | + for (; i < arguments.length; i++) { | |
31 | + var attributes = arguments[ i ]; | |
32 | + for (var key in attributes) { | |
33 | + result[key] = attributes[key]; | |
34 | + } | |
35 | + } | |
36 | + return result; | |
37 | + } | |
38 | + | |
39 | + function init (converter) { | |
40 | + function api (key, value, attributes) { | |
41 | + var result; | |
42 | + if (typeof document === 'undefined') { | |
43 | + return; | |
44 | + } | |
45 | + | |
46 | + // Write | |
47 | + | |
48 | + if (arguments.length > 1) { | |
49 | + attributes = extend({ | |
50 | + path: '/' | |
51 | + }, api.defaults, attributes); | |
52 | + | |
53 | + if (typeof attributes.expires === 'number') { | |
54 | + var expires = new Date(); | |
55 | + expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); | |
56 | + attributes.expires = expires; | |
57 | + } | |
58 | + | |
59 | + try { | |
60 | + result = JSON.stringify(value); | |
61 | + if (/^[\{\[]/.test(result)) { | |
62 | + value = result; | |
63 | + } | |
64 | + } catch (e) {} | |
65 | + | |
66 | + if (!converter.write) { | |
67 | + value = encodeURIComponent(String(value)) | |
68 | + .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); | |
69 | + } else { | |
70 | + value = converter.write(value, key); | |
71 | + } | |
72 | + | |
73 | + key = encodeURIComponent(String(key)); | |
74 | + key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); | |
75 | + key = key.replace(/[\(\)]/g, escape); | |
76 | + | |
77 | + return (document.cookie = [ | |
78 | + key, '=', value, | |
79 | + attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE | |
80 | + attributes.path ? '; path=' + attributes.path : '', | |
81 | + attributes.domain ? '; domain=' + attributes.domain : '', | |
82 | + attributes.secure ? '; secure' : '' | |
83 | + ].join('')); | |
84 | + } | |
85 | + | |
86 | + // Read | |
87 | + | |
88 | + if (!key) { | |
89 | + result = {}; | |
90 | + } | |
91 | + | |
92 | + // To prevent the for loop in the first place assign an empty array | |
93 | + // in case there are no cookies at all. Also prevents odd result when | |
94 | + // calling "get()" | |
95 | + var cookies = document.cookie ? document.cookie.split('; ') : []; | |
96 | + var rdecode = /(%[0-9A-Z]{2})+/g; | |
97 | + var i = 0; | |
98 | + | |
99 | + for (; i < cookies.length; i++) { | |
100 | + var parts = cookies[i].split('='); | |
101 | + var cookie = parts.slice(1).join('='); | |
102 | + | |
103 | + if (cookie.charAt(0) === '"') { | |
104 | + cookie = cookie.slice(1, -1); | |
105 | + } | |
106 | + | |
107 | + try { | |
108 | + var name = parts[0].replace(rdecode, decodeURIComponent); | |
109 | + cookie = converter.read ? | |
110 | + converter.read(cookie, name) : converter(cookie, name) || | |
111 | + cookie.replace(rdecode, decodeURIComponent); | |
112 | + | |
113 | + if (this.json) { | |
114 | + try { | |
115 | + cookie = JSON.parse(cookie); | |
116 | + } catch (e) {} | |
117 | + } | |
118 | + | |
119 | + if (key === name) { | |
120 | + result = cookie; | |
121 | + break; | |
122 | + } | |
123 | + | |
124 | + if (!key) { | |
125 | + result[name] = cookie; | |
126 | + } | |
127 | + } catch (e) {} | |
128 | + } | |
129 | + | |
130 | + return result; | |
131 | + } | |
132 | + | |
133 | + api.set = api; | |
134 | + api.get = function (key) { | |
135 | + return api.call(api, key); | |
136 | + }; | |
137 | + api.getJSON = function () { | |
138 | + return api.apply({ | |
139 | + json: true | |
140 | + }, [].slice.call(arguments)); | |
141 | + }; | |
142 | + api.defaults = {}; | |
143 | + | |
144 | + api.remove = function (key, attributes) { | |
145 | + api(key, '', extend(attributes, { | |
146 | + expires: -1 | |
147 | + })); | |
148 | + }; | |
149 | + | |
150 | + api.withConverter = init; | |
151 | + | |
152 | + return api; | |
153 | + } | |
154 | + | |
155 | + return init(function () {}); | |
156 | +})); | |
0 | 157 | \ No newline at end of file | ... | ... |
core/templates/base.html
... | ... | @@ -29,6 +29,7 @@ |
29 | 29 | <script type="text/javascript" src="{% static 'js/vendor/ripples.min.js' %}"></script> |
30 | 30 | <script type="text/javascript" src="{% static 'js/vendor/bootstrap-datepicker.js' %}"></script> |
31 | 31 | <script type="text/javascript" src="{% static 'js/vendor/alertify.min.js' %}"></script> |
32 | + <script type="text/javascript" src="{% static 'js/vendor/jscookie.js' %}"></script> | |
32 | 33 | |
33 | 34 | <!-- Font awesome --> |
34 | 35 | <link rel="stylesheet" type="text/css" href="{% static 'font-awesome-4.6.3/css/font-awesome.min.css' %}"> | ... | ... |
forum/static/js/forum.js
1 | -/* | |
2 | -* | |
3 | -* Function to get a cookie stored on browser | |
4 | -* | |
5 | -*/ | |
6 | -function getCookie(name) { | |
7 | - var cookieValue = null; | |
8 | - if (document.cookie && document.cookie !== '') { | |
9 | - var cookies = document.cookie.split(';'); | |
10 | - for (var i = 0; i < cookies.length; i++) { | |
11 | - var cookie = jQuery.trim(cookies[i]); | |
12 | - // Does this cookie string begin with the name we want? | |
13 | - if (cookie.substring(0, name.length + 1) === (name + '=')) { | |
14 | - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); | |
15 | - break; | |
16 | - } | |
17 | - } | |
18 | - } | |
19 | - return cookieValue; | |
20 | -} | |
1 | +var new_posts = []; //Store the new posts ids | |
21 | 2 | |
22 | 3 | /* |
23 | 4 | * |
... | ... | @@ -31,8 +12,16 @@ $(document).ready(function (){ |
31 | 12 | type: frm.attr('method'), |
32 | 13 | url: frm.attr('action'), |
33 | 14 | data: frm.serialize(), |
15 | + dataType: 'json', | |
34 | 16 | success: function (data) { |
35 | - $("#posts_list").append(data); | |
17 | + if ($("#load_more_posts").length == 0) { | |
18 | + $("#posts_list").append(data.html); | |
19 | + } else { | |
20 | + $("#load_more_posts").before(data.html); | |
21 | + } | |
22 | + | |
23 | + new_posts.push(data.new_id); | |
24 | + | |
36 | 25 | frm[0].reset(); |
37 | 26 | }, |
38 | 27 | error: function(data) { |
... | ... | @@ -86,8 +75,6 @@ function setForumCreateFormSubmit() { |
86 | 75 | $('.foruns_list').append("<li><i class='fa fa-commenting' aria-hidden='true'></i> <a id='forum_"+data[1]+"' href='"+data[0]+"'> "+data[2]+"</a></li>"); |
87 | 76 | |
88 | 77 | $("#createForum").modal('hide'); |
89 | - | |
90 | - showForum(data[0], data[1]); | |
91 | 78 | }, |
92 | 79 | error: function(data) { |
93 | 80 | $(".forum_form").html(data.responseText); |
... | ... | @@ -157,7 +144,7 @@ function setForumUpdateFormSubmit(success_message) { |
157 | 144 | */ |
158 | 145 | function delete_forum(url, forum, message, return_url) { |
159 | 146 | alertify.confirm(message, function(){ |
160 | - var csrftoken = getCookie('csrftoken'); | |
147 | + var csrftoken = Cookies.get('csrftoken'); | |
161 | 148 | |
162 | 149 | $.ajax({ |
163 | 150 | method: 'post', |
... | ... | @@ -225,7 +212,7 @@ function cancelEditPost(post_id) { |
225 | 212 | * |
226 | 213 | */ |
227 | 214 | function delete_post(url, post) { |
228 | - var csrftoken = getCookie('csrftoken'); | |
215 | + var csrftoken = Cookies.get('csrftoken'); | |
229 | 216 | |
230 | 217 | $.ajax({ |
231 | 218 | method: 'post', |
... | ... | @@ -255,6 +242,8 @@ function load_more_posts(pageNum, numberPages, url) { |
255 | 242 | |
256 | 243 | pageNum += 1; |
257 | 244 | |
245 | + var showing = new_posts.join(','); | |
246 | + | |
258 | 247 | // Show loader |
259 | 248 | $("#loading_posts").show(); |
260 | 249 | |
... | ... | @@ -262,11 +251,22 @@ function load_more_posts(pageNum, numberPages, url) { |
262 | 251 | setTimeout(function (){ |
263 | 252 | $.ajax({ |
264 | 253 | url: url, |
265 | - data: {'page': pageNum}, | |
254 | + data: {'page': pageNum, 'showing': showing}, | |
255 | + dataType: 'json', | |
266 | 256 | success: function(data) { |
267 | 257 | $("#loading_posts").hide(); |
268 | - | |
269 | - $("#posts_list").append(data); | |
258 | + | |
259 | + var child = $("#posts_list").find(".new_post:first"); | |
260 | + | |
261 | + if (child.length == 0) { | |
262 | + $("#posts_list").append(data.html); | |
263 | + } else { | |
264 | + child.before(data.html); | |
265 | + } | |
266 | + | |
267 | + if (data.page != data.num_pages) { | |
268 | + $("#posts_list").append('<a id="load_more_posts" href="javascript:load_more_posts(' + data.page + ',' + data.num_pages + ',\'' + url + '\');" class="btn btn-raised btn-primary btn-block">' + data.btn_text + '</a>'); | |
269 | + } | |
270 | 270 | }, |
271 | 271 | error: function(data) { |
272 | 272 | console.log(data); |
... | ... | @@ -364,7 +364,7 @@ function cancelEditPostAnswer(answer_id) { |
364 | 364 | */ |
365 | 365 | function delete_answer(url, answer, message) { |
366 | 366 | alertify.confirm(message, function(){ |
367 | - var csrftoken = getCookie('csrftoken'); | |
367 | + var csrftoken = Cookies.get('csrftoken'); | |
368 | 368 | |
369 | 369 | $.ajax({ |
370 | 370 | method: 'post', | ... | ... |
... | ... | @@ -0,0 +1,50 @@ |
1 | +{% load i18n permission_tags list_post_answer %} | |
2 | + | |
3 | +{% if posts|length > 0 %} | |
4 | + {% for post in posts %} | |
5 | + <div class="row loaded"> | |
6 | + <div id="post_{{ post.id }}" class="col-sm-12 col-xs-12"> | |
7 | + <h3 class="user-name"> | |
8 | + {{ post.user }} | |
9 | + <div class="pull-right"> | |
10 | + <a href="javascript:answer('{{ post.id }}', '{% url 'course:forum:reply_post' %}');"> | |
11 | + <i class="material-icons">reply</i> | |
12 | + </a> | |
13 | + {% if request.user|has_role:'system_admin' or request.user == post.user %} | |
14 | + {% csrf_token %} | |
15 | + <div class="btn-group icon-more-horiz"> | |
16 | + <a class="btn btn-default btn-xs dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | |
17 | + <i class="material-icons">more_horiz</i> | |
18 | + </a> | |
19 | + <ul class="dropdown-menu pull-right" aria-labelledby="dropdownMenu1"> | |
20 | + <li><a href="javascript:edit_post('{% url 'course:forum:update_post' post.id %}', '{{ post.id }}', '{% trans 'Post edited successfully!' %}')"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li> | |
21 | + <li><a href="javascript:javascript:delete_post('{% url 'course:forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li> | |
22 | + </ul> | |
23 | + </div> | |
24 | + {% endif %} | |
25 | + </div> | |
26 | + </h3> | |
27 | + <div class="post_content"> | |
28 | + <div class="card-data"> | |
29 | + <p class="comment-date"> | |
30 | + <i class="fa fa-clock-o"></i> {{ post.post_date|timesince }} {% trans 'ago' %} | |
31 | + {% if post.is_modified %} | |
32 | + <em> - {% trans 'Edited' %}</em> | |
33 | + {% endif %} | |
34 | + </p> | |
35 | + </div> | |
36 | + <p class="comment-text">{{ post.message|linebreaks }}</p> | |
37 | + </div> | |
38 | + <div class="answer_post"></div> | |
39 | + <div class="answer_list"> | |
40 | + {% list_post_answer request post %} | |
41 | + </div> | |
42 | + <div class="alert alert-primary loading_answers" role="alert" style="display: none"> | |
43 | + <center> | |
44 | + <span class="fa fa-spin fa-circle-o-notch"></span> | |
45 | + </center> | |
46 | + </div> | |
47 | + </div> | |
48 | + </div> | |
49 | + {% endfor %} | |
50 | +{% endif %} | |
0 | 51 | \ No newline at end of file | ... | ... |
forum/views.py
... | ... | @@ -5,15 +5,17 @@ from django.utils.translation import ugettext_lazy as _ |
5 | 5 | from django.views import generic |
6 | 6 | from django.contrib.auth.mixins import LoginRequiredMixin |
7 | 7 | from django.core.paginator import Paginator, EmptyPage |
8 | -from django.http import Http404 | |
8 | +from django.http import Http404, JsonResponse | |
9 | +from django.urls import reverse | |
10 | +from django.template.loader import render_to_string | |
9 | 11 | |
10 | 12 | from .models import Forum, Post, PostAnswer |
11 | 13 | from courses.models import Topic |
12 | -from core.mixins import NotificationMixin | |
13 | 14 | from core.models import Action, Resource |
14 | 15 | |
15 | 16 | from .forms import ForumForm, PostForm, PostAnswerForm |
16 | -from django.urls import reverse | |
17 | + | |
18 | +from core.mixins import NotificationMixin | |
17 | 19 | |
18 | 20 | """ |
19 | 21 | Forum Section |
... | ... | @@ -130,7 +132,13 @@ def load_posts(request, forum_id): |
130 | 132 | |
131 | 133 | forum = get_object_or_404(Forum, id = forum_id) |
132 | 134 | |
133 | - posts = Post.objects.filter(forum = forum).order_by('post_date') | |
135 | + showing = request.GET.get('showing', '') | |
136 | + | |
137 | + if showing == '': | |
138 | + posts = Post.objects.filter(forum = forum).order_by('post_date') | |
139 | + else: | |
140 | + showing = showing.split(',') | |
141 | + posts = Post.objects.filter(forum = forum).exclude(id__in = showing).order_by('post_date') | |
134 | 142 | |
135 | 143 | paginator = Paginator(posts, 2) |
136 | 144 | |
... | ... | @@ -150,7 +158,9 @@ def load_posts(request, forum_id): |
150 | 158 | context['posts'] = page_obj.object_list |
151 | 159 | context['forum'] = forum |
152 | 160 | |
153 | - return render(request, 'post/post_list.html', context) | |
161 | + html = render_to_string('post/post_load_more_render.html', context, request) | |
162 | + | |
163 | + return JsonResponse({'num_pages': paginator.num_pages, 'page': page_obj.number, 'btn_text': _('Load more posts'), 'html': html}) | |
154 | 164 | |
155 | 165 | class CreatePostView(LoginRequiredMixin, generic.edit.CreateView, NotificationMixin): |
156 | 166 | login_url = reverse_lazy("core:home") |
... | ... | @@ -181,7 +191,9 @@ def render_post(request, post): |
181 | 191 | context = {} |
182 | 192 | context['post'] = last_post |
183 | 193 | |
184 | - return render(request, "post/post_render.html", context) | |
194 | + html = render_to_string("post/post_render.html", context, request) | |
195 | + | |
196 | + return JsonResponse({'new_id': last_post.id, 'html': html}) | |
185 | 197 | |
186 | 198 | class PostUpdateView(LoginRequiredMixin, generic.UpdateView): |
187 | 199 | login_url = reverse_lazy("core:home") | ... | ... |