Commit 2c6e925930cab0a22941c76fb1ee5cfef977fa19

Authored by Zambom
1 parent 753af621

Fixing new post location [Issue: #215]

core/static/css/base/amadeus.css
@@ -368,4 +368,30 @@ ul, li { @@ -368,4 +368,30 @@ ul, li {
368 /* core/reset_password.html classes*/ 368 /* core/reset_password.html classes*/
369 .send-reset-email{ 369 .send-reset-email{
370 float: right; 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 \ No newline at end of file 398 \ No newline at end of file
core/static/js/vendor/jscookie.js 0 → 100644
@@ -0,0 +1,156 @@ @@ -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 \ No newline at end of file 157 \ No newline at end of file
core/templates/base.html
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 <script type="text/javascript" src="{% static 'js/vendor/ripples.min.js' %}"></script> 29 <script type="text/javascript" src="{% static 'js/vendor/ripples.min.js' %}"></script>
30 <script type="text/javascript" src="{% static 'js/vendor/bootstrap-datepicker.js' %}"></script> 30 <script type="text/javascript" src="{% static 'js/vendor/bootstrap-datepicker.js' %}"></script>
31 <script type="text/javascript" src="{% static 'js/vendor/alertify.min.js' %}"></script> 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 <!-- Font awesome --> 34 <!-- Font awesome -->
34 <link rel="stylesheet" type="text/css" href="{% static 'font-awesome-4.6.3/css/font-awesome.min.css' %}"> 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,8 +12,16 @@ $(document).ready(function (){
31 type: frm.attr('method'), 12 type: frm.attr('method'),
32 url: frm.attr('action'), 13 url: frm.attr('action'),
33 data: frm.serialize(), 14 data: frm.serialize(),
  15 + dataType: 'json',
34 success: function (data) { 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 frm[0].reset(); 25 frm[0].reset();
37 }, 26 },
38 error: function(data) { 27 error: function(data) {
@@ -86,8 +75,6 @@ function setForumCreateFormSubmit() { @@ -86,8 +75,6 @@ function setForumCreateFormSubmit() {
86 $('.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>"); 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 $("#createForum").modal('hide'); 77 $("#createForum").modal('hide');
89 -  
90 - showForum(data[0], data[1]);  
91 }, 78 },
92 error: function(data) { 79 error: function(data) {
93 $(".forum_form").html(data.responseText); 80 $(".forum_form").html(data.responseText);
@@ -157,7 +144,7 @@ function setForumUpdateFormSubmit(success_message) { @@ -157,7 +144,7 @@ function setForumUpdateFormSubmit(success_message) {
157 */ 144 */
158 function delete_forum(url, forum, message, return_url) { 145 function delete_forum(url, forum, message, return_url) {
159 alertify.confirm(message, function(){ 146 alertify.confirm(message, function(){
160 - var csrftoken = getCookie('csrftoken'); 147 + var csrftoken = Cookies.get('csrftoken');
161 148
162 $.ajax({ 149 $.ajax({
163 method: 'post', 150 method: 'post',
@@ -225,7 +212,7 @@ function cancelEditPost(post_id) { @@ -225,7 +212,7 @@ function cancelEditPost(post_id) {
225 * 212 *
226 */ 213 */
227 function delete_post(url, post) { 214 function delete_post(url, post) {
228 - var csrftoken = getCookie('csrftoken'); 215 + var csrftoken = Cookies.get('csrftoken');
229 216
230 $.ajax({ 217 $.ajax({
231 method: 'post', 218 method: 'post',
@@ -255,6 +242,8 @@ function load_more_posts(pageNum, numberPages, url) { @@ -255,6 +242,8 @@ function load_more_posts(pageNum, numberPages, url) {
255 242
256 pageNum += 1; 243 pageNum += 1;
257 244
  245 + var showing = new_posts.join(',');
  246 +
258 // Show loader 247 // Show loader
259 $("#loading_posts").show(); 248 $("#loading_posts").show();
260 249
@@ -262,11 +251,22 @@ function load_more_posts(pageNum, numberPages, url) { @@ -262,11 +251,22 @@ function load_more_posts(pageNum, numberPages, url) {
262 setTimeout(function (){ 251 setTimeout(function (){
263 $.ajax({ 252 $.ajax({
264 url: url, 253 url: url,
265 - data: {'page': pageNum}, 254 + data: {'page': pageNum, 'showing': showing},
  255 + dataType: 'json',
266 success: function(data) { 256 success: function(data) {
267 $("#loading_posts").hide(); 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 error: function(data) { 271 error: function(data) {
272 console.log(data); 272 console.log(data);
@@ -364,7 +364,7 @@ function cancelEditPostAnswer(answer_id) { @@ -364,7 +364,7 @@ function cancelEditPostAnswer(answer_id) {
364 */ 364 */
365 function delete_answer(url, answer, message) { 365 function delete_answer(url, answer, message) {
366 alertify.confirm(message, function(){ 366 alertify.confirm(message, function(){
367 - var csrftoken = getCookie('csrftoken'); 367 + var csrftoken = Cookies.get('csrftoken');
368 368
369 $.ajax({ 369 $.ajax({
370 method: 'post', 370 method: 'post',
forum/templates/post/post_load_more_render.html 0 → 100644
@@ -0,0 +1,50 @@ @@ -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 \ No newline at end of file 51 \ No newline at end of file
forum/views.py
@@ -5,15 +5,17 @@ from django.utils.translation import ugettext_lazy as _ @@ -5,15 +5,17 @@ from django.utils.translation import ugettext_lazy as _
5 from django.views import generic 5 from django.views import generic
6 from django.contrib.auth.mixins import LoginRequiredMixin 6 from django.contrib.auth.mixins import LoginRequiredMixin
7 from django.core.paginator import Paginator, EmptyPage 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 from .models import Forum, Post, PostAnswer 12 from .models import Forum, Post, PostAnswer
11 from courses.models import Topic 13 from courses.models import Topic
12 -from core.mixins import NotificationMixin  
13 from core.models import Action, Resource 14 from core.models import Action, Resource
14 15
15 from .forms import ForumForm, PostForm, PostAnswerForm 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 Forum Section 21 Forum Section
@@ -130,7 +132,13 @@ def load_posts(request, forum_id): @@ -130,7 +132,13 @@ def load_posts(request, forum_id):
130 132
131 forum = get_object_or_404(Forum, id = forum_id) 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 paginator = Paginator(posts, 2) 143 paginator = Paginator(posts, 2)
136 144
@@ -150,7 +158,9 @@ def load_posts(request, forum_id): @@ -150,7 +158,9 @@ def load_posts(request, forum_id):
150 context['posts'] = page_obj.object_list 158 context['posts'] = page_obj.object_list
151 context['forum'] = forum 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 class CreatePostView(LoginRequiredMixin, generic.edit.CreateView, NotificationMixin): 165 class CreatePostView(LoginRequiredMixin, generic.edit.CreateView, NotificationMixin):
156 login_url = reverse_lazy("core:home") 166 login_url = reverse_lazy("core:home")
@@ -181,7 +191,9 @@ def render_post(request, post): @@ -181,7 +191,9 @@ def render_post(request, post):
181 context = {} 191 context = {}
182 context['post'] = last_post 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 class PostUpdateView(LoginRequiredMixin, generic.UpdateView): 198 class PostUpdateView(LoginRequiredMixin, generic.UpdateView):
187 login_url = reverse_lazy("core:home") 199 login_url = reverse_lazy("core:home")