Infinite scroll for domains and identities.

see #585
This commit is contained in:
Antoine Nguyen
2014-10-08 09:01:51 +02:00
parent 6ef8df9daf
commit 24ac5be4d6
13 changed files with 178 additions and 89 deletions
@@ -24,11 +24,25 @@ Admin.prototype = {
},
list_cb: function(data) {
$("#listing").html(data.table);
$("#objects_table tbody").html(function(pos, oldhtml) {
return oldhtml + data.rows;
});
this.update_listing(data);
$("a.filter").click($.proxy(this.filter_by_tag, this));
},
/**
* 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.
*/
@@ -95,6 +109,14 @@ Domains.prototype = {
this.register_tag_handler("dom", this.generic_tag_handler);
},
end_of_list_reached: function(element) {
$("#objects_table").after(
$("<div class='alert alert-info'>{0}</div>".format(
gettext("No more domain to show")
))
);
},
list_cb: function(data) {
Admin.prototype.list_cb.call(this, data);
var deloptions = (data.handle_mailboxes)
@@ -237,6 +259,14 @@ Identities.prototype = {
this.navobj.baseurl($link.attr("href")).update();
},
end_of_list_reached: function(element) {
$("#objects_table").after(
$("<div class='alert alert-info'>{0}</div>".format(
gettext("No more identity to show")
))
);
},
list_cb: function(data) {
Admin.prototype.list_cb.call(this, data);
var deloptions = {};
@@ -4,9 +4,18 @@
{{ block.super }}
<script type="text/javascript">
$(document).ready(function() {
admin = new Domains();
admin = new Domains({
load_page_url: "{% url 'admin:_domain_list' %}"
});
});
</script>
{% extra_static_content "domains" user %}
{% endblock %}
{% block actions %}{% domains_menu "domains" user %}{% endblock %}
{% block table_header %}
<th id="name" class="sortable" data-sort_order="name" width="">{% trans "Name" %}</th>
<th id="aliases" width="">{% trans "Alias(es)" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
<th id="actions" width="">{% trans "Actions" %}</th>
{% endblock %}
@@ -1,21 +1,9 @@
{% load i18n admin_tags lib_tags %}
<table id="{{ tableid }}" class="table {{ styles }}">
<thead>
<tr>
<th id="name" class="sortable" data-sort_order="name" width="">{% trans "Name" %}</th>
<th id="aliases" width="">{% trans "Alias(es)" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
<th id="actions" width="">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for domain in domains %}
<tr id="{{ domain.id }}" class="{% if not domain.enabled %}muted{% endif %}" name="{{ forloop.counter }}">
<td name="name">{% domain_modify_link domain %}</td>
<td name="aliases">{% domain_aliases domain %} </td>
<td name="tags">{% render_tags domain.tags %}</td>
<td name="actions">{% domain_actions user domain %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% for domain in domains %}
<tr id="{{ domain.id }}" class="{% if not domain.enabled %}muted{% endif %}" name="{{ forloop.counter }}">
<td name="name">{% domain_modify_link domain %}</td>
<td name="aliases">{% domain_aliases domain %} </td>
<td name="tags">{% render_tags domain.tags %}</td>
<td name="actions">{% domain_actions user domain %}</td>
</tr>
{% endfor %}
@@ -6,10 +6,18 @@
<script type="text/javascript">
$(document).ready(function() {
admin = new Identities({
domain_list_url: "{% url 'admin:domain_flat_list' %}"
domain_list_url: "{% url 'admin:domain_flat_list' %}",
load_page_url: "{% url 'admin:_identity_list' %}"
});
});
</script>
{% extra_static_content "identities" user %}
{% endblock %}
{% block actions %}{% identities_menu user "identities" %}{% endblock %}
{% block table_header %}
<th id="identity" class="sortable" data-sort_order="identity" width="">{% trans "Name" %}</th>
<th id="name_or_rcpt" class="sortable" data-sort_order="name_or_rcpt" width="">{% trans "Fullname/Recipient" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
<th id="actions" width="">{% trans "Actions" %}</th>
{% endblock %}
@@ -1,21 +1,9 @@
{% load i18n admin_tags lib_tags %}
<table id="{{ tableid }}" class="table {{ styles }}">
<thead>
<tr>
<th id="identity" class="sortable" data-sort_order="identity" width="">{% trans "Name" %}</th>
<th id="name_or_rcpt" class="sortable" data-sort_order="name_or_rcpt" width="">{% trans "Fullname/Recipient" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
<th id="actions" width="">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for ident in identities %}
<tr id="{{ ident.id }}" class="{% disable_identity ident %}" name="{{ forloop.counter }}">
<td name="identity">{% identity_modify_link ident %}</td>
<td name="name_or_rcpt">{{ ident.name_or_rcpt }}</td>
<td name="tags">{% render_tags ident.tags %}</td>
<td name="actions">{% identity_actions user ident %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% for ident in identities %}
<tr id="{{ ident.id }}" class="{% disable_identity ident %}" name="{{ forloop.counter }}">
<td name="identity">{% identity_modify_link ident %}</td>
<td name="name_or_rcpt">{{ ident.name_or_rcpt }}</td>
<td name="tags">{% render_tags ident.tags %}</td>
<td name="actions">{% identity_actions user ident %}</td>
</tr>
{% endfor %}
@@ -5,6 +5,7 @@
<script type="text/javascript" src="{{ STATIC_URL }}js/creation_wizard.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/dynamic_input.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.sortable.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.infinitescroll.js"></script>
<script type="text/javascript">
var admin;
</script>
@@ -21,10 +22,19 @@ var admin;
<div class="col-sm-5">
{% include "admin/searchform.html" %}
</div>
<div id="taglist" class="col-sm-5 col-md-5">
</div>
<div id="taglist" class="col-sm-5"> </div>
</div>
<div id="listing">
<table id="objects_table" class="table {{ styles }}">
<thead>
<tr>
{% block table_header %}{% endblock %}
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div id="listing"></div>
{% endblock %}
{% block footerright %}{% endblock %}
@@ -105,7 +105,7 @@ def identities_menu(user, selection=None):
{"name": "import",
"label": _("Import"),
"img": "fa fa-folder-open",
"url": reverse("admin:domain_import"),
"url": reverse("admin:identity_import"),
"modal": True,
"modalcb": "admin.importform_cb"},
{"name": "export",
+25 -16
View File
@@ -1,4 +1,7 @@
import reversion
"""
Domain related views.
"""
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext as _, ungettext
from django.core.urlresolvers import reverse
@@ -8,6 +11,9 @@ from django.views.decorators.csrf import ensure_csrf_cookie
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
import reversion
from modoboa.lib import parameters, events
from modoboa.lib.webutils import (
_render_to_string, render_to_json_response
@@ -31,7 +37,8 @@ def index(request):
@login_required
@user_passes_test(
lambda u: u.has_perm("admin.view_domains") or u.has_perm("admin.view_mailboxes")
lambda u: u.has_perm("admin.view_domains")
or u.has_perm("admin.view_mailboxes")
)
def _domains(request):
sort_order, sort_dir = get_sort_order(request.GET, "name")
@@ -41,9 +48,7 @@ def _domains(request):
+ events.raiseQueryEvent('ExtraDomainFilters')
)
request.session['domains_filters'] = filters
domainlist = get_domains(
request.user, **filters
)
domainlist = get_domains(request.user, **filters)
if sort_order == 'name':
domainlist = sorted(
domainlist,
@@ -52,18 +57,22 @@ def _domains(request):
else:
domainlist = sorted(domainlist, key=lambda d: d.tags[0],
reverse=sort_dir == '-')
page = get_listing_page(domainlist, request.GET.get("page", 1))
return render_to_json_response({
"table": _render_to_string(request, 'admin/domains_table.html', {
'domains': page.object_list,
'tableid': 'domains'
}),
"page": page.number,
"paginbar": pagination_bar(page),
"handle_mailboxes": parameters.get_admin("HANDLE_MAILBOXES",
raise_error=False),
context = {
"handle_mailboxes": parameters.get_admin(
"HANDLE_MAILBOXES", raise_error=False),
"auto_account_removal": parameters.get_admin("AUTO_ACCOUNT_REMOVAL")
})
}
page = get_listing_page(domainlist, request.GET.get("page", 1))
if page is None:
context["length"] = 0
else:
context["rows"] = _render_to_string(
request, 'admin/domains_table.html', {
'domains': page.object_list,
}
)
context["page"] = page.number
return render_to_json_response(context)
@login_required
+20 -11
View File
@@ -1,4 +1,6 @@
import reversion
"""
Identity related views.
"""
from django.shortcuts import render
from django.db import transaction
from django.utils.translation import ugettext as _, ungettext
@@ -6,6 +8,9 @@ from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
from django.views.decorators.csrf import ensure_csrf_cookie
import reversion
from modoboa.lib import parameters, events
from modoboa.lib.exceptions import (
PermDeniedException, BadRequest
@@ -42,17 +47,21 @@ def _identities(request):
else:
objects = sorted(idents_list, key=lambda o: o.tags[0],
reverse=sort_dir == '-')
context = {
"handle_mailboxes": parameters.get_admin(
"HANDLE_MAILBOXES", raise_error=False)
}
page = get_listing_page(objects, request.GET.get("page", 1))
return render_to_json_response({
"table": _render_to_string(request, "admin/identities_table.html", {
"identities": page.object_list,
"tableid": "objects_table"
}),
"handle_mailboxes": parameters.get_admin("HANDLE_MAILBOXES",
raise_error=False),
"page": page.number,
"paginbar": pagination_bar(page)
})
if page is None:
context["length"] = 0
else:
context["rows"] = _render_to_string(
request, "admin/identities_table.html", {
"identities": page.object_list
}
)
context["page"] = page.number
return render_to_json_response(context)
@login_required
+1 -1
View File
@@ -38,5 +38,5 @@ def get_listing_page(objects, pagenum):
try:
page = paginator.page(int(pagenum))
except (EmptyPage, PageNotAnInteger, ValueError):
page = paginator.page(paginator.num_pages)
page = None
return page
+1 -1
View File
@@ -99,7 +99,7 @@
* @param {string} direction - direction of the scroll (up or down)
*/
load_page: function(direction) {
var args = this.$options.get_args();
var args = (this.$options.get_args) ? this.$options.get_args() : {};
var $this = this;
this.executing = true;
+15 -1
View File
@@ -1,4 +1,11 @@
(function($) {
/**
* Return an instance of Sortable.
*
* @constructor
* @param {Object} element -
* @param {Object} options - instance options
*/
var Sortable = function(element, options) {
this.initialize(element, options);
};
@@ -32,8 +39,15 @@
}
},
/**
* Mark a column as the one used to sort content.
*
* @this Sortable
* @param {string} dir - optional sort direction (up or down)
* @return {string} - new sort direction
*/
select: function(dir) {
$("i.sort-selection").remove();
$("span.sort-selection").remove();
if (dir !== undefined) {
this.$element.removeClass("sort-header-" + ((dir == "asc") ? "desc" : "asc"));
this.$element.addClass("sort-header-" + dir);
+35 -11
View File
@@ -1,3 +1,9 @@
/**
* Return an instance of Listing.
*
* @constructor
* @param {Object} options - instance options
*/
var Listing = function(options) {
this.initialize(options);
};
@@ -10,7 +16,8 @@ Listing.prototype = {
pbar_container: '#bottom-bar-right',
pbar_id: '#pagination_bar',
sortable_selector: '.sortable',
with_searchform: true
with_searchform: true,
load_page_url: null
},
initialize: function(options) {
@@ -69,17 +76,24 @@ Listing.prototype = {
this.navobj.updateparams($link.attr("href")).update();
},
/**
* Update the listing with the received data.
*
* @this Listing
* @param {Object} data - new content
*/
update_listing: function(data) {
if (data.paginbar) {
$(this.options.pbar_container).html(data.paginbar);
$(this.options.presp_container).html(data.paginbar);
$(this.options.pbar_id).find(".disabled a").click(function(e) {
e.preventDefault();
});
}
if (data.page && data.page != this.navobj.getparam("page")) {
this.navobj.setparam("page", data.page).update(false, true);
}
$(window).infinite_scroll({
url: this.options.load_page_url,
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
});
var $sortables = $(this.options.sortable_selector);
if ($sortables.length) {
$(this.options.sortable_selector).sortable({
@@ -89,6 +103,11 @@ Listing.prototype = {
}
},
/**
* Set current sort order.
*
* @this Listing
*/
set_sort_order: function() {
var sort_order = this.navobj.getparam("sort_order");
var sort_dir;
@@ -105,6 +124,11 @@ Listing.prototype = {
$("th[data-sort_order=" + sort_order + "]").sortable('select', sort_dir);
},
/**
* Change current sort order.
*
* @this Listing
*/
change_sort_order: function(sort_order, dir) {
if (dir == "desc") {
sort_order = "-" + sort_order;