From 88a74558407bedb2fa01237843fac61d6f79d4c9 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 8 Oct 2014 15:13:00 +0200 Subject: [PATCH] Infinite scroll for the audit trail. see #585 --- modoboa/core/static/core/js/settings.js | 36 +++++++++++++ modoboa/core/templates/core/logs.html | 11 ++-- modoboa/core/templates/core/logs_page.html | 10 ++++ .../core/templates/core/settings_header.html | 7 ++- modoboa/core/templatetags/core_tags.py | 6 ++- modoboa/core/urls.py | 2 + modoboa/core/views/admin.py | 40 +++++++++++---- .../extensions/admin/static/admin/js/admin.js | 12 ----- modoboa/static/js/listing.js | 51 +++++++++++++------ modoboa/static/js/twocols_nav.js | 17 +++++-- 10 files changed, 139 insertions(+), 53 deletions(-) create mode 100644 modoboa/core/static/core/js/settings.js create mode 100644 modoboa/core/templates/core/logs_page.html diff --git a/modoboa/core/static/core/js/settings.js b/modoboa/core/static/core/js/settings.js new file mode 100644 index 000000000..f43499cba --- /dev/null +++ b/modoboa/core/static/core/js/settings.js @@ -0,0 +1,36 @@ +/** + * Return an instance of Settings. + * + * @constructor + * @param {Object} options - instance options + */ +var Settings = function(options) { + TwocolsNav.call(this, options); +}; + +Settings.prototype = { + defaults: { + main_table_id: "logs_table" + }, + + initialize: function(options) { + this.options = $.extend({}, this.defaults, options); + TwocolsNav.prototype.initialize.call(this, this.options); + this.navobj.register_callback( + "logs", $.proxy(this.logs_callback, this)); + }, + + /** + * Navigation callback: logs. + * + */ + logs_callback: function(data) { + this.select_left_menu(); + if (data.content) { + $('#' + this.options.divid).html(data.content); + } + this.update_listing(data); + } +}; + +Settings.prototype = $.extend({}, TwocolsNav.prototype, Settings.prototype); diff --git a/modoboa/core/templates/core/logs.html b/modoboa/core/templates/core/logs.html index 741e9e6eb..fde47e1d9 100644 --- a/modoboa/core/templates/core/logs.html +++ b/modoboa/core/templates/core/logs.html @@ -1,6 +1,6 @@ -{% load i18n core_tags %} +{% load i18n %}

{% trans "Logs" %} {% trans "Previously on Modoboa..." %}


- +
@@ -10,11 +10,6 @@ - {% for l in logs %} - - - - - {% endfor %} + {% include "core/logs_page.html" %}
{% trans "Date" %}
{{ l.date_created }}{{ l.level|colorize_level|safe }}{{ l.logger }}{{ l.message|tohtml|safe }}
diff --git a/modoboa/core/templates/core/logs_page.html b/modoboa/core/templates/core/logs_page.html new file mode 100644 index 000000000..171a3e9df --- /dev/null +++ b/modoboa/core/templates/core/logs_page.html @@ -0,0 +1,10 @@ +{% load core_tags %} + +{% for l in logs %} + + {{ l.date_created }} + {{ l.level|colorize_level|safe }} + {{ l.logger }} + {{ l.message|tohtml|safe }} + +{% endfor %} diff --git a/modoboa/core/templates/core/settings_header.html b/modoboa/core/templates/core/settings_header.html index 743dfce69..3ca5a607d 100644 --- a/modoboa/core/templates/core/settings_header.html +++ b/modoboa/core/templates/core/settings_header.html @@ -4,17 +4,20 @@ {% block html_head %} + + diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py index 2e2a6667b..3ba0d65fb 100644 --- a/modoboa/core/templatetags/core_tags.py +++ b/modoboa/core/templatetags/core_tags.py @@ -119,10 +119,11 @@ def uprefs_menu(selection, user): @register.filter def colorize_level(level): + """A simple filter a text using a boostrap color.""" classes = { "INFO": "text-info", "WARNING": "text-warning", - "CRITICAL": "text-error" + "CRITICAL": "text-danger" } if not level in classes: return level @@ -131,7 +132,8 @@ def colorize_level(level): @register.filter def tohtml(message): - return re.sub("'(.*?)'", "\g<1>", message) + """Simple tag to format a text using HTML.""" + return re.sub(r"'(.*?)'", "\g<1>", message) @register.simple_tag diff --git a/modoboa/core/urls.py b/modoboa/core/urls.py index a35fd4242..4402f37d2 100644 --- a/modoboa/core/urls.py +++ b/modoboa/core/urls.py @@ -22,6 +22,8 @@ urlpatterns = patterns( url(r'^core/info/$', 'modoboa.core.views.admin.information', name="information"), url(r'^core/logs/$', 'modoboa.core.views.admin.logs', name="log_list"), + url(r'^core/logs/page/$', 'modoboa.core.views.admin.logs_page', + name="logs_page"), url(r'^core/top_notifications/check/$', 'modoboa.core.views.admin.check_top_notifications', name='top_notifications_check'), diff --git a/modoboa/core/views/admin.py b/modoboa/core/views/admin.py index e2b12671b..eb9ac8e41 100644 --- a/modoboa/core/views/admin.py +++ b/modoboa/core/views/admin.py @@ -101,27 +101,45 @@ def information(request, tplname="core/information.html"): }) -@login_required -@user_passes_test(lambda u: u.is_superuser) -def logs(request, tplname="core/logs.html"): - from modoboa.lib.templatetags.lib_tags import pagination_bar - +def get_logs_page(request): + """Return a page of logs.""" sort_order, sort_dir = get_sort_order( request.GET, "date_created", allowed_values=['date_created', 'level', 'logger', 'message'] ) - page = get_listing_page( + return get_listing_page( Log.objects.all().order_by("%s%s" % (sort_dir, sort_order)), request.GET.get("page", 1) ) + + +@login_required +@user_passes_test(lambda u: u.is_superuser) +def logs(request, tplname="core/logs.html"): + """Return a list of log entries. + + This view is only called the first time the page is displayed. + + """ + page = get_logs_page(request) return render_to_json_response({ - "content": render_to_string(tplname, { - "logs": page.object_list, - }), - "page": page.number, - "paginbar": pagination_bar(page), + "callback": "logs", + "content": render_to_string(tplname, {"logs": page.object_list}), + "page": page.number }) + +@login_required +@user_passes_test(lambda u: u.is_superuser) +def logs_page(request, tplname="core/logs_page.html"): + """Return a page containing logs.""" + page = get_logs_page(request) + return render_to_json_response({ + "rows": render_to_string(tplname, {"logs": page.object_list}), + "page": page.number + }) + + @login_required def check_top_notifications(request): """ diff --git a/modoboa/extensions/admin/static/admin/js/admin.js b/modoboa/extensions/admin/static/admin/js/admin.js index 1c18507a5..317549165 100644 --- a/modoboa/extensions/admin/static/admin/js/admin.js +++ b/modoboa/extensions/admin/static/admin/js/admin.js @@ -35,18 +35,6 @@ Admin.prototype = { this.update_listing(data); }, - /** - * A new page has been received, inject it. - * - * @param {Object} data - page content - * @param {string} direction - - */ - add_new_page: function(data, direction) { - $("#objects_table tbody").html(function(pos, oldhtml) { - return oldhtml + data.rows; - }); - }, - /** * Children must override this method. */ diff --git a/modoboa/static/js/listing.js b/modoboa/static/js/listing.js index b1c745d7c..e2bba6185 100644 --- a/modoboa/static/js/listing.js +++ b/modoboa/static/js/listing.js @@ -18,7 +18,8 @@ Listing.prototype = { sortable_selector: '.sortable', with_searchform: true, load_page_url: null, - navigation_params: ["sort_order", "searchquery"] + navigation_params: ["sort_order", "searchquery"], + main_table_id: "objects_table" }, initialize: function(options) { @@ -94,26 +95,46 @@ Listing.prototype = { return args; }, + /** + * A new page has been received, inject it. + * + * @param {Object} data - page content + * @param {string} direction - + */ + add_new_page: function(data, direction) { + console.log("new page"); + console.log(this.options.main_table_id); + $("#{0} tbody".format(this.options.main_table_id)) + .html(function(pos, oldhtml) { + return oldhtml + data.rows; + }); + }, + /** * Update the listing with the received data. * * @this Listing * @param {Object} data - new content */ - update_listing: function(data) { - if ($(window).data("infinite-scroll") !== undefined) { - $(window).infinite_scroll("reset_loaded_pages", data.page); - } else { - $(window).infinite_scroll({ - url: this.options.load_page_url, - get_args: $.proxy(this.get_load_page_args, this), - calculate_bottom: function($element) { - var $last_row = $("#objects_table").find("tr").last(); - return $last_row.offset().top - $element.height(); - }, - process_results: this.add_new_page, - end_of_list_reached: this.end_of_list_reached - }); + update_listing: function(data, with_infinite_scroll) { + var $this = this; + + if (with_infinite_scroll || with_infinite_scroll === undefined) { + if ($(window).data("infinite-scroll") !== undefined) { + $(window).infinite_scroll("reset_loaded_pages", data.page); + } else { + $(window).infinite_scroll({ + url: this.options.load_page_url, + get_args: $.proxy(this.get_load_page_args, this), + calculate_bottom: function($element) { + var $last_row = $("#" + $this.options.main_table_id) + .find("tr").last(); + return $last_row.offset().top - $element.height(); + }, + process_results: $.proxy(this.add_new_page, this), + end_of_list_reached: this.end_of_list_reached + }); + } } var $sortables = $(this.options.sortable_selector); diff --git a/modoboa/static/js/twocols_nav.js b/modoboa/static/js/twocols_nav.js index 5a499b75e..3999e3c99 100644 --- a/modoboa/static/js/twocols_nav.js +++ b/modoboa/static/js/twocols_nav.js @@ -1,3 +1,8 @@ +/** + * Return an instance of TwocolsNav. + * + * @constructor + */ var TwocolsNav = function(options) { Listing.call(this, options); }; @@ -33,7 +38,7 @@ TwocolsNav.prototype = { if (data.content) { $('#' + this.options.divid).html(data.content); } - this.update_listing(data); + this.update_listing(data, false); if (data.onload_cb) { eval(data.onload_cb + '()'); } @@ -121,10 +126,16 @@ TwocolsNav.prototype = { this.navobj.parse_string($link.attr("href"), true).update(); }, - - default_cb: function(data) { + /** + * Select an entry in the left menu based on current url. + */ + select_left_menu: function() { $("a.ajaxlink").parent().removeClass("active"); $("a[name=" + this.navobj.getbaseurl() + "]").parent().addClass("active"); + }, + + default_cb: function(data) { + this.select_left_menu(); this.update_content(data); } };