Statistics plugin almost done. Needs more testing.

This commit is contained in:
tonio
2009-11-03 15:25:29 +01:00
parent 53df75f045
commit 64ae5f4a94
15 changed files with 397 additions and 234 deletions
+9 -1
View File
@@ -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):
+5 -3
View File
@@ -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())
+7 -7
View File
@@ -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 = ""
+46 -29
View File
@@ -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)
+44 -18
View File
@@ -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")
+41 -31
View File
@@ -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
})
@@ -0,0 +1,52 @@
{% extends "layout.html" %}
{% load i18n %}
{% load stats_extra %}
{% block html_head %}
<script src="/static/js/mootools-more.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="/static/stylesheets/box.css" />
<style type="text/css">
#graphcontent {
margin: 25px auto 10px;
width:90%;
}
#selectors {
float: right;
}
</style>
<script type="text/javascript">
window.addEvent("domready", function() {
$("period").addEvent("change", function(event) {
{% url extensions.stats.main.adminindex as stats_url %}
location.href = "{{ stats_url }}" + "?period=" + $("period").value;
});
$("domain").addEvent("change", function(event) {
{% url extensions.stats.main.adminindex as stats_url %}
location.href = "{{ stats_url }}" + "?domain=" + $("domain").value;
});
});
</script>
{% endblock %}
{% block middletitle %}{% trans "Global statistics" %}{% endblock %}
{% block content %}
<div id="graphcontent">
<div id="selectors">
<label for="domain">{% trans "Domain:" %}</label>
<select id="domain" name="domain">
<option value="global" {% ifequal domain "global" %}selected{% endifequal %}>{% trans "Global" %}</option>
{% for d in domains %}
<option value="{{ d.id }}" {% ifequal d.name domain %}selected{% endifequal %}>{{ d.name }}</option>
{% endfor %}
</select>
<label for="period">{% trans "Period:" %}</label>
<select id="period" name="period">
{% for p in periods %}
<option value="{{ p }}" {% ifequal p period %}selected{% endifequal %}>{% getLabel p %}</option>
{% endfor %}
</select>
</div>
{% include "stats/graphs.html" %}
</div>
{% endblock %}
+6
View File
@@ -0,0 +1,6 @@
{% load i18n %}
{% for g, title in graphs.items %}
<h3>{{ title }}</h3>
<p><img src="/static/graphs/{{ g }}_{{ domain }}_AVERAGE_{{ period }}.png"
alt="{% trans 'No statistics available' %}" /></p>
{% endfor %}
+19 -62
View File
@@ -1,34 +1,21 @@
{% extends "layout.html" %}
{% extends "admin/domain_header.html" %}
{% load i18n %}
{% block html_head %}
<script src="/static/js/mootools-more.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="/static/stylesheets/box.css" />
<style type="text/css">
form {
margin: 0 auto;
width: 98%;
#selectors {
float: right;
}
td {
font-size: smaller;
height: 40px;
}
errors {
text-align: center;
width: 100%;
margin: 20px 20px;
color: red;
}
#graphcontent {
margin:60px auto 10px;
width:90%;
}
</style>
<script type="text/javascript">
window.addEvent("domready", function() {
$("period").addEvent("change", function(event) {
{% url extensions.stats.main.index domain.id as stats_url %}
location.href = "{{ stats_url }}" + "?period=" + $("period").value;
});
});
</script>
{% 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 %}
<div id="graphcontent">
<table>
{% if domain %}
{% for p in periods %}
<tr>
<td>
<img src="/static/graphs/{{ domain.name }}_AVERAGE_{{ p }}.png" alt="No statistics available" />
</td>
</tr>
{% endfor %}
{% else %}
{% if domains %}
<tr>
<th width="50px">{% trans "Graph type" %}</th>
<th width="80px">{% trans "Domain name" %}</th>
</tr>
{% for dom in domains %}
<tr class="{% cycle 'odd' 'even' %}">
<td>
{% for t in types %}
{% url extensions.stats.main.graph_display dom.id,t as graphurl %}
<a name="loadGraph" href="{{ graphurl }}">{{ t|lower }} </a>
{% endfor %}
</td>
{% url extensions.stats.main.domain dom.id as domurl %}
<td><a name="loadDom" href="{{ domurl }}">{{ dom.name }}</a></td>
</tr>
{% endfor %}
{% endif %}
{% endif %}
</table>
{% block domcontent %}
<div id="selectors">
<label for="period">{% trans "Period:" %}</label>
<select id="period" name="period">
{% for p in periods %}
<option value="{{ p }}" {% ifequal p period %}selected{% endifequal %}>{{ p }}</option>
{% endfor %}
</select>
</div>
{% include "stats/graphs.html" %}
</div>
{% endblock %}
@@ -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
+1 -1
View File
@@ -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<dom_id>\d+)/$', 'index', name='domindex'),
(r'^display/(?P<dom_id>\d+)/(?P<graph_t>\w+)/$', 'graph_display'),
(r'^domain/(?P<dom_id>\d+)/$', 'domain'),
+149 -81
View File
@@ -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 <tonio@ngyn.org>\n"
"Language-Team: LANGUAGE <LL@li.org>\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"
+2 -1
View File
@@ -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})
+2
View File
@@ -61,6 +61,7 @@
</div>
<br/>
{% if user.is_superuser %}
{% load admin_extras %}
<div id="menubox" class="leftbox">
<ul>
<li>
@@ -85,6 +86,7 @@
{% trans "Settings" %}
</a>
</li>
{% loadadminextmenu perms %}
</ul>
</div>
{% endif %}