Infinite scroll for the quarantine.

see #585
This commit is contained in:
Antoine Nguyen
2014-10-11 16:10:21 +02:00
parent 3148fee75f
commit 0a3353ffbc
21 changed files with 380 additions and 149 deletions
@@ -86,7 +86,7 @@ def check_for_pending_requests(user, include_all):
url += "#listing/?viewrequests=1"
return [{
"id": "nbrequests", "url": url, "text": _("Pending requests"),
"counter": nbrequests, "level": "important"
"counter": nbrequests, "level": "danger"
}]
+9
View File
@@ -92,6 +92,15 @@ class QuarantineNavigationParameters(NavigationParameters):
('msgtype', None, False), ('viewrequests', None, False)
]
def _store_page(self):
"""Specific method to store the current page."""
if self.request.GET.get("reset_page", None) or "page" not in self:
self["page"] = 1
else:
page = self.request.GET.get("page", None)
if page is not None:
self["page"] = int(page)
def back_to_listing(self):
"""Return the current listing URL.
+5 -3
View File
@@ -1,6 +1,8 @@
"""
SQL connector module.
"""
import datetime
from django.db.models import Q
from modoboa.extensions.admin.models import Domain
@@ -124,7 +126,7 @@ class SQLconnector(object):
messages = self._apply_extra_select_filters(messages)
return messages
def messages_count(self, **kwargs):
def messages_count(self):
"""Return the total number of messages living in the quarantine.
We also store the built queryset for a later use.
@@ -148,7 +150,7 @@ class SQLconnector(object):
return self._messages_count
def fetch(self, start=None, stop=None, **kwargs):
def fetch(self, start=None, stop=None):
"""Fetch a range of messages from the internal cache.
"""
@@ -158,7 +160,7 @@ class SQLconnector(object):
"to": qm["rid__email"],
"subject": qm["mail__subject"],
"mailid": qm["mail__mail_id"],
"date": qm["mail__time_num"],
"date": datetime.datetime.fromtimestamp(qm["mail__time_num"]),
"type": qm["content"],
"score": qm["bspam_level"]}
rs = qm["rs"]
@@ -40,21 +40,14 @@
margin: 0;
}
#listing.fluid-content-quarantine {
position: relative;
padding-top: 30px;
height: 0;
overflow: hidden;
padding-bottom: 56.25%; /* proportion value to aspect ratio 16:9 (9 / 16 = 0.5625 or 56.25%) */
}
#listing.fluid-content-quarantine iframe {
#listing {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
top: 125px;
bottom: 0;
margin: 0;
overflow: auto;
padding-left: 15px;
padding-right: 15px;
}
#mailcontent {
@@ -1,3 +1,9 @@
/**
* Return an instance of Quarantine.
*
* @constructor
* @param {Object} options - instance options
*/
var Quarantine = function(options) {
Listing.call(this, options);
};
@@ -6,7 +12,11 @@ Quarantine.prototype = {
constructor: Quarantine,
defaults: {
deflocation: "listing"
deflocation: "listing",
main_table_id: "emails",
scroll_container: "#listing",
navigation_params: ["sort_order", "pattern", "criteria", "msgtype"],
eor_message: gettext("No more message to show")
},
initialize: function(options) {
@@ -18,10 +28,22 @@ Quarantine.prototype = {
this.set_msgtype();
$(document).on('click', '#selectall', this.toggle_selection);
$("#searchfield").searchbar({navobj: this.navobj});
this.initialize_searchfield();
},
/*
/**
* Search field initialization.
*/
initialize_searchfield: function() {
$("#searchfield").searchbar({
navobj: this.navobj,
pattern_changed: function(navobj) {
navobj.setparam("reset_page", "true");
}
});
},
/**
* Activate the button corresponding to the current message type
* filter. If no message type is found, the 'All' button is
* selected.
@@ -29,28 +51,40 @@ Quarantine.prototype = {
set_msgtype: function() {
var msgtype = this.navobj.getparam('msgtype');
$("button[name*=msgtype]").removeClass('active');
if (msgtype != undefined) {
$("button[name=msgtype_" + msgtype + "]").addClass('active');
$("input[name*=msgtype]").attr("checked", false)
.parent().removeClass('active');
if (msgtype !== undefined) {
$("input[name=msgtype_" + msgtype + "]").attr("checked", true)
.parent().addClass('active');
} else {
$("button[name=msgtype_all]").addClass('active');
$("button[name=msgtype_all]").attr("checked", true)
.parent().addClass('active');
}
},
/**
* Update the main page content.
*
* @this Quarantine
* @param {Object} data - new content
*/
update_page: function(data) {
if (data.menu != undefined) {
if (data.menu !== undefined) {
$("#menubar").html(data.menu);
$("#searchfield").searchbar({navobj: this.navobj});
this.initialize_searchfield();
}
if (data.listing != undefined) {
if (data.listing !== undefined) {
$("#listing").html(data.listing);
$("#listing:has(iframe)").addClass("fluid-content-quarantine");
}
this.update_listing(data);
if (this.navobj.getbaseurl() == "listing") {
this.update_listing(data);
$(this.options.scroll_container).scrollTop(10);
} else {
this.update_listing(data, false);
$(this.options.scroll_container).infinite_scroll("pause");
}
},
listen: function() {
@@ -66,7 +100,7 @@ Quarantine.prototype = {
$(document).on("click", "a[name=delete]", $.proxy(this.delete, this));
$(document).on("click", "a[name=headers]", $.proxy(this.headers, this));
$(document).on("click", "td[name=type] span", $.proxy(this.filter_by_type, this));
$(document).on("click", "#filters button", $.proxy(this.filter_by_type, this));
$(document).on("click", "#filters label", $.proxy(this.filter_by_type, this));
},
load_page: function(e) {
@@ -74,6 +108,32 @@ Quarantine.prototype = {
this.navobj.delparam("rcpt").update();
},
/**
* Return extra arguments used to fetch a page.
*
* @this Listing
*/
get_load_page_args: function() {
var args = Listing.prototype.get_load_page_args.call(this);
args.scroll = true;
return args;
},
/**
* Calculate the bottom position of the scroll container.
*
* @param {Object} $element - scroll container object
*/
calculate_bottom: function($element) {
var emails_height = $("#emails").height();
if (!emails_height) {
return undefined;
}
return $("#emails").height() - $element.height();
},
view_requests: function(e) {
e.preventDefault();
this.navobj.setparam("viewrequests", "1").update();
@@ -132,8 +192,10 @@ Quarantine.prototype = {
}
},
/*
/**
* Filter listing by message type (spam, virus, etc.)
*
* @param {Object} evt - event object
*/
filter_by_type: function(evt) {
var $target = $(evt.target);
@@ -142,7 +204,7 @@ Quarantine.prototype = {
if ($target.is("span")) {
msgtype = $target.html().trim();
} else {
msgtype = $target.attr("name").replace("msgtype_", "");
msgtype = $target.children("input").attr("name").replace("msgtype_", "");
}
if (msgtype != 'all') {
@@ -272,6 +334,12 @@ Quarantine.prototype = {
}
},
/**
* Navigation callback: listing.
*
* @this Quarantine
* @param {Object} data - ajax call response (JSON)
*/
listing_cb: function(data) {
this.update_page(data);
this.navobj.delparam("rcpt").update();
@@ -282,11 +350,13 @@ Quarantine.prototype = {
});
this.htmltable = $("#emails").data("htmltable");
this.deactivate_buttons();
if (this.navobj.hasparam("reset_page")) {
this.navobj.delparam("reset_page").update(false, true);
}
},
viewmail_cb: function(data) {
this.update_page(data);
/*$("#listing").css("overflow", "hidden");*/
$("body").css("overflow", "hidden");
},
@@ -0,0 +1,31 @@
{% load i18n %}
<form method="POST" id="listingform">
<table id="emails" class="table table-condensed">
<thead>
<tr>
<th id="selection" width="5px"></th>
<th id="type" width="30px" class="sortable sort-header" align="center" data-sort_order="type"></th>
<th id="rstatus" width="25px"></th>
<th id="score" class="sortable sort-header" data-sort_order="score">
{% trans "Score" %}
</th>
<th id="to" class="sortable sort-header" data-sort_order="to">
{% trans "To" %}
</th>
<th id="from" class="sortable sort-header" data-sort_order="from">
{% trans "From" %}
</th>
<th id="subject" class="sortable sort-header" data-sort_order="subject">
{% trans "Subject" %}
</th>
<th id="date" class="sortable sort-header" data-sort_order="date">
{% trans "Date" %}
</th>
</tr>
</thead>
<tbody>
{% include "amavis/emails_page.html" %}
</tbody>
</table>
</form>
@@ -0,0 +1,26 @@
{% load amavis_tags %}
{% for email in email_list %}
<tr id="{{ email.mailid }}" name="{{ forloop.counter }}">
<td name="selection" width="5px">
<input id="selection" type="checkbox" name="select_{{ email.mailid }}" value="{{ email.mailid }}">
</td>
<td align="center" width="30px" name="type" class="{{ email.class }}">
{{ email.type|msgtype_to_html }}
</td>
<td width="25px" name="rstatus">
{% if email.img_rstatus %}<img src="{{ email.img_rstatus }}" />{% endif %}
</td>
<td class="openable {{ email.class }}" name="score">{{ email.score }}</td>
<td class="openable {{ email.class }}" name="to">{{ email.to }}</td>
<td class="openable {{ email.class }}" name="from">
{{ email.from|truncatechars:30 }}
</td>
<td class="openable {{ email.class }}" name="subject">
{{ email.subject|truncatechars:30 }}
</td>
<td class="openable {{ email.class }}" name="date">
{{ email.date|date:"SHORT_DATETIME_FORMAT" }}
</td>
</tr>
{% endfor %}
@@ -4,6 +4,7 @@
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}amavis/css/quarantine.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/jquery.sortable.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.htmltable.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.infinitescroll.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.sortable.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/searchbar.js"></script>
@@ -12,17 +13,18 @@
<script type="text/javascript">
$(document).ready(function() {
var qm = new Quarantine({
deflocation: "{{ deflocation }}",
defcallback: "{{ defcallback }}"
deflocation: "listing/?sort_order=-date&reset_page=true",
defcallback: "listing_cb",
load_page_url: "{% url 'amavis:mail_page' %}"
});
});
</script>
{% endblock %}
{% block fixed_content %}
<div class="row" style="padding-top: 20px">
<div id="menubar">{% quar_menu %}</div>
<div id="menubar" class="row" style="padding-top: 20px">
{% quar_menu %}
</div>
<div class="row">
<div id="listing" class="col-xs-12 fluid-content-quarantine"></div>
<div id="listing" class="col-xs-12"></div>
</div>
{% endblock %}
@@ -1,49 +1,61 @@
{% load lib_tags i18n %}
<div class="col-lg-9 col-md-8 col-sm-12 col-xs-12">
<div class="navbar-nav">
<div class="col-lg-9 col-md-8 col-xs-12">
<div class="navbar-nav">
<div>
<div class="btn-group" title="{% trans 'Select' %}">
<a class="btn btn-default"><input type="checkbox" id="selectall" /></a>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a name="selectmsgs" href="S">{% trans "Spam" %}</a></li>
<li><a name="selectmsgs" href="V">{% trans "Virus" %}</a></li>
<li><a name="selectmsgs" href="H">{% trans "Bad Header" %}</a></li>
<li><a name="selectmsgs" href="M">{% trans "Bad MIME" %}</a></li>
<li><a name="selectmsgs" href="B">{% trans "Banned" %}</a></li>
</ul>
</div>
<div class="btn-group">
<a name="release-multi" href="{% url 'amavis:mail_process' %}" class="btn btn-success disabled">
<span class="fa fa-check"></span> <span>{% trans "Release" %}</span>
</a>
</div>
<div class="btn-group">
<a name="delete-multi" href="{% url 'amavis:mail_process' %}" class="btn btn-danger disabled">
<span class="fa fa-trash"></span> <span>{% trans "Delete" %}</span>
</a>
</div>
<div class="btn-group">
{% include "common/email_searchbar.html" with extraopts=extraopts %}
</div>
</div>
</div>
</div>
<div>
<div class="btn-group" title="{% trans 'Select' %}">
<a class="btn btn-default"><input type="checkbox" id="selectall" /></a>
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a name="selectmsgs" href="S">{% trans "Spam" %}</a></li>
<li><a name="selectmsgs" href="V">{% trans "Virus" %}</a></li>
<li><a name="selectmsgs" href="H">{% trans "Bad Header" %}</a></li>
<li><a name="selectmsgs" href="M">{% trans "Bad MIME" %}</a></li>
<li><a name="selectmsgs" href="B">{% trans "Banned" %}</a></li>
</ul>
</div>
<div class="btn-group">
<a name="release-multi" href="{% url 'amavis:mail_process' %}" class="btn btn-success disabled">
<i class="fa fa-check"></i> <span>{% trans "Release" %}</span>
</a>
</div>
<div class="btn-group">
<a name="delete-multi" href="{% url 'amavis:mail_process' %}" class="btn btn-danger disabled">
<i class="fa fa-trash"></i> <span>{% trans "Delete" %}</span>
</a>
</div>
<div class="btn-group">
{% include "common/email_searchbar.html" with extraopts=extraopts %}
</div>
</div>
<div class="col-lg-3 col-md-4">
<div class="btn-group">
<div id="filters_bar" class="navbar-nav navbar-right">
<span class="label label-default">{% trans "Display" %}</span>
<div id="filters" class="btn-group" data-toggle="buttons">
<label class="btn btn-default active">
<input type="radio" name="msgtype_all" title="{% trans 'All messages' %}" checked> {% trans "All" %}
</label>
<label class="btn btn-danger">
<input type="radio" name="msgtype_S" title="{% trans 'Spam only' %}"> S
</label>
<label class="btn btn-danger">
<input type="radio" name="msgtype_V" title="{% trans 'Virus only' %}"> V
</label>
<label class="btn btn-warning">
<input type="radio" name="msgtype_S" title="{% trans 'Bad Header only' %}"> H
</label>
<label class="btn btn-warning">
<input type="radio" name="msgtype_M" title="{% trans 'Bad MIME only' %}"> M
</label>
<label class="btn btn-warning">
<input type="radio" name="msgtype_B" title="{% trans 'Banned only' %}"> B
</label>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-4">
<div class="btn-group">
<div id="filters_bar" class="navbar-nav navbar-right">
<span class="label label-default">{% trans "Display" %}</span>
<div id="filters" class="btn-group" data-toggle="buttons-radio">
<button name="msgtype_all" class="btn btn-default" title="{% trans 'All messages' %}">{% trans "All" %}</button>
<button name="msgtype_S" class="btn btn-danger" title="{% trans 'Spam only' %}">S</button>
<button name="msgtype_V" class="btn btn-danger" title="{% trans 'Virus only' %}">V</button>
<button name="msgtype_H" class="btn btn-warning" title="{% trans 'Bad Header only' %}">H</button>
<button name="msgtype_M" class="btn btn-warning" title="{% trans 'Bad MIME only' %}">M</button>
<button name="msgtype_B" class="btn btn-warning" title="{% trans 'Banned only' %}">B</button>
</div>
</div>
</div>
</div>
@@ -1,9 +1,9 @@
from django import template
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from django.conf import settings
from modoboa.lib.webutils import _render_to_string
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
register = template.Library()
@@ -12,7 +12,7 @@ register = template.Library()
def viewm_menu(mail_id, rcpt):
entries = [
{"name": "back",
"url": "javascript:history.go(-2);",
"url": "javascript:history.go(-1);",
"img": "fa fa-arrow-left",
"class": "btn-primary",
"label": _("Back")},
@@ -82,3 +82,12 @@ def quar_menu():
return render_to_string('amavis/main_action_bar.html', {
'extraopts': extraopts
})
@register.filter
def msgtype_to_html(msgtype):
"""Transform a message type to a bootstrap label."""
color = 'danger' if msgtype in ['S', 'V'] else 'warning'
return mark_safe(
'<span class="label label-%s">%s</span>' % (color, msgtype)
)
+1
View File
@@ -5,6 +5,7 @@ urlpatterns = patterns(
url(r'^$', 'index', name="index"),
url(r'^listing/$', '_listing', name="_mail_list"),
url(r'^listing/page/$', 'listing_page', name="mail_page"),
url(r'^getmailcontent/(?P<mail_id>[\w\-\+]+)/$', 'getmailcontent',
name="mailcontent_get"),
url(r'^process/$', 'process', name="mail_process"),
+50 -17
View File
@@ -1,4 +1,7 @@
# coding: utf-8
"""
Amavis quarantine views.
"""
import email
from django.shortcuts import render
@@ -10,10 +13,10 @@ from django.contrib.auth.decorators import login_required
from modoboa.lib import parameters
from modoboa.lib.exceptions import BadRequest
from modoboa.lib.paginator import Paginator
from modoboa.lib.webutils import (
getctx, ajax_response, render_to_json_response
getctx, ajax_response, render_to_json_response, _render_to_string
)
from modoboa.lib.templatetags.lib_tags import pagination_bar
from modoboa.extensions.admin.models import Mailbox, Domain
from modoboa.extensions.amavis.templatetags.amavis_tags import (
quar_menu, viewm_menu
@@ -21,15 +24,51 @@ from modoboa.extensions.amavis.templatetags.amavis_tags import (
from .lib import selfservice, AMrelease, QuarantineNavigationParameters
from .models import Msgrcpt
from .sql_connector import get_connector
from .sql_listing import SQLlisting, SQLemail
from .sql_listing import SQLemail
def empty_quarantine(request):
"""Shortcut to use when no content can be displayed."""
content = "<div class='alert alert-info'>%s</div>" % _("Empty quarantine")
ctx = getctx("ok", level=2, listing=content)
return render_to_json_response(ctx)
def get_listing_page(request, connector):
"""."""
paginator = Paginator(
connector.messages_count(),
int(parameters.get_user(request.user, "MESSAGES_PER_PAGE"))
)
page_id = int(connector.navparams.get("page"))
page = paginator.getpage(page_id)
if not page:
return None
return page
@login_required
def listing_page(request):
"""Return a listing page."""
navparams = QuarantineNavigationParameters(request)
previous_page_id = int(navparams["page"]) if "page" in navparams else None
navparams.store()
connector = get_connector(user=request.user, navparams=navparams)
page = get_listing_page(request, connector)
if page is None:
context = {"length": 0}
navparams["page"] = previous_page_id
else:
context = {
"rows": _render_to_string(request, "amavis/emails_page.html", {
"email_list": connector.fetch(page.id_start, page.id_stop)
}),
"page": page.number
}
return render_to_json_response(context)
@login_required
def _listing(request):
if not request.user.is_superuser and request.user.group != 'SimpleUsers':
@@ -39,21 +78,15 @@ def _listing(request):
navparams = QuarantineNavigationParameters(request)
navparams.store()
lst = SQLlisting(
request.user,
navparams=navparams,
elems_per_page=int(
parameters.get_user(request.user, "MESSAGES_PER_PAGE")
)
)
page = lst.paginator.getpage(navparams.get('page'))
if not page:
connector = get_connector(user=request.user, navparams=navparams)
page = get_listing_page(request, connector)
if page is None:
return empty_quarantine(request)
content = lst.fetch(request, page.id_start, page.id_stop)
ctx = getctx(
"ok", listing=content, paginbar=pagination_bar(page), page=page.number
)
content = _render_to_string(request, "amavis/email_list.html", {
"email_list": connector.fetch(page.id_start, page.id_stop),
})
ctx = getctx("ok", listing=content, page=page.number)
if request.session.get('location', 'listing') != 'listing':
ctx['menu'] = quar_menu()
request.session['location'] = 'listing'
@@ -62,8 +95,8 @@ def _listing(request):
@login_required
def index(request):
"""Default view."""
return render(request, "amavis/index.html", dict(
deflocation="listing/?sort_order=-date", defcallback="listing_cb",
selection="quarantine"
))
@@ -34,7 +34,7 @@ $(document).ready(function() {
{% block fixed_content %}
<div class="row">
<div id="leftcol" class="col-lg-2 col-md-3 col-sm-3 col-xs-0 sidebar">
<div id="leftcol" class="col-xs-0 col-sm-3 col-lg-2 sidebar">
<div class="leftcol-heading text-center">
<a name="compose" class="btn btn-success btn-lg" href="compose">
<span class="fa fa-edit"></span>
@@ -43,14 +43,14 @@ $(document).ready(function() {
</div>
<div id="folders" class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
<div class="col-sm-12">
{{ mboxes }}
</div>
</div>
<div id="left-toolbar" class="row">
<div class="col-sm-2 col-md-2 col-lg-2">
<div class="col-sm-2">
<div id="mboxactions" class="btn-group dropup">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
<span class="fa fa-cog"></span>
@@ -59,7 +59,7 @@ $(document).ready(function() {
</div>
</div>
{% if quota != -1 %}
<div id="quotabox" class="col-sm-10 col-md-10 col-lg-10">
<div id="quotabox" class="col-sm-10">
<div id="quotabar" class="progress">
<div class="progress-bar {% progress_color quota %}" role="progressbar" style="width: {{ quota }}%" aria-valuenow="{{ quota }}" aria-valuemin="0" aria-valuemax="100">{{ quota }}%</div>
</div>
+1 -6
View File
@@ -139,18 +139,13 @@ label {
}
.dropdown-menu .radio {
padding: 3px 33px;
padding: 3px 15px;
}
.no-margin {
margin: 0 !important;
}
.dropdown-menu label:hover {
background-color: #0088CC;
color: white;
}
.buttons_list .bset {
display: none;
}
+6
View File
@@ -177,6 +177,12 @@ History.prototype = {
return this;
},
/**
* Set several parameters.
*
* @param {Object} params - a dictionary of parameters
* @return {Object} - History instance
*/
setparams: function(params) {
$.each(params, $.proxy(this.setparam, this));
return this;
@@ -38,6 +38,9 @@
on_scroll: function() {
var bottom = this.$options.calculate_bottom(this.$element);
if (bottom === undefined) {
return;
}
if (this.$element.scrollTop() >= bottom) {
this.load_next_page();
} else if (this.$element.scrollTop() === 0) {
+32 -15
View File
@@ -19,6 +19,7 @@ Listing.prototype = {
with_searchform: true,
load_page_url: null,
navigation_params: ["sort_order", "searchquery"],
scroll_container: null,
main_table_id: "objects_table",
eor_message: gettext("No more entry to show")
},
@@ -97,17 +98,35 @@ Listing.prototype = {
return args;
},
/**
* Calculate the bottom position of the scroll container.
*
* @param {Object} $element - scroll container object
*/
calculate_bottom: function($element) {
var $last_row = $("#" + this.options.main_table_id)
.find("tr").last();
return $last_row.offset().top - $element.height();
},
/**
* A new page has been received, inject it.
*
* @param {Object} data - page content
* @param {string} direction -
* @param {string} direction - scroll direction (up or down)
*/
add_new_page: function(data, direction) {
$("#{0} tbody".format(this.options.main_table_id))
.html(function(pos, oldhtml) {
var $container = $("#{0} tbody".format(this.options.main_table_id));
if (direction == "down") {
$container.html(function(pos, oldhtml) {
return oldhtml + data.rows;
});
} else {
$container.html(function(pos, oldhtml) {
return data.rows + oldhtml;
});
}
},
/**
@@ -131,27 +150,25 @@ Listing.prototype = {
*/
update_listing: function(data, with_infinite_scroll) {
var $this = this;
var $scroll_container = (this.options.scroll_container) ?
$(this.options.scroll_container) : $(window);
$(window).scrollTop(0);
$scroll_container.scrollTop(0);
if (with_infinite_scroll || with_infinite_scroll === undefined) {
if ($(window).data("infinite-scroll") !== undefined) {
$(window).infinite_scroll("reset_loaded_pages", data.page);
$(window).infinite_scroll("resume");
if ($scroll_container.data("infinite-scroll") !== undefined) {
$scroll_container.infinite_scroll("reset_loaded_pages", data.page);
$scroll_container.infinite_scroll("resume");
} else {
$(window).infinite_scroll({
$scroll_container.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();
},
calculate_bottom: $.proxy(this.calculate_bottom, this),
process_results: $.proxy(this.add_new_page, this),
end_of_list_reached: $.proxy(this.end_of_list_reached, this)
});
}
} else if ($(window).data("infinite-scroll") !== undefined) {
$(window).infinite_scroll("pause");
} else if ($scroll_container.data("infinite-scroll") !== undefined) {
$scroll_container.infinite_scroll("pause");
}
var $sortables = $(this.options.sortable_selector);
+28 -4
View File
@@ -3,12 +3,36 @@
<div class="input-group">
<input type="text" class="form-control" name="searchfield" id="searchfield" placeholder="{% trans "Search..." %}" />
<ul class="dropdown-menu pull-right" id="searchparams">
<li><label class="radio"><input type="radio" id="crit_from_addr" name="scriteria" value="from_addr" checked>{% trans "From address" %}</label></li>
<li><label class="radio"><input type="radio" id="crit_subject" name="scriteria" value="subject">{% trans "Subject" %}</label></li>
<li>
<div class="radio">
<label>
<input type="radio" id="crit_from_addr" name="scriteria" value="from_addr" checked> {% trans "From address" %}
</label>
</div>
</li>
<li>
<div class="radio">
<label>
<input type="radio" id="crit_subject" name="scriteria" value="subject"> {% trans "Subject" %}
</label>
</div>
</li>
{% for opt in extraopts %}
<li><label class="radio"><input type="radio" id="crit_{{ opt.name }}" name="scriteria" value="{{ opt.name }}">{{ opt.label }}</label></li>
<li>
<div class="radio">
<label>
<input type="radio" id="crit_{{ opt.name }}" name="scriteria" value="{{ opt.name }}">{{ opt.label }}
</label>
</div>
</li>
{% endfor %}
<li><label class="radio"><input type="radio" id="crit_both" name="scriteria" value="both">{% trans "Both" %}</label></li>
<li>
<div class="radio">
<label>
<input type="radio" id="crit_both" name="scriteria" value="both">{% trans "Both" %}
</label>
</div>
</li>
</ul>
<a href="#" class="input-group-addon" data-toggle="dropdown"><span class="fa fa-search" id="searchopts"></span></a>
</div>
@@ -1,16 +1,16 @@
<ul id="top_notifications" class="nav pull-right{% if not top_notifications %} hidden{% endif %}">
<ul id="top_notifications" class="nav navbar-nav navbar-right{% if not top_notifications %} hidden{% endif %}">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-bell"></i> <span id="alerts-counter" class="label label-important">{{ top_notifications|length }}</span>
<span class="fa fa-bell"></span> <span id="alerts-counter" class="label label-danger">{{ top_notifications|length }}</span>
</a>
<ul class="dropdown-menu">
{% for notification in top_notifications %}
<li>
<a id="{{ notification.id }}" href="{{ notification.url }}">
{% if notification.counter %}
<span class="label label-{{ notification.level }}">{{ notification.counter }}</span> {{ notification.text|safe }}
<span class="label label-{{ notification.level }}"> {{ notification.counter }}</span> {{ notification.text|safe }}
{% else %}
<i class="icon-info-sign"></i> {{ notification.text|safe }}
<span class="fa fa-info-circle"></span> {{ notification.text|safe }}
{% endif %}
</a>
</li>
+3 -5
View File
@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>{% block pagetitle %}{% endblock %}</title>
<link rel="icon" type="image/png" href="{{ STATIC_URL }}css/logo-icon.png" />
<link rel="stylesheet" href="{{ STATIC_URL }}css/font-awesome/css/font-awesome.min.css">
@@ -15,6 +16,7 @@
<script src="{{ STATIC_URL }}js/bootstrap-datetimepicker.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/bootstrap-select.min.js" type="text/javascript"></script>
{% if LANGUAGE_CODE != "en" %}<script src="{{ STATIC_URL }}js/locales/bootstrap-datetimepicker.{{ LANGUAGE_CODE }}.js" type="text/javascript"></script>{% endif %}
<script src="//maxcdn.bootstrapcdn.com/bootlint/0.4.0/bootlint.min.js"></script>
<script src="{{ STATIC_URL }}js/global.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/csrf.js" type="text/javascript"></script>
<script src="{{ STATIC_URL }}js/confirmation.js" type="text/javascript"></script>
@@ -47,10 +49,6 @@
</button>
</div>
<div id="pagination-responsive" class="pull-right">
</div>
<div id="navigate-navbar-collapse" class="collapse navbar-collapse">
<ul class="nav navbar-nav display-less-1198">
<li class="droppable active ui-droppable" name="INBOX">
@@ -93,7 +91,7 @@
<ul class="nav navbar-nav navbar-right">
{% if selection %}{% user_menu user selection %}{% else %}{% user_menu user "" %}{% endif %}
</ul>
<div class="pull-right">{% include "common/top_notifications.html" %}</div>
{% include "common/top_notifications.html" %}
</div>
</div>
</div>
+1 -1
View File
@@ -1,7 +1,7 @@
{% extends "fluid.html" %}
{% load core_tags %}
{% block rowcontent %}
<div class="col-lg-2 col-md-2 col-sm-3 col-xs-12 sidebar">
<div class="col-md-2 col-sm-3 col-xs-12 sidebar">
{% block leftcol %}{% endblock %}
</div>
<div class="main col-lg-offset-2 col-lg-10 col-md-offset-2 col-md-10 col-sm-offset-3 col-sm-9 col-xs-12">