Infinite scroll for the audit trail.

see #585
This commit is contained in:
Antoine Nguyen
2014-10-08 15:13:00 +02:00
parent 03279dc409
commit 88a7455840
10 changed files with 139 additions and 53 deletions
+36
View File
@@ -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);
+3 -8
View File
@@ -1,6 +1,6 @@
{% load i18n core_tags %}
{% load i18n %}
<h2>{% trans "Logs" %} <small>{% trans "Previously on Modoboa..." %}</small></h2><hr>
<table class="table">
<table id="logs_table" class="table">
<thead>
<tr>
<th class="sortable" data-sort_order="date_created" width="20%">{% trans "Date" %}</th>
@@ -10,11 +10,6 @@
</tr>
</thead>
<tbody>
{% for l in logs %}<tr>
<td>{{ l.date_created }}</td>
<td>{{ l.level|colorize_level|safe }}</td>
<td>{{ l.logger }}</td>
<td>{{ l.message|tohtml|safe }}</td>
</tr>{% endfor %}
{% include "core/logs_page.html" %}
</tbody>
</table>
@@ -0,0 +1,10 @@
{% load core_tags %}
{% for l in logs %}
<tr>
<td>{{ l.date_created }}</td>
<td>{{ l.level|colorize_level|safe }}</td>
<td>{{ l.logger }}</td>
<td>{{ l.message|tohtml|safe }}</td>
</tr>
{% endfor %}
@@ -4,17 +4,20 @@
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/settings.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/jquery.sortable.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.infinitescroll.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/listing.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/twocols_nav.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.sortable.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}core/js/settings.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var settings = new TwocolsNav({
var settings = new Settings({
deflocation: "parameters/",
formid: "settings_form",
divid: "settings_content",
reload_exceptions: ["extensions"]
reload_exceptions: ["extensions"],
load_page_url: "{% url 'core:logs_page' %}"
});
});
</script>
+4 -2
View File
@@ -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("'(.*?)'", "<strong>\g<1></strong>", message)
"""Simple tag to format a text using HTML."""
return re.sub(r"'(.*?)'", "<strong>\g<1></strong>", message)
@register.simple_tag
+2
View File
@@ -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'),
+29 -11
View File
@@ -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):
"""
@@ -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.
*/
+36 -15
View File
@@ -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);
+14 -3
View File
@@ -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);
}
};