From 64ae5f4a94f43061efd509fa61ca44982b3e1330 Mon Sep 17 00:00:00 2001 From: tonio Date: Tue, 3 Nov 2009 15:25:29 +0100 Subject: [PATCH] Statistics plugin almost done. Needs more testing. --- admin/templatetags/admin_extras.py | 10 +- admin/views.py | 8 +- extensions/amavis_quarantine/main.py | 14 +- extensions/stats/grapher.py | 75 +++--- extensions/stats/logparser.py | 62 +++-- extensions/stats/main.py | 72 +++--- extensions/stats/templates/adminindex.html | 52 +++++ extensions/stats/templates/graphs.html | 6 + extensions/stats/templates/index.html | 81 ++----- extensions/stats/templatetags/__init__.py | 0 extensions/stats/templatetags/stats_extra.py | 14 ++ extensions/stats/urls.py | 2 +- locale/fr/LC_MESSAGES/django.po | 230 ++++++++++++------- main/templatetags/main_extras.py | 3 +- templates/layout.html | 2 + 15 files changed, 397 insertions(+), 234 deletions(-) create mode 100644 extensions/stats/templates/adminindex.html create mode 100644 extensions/stats/templates/graphs.html create mode 100644 extensions/stats/templatetags/__init__.py create mode 100644 extensions/stats/templatetags/stats_extra.py diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 99ca98f98..4e5cbf15c 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -39,7 +39,8 @@ def domain_menu(domain_id, selection, perms): "class" : "boxed", "rel" : "{handler:'iframe',size:{x:300,y:180}}"} ] - entries += events.raiseQueryEvent("AdminMenuDisplay", target="admin_menu_bar" , domain=domain_id) + entries += events.raiseQueryEvent("AdminMenuDisplay", target="admin_menu_bar", + perms=perms, domain=domain_id) return render_to_string('main/menu.html', {"selection" : selection, "entries" : entries, "perms" : perms}) @@ -49,6 +50,13 @@ def settings_menu(selection): return render_to_string('admin/settings_menu.html', {"selection" : selection}) +@register.simple_tag +def loadadminextmenu(perms): + menu = events.raiseQueryEvent("AdminMenuDisplay", target="admin_menu_box", + perms=perms) + return render_to_string('main/menulist.html', + {"menu" : menu, "perms" : perms}) + @register.filter def gender(value, target): if genders.has_key(value): diff --git a/admin/views.py b/admin/views.py index cbeae3cf2..5c1cfa794 100644 --- a/admin/views.py +++ b/admin/views.py @@ -16,6 +16,7 @@ from forms import MailboxForm, DomainForm, AliasForm, PermissionForm from mailng.lib.authbackends import crypt_password from mailng.lib import _render, _ctx_ok, _ctx_ko from mailng.lib import events +import string import copy def is_superuser(user): @@ -29,9 +30,10 @@ def good_domain(f): if request.user.is_superuser: return f(request, dom_id, **kwargs) mb = Mailbox.objects.get(user=request.user.id) - domid = isinstance(dom_id, str) and "%d" % dom_id or dom_id - if domid == mb.domain.id: - return f(request, domid, **kwargs) + if isinstance(dom_id, str) or isinstance(dom_id, unicode): + dom_id = string.atoi(dom_id) + if dom_id == mb.domain.id: + return f(request, dom_id, **kwargs) from django.conf import settings path = urlquote(request.get_full_path()) diff --git a/extensions/amavis_quarantine/main.py b/extensions/amavis_quarantine/main.py index 312c22848..a3fd1fb50 100644 --- a/extensions/amavis_quarantine/main.py +++ b/extensions/amavis_quarantine/main.py @@ -43,13 +43,13 @@ def urls(): include('mailng.extensions.amavis_quarantine.urls')) def menu(**kwargs): - if kwargs["target"] != "user_menu_box": - return [] - return [ - {"name" : _("Quarantine"), - "url" : reverse(index), - "img" : "/static/pics/quarantine.png"} - ] + if kwargs["target"] == "user_menu_box": + return [ + {"name" : _("Quarantine"), + "url" : reverse(index), + "img" : "/static/pics/quarantine.png"} + ] + return [] def lookup(*args): filter = "" diff --git a/extensions/stats/grapher.py b/extensions/stats/grapher.py index 4f33e817c..ea493de20 100644 --- a/extensions/stats/grapher.py +++ b/extensions/stats/grapher.py @@ -4,19 +4,48 @@ import os import time import datetime import rrdtool +from django.utils.translation import ugettext as _ from mailng.lib import getoption -default_avg_template = { +traffic_avg_template = { + 'name' : 'traffic', 'width' : '540', 'height' : '100', - 'title' : 'average message flow per minute', 'vertlabel' : 'msgs/min', 'cf' : 'AVERAGE', 'vars' : { - 'sent' : { "type" : "AREA", "color" : "#00FF00", "legend" : "sent messages" }, - 'recv' : { "type" : "AREA", "color" : "#0000FF", "legend" : "received messages" }, -# 'bounced' : { "color" : "#FFFF00", "legend" : "bounced messages" }, -# 'reject' : { "color" : "#FF0000", "legend" : "rejected messages" }, + 'sent' : { "type" : "AREA", "color" : "#00FF00", + "legend" : _("sent messages") }, + 'recv' : { "type" : "AREA", "color" : "#0000FF", + "legend" : _("received messages") }, + } +} + +badtraffic_avg_template = { + 'name' : 'badtraffic', + 'width' : '540', + 'height' : '100', + 'vertlabel' : _('msgs/min'), + 'cf' : 'AVERAGE', + 'vars' : { + 'bounced' : { "type" : "AREA", "color" : "#FFFF00", + "legend" : _("bounced messages") }, + 'reject' : { "type" : "AREA", "color" : "#FF0000", + "legend" : _("rejected messages") }, + } +} + +size_avg_template = { + 'name' : 'size', + 'width' : '540', + 'height' : '100', + 'vertlabel' : _('bytes/min'), + 'cf' : 'AVERAGE', + 'vars' : { + 'size_recv' : { "type" : "AREA", "color" : "#FF9900", + "legend" : _("received size") }, + 'size_sent' : { "type" : "AREA", "color" : "#339999", + "legend" : _("sent size") }, } } @@ -25,12 +54,13 @@ class Grapher(object): self.rrd_rootdir = getoption("RRD_ROOTDIR", "/tmp") self.img_rootdir = getoption("IMG_ROOTDIR", "/tmp") - def process(self, target, suffix, start, end, tpl=default_avg_template): + def process(self, target, suffix, start, end, tpl=traffic_avg_template): rrdfile = "%s/%s.rrd" % (self.rrd_rootdir, target) if not os.path.exists(rrdfile): return ext = "png" - path = "%s/%s_%s_%s.%s" % (self.img_rootdir, target, tpl['cf'], suffix, ext) + path = "%s/%s_%s_%s_%s.%s" % (self.img_rootdir, tpl['name'], + target, tpl['cf'], suffix, ext) start = str(start) end = str(end) defs = [] @@ -39,7 +69,8 @@ class Grapher(object): defs += ['DEF:%s=%s:%s:%s' % (v, rrdfile, v, tpl['cf']), 'CDEF:%spm=%s,60,*' % (v, v)] type = d.has_key("type") and d["type"] or "LINE" - lines += ["%s:%spm%s:%s" % (type, v, d["color"], d["legend"])] + lines += ["%s:%spm%s:%s" % (type, v, d["color"], + d["legend"].encode("utf-8"))] params = defs + lines rrdtool.graph(path, "--imgformat", "PNG", @@ -47,30 +78,16 @@ class Grapher(object): "--height", tpl["height"], "--start", start, "--end", end, - "--vertical-label", tpl["vertlabel"], - "--title", tpl["title"], + "--vertical-label", tpl["vertlabel"].encode("utf-8"), "--lower-limit", "0", "--slope-mode", *params) - def make_defaults(self, target): - #now = int(time.time()) - #start = now - (now % 86400) - #end = start + 86400 + def make_defaults(self, target, tpl=traffic_avg_template): end = "now" - self.process(target, "day", "-1day", end) - - #today = datetime.date.today() - #day = today.day - today.weekday() - #weekbegin = datetime.date(today.year, today.month, day) - #start = weekbegin.strftime("%Y%m%d") - #end = "+1week" - self.process(target, "week", "-1week", end) - -# monthbegin = datetime.date(today.year, today.month, 1) -# start = monthbegin.strftime("%Y%m%d") -# end = "+1month" - self.process(target, "month", "-1month", end) - self.process(target, "year", "-1year", end) + self.process(target, "day", "-1day", end, tpl) + self.process(target, "week", "-1week", end, tpl) + self.process(target, "month", "-1month", end, tpl) + self.process(target, "year", "-1year", end, tpl) diff --git a/extensions/stats/logparser.py b/extensions/stats/logparser.py index 40b656fba..07674f2f4 100755 --- a/extensions/stats/logparser.py +++ b/extensions/stats/logparser.py @@ -5,6 +5,7 @@ import sys import os import re import rrdtool +import string from optparse import OptionParser from mailng.lib import getoption from mailng.admin.models import Domain @@ -19,6 +20,8 @@ Postfix log parser. rrdstep = 60 xpoints = 540 points_per_sample = 3 +variables = ["sent", "recv", "bounced", "reject", "spam", "virus", + "size_sent", "size_recv"] class LogParser(object): def __init__(self, logfile, workdir, @@ -86,7 +89,7 @@ class LogParser(object): # Set up data sources for our RRD params = [] - for v in ["sent", "recv", "bounced", "reject"]: + for v in variables: params += ['DS:%s:%s:%s:0:U' % (v, ds_type, rrdstep * 2)] # Set up RRD to archive data @@ -125,39 +128,54 @@ class LogParser(object): print "[rrd] VERBOSE events at %s already recorded in RRD" %m return False + tpl = "" + for v in variables: + if tpl != "": + tpl += ":" + tpl += v # Missing some RRD steps # Est ce vraiment nécessaire... ? if m > self.lupdates[fname] + rrdstep: + values = "" + for v in variables: + if values != "": values += ":" + values += "0" for p in range(self.lupdates[fname] + rrdstep, m, rrdstep): if self.verbose: print "[rrd] VERBOSE update %s:%s:%s:%s:%s (SKIP)" \ %(p,'0','0','0','0') - rrdtool.update(fname, "%s:%s:%s:%s:%s" \ - % (p, '0', '0', '0', '0')) + rrdtool.update(fname, "-t", tpl, "%s:%s" % (p, values)) if self.verbose: print "[rrd] VERBOSE update %s:%s:%s:%s:%s" \ %(m, self.data[dom][m]['sent'], self.data[dom][m]['recv'],\ self.data[dom][m]['bounced'], self.data[dom][m]['reject']) - rrdtool.update(fname, "%s:%s:%s:%s:%s" \ - % (m, self.data[dom][m]['sent'], - self.data[dom][m]['recv'], - self.data[dom][m]['bounced'], - self.data[dom][m]['reject'])) + values = "%s" % m + tpl = "" + for v in variables: + values += ":" + values += str(self.data[dom][m][v]) + if tpl != "": + tpl += ":" + tpl += v + rrdtool.update(fname, "-t", tpl, values) self.lupdates[fname] = m return True - def inc_counter(self, dom, cur_t, counter): + def initcounters(self, dom, cur_t): + init = {} + for v in variables: init[v] = 0 + self.data[dom][cur_t] = init + + def inc_counter(self, dom, cur_t, counter, val=1): if not self.data[dom].has_key(cur_t): - self.data[dom][cur_t] = \ - {'sent' : 0, 'recv' : 0, 'bounced' : 0, 'reject' : 0} - self.data[dom][cur_t][counter] += 1 + self.initcounters(dom, cur_t) + self.data[dom][cur_t][counter] += val if not self.data["global"].has_key(cur_t): - self.data["global"][cur_t] = \ - {'sent' : 0, 'recv' : 0, 'bounced' : 0, 'reject' : 0} - self.data["global"][cur_t][counter] += 1 + self.initcounters("global", cur_t) + self.data["global"][cur_t][counter] += val def process(self): for line in self.f.readlines(): @@ -170,9 +188,10 @@ class LogParser(object): cur_t = self.str2Time(self.year, mo, da, ho, mi, se) cur_t = cur_t - cur_t % rrdstep - m = re.search("(\w{10}): from=<(.*)>", log) + m = re.search("(\w{10}): from=<(.*)>, size=(\d+)", log) if m: - self.workdict[m.group(1)] = {'from' : m.group(2)} + self.workdict[m.group(1)] = {'from' : m.group(2), + 'size' : string.atoi(m.group(3))} continue m = re.search("(\w{10}): to=<(.*)>.*status=(\S+)", log) @@ -183,10 +202,14 @@ class LogParser(object): addrfrom = re.match("([^@]+)@(.+)", self.workdict[m.group(1)]['from']) if addrfrom and addrfrom.group(2) in self.domains: self.inc_counter(addrfrom.group(2), cur_t, 'sent') + self.inc_counter(addrfrom.group(2), cur_t, 'size_sent', + self.workdict[m.group(1)]['size']) addrto = re.match("([^@]+)@(.+)", m.group(2)) if addrto.group(2) in self.domains: if m.group(3) == "sent": self.inc_counter(addrto.group(2), cur_t, 'recv') + self.inc_counter(addrto.group(2), cur_t, 'size_recv', + self.workdict[m.group(1)]['size']) else: self.inc_counter(addrto.group(2), cur_t, m.group(3)) continue @@ -205,7 +228,10 @@ class LogParser(object): sortedData = [ (i, data[i]) for i in sorted(data.keys()) ] for t, dict in sortedData: self.update_rrd(dom, t) - G.make_defaults(dom) + + G.make_defaults(dom, tpl=grapher.traffic_avg_template) + G.make_defaults(dom, tpl=grapher.badtraffic_avg_template) + G.make_defaults(dom, tpl=grapher.size_avg_template) if __name__ == "__main__": log_file = getoption("LOGFILE", "/var/log/maillog") diff --git a/extensions/stats/main.py b/extensions/stats/main.py index 75ceb9dda..808bf18ba 100644 --- a/extensions/stats/main.py +++ b/extensions/stats/main.py @@ -11,38 +11,36 @@ from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ from django.conf.urls.defaults import * from mailng.lib import events, _render -from mailng.admin.views import is_superuser +from mailng.admin.views import good_domain from mailng.admin.models import Domain, Mailbox -#from mailng.extensions.stats.mailrrd import parser from django.contrib.auth.decorators \ - import login_required -import pdb + import login_required, user_passes_test, permission_required -graph_nature = ["sent_recv","boun_reje"] -graph_types = ["AVERAGE","MAX"] -tmp_path = "static/tmp" +graph_types = ['AVERAGE', 'MAX'] def init(): - events.register("AdminMenuDisplay", menu) + events.register("AdminMenuDisplay", menu) def urls(): return (r'^mailng/stats/', include('mailng.extensions.stats.urls')) - def menu(**kwargs): - print kwargs - domain_id = kwargs['domain'] if kwargs["target"] == "admin_menu_bar": + domain_id = kwargs['domain'] return [ {"label" : _("Statistics"), "name" : "stats", "url" : reverse('domindex', args=[domain_id]), "img" : "/static/pics/graph.png"} ] - else: - return [] - + if kwargs["target"] == "admin_menu_box": + return [ + {"name" : _("Statistics"), + "url" : reverse('fullindex'), + "img" : "/static/pics/graph.png"} + ] + return [] @login_required def domain(request,dom_id): @@ -82,26 +80,38 @@ def graph_display(request,dom_id,graph_t=graph_types): "domain":domain,"domains" : domains, "messages" : errors , "types" : graph_type, "tmp_path" : tmp_path}) +@login_required +@user_passes_test(lambda u: u.is_superuser) +def adminindex(request): + period = request.GET.has_key("period") and request.GET["period"] or "day" + domain = request.GET.has_key("domain") and request.GET["domain"] or "global" + if domain != "global": + domain = Domain.objects.get(pk=domain) + domain = domain.name + domains = Domain.objects.all() + return _render(request, 'stats/adminindex.html', { + "domains" : domains, "domain" : domain, + "graphs" : {"traffic" : _("Average normal traffic"), + "badtraffic" : _("Average bad traffic"), + "size" : _("Average normal traffic size")}, + "periods" : ["day", "week", "month", "year"], + "period" : period + }) @login_required +@good_domain +@permission_required("admin.view_mailboxes") def index(request, dom_id=None): - domains = [] - errors = [] domain = None - if dom_id: - if request.user.has_perm("admin.view_mailboxes"): - domain = Domain.objects.get(pk=dom_id) - if domain: - domains.append(domain) - else: - print "pas de dom" - else: - domains = Domain.objects.all() + period = request.GET.has_key("period") and request.GET["period"] or "day" + if request.user.has_perm("admin.view_mailboxes"): + domain = Domain.objects.get(pk=dom_id) - if domains == []: - errors.append(_("No Domain defined")) return _render(request, 'stats/index.html', { - "page" : "Statistics", - "domain" : domain, "domains": domains, "messages" : errors , - "periods" : ["day", "week", "month", "year"] - }) + "domain" : domain, + "graphs" : {"traffic" : _("Average normal traffic"), + "badtraffic" : _("Average bad traffic"), + "size" : _("Average normal traffic size")}, + "periods" : ["day", "week", "month", "year"], + "period" : period + }) diff --git a/extensions/stats/templates/adminindex.html b/extensions/stats/templates/adminindex.html new file mode 100644 index 000000000..25cf9ff8e --- /dev/null +++ b/extensions/stats/templates/adminindex.html @@ -0,0 +1,52 @@ +{% extends "layout.html" %} +{% load i18n %} +{% load stats_extra %} +{% block html_head %} + + + + + +{% endblock %} +{% block middletitle %}{% trans "Global statistics" %}{% endblock %} +{% block content %} +
+
+ + + + + +
+ {% include "stats/graphs.html" %} +
+{% endblock %} diff --git a/extensions/stats/templates/graphs.html b/extensions/stats/templates/graphs.html new file mode 100644 index 000000000..5c1709303 --- /dev/null +++ b/extensions/stats/templates/graphs.html @@ -0,0 +1,6 @@ +{% load i18n %} +{% for g, title in graphs.items %} +

{{ title }}

+

{% trans 'No statistics available' %}

+{% endfor %} diff --git a/extensions/stats/templates/index.html b/extensions/stats/templates/index.html index 90f43880b..374e780cb 100644 --- a/extensions/stats/templates/index.html +++ b/extensions/stats/templates/index.html @@ -1,34 +1,21 @@ -{% extends "layout.html" %} +{% extends "admin/domain_header.html" %} {% load i18n %} {% block html_head %} {% endblock %} @@ -38,45 +25,15 @@ {% domain_menu domain.id "stats" perms %} {% endif %} {% endblock %} -{% block middletitle %} -{% if domain %} - {% trans "Statistics for" %} {{ domain.name }} -{% else %} - {% trans "Global statistics" %} -{% endif %} -{% endblock %} -{% block content %} -
- - {% if domain %} - {% for p in periods %} - - - - {% endfor %} - {% else %} - {% if domains %} - - - - - - {% for dom in domains %} - - - {% url extensions.stats.main.domain dom.id as domurl %} - - - {% endfor %} - {% endif %} - {% endif %} -
- No statistics available -
{% trans "Graph type" %}{% trans "Domain name" %}
- {% for t in types %} - {% url extensions.stats.main.graph_display dom.id,t as graphurl %} - {{ t|lower }} - {% endfor %} - {{ dom.name }}
+{% block domcontent %} +
+ + +
+ {% include "stats/graphs.html" %}
{% endblock %} diff --git a/extensions/stats/templatetags/__init__.py b/extensions/stats/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/extensions/stats/templatetags/stats_extra.py b/extensions/stats/templatetags/stats_extra.py new file mode 100644 index 000000000..3f4222c73 --- /dev/null +++ b/extensions/stats/templatetags/stats_extra.py @@ -0,0 +1,14 @@ +from django import template +from django.utils.translation import ugettext as _ + +register = template.Library() + +@register.simple_tag +def getLabel(var): + labels = { + "day" : _("Day"), + "week" : _("Week"), + "month" : _("Month"), + "year" : _("Year") + } + return labels.has_key(var) and labels[var] or var diff --git a/extensions/stats/urls.py b/extensions/stats/urls.py index bb2ee66dd..904b014c4 100644 --- a/extensions/stats/urls.py +++ b/extensions/stats/urls.py @@ -1,7 +1,7 @@ from django.conf.urls.defaults import * urlpatterns = patterns('mailng.extensions.stats.main', - url(r'^$', 'index', name='fullindex'), + url(r'^$', 'adminindex', name='fullindex'), url(r'^(?P\d+)/$', 'index', name='domindex'), (r'^display/(?P\d+)/(?P\w+)/$', 'graph_display'), (r'^domain/(?P\d+)/$', 'domain'), diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 864e8c9ee..8c1760074 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-10-30 11:19+0100\n" +"POT-Creation-Date: 2009-11-03 15:22+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Antoine Nguyen \n" "Language-Team: LANGUAGE \n" @@ -17,64 +17,69 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" #: admin/forms.py:16 templates/admin/domains.html:17 -#: templates/admin/mailboxes.html:18 +#: templates/admin/mailboxes.html:17 msgid "Quota" msgstr "" #: admin/forms.py:17 +msgid "Mailbox quota in MB (default to domain quota if blank)" +msgstr "" +"Quota en Mo de la boite aux lettres (par défaut celui du domaine si vide)" + +#: admin/forms.py:18 msgid "Password" msgstr "Mot de passe" -#: admin/forms.py:18 +#: admin/forms.py:19 msgid "Password used to log in" msgstr "Mot de passe utilisé pour se connecter" -#: admin/forms.py:19 main/forms.py:21 +#: admin/forms.py:20 main/forms.py:21 msgid "Confirmation" msgstr "" -#: admin/forms.py:20 +#: admin/forms.py:21 msgid "Password confirmation" msgstr "Confirmation du mot de passe" -#: admin/forms.py:23 +#: admin/forms.py:24 msgid "Check to activate this mailbox" msgstr "Cocher pour activer cette boite aux lettres" -#: admin/forms.py:36 main/forms.py:36 +#: admin/forms.py:37 main/forms.py:36 msgid "Passwords mismatch" msgstr "Les mots de passe diffèrent" -#: admin/forms.py:55 templates/admin/domain_header.html:7 +#: admin/forms.py:56 templates/admin/domain_header.html:6 #: templates/admin/editdomain.html:7 templates/admin/permissions.html:42 msgid "Domain" msgstr "Domaine" -#: admin/forms.py:57 +#: admin/forms.py:58 msgid "Select a domain in the list" msgstr "Sélectionner un domaine dans la liste" -#: admin/forms.py:58 +#: admin/forms.py:59 msgid "User" msgstr "Utilisateur" -#: admin/forms.py:59 +#: admin/forms.py:60 msgid "Select a user in the list" msgstr "Sélectionner un utilisateur dans la liste" -#: admin/forms.py:60 +#: admin/forms.py:61 msgid "Role" msgstr "Rôle" -#: admin/forms.py:61 +#: admin/forms.py:62 msgid "Super administrator" msgstr "Super administrateur" -#: admin/forms.py:62 +#: admin/forms.py:63 msgid "Domain administrator" msgstr "Admin. de domaine" -#: admin/forms.py:63 +#: admin/forms.py:64 msgid "Select the role you want this user to have" msgstr "Sélectionner le rôle souhaité pour cet utilisateur" @@ -114,11 +119,6 @@ msgstr "Adresse de la boite aux lettres (sans la partie @domain.tld)" msgid "password" msgstr "Mot de passe" -#: admin/models.py:44 -msgid "Mailbox quota in MB (default to domain quota if blank)" -msgstr "" -"Quota en Mo de la boite aux lettres (par défaut celui du domaine si vide)" - #: admin/models.py:107 msgid "The alias address (without the domain part)" msgstr "L'adresse de l'alias (sans la partie domaine)" @@ -135,44 +135,44 @@ msgstr "La boite aux lettres sur laquelle pointe cet alias" msgid "Check to activate this alias" msgstr "Cocher pour activer cet alias" -#: admin/views.py:23 +#: admin/views.py:24 msgid "Permission denied." msgstr "Permission refusée." -#: admin/views.py:67 admin/views.py:97 +#: admin/views.py:69 admin/views.py:99 msgid "Domain with this name already defined" msgstr "Un domaine avec ce nom existe déjà" -#: admin/views.py:76 +#: admin/views.py:78 msgid "Failed to initialise domain, check permissions" msgstr "Impossible d'initialiser le domaine, vérifier les permissions" -#: admin/views.py:100 +#: admin/views.py:102 msgid "Failed to rename domain, check permissions" msgstr "Impossible de renommer le domaine, vérifier les permissions" -#: admin/views.py:154 admin/views.py:213 +#: admin/views.py:156 admin/views.py:215 msgid "Mailbox with this address already exists" msgstr "Une boite aux lettres pour cette adresse existe déjà" -#: admin/views.py:185 +#: admin/views.py:187 msgid "Mailbox created." msgstr "Boite aux lettres créé." -#: admin/views.py:189 +#: admin/views.py:191 msgid "Failed to initialise mailbox, check permissions" msgstr "" "Impossible d'initialiser la boite aux lettres, vérifier les permissions" -#: admin/views.py:216 +#: admin/views.py:218 msgid "Failed to rename mailbox, check permissions" msgstr "Impossible de renommer la boite aux lettres, vérifier les permissions." -#: admin/views.py:292 admin/views.py:322 +#: admin/views.py:294 admin/views.py:324 msgid "Alias with this address already exists" msgstr "Un alias avec cette adresse existe déjà" -#: admin/views.py:395 +#: admin/views.py:397 msgid "admin is intouchable!!" msgstr "admin est intouchable!!" @@ -200,8 +200,8 @@ msgstr "Alias" msgid "Properties" msgstr "Propriétés" -#: extensions/amavis_quarantine/main.py:49 -#: extensions/amavis_quarantine/templates/index.html:153 +#: extensions/amavis_quarantine/main.py:48 +#: extensions/amavis_quarantine/templates/index.html:152 msgid "Quarantine" msgstr "Quarantaine" @@ -215,29 +215,29 @@ msgstr "%(count)d message%(plural)s supprimé correctement" msgid "%(count)d message%(plural)s released successfully" msgstr "%(count)d messages relâché correctement" -#: extensions/amavis_quarantine/templates/index.html:95 +#: extensions/amavis_quarantine/templates/index.html:94 msgid "Delete this selection?" msgstr "Supprimer cette sélection?" -#: extensions/amavis_quarantine/templates/index.html:102 +#: extensions/amavis_quarantine/templates/index.html:101 msgid "Release this selection?" msgstr "Relâcher cette sélection?" -#: extensions/amavis_quarantine/templates/index.html:136 -#: extensions/amavis_quarantine/templates/index.html:158 +#: extensions/amavis_quarantine/templates/index.html:135 +#: extensions/amavis_quarantine/templates/index.html:157 msgid "Search..." msgstr "Chercher..." -#: extensions/amavis_quarantine/templates/index.html:160 +#: extensions/amavis_quarantine/templates/index.html:159 msgid "From address" msgstr "Emetteur" -#: extensions/amavis_quarantine/templates/index.html:161 +#: extensions/amavis_quarantine/templates/index.html:160 #: extensions/amavis_quarantine/templates/listing.html:35 msgid "Subject" msgstr "Sujet" -#: extensions/amavis_quarantine/templates/index.html:162 +#: extensions/amavis_quarantine/templates/index.html:161 msgid "Both" msgstr "Tous" @@ -277,11 +277,11 @@ msgstr "" msgid "Message headers" msgstr "Entêtes du message" -#: extensions/amavis_quarantine/templates/viewmail.html:32 +#: extensions/amavis_quarantine/templates/viewmail.html:22 msgid "Mail content" msgstr "Contenu du message" -#: extensions/amavis_quarantine/templates/viewmail.html:41 +#: extensions/amavis_quarantine/templates/viewmail.html:31 msgid "Message" msgstr "" @@ -327,29 +327,98 @@ msgstr "Supprimer ce message?" msgid "Options" msgstr "" -#: extensions/dummy_plugin/main.py:17 -msgid "Dummy" -msgstr "" - #: extensions/postfix_autoreply/main.py:35 msgid "Auto-reply message" msgstr "Message de réponse automatique" -#: extensions/stats/main.py:39 +#: extensions/stats/grapher.py:18 +msgid "sent messages" +msgstr "messages envoyés" + +#: extensions/stats/grapher.py:20 +msgid "received messages" +msgstr "messages reçus" + +#: extensions/stats/grapher.py:28 +msgid "msgs/min" +msgstr "" + +#: extensions/stats/grapher.py:32 +msgid "bounced messages" +msgstr "messages ?" + +#: extensions/stats/grapher.py:34 +msgid "rejected messages" +msgstr "messages rejetés" + +#: extensions/stats/grapher.py:42 +msgid "bytes/min" +msgstr "octes/min" + +#: extensions/stats/grapher.py:46 +msgid "received size" +msgstr "taille reçue" + +#: extensions/stats/grapher.py:48 +msgid "sent size" +msgstr "taille envoyée" + +#: extensions/stats/main.py:32 extensions/stats/main.py:39 msgid "Statistics" msgstr "Statistiques" -#: extensions/stats/main.py:66 extensions/stats/main.py:95 +#: extensions/stats/main.py:68 msgid "No Domain defined" msgstr "Aucun domaine défini." -#: extensions/stats/templates/index.html:62 -msgid "Graph type" +#: extensions/stats/main.py:94 extensions/stats/main.py:112 +msgid "Average normal traffic" +msgstr "Trafic normal moyen" + +#: extensions/stats/main.py:95 extensions/stats/main.py:113 +msgid "Average bad traffic" +msgstr "Mauvais trafic moyen" + +#: extensions/stats/main.py:96 extensions/stats/main.py:114 +msgid "Average normal traffic size" +msgstr "Taille moyenne du trafic moyen" + +#: extensions/stats/templates/adminindex.html:31 +msgid "Global statistics" +msgstr "Statistiques globales" + +#: extensions/stats/templates/adminindex.html:35 +msgid "Domain:" +msgstr "Domaine :" + +#: extensions/stats/templates/adminindex.html:37 +msgid "Global" msgstr "" -#: extensions/stats/templates/index.html:63 -msgid "Domain name" -msgstr "Nom du domaine" +#: extensions/stats/templates/adminindex.html:43 +#: extensions/stats/templates/index.html:30 +msgid "Period:" +msgstr "Période :" + +#: extensions/stats/templates/graphs.html:5 +msgid "No statistics available" +msgstr "Aucune statistique disponible" + +#: extensions/stats/templatetags/stats_extra.py:9 +msgid "Day" +msgstr "Jour" + +#: extensions/stats/templatetags/stats_extra.py:10 +msgid "Week" +msgstr "Semaine" + +#: extensions/stats/templatetags/stats_extra.py:11 +msgid "Month" +msgstr "Mois" + +#: extensions/stats/templatetags/stats_extra.py:12 +msgid "Year" +msgstr "Année" #: main/forms.py:17 msgid "Old password" @@ -371,10 +440,6 @@ msgstr "sujet" msgid "content" msgstr "contenu" -#: main/models.py:10 -msgid "until" -msgstr "" - #: main/views.py:45 msgid "Auto reply message updated successfully." msgstr "Message de réponse auto. mis à jour." @@ -383,27 +448,27 @@ msgstr "Message de réponse auto. mis à jour." msgid "Change password" msgstr "Changer de mot de passe" -#: templates/layout.html:42 +#: templates/layout.html:44 msgid "Admin" msgstr "" -#: templates/layout.html:49 +#: templates/layout.html:51 msgid "Preferences" msgstr "Préférences" -#: templates/layout.html:56 +#: templates/layout.html:58 msgid "Logout" msgstr "Déconnexion" -#: templates/layout.html:69 templates/admin/newdomain.html:7 +#: templates/layout.html:72 templates/admin/newdomain.html:7 msgid "New domain" msgstr "Nouveau domaine" -#: templates/layout.html:76 +#: templates/layout.html:79 msgid "Domains" msgstr "Domaines" -#: templates/layout.html:83 templates/admin/settings_header.html:6 +#: templates/layout.html:86 templates/admin/settings_header.html:6 msgid "Settings" msgstr "Paramètres" @@ -416,34 +481,34 @@ msgstr "Ajouter une permission" msgid "Create" msgstr "Créer" -#: templates/admin/aliases.html:10 +#: templates/admin/aliases.html:9 msgid "No alias defined." msgstr "Aucun alias défini." -#: templates/admin/aliases.html:14 templates/admin/domains.html:14 -#: templates/admin/mailboxes.html:15 templates/admin/permissions.html:14 +#: templates/admin/aliases.html:13 templates/admin/domains.html:14 +#: templates/admin/mailboxes.html:14 templates/admin/permissions.html:14 #: templates/admin/permissions.html:41 msgid "Actions" msgstr "" -#: templates/admin/aliases.html:15 templates/admin/mailboxes.html:17 +#: templates/admin/aliases.html:14 templates/admin/mailboxes.html:16 msgid "Address" msgstr "Adresse" -#: templates/admin/aliases.html:16 templates/admin/editmailbox.html:7 +#: templates/admin/aliases.html:15 templates/admin/editmailbox.html:7 msgid "Mailbox" msgstr "Boite aux lettres" -#: templates/admin/aliases.html:17 templates/admin/domains.html:18 +#: templates/admin/aliases.html:16 templates/admin/domains.html:18 #: templates/admin/permissions.html:17 templates/admin/permissions.html:44 msgid "Enabled" msgstr "Activé" -#: templates/admin/aliases.html:25 +#: templates/admin/aliases.html:24 msgid "Edit alias" msgstr "Editer cet alias" -#: templates/admin/aliases.html:29 +#: templates/admin/aliases.html:28 msgid "Delete alias" msgstr "Supprimer cet alias" @@ -455,7 +520,7 @@ msgstr "Domaines disponibles" msgid "No domain defined." msgstr "Aucun domaine défini." -#: templates/admin/domains.html:15 templates/admin/mailboxes.html:16 +#: templates/admin/domains.html:15 templates/admin/mailboxes.html:15 #: templates/admin/permissions.html:43 msgid "Name" msgstr "Nom" @@ -468,37 +533,37 @@ msgstr "Editer le domaine" msgid "Delete domain" msgstr "Supprimer le domaine" -#: templates/admin/domains.html:36 templates/admin/mailboxes.html:46 +#: templates/admin/domains.html:36 templates/admin/mailboxes.html:45 msgid "MB" msgstr "Mo" -#: templates/admin/domains.html:37 templates/admin/mailboxes.html:47 +#: templates/admin/domains.html:37 templates/admin/mailboxes.html:46 msgid "yes,no" msgstr "oui,non" #: templates/admin/editalias.html:14 templates/admin/editdomain.html:15 -#: templates/admin/editmailbox.html:16 templates/main/autoreply.html:19 +#: templates/admin/editmailbox.html:16 templates/main/autoreply.html:17 #: templates/main/chpassword.html:15 msgid "Update" msgstr "Mettre à jour" -#: templates/admin/mailboxes.html:11 +#: templates/admin/mailboxes.html:10 msgid "No mailbox defined." msgstr "Aucune boite aux lettres définie." -#: templates/admin/mailboxes.html:27 +#: templates/admin/mailboxes.html:26 msgid "Edit mailbox" msgstr "Editer la boite aux lettres" -#: templates/admin/mailboxes.html:32 +#: templates/admin/mailboxes.html:31 msgid "Delete mailbox" msgstr "Supprimer la boite aux lettres" -#: templates/admin/mailboxes.html:37 +#: templates/admin/mailboxes.html:36 msgid "Add new alias" msgstr "Ajouter un alias" -#: templates/admin/mailboxes.html:41 +#: templates/admin/mailboxes.html:40 msgid "Mailbox aliases" msgstr "Alias de cette boite aux lettres" @@ -538,11 +603,11 @@ msgstr "Aucun admin de domaine défini." msgid "Permissions" msgstr "" -#: templates/main/confirm.html:26 +#: templates/main/confirm.html:25 msgid "Do not delete mailbox directory" msgstr "Ne pas supprimer le répertoire de la boite aux lettres" -#: templates/main/header.html:8 +#: templates/main/header.html:6 msgid "General" msgstr "Général" @@ -563,3 +628,6 @@ msgid "Your username and password didn't match. Please try again." msgstr "" "Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez " "réessayer." + +#~ msgid "Domain name" +#~ msgstr "Nom du domaine" diff --git a/main/templatetags/main_extras.py b/main/templatetags/main_extras.py index 152623139..774ac99b5 100644 --- a/main/templatetags/main_extras.py +++ b/main/templatetags/main_extras.py @@ -25,6 +25,7 @@ def menu(selection, perms): @register.simple_tag def loadextmenu(perms): - menu = events.raiseQueryEvent("UserMenuDisplay", target="user_menu_box") + menu = events.raiseQueryEvent("UserMenuDisplay", target="user_menu_box", + perms=perms) return render_to_string('main/menulist.html', {"menu" : menu, "perms" : perms}) diff --git a/templates/layout.html b/templates/layout.html index d64dd6ec1..a9e384ecf 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -61,6 +61,7 @@
{% if user.is_superuser %} + {% load admin_extras %} {% endif %}