mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
@@ -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"
|
||||
}]
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,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,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">
|
||||
|
||||
Reference in New Issue
Block a user