Merge branch 'master' into nvd3

This commit is contained in:
Antoine Nguyen
2014-01-24 15:26:09 +01:00
37 changed files with 842 additions and 1045 deletions
+3 -3
View File
@@ -41,16 +41,16 @@ master_doc = 'index'
# General information about the project.
project = u'Modoboa'
copyright = u'2013, Antoine Nguyen'
copyright = u'2014, Antoine Nguyen'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
version = '1.1'
# The full version, including alpha/beta/rc tags.
release = '1.0.1'
release = '1.1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+18 -5
View File
@@ -242,6 +242,17 @@ Modoboa should connect to your LDAP server. They are described just below:
| |server is an Active | |
| |Directory one | |
+--------------------+---------------------------------+--------------------+
|Administrator groups|Members of those LDAP Posix | |
| |groups will be created ad domain | |
| |administrators. Use ';' | |
| |characters to separate groups. | |
+--------------------+---------------------------------+--------------------+
|Groups search base |The distinguished name of the | |
| |search base used to find groups | |
| | | |
| | | |
+--------------------+---------------------------------+--------------------+
If you need additional parameters, you will find a detailled
documentation `here <http://packages.python.org/django-auth-ldap/>`_.
@@ -250,12 +261,14 @@ Once the authentication is properly configured, the users defined in
your LDAP directory will be able to connect to Modoboa, the associated
domain and mailboxes will be automatically created if needed.
.. note::
The first time a user connects to Modoboa, a local account is created
if the LDAP username is a valid email address. By default, this
account belongs to the *SimpleUsers* group and it has a mailbox.
Users created automatically from an LDAP directory belongs to the
*SimpleUsers* group and have a mailbox. To do so, usernames have to
be valid email addresses. If not, LDAP users won't be able to
access Modoboa.
To automatically create domain administrators, you can use the
**Administrator groups** setting. If a LDAP user belongs to one the
listed groups, its local account will belong to the *DomainAdmins*
group. In this case, the username is not necessarily an email address.
Users will also be able to update their LDAP password directly from
Modoboa.
+25 -9
View File
@@ -46,7 +46,7 @@ listing page: *relay domain* and *relay domain alias*.
The extension is compatible with the *amavis* and *limits*
ones. Resellers will be able to create both new objects.
To tell Postfix this feature exists, you must generate two new map
Replace <driver> by the name of the database you use.To tell Postfix this feature exists, you must generate two new map
files and then update your configuration.
To generate the map files, run the following command::
@@ -55,7 +55,23 @@ To generate the map files, run the following command::
Replace values between ``<>`` by yours.
Edit the :file:`/etc/postfix/main.cf` file.
Edit the :file:`/etc/postfix/main.cf` file and copy the following
lines inside::
relay_domains = <driver>:/etc/postfix/maps/sql-relaydomains.cf
transport_maps =
<driver>:/etc/postfix/maps/sql-relaydomains-transport.cf
<driver>:/etc/postfix/maps/sql-relaydomain-aliases-transport.cf
smtpd_recipient_restrictions =
permit_mynetworks
reject_unauth_destination
check_recipient_access
<driver>:/etc/postfix/maps/sql-relay-recipient-verification.cf
Replace ``<driver>`` by the name of the database you use.
Reload postfix.
.. _amavis_frontend:
@@ -311,12 +327,11 @@ configuration files as follows:
``/etc/postfix/main.cf``::
transport_maps = mysql:/etc/postfix/maps/sql-transport.cf
virtual_alias_maps = mysql:/etc/postfix/maps/sql-aliases.cf
mysql:/etc/postfix/mapfiles/sql-domain-aliases-mailboxes.cf,
mysql:/etc/postfix/mapfiles/sql-email2email.cf,
mysql:/etc/postfix/maps/sql-autoreplies.cf,
mysql:/etc/postfix/mapfiles/sql-catchall-aliases.cf
transport_maps = mysql:/etc/postfix/maps/sql-autoreplies-transport.cf
virtual_alias_maps = <driver>:/etc/postfix/maps/sql-aliases.cf
<driver>:/etc/postfix/mapfiles/sql-domain-aliases-mailboxes.cf,
<driver>:/etc/postfix/maps/sql-autoreplies.cf,
<driver>:/etc/postfix/mapfiles/sql-catchall-aliases.cf
.. note::
@@ -327,7 +342,8 @@ configuration files as follows:
autoreply unix - n n - - pipe
flags= user=vmail:<group> argv=<modoboa_site>/manage.py autoreply $sender $mailbox
``<modoboa_site>`` is the path of your Modoboa instance.
Replace ``<driver>`` by the name of the database you
use. ``<modoboa_site>`` is the path of your Modoboa instance.
Then, create the requested map files::
+6 -3
View File
@@ -50,8 +50,8 @@ Finally, follow the common procedure::
.. _1.1.0:
1.1.0: FIXME
============
1.1.0: relay domains and better passwords encryption
====================================================
Due to code refactoring, some modifications need to be done into
:file:`settings.py`:
@@ -72,7 +72,9 @@ Due to code refactoring, some modifications need to be done into
'modoboa.extensions.sievefilters',
)
#. Add ``'modoboa.extensions.postfix_relay_domains'`` to ``MODOBOA_APPS``
#. Add ``'modoboa.extensions.postfix_relay_domains'`` to
``MODOBOA_APPS``, just before
``'modoboa.extensions.limits'``
#. ``AUTH_USER_MODEL`` must be set to ``core.User``
@@ -81,6 +83,7 @@ Due to code refactoring, some modifications need to be done into
Then, run the following commands to migrate your installation::
$ python manage.py syncdb
$ python manage.py migrate core 0001 --fake
$ python manage.py migrate
$ python manage.py collectstatic
+14 -8
View File
@@ -44,14 +44,26 @@ Then, edit the ``inbox`` namespace and add the following lines::
special_use = \Trash
}
It ensures all the special mailboxes will be automaticaly created for
new accounts.
With dovecot 2.1+, it ensures all the special mailboxes will be
automaticaly created for new accounts.
For dovecot 2.0 and older, use the `autocreate
<http://wiki2.dovecot.org/Plugins/Autocreate>`_ plugin.
.. _fs_operations:
Operations on the file system
-----------------------------
.. warning::
Modoboa needs to access the ``dovecot`` binary to check its
version. To find the binary path, we use the ``which`` command
first and then try known locations (:file:`/usr/sbin/dovecot` and
:file:`/usr/local/sbin/dovecot`). If you installed dovecot in a
custom location, please tell us where the binary is by using the
``DOVECOT_LOOKUP_PATH`` setting (see :file:`settings.py`).
Three operation types are considered:
#. Mailbox creation
@@ -443,15 +455,9 @@ Use the following configuration in the :file:`/etc/postfix/main.cf` file
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1
virtual_minimum_uid = <vmail user id>
virtual_gid_maps = static:<vmail group id>
virtual_uid_maps = static:<vmail user id>
virtual_mailbox_base = /var/vmail
relay_domains =
virtual_mailbox_domains = <driver>:/etc/postfix/sql-domains.cf
virtual_alias_domains = <driver>:/etc/postfix/sql-domain-aliases.cf
virtual_mailbox_maps = <driver>:/etc/postfix/sql-mailboxes.cf
virtual_alias_maps = <driver>:/etc/postfix/sql-aliases.cf,
<driver>:/etc/postfix/sql-domain-aliases-mailboxes.cf,
<driver>:/etc/postfix/sql-catchall-aliases.cf
+23 -14
View File
@@ -104,13 +104,6 @@ class DomainsAliasesMap(MapFile):
sqlite = "SELECT dom.name FROM admin_domain dom INNER JOIN admin_domainalias domal ON dom.id=domal.target_id WHERE domal.name='%s' AND domal.enabled=1 AND dom.enabled=1"
class MailboxesMap(MapFile):
filename = 'sql-mailboxes.cf'
mysql = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'"
postgres = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user u ON mb.user_id=u.id WHERE dom.enabled AND dom.name='%d' AND u.is_active AND mb.address='%u'"
sqlite = "SELECT 1 FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id INNER JOIN core_user user ON mb.user_id=user.id WHERE dom.enabled=1 AND dom.name='%d' AND user.is_active=1 AND mb.address='%u'"
class AliasesMap(MapFile):
filename = 'sql-aliases.cf'
mysql = "(SELECT concat(mb.address, '@', dom.name) FROM admin_mailbox mb INNER JOIN admin_domain dom ON mb.domain_id=dom.id WHERE mb.id IN (SELECT al_mb.mailbox_id FROM admin_alias_mboxes al_mb INNER JOIN admin_alias al ON al_mb.alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1)) UNION (SELECT concat(al.address, '@', dom.name) FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE al.id IN (SELECT al_al.to_alias_id FROM admin_alias_aliases al_al INNER JOIN admin_alias al ON al_al.from_alias_id=al.id INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1)) UNION (SELECT al.extmboxes FROM admin_alias al INNER JOIN admin_domain dom ON al.domain_id=dom.id WHERE dom.name='%d' AND dom.enabled=1 AND al.address='%u' AND al.enabled=1 AND al.extmboxes<>'')"
@@ -141,7 +134,7 @@ class MaintainMap(MapFile):
class TransportMap(MapFile):
category = "autoreply"
filename = 'sql-transport.cf'
filename = 'sql-autoreplies-transport.cf'
mysql = "SELECT method FROM postfix_autoreply_transport WHERE domain='%s'"
postgres = "SELECT method FROM postfix_autoreply_transport WHERE domain='%s'"
sqlite = "SELECT method FROM postfix_autoreply_transport WHERE domain='%s'"
@@ -155,20 +148,36 @@ class AutoRepliesMap(MapFile):
sqlite = "SELECT full_address, autoreply_address FROM postfix_autoreply_alias WHERE full_address='%s'"
class RelayDomainsMap(MapFile):
category = "relaydomains"
filename = "sql-relaydomains.cf"
mysql = "SELECT name FROM postfix_relay_domains_relaydomain WHERE name='%s' AND enabled=1"
postgres = "SELECT name FROM postfix_relay_domains_relaydomain WHERE name='%s' AND enabled"
sqlite = "SELECT name FROM postfix_relay_domains_relaydomain WHERE name='%s' AND enabled=1"
class RelayDomainsTransportMap(MapFile):
category = "relaydomains"
filename = "sql-relaydomains-transport.cf"
mysql = "SELECT CONCAT(srv.name, ':') FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled=1 AND rdom.name='%s'"
postgres = "SELECT srv.name || ':' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled AND rdom.name='%s'"
sqlite = "SELECT srv.name || ':' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled=1 AND rdom.name='%s'"
mysql = "SELECT CONCAT(srv.name, ':[', rdom.target_host, ']') FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled=1 AND rdom.name='%s'"
postgres = "SELECT srv.name || ':[' || rdom.target_host || ']' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled AND rdom.name='%s'"
sqlite = "SELECT srv.name || ':[' || rdom.target_host || ']' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id WHERE rdom.enabled=1 AND rdom.name='%s'"
class RelayDomainAliasesTransportMap(MapFile):
category = "relaydomains"
filename = "sql-relaydomain-aliases-transport.cf"
mysql = "SELECT CONCAT(srv.name, ':') FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relay_domains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled=1 AND rdomalias.enabled=1 AND rdomalias.name='%s'"
postgres = "SELECT srv.name || ':' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relay_domains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled AND rdomalias.enabled AND rdomalias.name='%s'"
sqlite = "SELECT srv.name || ':' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relay_domains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled=1 AND rdomalias.enabled=1 AND rdomalias.name='%s'"
mysql = "SELECT CONCAT(srv.name, ':[', rdom.target_host, ']') FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relay_domains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled=1 AND rdomalias.enabled=1 AND rdomalias.name='%s'"
postgres = "SELECT srv.name || ':[' || rdom.target_host || ']' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relay_domains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled AND rdomalias.enabled AND rdomalias.name='%s'"
sqlite = "SELECT srv.name || ':[' || rdom.target_host || ']' FROM postfix_relay_domains_service AS srv INNER JOIN postfix_relay_domains_relaydomain AS rdom ON rdom.service_id=srv.id INNER JOIN postfix_relay_domains_relaydomainalias AS rdomalias ON rdom.id=rdomalias.target_id WHERE rdom.enabled=1 AND rdomalias.enabled=1 AND rdomalias.name='%s'"
class RelayRecipientVerification(MapFile):
category = "relaydomains"
filename = "sql-relay-recipient-verification.cf"
mysql = "SELECT 'reject_unverified_recipient' FROM postfix_relay_domains_relaydomain WHERE verify_recipients=1 AND name='%d'"
postgres = "SELECT 'reject_unverified_recipient' FROM postfix_relay_domains_relaydomain WHERE verify_recipients AND name='%d'"
sqlite = "SELECT 'reject_unverified_recipient' FROM postfix_relay_domains_relaydomain WHERE verify_recipients=1 AND name='%d'"
class PostfixMapsCommand(Command):
@@ -172,6 +172,8 @@ SOUTH_TESTS_MIGRATE = False
#MODOBOA_CUSTOM_LOGO = os.path.join(MEDIA_URL, "custom_logo.png")
#DOVECOT_LOOKUP_PATH = ('/path/to/dovecot', )
AUTHENTICATION_BACKENDS = (
'modoboa.lib.authbackends.SimpleBackend',
)
@@ -4,7 +4,7 @@ import datetime
from optparse import make_option
from django.core.management.base import BaseCommand
from django.utils import timezone
from modoboa.core import load_settings
from modoboa.core import load_core_settings
from modoboa.core.models import Log
from modoboa.lib import parameters
@@ -37,7 +37,7 @@ class Command(BaseCommand):
l.addHandler(logging.StreamHandler())
self.verbose = options["verbose"]
load_settings()
load_core_settings()
log_maximum_age = int(parameters.get_admin("LOG_MAXIMUM_AGE"))
self.__vprint("Deleting logs older than %d days..." % log_maximum_age)
limit = timezone.now() - datetime.timedelta(log_maximum_age)
+7 -1
View File
@@ -17,7 +17,7 @@ from django.contrib.auth.models import (
from django.contrib.auth.hashers import make_password, is_password_usable
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import (
PermDeniedException, InternalError, BadRequest
PermDeniedException, InternalError, BadRequest, Conflict
)
from modoboa.lib.sysutils import exec_cmd
from modoboa.core.extensions import exts_pool
@@ -325,6 +325,12 @@ class User(PermissionsMixin):
_("You can't import an account with a role greater than yours")
)
self.username = row[1].strip()
try:
User.objects.get(username=self.username)
except User.DoesNotExist:
pass
else:
raise Conflict
if role == "SimpleUsers":
if (len(row) < 8 or not row[7].strip()):
raise BadRequest(
+2 -2
View File
@@ -20,7 +20,7 @@ def get_password_hasher(scheme):
"""
try:
scheme = scheme.replace('-', '')
hasher = globals()['%sHasher' % scheme]
except AttributeError:
hasher = globals()['%sHasher' % scheme.upper()]
except KeyError:
hasher = PLAINHasher
return hasher
+1 -1
View File
@@ -5,7 +5,7 @@ from django.contrib.auth.decorators import login_required
from django.utils.translation import ugettext as _
from modoboa.lib.webutils import render_to_json_response
from modoboa.lib.emailutils import sendmail_simple, sendmail_fromfile
from modoboa.lib.decorators import needs_mailbox
from modoboa.extensions.admin.lib import needs_mailbox
@login_required
+12
View File
@@ -1,8 +1,10 @@
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy
from modoboa.lib.formutils import YesNoField, SeparatorField
from modoboa.lib.sysutils import exec_cmd
from modoboa.lib import parameters
import os
class AdminParametersForm(parameters.AdminParametersForm):
@@ -46,9 +48,19 @@ class AdminParametersForm(parameters.AdminParametersForm):
def __init__(self, *args, **kwargs):
super(AdminParametersForm, self).__init__(*args, **kwargs)
hide_fields = False
dpath = None
code, output = exec_cmd("which dovecot")
if not code:
dpath = output.strip()
else:
known_paths = getattr(
settings, "DOVECOT_LOOKUP_PATH",
("/usr/sbin/dovecot", "/usr/local/sbin/dovecot")
)
for fpath in known_paths:
if os.path.isfile(fpath) and os.access(fpath, os.X_OK):
dpath = fpath
if dpath:
try:
code, version = exec_cmd("%s --version" % dpath)
except OSError:
+1 -1
View File
@@ -1 +1 @@
[{"pk": 1, "model": "admin.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-05-03 16:58:51", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}}, {"pk": 2, "model": "admin.user", "fields": {"username": "admin@demo.local", "first_name": "Demo", "last_name": "Administrator", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-05-24 05:00:12", "groups": [1], "user_permissions": [], "password": "{CRYPT}SJ2bQWwabfH/6", "email": "admin@demo.local", "date_joined": "2012-05-03 16:50:15"}}, {"pk": 4, "model": "admin.user", "fields": {"username": "user@demo.local", "first_name": "Demo", "last_name": "User", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-05-03 16:56:50", "groups": [2], "user_permissions": [], "password": "{CRYPT}2iAFXtxUA6.7Y", "email": "user@demo.local", "date_joined": "2012-05-03 16:56:50"}}, {"pk": 2, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "domain"], "object_id": 1}}, {"pk": 3, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "user"], "object_id": 2}}, {"pk": 4, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "mailbox"], "object_id": 1}}, {"pk": 5, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "alias"], "object_id": 1}}, {"pk": 6, "model": "admin.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "domain"], "object_id": 1}}, {"pk": 7, "model": "admin.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "mailbox"], "object_id": 1}}, {"pk": 8, "model": "admin.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "user"], "object_id": 2}}, {"pk": 9, "model": "admin.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "alias"], "object_id": 1}}, {"pk": 13, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "user"], "object_id": 4}}, {"pk": 14, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "mailbox"], "object_id": 3}}, {"pk": 15, "model": "admin.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "mailbox"], "object_id": 3}}, {"pk": 16, "model": "admin.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "user"], "object_id": 4}}, {"pk": 1, "model": "admin.objectdates", "fields": {"creation": "2012-05-03 16:49:40", "last_modification": "2012-05-03 16:49:40"}}, {"pk": 2, "model": "admin.objectdates", "fields": {"creation": "2012-05-03 16:50:15", "last_modification": "2012-05-03 16:50:36"}}, {"pk": 3, "model": "admin.objectdates", "fields": {"creation": "2012-05-03 16:50:15", "last_modification": "2012-05-03 16:50:15"}}, {"pk": 4, "model": "admin.objectdates", "fields": {"creation": "2012-05-03 16:51:04", "last_modification": "2012-05-03 16:51:04"}}, {"pk": 5, "model": "admin.objectdates", "fields": {"creation": "2012-05-03 16:56:50", "last_modification": "2012-05-03 16:56:50"}}, {"pk": 1, "model": "admin.domain", "fields": {"dates": 1, "enabled": true, "name": "demo.local", "quota": 10}}, {"pk": 1, "model": "admin.mailbox", "fields": {"dates": 2, "quota": 10, "user": 2, "domain": 1, "address": "admin"}}, {"pk": 3, "model": "admin.mailbox", "fields": {"dates": 5, "quota": 10, "user": 4, "domain": 1, "address": "user"}}, {"pk": 1, "model": "admin.alias", "fields": {"dates": 3, "enabled": true, "domain": 1, "mboxes": [1], "address": "postmaster", "extmboxes": ""}}, {"pk": 1, "model": "admin.extension", "fields": {"enabled": true, "name": "limits"}}, {"pk": 2, "model": "admin.extension", "fields": {"enabled": true, "name": "postfix_autoreply"}}, {"pk": 3, "model": "admin.extension", "fields": {"enabled": true, "name": "stats"}}, {"pk": 4, "model": "admin.extension", "fields": {"enabled": true, "name": "amavis"}}, {"pk": 5, "model": "admin.extension", "fields": {"enabled": true, "name": "webmail"}}, {"pk": 6, "model": "admin.extension", "fields": {"enabled": true, "name": "sievefilters"}}, {"pk": "admin@demo.local", "model": "admin.Quota", "fields": {"mbox": 1, "username": "admin@demo.local", "bytes": 0, "messages": 0}}, {"pk": "user@demo.local", "model": "admin.Quota", "fields": {"mbox": 3, "username": "user@demo.local", "bytes": 0, "messages": 0}}]
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-05-03T16:58:51+01:00", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26T15:19:25+01:00"}}, {"pk": 2, "model": "core.user", "fields": {"username": "admin@demo.local", "first_name": "Demo", "last_name": "Administrator", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-05-24T05:00:12+01:00", "groups": [1], "user_permissions": [], "password": "{CRYPT}SJ2bQWwabfH/6", "email": "admin@demo.local", "date_joined": "2012-05-03T16:50:15+01:00"}}, {"pk": 4, "model": "core.user", "fields": {"username": "user@demo.local", "first_name": "Demo", "last_name": "User", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2012-05-03T16:56:50+01:00", "groups": [2], "user_permissions": [], "password": "{CRYPT}2iAFXtxUA6.7Y", "email": "user@demo.local", "date_joined": "2012-05-03T16:56:50+01:00"}}, {"pk": 2, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "domain"], "object_id": 1}}, {"pk": 3, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 2}}, {"pk": 4, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "mailbox"], "object_id": 1}}, {"pk": 5, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "alias"], "object_id": 1}}, {"pk": 6, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "domain"], "object_id": 1}}, {"pk": 7, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "mailbox"], "object_id": 1}}, {"pk": 8, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["core", "user"], "object_id": 2}}, {"pk": 9, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "alias"], "object_id": 1}}, {"pk": 13, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 4}}, {"pk": 14, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "mailbox"], "object_id": 3}}, {"pk": 15, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["admin", "mailbox"], "object_id": 3}}, {"pk": 16, "model": "core.objectaccess", "fields": {"is_owner": false, "user": 2, "content_type": ["core", "user"], "object_id": 4}}, {"pk": 1, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:49:40+01:00", "last_modification": "2012-05-03T16:49:40+01:00"}}, {"pk": 2, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:50:15+01:00", "last_modification": "2012-05-03T16:50:36+01:00"}}, {"pk": 3, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:50:15+01:00", "last_modification": "2012-05-03T16:50:15+01:00"}}, {"pk": 4, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:51:04+01:00", "last_modification": "2012-05-03T16:51:04+01:00"}}, {"pk": 5, "model": "admin.objectdates", "fields": {"creation": "2012-05-03T16:56:50+01:00", "last_modification": "2012-05-03T16:56:50+01:00"}}, {"pk": 1, "model": "admin.domain", "fields": {"dates": 1, "enabled": true, "name": "demo.local", "quota": 10}}, {"pk": 1, "model": "admin.mailbox", "fields": {"dates": 2, "quota": 10, "user": 2, "domain": 1, "address": "admin"}}, {"pk": 3, "model": "admin.mailbox", "fields": {"dates": 5, "quota": 10, "user": 4, "domain": 1, "address": "user"}}, {"pk": 1, "model": "admin.alias", "fields": {"dates": 3, "enabled": true, "domain": 1, "mboxes": [1], "address": "postmaster", "extmboxes": ""}}, {"pk": 1, "model": "core.extension", "fields": {"enabled": true, "name": "limits"}}, {"pk": 2, "model": "core.extension", "fields": {"enabled": true, "name": "postfix_autoreply"}}, {"pk": 3, "model": "core.extension", "fields": {"enabled": true, "name": "stats"}}, {"pk": 4, "model": "core.extension", "fields": {"enabled": true, "name": "amavis"}}, {"pk": 5, "model": "core.extension", "fields": {"enabled": true, "name": "webmail"}}, {"pk": 6, "model": "core.extension", "fields": {"enabled": true, "name": "sievefilters"}}, {"pk": 7, "model": "core.extension", "fields": {"enabled": true, "name": "postfix_relay_domains"}}, {"pk": "admin@demo.local", "model": "admin.quota", "fields": {"mbox": 1, "username": "admin@demo.local", "bytes": 0, "messages": 0}}, {"pk": "user@demo.local", "model": "admin.quota", "fields": {"mbox": 3, "username": "user@demo.local", "bytes": 0, "messages": 0}}]
+9 -1
View File
@@ -2,7 +2,9 @@ import reversion
from django.db import models
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib.emailutils import split_mailbox
from modoboa.lib.exceptions import PermDeniedException, BadRequest
from modoboa.lib.exceptions import (
PermDeniedException, BadRequest, Conflict
)
from .base import AdminObject
from .domain import Domain
from .mailbox import Mailbox
@@ -155,6 +157,12 @@ class Alias(AdminObject):
raise BadRequest(_("Domain '%s' does not exist" % domname))
if not user.can_access(domain):
raise PermDeniedException
try:
Alias.objects.get(address=localpart, domain__name=domain)
except Alias.DoesNotExist:
pass
else:
raise Conflict
self.address = localpart
self.domain = domain
self.enabled = (row[2].strip() == 'True')
+16 -1
View File
@@ -4,7 +4,7 @@ from django.db.models.manager import Manager
from django.utils.translation import ugettext as _, ugettext_lazy
from django.contrib.contenttypes import generic
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import BadRequest
from modoboa.lib.exceptions import BadRequest, Conflict
from modoboa.core.models import User, ObjectAccess
from .base import AdminObject
@@ -131,9 +131,24 @@ class Domain(AdminObject):
return self.name
def from_csv(self, user, row):
"""Create a new domain from a CSV entry.
The expected fields order is the following::
"domain", name, quota, enabled
:param ``core.User`` user: user creating the domain
:param str row: a list containing domain's definition
"""
if len(row) < 4:
raise BadRequest(_("Invalid line"))
self.name = row[1].strip()
try:
Domain.objects.get(name=self.name)
except Domain.DoesNotExist:
pass
else:
raise Conflict
try:
self.quota = int(row[2].strip())
except ValueError:
@@ -38,12 +38,13 @@ class DomainAlias(AdminObject):
if len(row) < 4:
raise BadRequest(_("Invalid line"))
self.name = row[1].strip()
try:
Domain.objects.get(name=self.name)
except Domain.DoesNotExist:
pass
else:
raise Conflict(_("A domain %s already exists" % self.name))
for model in [DomainAlias, Domain]:
try:
model.objects.get(name=self.name)
except model.DoesNotExist:
pass
else:
raise Conflict
domname = row[2].strip()
try:
self.target = Domain.objects.get(name=domname)
@@ -187,11 +187,6 @@ class PermissionsTestCase(ModoTestCase):
account = User.objects.get(username="superadmin2@test.com")
self.clt.logout()
self.clt.login(username="admin@test.com", password="toto")
self.ajax_get(
reverse("modoboa.extensions.admin.views.identity.delaccount",
args=[account.id]),
{}, 403
)
self.ajax_post(
reverse("modoboa.extensions.admin.views.identity.delaccount",
args=[account.id]), {}, 403
+17 -7
View File
@@ -20,17 +20,27 @@ class ExportTestCase(ModoTestCase):
{"filename": "test.csv"}
)
def assertListEqual(self, list1, list2):
list2 = list2.split('\r\n')
for entry in list1.split('\r\n'):
if not entry:
continue
self.assertIn(entry, list2)
def test_export_identities(self):
response = self.__export_identities()
self.assertEqual(response.content, "account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\nalias;alias@test.com;True;user@test.com\r\nforward;forward@test.com;True;user@external.com\r\ndlist;postmaster@test.com;True;toto@titi.com;test@truc.fr\r\n")
self.assertListEqual(
"account;admin;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\nalias;alias@test.com;True;user@test.com\r\nforward;forward@test.com;True;user@external.com\r\ndlist;postmaster@test.com;True;toto@titi.com;test@truc.fr\r\n",
response.content.strip()
)
def test_export_simpleusers(self):
response = self.__export_identities(
idtfilter="account", grpfilter="SimpleUsers"
)
self.assertEqual(
response.content.strip(),
"account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10"
self.assertListEqual(
"account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10",
response.content.strip()
)
def test_export_superadmins(self):
@@ -46,9 +56,9 @@ class ExportTestCase(ModoTestCase):
response = self.__export_identities(
idtfilter="account", grpfilter="DomainAdmins"
)
self.assertEqual(
response.content.strip(),
"account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com"
self.assertListEqual(
"account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com",
response.content.strip()
)
def test_export_aliases(self):
+1 -1
View File
@@ -49,7 +49,7 @@ domainalias;test.alias;test.com;True
"sourcefile": f
}
)
self.assertIn('A domain test.alias already exists', resp.content)
self.assertIn('Object already exists: domainalias', resp.content)
def test_identities_import(self):
f = ContentFile(b"""
+1 -1
View File
@@ -122,7 +122,7 @@ def importdata(request, formclass=ImportDataForm):
fct = fct[0]
try:
fct(request.user, row, form.cleaned_data)
except IntegrityError, e:
except Conflict:
if form.cleaned_data["continue_if_exists"]:
continue
raise Conflict(
+3 -4
View File
@@ -12,7 +12,6 @@ from modoboa.lib.webutils import NavigationParameters
from modoboa.extensions.amavis.models import Users, Policy
def selfservice(ssfunc=None):
"""Decorator used to expose views to the 'self-service' feature
@@ -121,7 +120,7 @@ def create_user_and_policy(name):
:param str name: name
"""
policy = Policy.objects.create(policy_name=name)
policy = Policy.objects.create(policy_name=name[:32])
Users.objects.create(
email="@%s" % name, fullname=name,
priority=7, policy=policy
@@ -134,7 +133,7 @@ def create_user_and_use_policy(name, policy_name):
:param str name: user record name
:param str policy_name: policy name
"""
policy = Policy.objects.get(policy_name=policy_name)
policy = Policy.objects.get(policy_name=policy_name[:32])
Users.objects.create(
email="@%s" % name, fullname=name,
priority=7, policy=policy
@@ -152,7 +151,7 @@ def update_user_and_policy(oldname, newname):
u = Users.objects.get(email="@%s" % oldname)
u.email = "@%s" % newname
u.fullname = newname
u.policy.policy_name = newname
u.policy.policy_name = newname[:32]
u.policy.save()
u.save()
+1 -1
View File
@@ -83,7 +83,7 @@ class Msgrcpt(models.Model):
class Policy(models.Model):
policy_name = models.CharField(max_length=96, blank=True)
policy_name = models.CharField(max_length=32, blank=True)
virus_lover = models.CharField(max_length=3, blank=True, null=True)
spam_lover = models.CharField(max_length=3, blank=True, null=True)
unchecked_lover = models.CharField(max_length=3, blank=True, null=True)
+1 -1
View File
@@ -1 +1 @@
[{"pk": 1, "model": "limits.limitspool", "fields": {"user": 2}}, {"pk": 1, "model": "limits.limit", "fields": {"curvalue": 0, "name": "domain_admins_limit", "pool": 1, "maxvalue": -2}}, {"pk": 2, "model": "limits.limit", "fields": {"curvalue": 0, "name": "domains_limit", "pool": 1, "maxvalue": -2}}, {"pk": 3, "model": "limits.limit", "fields": {"curvalue": 0, "name": "domain_aliases_limit", "pool": 1, "maxvalue": -2}}, {"pk": 4, "model": "limits.limit", "fields": {"curvalue": 0, "name": "mailboxes_limit", "pool": 1, "maxvalue": 5}}, {"pk": 5, "model": "limits.limit", "fields": {"curvalue": 0, "name": "mailbox_aliases_limit", "pool": 1, "maxvalue": 5}},{"pk": 3, "model": "auth.group", "fields": {"name": "Resellers", "permissions": [["add_user", "admin", "user"], ["change_user", "admin", "user"], ["delete_user", "admin", "user"], ["view_domains", "admin", "domain"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"],["view_permissions", "auth", "permission"], ["view_domains", "admin", "domain"], ["add_domain", "admin", "domain"], ["change_domain", "admin", "domain"], ["delete_domain", "admin", "domain"]]}}]
[{"pk": 1, "model": "limits.limitspool", "fields": {"user": 2}}, {"pk": 1, "model": "limits.limit", "fields": {"curvalue": 0, "name": "domain_admins_limit", "pool": 1, "maxvalue": -2}}, {"pk": 2, "model": "limits.limit", "fields": {"curvalue": 0, "name": "domains_limit", "pool": 1, "maxvalue": -2}}, {"pk": 3, "model": "limits.limit", "fields": {"curvalue": 0, "name": "domain_aliases_limit", "pool": 1, "maxvalue": -2}}, {"pk": 4, "model": "limits.limit", "fields": {"curvalue": 0, "name": "mailboxes_limit", "pool": 1, "maxvalue": 5}}, {"pk": 5, "model": "limits.limit", "fields": {"curvalue": 0, "name": "mailbox_aliases_limit", "pool": 1, "maxvalue": 5}},{"pk": 3, "model": "auth.group", "fields": {"name": "Resellers", "permissions": [["add_user", "core", "user"], ["change_user", "core", "user"], ["delete_user", "core", "user"], ["view_domains", "admin", "domain"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"], ["view_domains", "admin", "domain"], ["add_domain", "admin", "domain"], ["change_domain", "admin", "domain"], ["delete_domain", "admin", "domain"]]}}]
@@ -27,6 +27,8 @@ class PostfixAutoreply(ModoExtension):
def init(self):
from modoboa.extensions.admin.models import Domain
from modoboa.extensions.postfix_autoreply.general_callbacks \
import onDomainCreated, onMailboxCreated
for dom in Domain.objects.all():
try:
@@ -30,9 +30,12 @@ class ARmessageForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ARmessageForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['subject', 'content', 'fromdate', 'untildate', 'enabled']
self.fields.keyOrder = [
'subject', 'content', 'fromdate', 'untildate', 'enabled'
]
if 'instance' in kwargs and kwargs['instance'] is not None:
self.fields['fromdate'].initial = kwargs['instance'].fromdate
self.fields['fromdate'].initial = \
kwargs['instance'].fromdate.replace(second=0, microsecond=0)
self.fields['untildate'].initial = kwargs['instance'].untildate
def clean(self):
@@ -46,7 +49,7 @@ class ARmessageForm(forms.ModelForm):
raise forms.ValidationError(self._errors)
if self.cleaned_data["fromdate"] is not None:
if self.cleaned_data["fromdate"] < timezone.now():
self._errrors["fromdate"] = self.error_class([_("This date is over")])
self._errors["fromdate"] = self.error_class([_("This date is over")])
del self.cleaned_data['fromdate']
else:
self.cleaned_data['fromdate'] = timezone.now()
@@ -54,7 +57,8 @@ class ARmessageForm(forms.ModelForm):
if self.cleaned_data["untildate"] < timezone.now():
self._errors["untildate"] = self.error_class([_("This date is over")])
del self.cleaned_data['untildate']
elif self.cleaned_data['untildate'] < self.cleaned_data['fromdate']:
elif 'fromdate' in self.cleaned_data and \
self.cleaned_data['untildate'] < self.cleaned_data['fromdate']:
self._errors["untildate"] = \
self.error_class([_("Must be greater than start date")])
del self.cleaned_data['untildate']
@@ -12,7 +12,7 @@ from modoboa.extensions.postfix_autoreply.models import ARmessage, ARhistoric
def send_autoreply(sender, mailbox, armessage):
if armessage.fromdate < timezone.now():
if armessage.fromdate > timezone.now():
return
if armessage.untildate is not None \
and armessage.untildate < timezone.now():
@@ -39,18 +39,20 @@ variables = ["sent", "recv", "bounced", "reject", "spam", "virus",
class LogParser(object):
def __init__(self, options, workdir, year=None):
"""Constructor
"""
self.logfile = options["logfile"]
self.debug = options["debug"]
self.verbose = options["verbose"]
try:
self.f = open(self.logfile)
except IOError, errno:
if options["debug"]:
print "%s" % errno
except IOError as errno:
self._dprint("%s" % errno)
sys.exit(1)
self.workdir = workdir
self.__year = year
self.debug = options["debug"]
self.verbose = options["verbose"]
self.cfs = ['AVERAGE', 'MAX']
curtime = time.localtime()
@@ -59,16 +61,74 @@ class LogParser(object):
self.curmonth = curtime.tm_mon
self.data = {}
domains = Domain.objects.all()
self.domains = []
for dom in domains:
for dom in Domain.objects.all():
self.domains += [str(dom.name)]
self.data[str(dom.name)] = {}
self.data["global"] = {}
self.workdict = {}
self.lupdates = {}
self.line_expr = re.compile("(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+([-\w]+)\s+(\w+)/?\w*[[](\d+)[]]:\s+(.*)")
self._s_date_expr = \
re.compile(r"(?P<month>\w+)\s+(?P<day>\d+)\s+(?P<hour>\d+):(?P<min>\d+):(?P<sec>\d+)(?P<eol>.*)")
self._hp_date_expr = \
re.compile(r"(?P<year>\d+)-(?P<month>\d+)-(?P<day>\d+)T(?P<hour>\d+):(?P<min>\d+):(?P<sec>\d+)\.\d+\+\d+:\d+(?P<eol>.*)")
self.date_expr = None
self.line_expr = \
re.compile(r"\s+([-\w]+)\s+(\w+)/?\w*[[](\d+)[]]:\s+(.*)")
self._id_expr = re.compile(r"(\w+): (.*)")
self._prev_se = -1
self._prev_mi = -1
self._prev_ho = -1
self.cur_t = 0
def _dprint(self, msg):
"""Print a debug message if required.
:param str msg: debug message
"""
if not self.debug:
return
print msg
def _parse_date(self, line):
"""Try to match a date inside :kw:`line` and to convert it to
a timestamp.
We try different date format until we find valid one. We then
store it for future use.
:param str line: a log entry
:return: the remaining part of the line or None
"""
match = None
if self.date_expr is None:
for expr in [self._s_date_expr, self._hp_date_expr]:
match = expr.match(line)
if match is not None:
self.date_expr = expr
break
else:
match = self.date_expr.match(line)
if match is None:
return None
ho = match.group("hour")
mi = match.group("min")
se = match.group("sec")
se = int(int(se) / rrdstep) # rrd step is one-minute => se = 0
if self._prev_se != se or self._prev_mi != mi or self._prev_ho != ho:
mo = match.group("month")
da = match.group("day")
try:
ye = match.group("year")
except IndexError:
ye = self.year(mo)
self.cur_t = str2Time(ye, mo, da, ho, mi, se)
self.cur_t = self.cur_t - self.cur_t % rrdstep
self._prev_mi = mi
self._prev_ho = ho
self._prev_se = se
return match.group('eol')
def init_rrd(self, fname, m):
"""init_rrd
@@ -119,8 +179,7 @@ class LogParser(object):
m = t - (t % rrdstep)
if not os.path.exists(fname):
self.lupdates[fname] = self.init_rrd(fname, m)
if self.debug:
print "[rrd] create new RRD file %s" % fname
self._dprint("[rrd] create new RRD file %s" % fname)
else:
if not fname in self.lupdates:
self.lupdates[fname] = rrdtool.last(str(fname))
@@ -164,21 +223,21 @@ class LogParser(object):
self.lupdates[fname] = m
return True
def initcounters(self, dom, cur_t):
def initcounters(self, dom):
init = {}
for v in variables:
init[v] = 0
self.data[dom][cur_t] = init
self.data[dom][self.cur_t] = init
def inc_counter(self, dom, cur_t, counter, val=1):
def inc_counter(self, dom, counter, val=1):
if dom is not None and dom in self.domains:
if not cur_t in self.data[dom]:
self.initcounters(dom, cur_t)
self.data[dom][cur_t][counter] += val
if not self.cur_t in self.data[dom]:
self.initcounters(dom)
self.data[dom][self.cur_t][counter] += val
if not cur_t in self.data["global"]:
self.initcounters("global", cur_t)
self.data["global"][cur_t][counter] += val
if not self.cur_t in self.data["global"]:
self.initcounters("global")
self.data["global"][self.cur_t][counter] += val
def year(self, month):
"""Return the appropriate year
@@ -203,82 +262,76 @@ class LogParser(object):
return self.__year - 1
return self.__year
def process(self):
id_expr = re.compile("([0-9A-F]+): (.*)")
prev_se = -1
prev_mi = -1
prev_ho = -1
for line in self.f.readlines():
m = self.line_expr.match(line)
if not m:
continue
(mo, da, ho, mi, se, host, prog, pid, log) = m.groups()
se = int(int(se) / rrdstep) # rrd step is one-minute => se = 0
def _parse_line(self, line):
"""Parse a single log line.
if prev_se != se or prev_mi != mi or prev_ho != ho:
cur_t = str2Time(self.year(mo), mo, da, ho, mi, se)
cur_t = cur_t - cur_t % rrdstep
prev_mi = mi
prev_ho = ho
prev_se = se
m = id_expr.match(log)
if m:
(line_id, line_log) = m.groups()
:param str line: log line
"""
line = self._parse_date(line)
if line is None:
return
m = self.line_expr.match(line)
if not m:
return
host, prog, pid, log = m.groups()
m = self._id_expr.match(log)
if m is None:
self._dprint("Unknown line format: %s" % log)
return
(line_id, line_log) = m.groups()
if line_id == "NOQUEUE":
addrto = re.match("reject: .*from=<.*> to=<[^@]+@([^>]+)>", line_log)
if addrto and addrto.group(1) in self.domains:
self.inc_counter(addrto.group(1), 'reject')
return
m = re.search("message-id=<([^>]*)>", line_log)
if m is not None:
self.workdict[line_id] = {'from': m.group(1), 'size': 0}
return
m = re.search("from=<([^>]*)>, size=(\d+)", line_log)
if m is not None:
self.workdict[line_id] = {
'from': m.group(1), 'size': string.atoi(m.group(2))
}
return
m = re.search("message-id=<([^>]*)>", line_log)
if m:
self.workdict[line_id] = {'from': m.group(1), 'size': 0}
continue
m = re.search("from=<([^>]*)>, size=(\d+)", line_log)
if m:
self.workdict[line_id] = {'from': m.group(1),
'size': string.atoi(m.group(2))}
continue
m = re.search("to=<([^>]*)>.*status=(\S+)", line_log)
if m:
if not line_id in self.workdict:
if self.debug:
print "Inconsistent mail (%s: %s), skipping" % (line_id, m.group(1))
continue
if not m.group(2) in variables:
if self.debug:
print "Unsupported status %s, skipping" % m.group(2)
continue
addrfrom = re.match("([^@]+)@(.+)", self.workdict[line_id]['from'])
if addrfrom is not None 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[line_id]['size'])
addrto = re.match("([^@]+)@(.+)", m.group(1))
domname = addrto.group(2) if addrto is not None else None
if m.group(2) == "sent":
self.inc_counter(addrto.group(2), cur_t, 'recv')
self.inc_counter(addrto.group(2), cur_t, 'size_recv',
self.workdict[line_id]['size'])
else:
self.inc_counter(domname, cur_t, m.group(2))
continue
if self.debug:
print "Unknown line format: %s" % line_log
m = re.search("to=<([^>]*)>.*status=(\S+)", line_log)
if m is not None:
if not line_id in self.workdict:
self._dprint("Inconsistent mail (%s: %s), skipping" \
% (line_id, m.group(1)))
return
if not m.group(2) in variables:
self._dprint("Unsupported status %s, skipping" % m.group(2))
return
addrfrom = re.match("([^@]+)@(.+)", self.workdict[line_id]['from'])
if addrfrom is not None and addrfrom.group(2) in self.domains:
self.inc_counter(addrfrom.group(2), 'sent')
self.inc_counter(addrfrom.group(2), 'size_sent',
self.workdict[line_id]['size'])
addrto = re.match("([^@]+)@(.+)", m.group(1))
domname = addrto.group(2) if addrto is not None else None
if m.group(2) == "sent":
self.inc_counter(addrto.group(2), 'recv')
self.inc_counter(addrto.group(2), 'size_recv',
self.workdict[line_id]['size'])
else:
m = re.match("NOQUEUE: reject: .*from=<(.*)> to=<([^>]*)>", log)
if m:
addrto = re.match("([^@]+)@(.+)", m.group(2))
if addrto and addrto.group(2) in self.domains:
self.inc_counter(addrto.group(2), cur_t, 'reject')
continue
if self.debug:
print "Unknown line format: %s" % log
self.inc_counter(domname, m.group(2))
return
self._dprint("Unknown line format: %s" % line_log)
def process(self):
"""Process the log file.
We parse it and then generate standard graphics (day, week,
month).
"""
for line in self.f.readlines():
self._parse_line(line)
# Sort everything by time
G = Grapher()
for dom, data in self.data.iteritems():
if self.debug:
print "[rrd] dealing with domain %s" % dom
self._dprint("[rrd] dealing with domain %s" % dom)
for t in sorted(data.keys()):
self.update_rrd(dom, t)
+2 -2
View File
@@ -582,8 +582,8 @@ def save_attachment(f):
def clean_attachments(attlist):
"""Remove all attachments from the filesystem
:param attlist: a list of 2-uple. Each element must contain the following information :
(random name, real name).
:param attlist: a list of 2-uple. Each element must contain the
following information : (random name, real name).
"""
for att in attlist:
fullpath = os.path.join(settings.MEDIA_ROOT, "webmail", att["tmpname"])
+4 -12
View File
@@ -3,7 +3,7 @@
"pk": 2,
"model": "lib.parameter",
"fields": {
"name": "admin.PASSWORD_SCHEME",
"name": "core.PASSWORD_SCHEME",
"value": "crypt"
}
},
@@ -11,7 +11,7 @@
"pk": 3,
"model": "lib.parameter",
"fields": {
"name": "admin.ITEMS_PER_PAGE",
"name": "core.ITEMS_PER_PAGE",
"value": "30"
}
},
@@ -19,23 +19,15 @@
"pk": 4,
"model": "lib.parameter",
"fields": {
"name": "general.DEFAULT_TOP_REDIRECTION",
"name": "core.DEFAULT_TOP_REDIRECTION",
"value": "user"
}
},
{
"pk": 6,
"model": "lib.parameter",
"fields": {
"name": "general.SECRET_KEY",
"value": "abcdefghijklmnop"
}
},
{
"pk": 8,
"model": "lib.parameter",
"fields": {
"name": "admin.AUTHENTICATION_TYPE",
"name": "core.AUTHENTICATION_TYPE",
"value": "local"
}
},
+12 -15
View File
@@ -56,21 +56,18 @@ def grant_access_to_object(user, obj, is_owner=False):
else:
return
try:
ObjectAccess.objects.create(user=user, content_type=ct,
object_id=obj.id, is_owner=is_owner)
if is_owner:
from modoboa.core.models import User
for su in User.objects.filter(is_superuser=True):
if su == user:
continue
try:
ObjectAccess.objects.create(user=su, content_type=ct, object_id=obj.id)
except IntegrityError:
pass
except IntegrityError, e:
raise ModoboaException(_("Failed to grant access (%s)" % str(e)))
ObjectAccess.objects.create(
user=user, content_type=ct, object_id=obj.id, is_owner=is_owner
)
if not is_owner:
return
from modoboa.core.models import User
for su in User.objects.filter(is_superuser=True):
if su == user:
continue
ObjectAccess.objects.get_or_create(
user=su, content_type=ct, object_id=obj.id
)
def grant_access_to_objects(user, objects, ct):
File diff suppressed because it is too large Load Diff
+115 -200
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-12-15 10:20+0100\n"
"PO-Revision-Date: 2013-09-27 00:07+0200\n"
"POT-Creation-Date: 2013-12-22 23:15+0100\n"
"PO-Revision-Date: 2013-12-23 00:56+0100\n"
"Last-Translator: Alberto Berti <alberto@metapensiero.it>\n"
"Language-Team: ITALIANO <LL@li.org>\n"
"Language: it\n"
@@ -49,23 +49,23 @@ msgstr "Schema password predefinito"
#: core/app_settings.py:51
msgid "md5crypt (weak)"
msgstr ""
msgstr "md5crypt (debole)"
#: core/app_settings.py:52
msgid "sha256 (weak)"
msgstr ""
msgstr "sha256 (debole)"
#: core/app_settings.py:53
msgid "md5 (weak)"
msgstr ""
msgstr "md5 (debole)"
#: core/app_settings.py:54
msgid "crypt (weak)"
msgstr ""
msgstr "crypt (debole)"
#: core/app_settings.py:55
msgid "plain (weak)"
msgstr ""
msgstr "plain (debole)"
#: core/app_settings.py:57
msgid "Scheme used to crypt mailbox passwords"
@@ -74,13 +74,13 @@ msgstr ""
#: core/app_settings.py:61
msgid "Rounds"
msgstr ""
msgstr "Cicli"
#: core/app_settings.py:64
msgid ""
"Number of rounds to use (only used by sha256crypt and sha512crypt). Must be "
"between 1000 and 999999999, inclusive."
msgstr ""
msgstr "Numero di cicli da utilizzare (valido per sha256crypt e sha512crypt). Dev'essere tra 1000 e 999999999, inclusivo."
#: core/app_settings.py:70
msgid "Secret key"
@@ -159,14 +159,12 @@ msgstr ""
"DN')"
#: core/app_settings.py:121
#, fuzzy
msgid "Users search base"
msgstr "Ricerca base"
msgstr "Base ricerca per gli utenti"
#: core/app_settings.py:123
#, fuzzy
msgid "The distinguished name of the search base used to find users"
msgstr "Nome particolare della ricerca base"
msgstr "L'identificativo (dn:) della base di ricerca utilizzata per cercare gli utenti"
#: core/app_settings.py:128
msgid "Search filter"
@@ -210,25 +208,22 @@ msgid "Tell if the LDAP server is an Active Directory one"
msgstr "Segnala se il server LDAP è una Active Directory"
#: core/app_settings.py:154
#, fuzzy
msgid "Administrator groups"
msgstr "Amministratori"
msgstr "Gruppi degli amministratori"
#: core/app_settings.py:157
msgid ""
"Members of those LDAP Posix groups will be created as domain administrators. "
"Use ';' characters to separate groups."
msgstr ""
msgstr "Gli appartenenti a questi gruppi LDAP Posix avranno i privilegi di amministratori di dominio. Utilizza il carattere ';' per separare valori multipli."
#: core/app_settings.py:164
#, fuzzy
msgid "Groups search base"
msgstr "Ricerca base"
msgstr "Base ricerca per i gruppi"
#: core/app_settings.py:167
#, fuzzy
msgid "The distinguished name of the search base used to find groups"
msgstr "Nome particolare della ricerca base"
msgstr "L'identificatore (dn:) della base di ricerca utilizzato per trovare i gruppi"
#: core/app_settings.py:172
#: extensions/admin/templates/admin/editdomainform.html:7
@@ -270,9 +265,8 @@ msgid "Invalid syntax"
msgstr "Sintassi errata"
#: core/app_settings.py:233
#, fuzzy
msgid "Invalid rounds number"
msgstr "Richiesta non valida"
msgstr "Numero di cicli non valido"
#: core/app_settings.py:256
msgid "This field is required"
@@ -322,14 +316,12 @@ msgid "Passwords mismatch"
msgstr "La password non corrisponde"
#: core/models.py:50
#, fuzzy
msgid "password"
msgstr "Password"
msgstr "password"
#: core/models.py:52
#, fuzzy
msgid "last login"
msgstr "Ultimo login"
msgstr "ultimo login"
#: core/models.py:81
msgid "You can't delete your own account"
@@ -513,7 +505,7 @@ msgstr "Aggiorna le informazioni personali"
#: extensions/admin/views/identity.py:197
#: extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html:9
#: extensions/postfix_relay_domains/views.py:63
#: extensions/sievefilters/views.py:163 extensions/webmail/views.py:170
#: extensions/sievefilters/views.py:166 extensions/webmail/views.py:170
#: extensions/webmail/views.py:205
msgid "Update"
msgstr "Aggiorna"
@@ -613,9 +605,8 @@ msgid "Web based console to manage domains, accounts and aliases"
msgstr "Interfaccia web per l'amministrazione di domini, account e alias"
#: extensions/admin/__init__.py:55
#, fuzzy
msgid "Administration"
msgstr "Amministratori"
msgstr "Amministrazione"
#: extensions/admin/__init__.py:79
#: extensions/admin/templates/admin/domains.html:2
@@ -668,15 +659,14 @@ msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "L'account UNIX a cui appartengono le caselle postali nel filsystem"
#: extensions/admin/app_settings.py:26
#, fuzzy
msgid "Default domain quota"
msgstr "Limiti predefiniti"
msgstr "Quota predefinita per il domini"
#: extensions/admin/app_settings.py:29
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
msgstr "Quota predefinita (in MB) applicata ai nuovi domini che non la specificano. Un valore 0 implica nessuna quota."
#: extensions/admin/app_settings.py:36
msgid "Automatic account removal"
@@ -690,19 +680,17 @@ msgstr ""
#: extensions/admin/app_settings.py:69 extensions/admin/forms/account.py:178
#: extensions/admin/forms/domain.py:56
msgid "Must be a positive integer"
msgstr ""
msgstr "Dev'essere un intero positivo"
#: extensions/admin/lib.py:28
#, fuzzy
msgid "A mailbox is required"
msgstr "È richiesta una quota"
msgstr "É necessaria una casella"
#: extensions/admin/forms/account.py:21
#, fuzzy
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr "L'utente '%s' deve avere un indirizzo email valido"
msgstr "Il nome dell'utente. Dev'essere un indirizzo e-mail per gli utenti semplici o gli amministratori che posseggono una casella"
#: extensions/admin/forms/account.py:26
msgid "Role"
@@ -839,11 +827,10 @@ msgid "No recipient defined"
msgstr "Non è stato definito un destinatario"
#: extensions/admin/forms/domain.py:20
#, fuzzy
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr "Quota in MB predefinita applicata alle caselle di posta"
msgstr "Quota in MB predefinita applicata alle caselle di posta. Lascialo vuoto se vuoi applicare il valore predefinito."
#: extensions/admin/forms/domain.py:29
msgid ""
@@ -856,23 +843,21 @@ msgstr ""
#: extensions/admin/forms/domain.py:75
#: extensions/postfix_relay_domains/forms.py:50
#: extensions/postfix_relay_domains/forms.py:70
#, fuzzy
msgid "domain alias"
msgstr "Alias del dominio"
msgstr "alias del dominio"
#: extensions/admin/forms/domain.py:82 extensions/admin/forms/domain.py:98
#: extensions/postfix_relay_domains/forms.py:58
#: extensions/postfix_relay_domains/forms.py:78
#, fuzzy, python-format
#, python-format
msgid "A %s with this name already exists"
msgstr "Un alias con questo nome esiste già"
msgstr "Un %s con questo nome esiste già"
#: extensions/admin/forms/domain.py:92
#: extensions/postfix_relay_domains/forms.py:49
#: extensions/postfix_relay_domains/forms.py:71
#, fuzzy
msgid "domain"
msgstr "Domini"
msgstr "dominio"
#: extensions/admin/forms/domain.py:176
msgid "Create a domain administrator"
@@ -1032,14 +1017,13 @@ msgid "Check to activate this domain"
msgstr "Seleziona per attivare questo dominio"
#: extensions/admin/models/domain.py:61
#, fuzzy
msgid "Domain"
msgstr "Domini"
msgstr "Dominio"
#: extensions/admin/models/domain.py:140
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr ""
msgstr "Valore della quota non valido per il dominio '%s'"
#: extensions/admin/models/domain_alias.py:11
#: extensions/postfix_relay_domains/models.py:160
@@ -1056,9 +1040,9 @@ msgid "The domain this alias points to"
msgstr "Il dominio a cui punta questo alias"
#: extensions/admin/models/domain_alias.py:46
#, fuzzy, python-format
#, python-format
msgid "A domain %s already exists"
msgstr "La casella di posta %s esiste già"
msgstr "Esiste già un dominio %s"
#: extensions/admin/models/domain_alias.py:51
#, python-format
@@ -1090,9 +1074,8 @@ msgid "MB"
msgstr "MB"
#: extensions/admin/templates/admin/domain_options_form.html:5
#, fuzzy
msgid "You can't create a new domain administrator"
msgstr "Crea un amministratore di dominio"
msgstr "Non ti è possibile creare un amministratore di dominio"
#: extensions/admin/templates/admin/domains_table.html:7
#: extensions/admin/templates/admin/identities_table.html:7
@@ -1102,7 +1085,7 @@ msgstr "Tag"
#: extensions/admin/templates/admin/domains_table.html:8
#: extensions/admin/templates/admin/identities_table.html:8
#: extensions/admin/templates/admin/listing.html:17
#: extensions/sievefilters/views.py:93
#: extensions/sievefilters/views.py:95
#: extensions/sievefilters/templates/sievefilters/filter.html:17
#: extensions/sievefilters/templates/sievefilters/guieditor.html:5
#: extensions/webmail/templatetags/webmail_tags.py:101
@@ -1257,7 +1240,7 @@ msgstr "Un alias con questo nome esiste già"
#: extensions/admin/views/domain.py:129 extensions/admin/views/identity.py:131
#: extensions/admin/views/identity.py:158
#: extensions/postfix_relay_domains/views.py:32
#: extensions/sievefilters/views.py:141 extensions/sievefilters/views.py:230
#: extensions/sievefilters/views.py:144 extensions/sievefilters/views.py:233
#: extensions/webmail/views.py:151
msgid "Create"
msgstr "Crea"
@@ -1374,7 +1357,7 @@ msgstr[0] "Account cancellato"
msgstr[1] "Account cancellati"
#: extensions/admin/views/import.py:39
#, fuzzy, python-format
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
@@ -1389,10 +1372,10 @@ msgstr ""
"Fornire un file CSV le cui righe rispettino uno dei seguenti formati:\n"
"<ul>\n"
" <li><em>dominio; nome; quota; abilitato</em></li>\n"
" <li><em>aliasdominio; nome; dominio target; abilitato<em></li>\n"
" <li><em>aliasdominio; nome; dominio di destinazione; abilitato<em></li>\n"
" %s\n"
"</ul>\n"
"<p>Il primo elemento di ogni riga è obbligatorio e deve essere uguale a "
"quello dei valori precedenti.</p>\n"
"<p>Il primo elemento di ogni riga è obbligatorio e deve essere uguale a quello dei valori precedenti.</p>\n"
"\n"
"<p>Come separatore puoi utilizzare un carattere diverso.</p>\n"
@@ -1411,7 +1394,6 @@ msgid "%d objects imported successfully"
msgstr "%d oggetti importati correttamente"
#: extensions/admin/views/import.py:153
#, fuzzy
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
@@ -1428,15 +1410,12 @@ msgid ""
msgstr ""
"Fornire un file CSV le cui righe rispettino uno dei seguenti formati:\n"
"<ul>\n"
"<li><em>account; loginname; password; nome; cognome; abilitato; group; "
"indirizzo[, dominio, ...]</em></li>\n"
"<li><em>account; id utente; password; nome; cognome; abilitato; gruppo; indirizzo; quota; [, dominio, ...]</em></li>\n"
"<li><em>alias; indirizzo; abilitato; destinatario interno</em></li>\n"
"<li><em>forward; indirizzo; abilitato; destinatario esterno</em></li>\n"
"<li><em>dlist; indirizzo; abilitato; destinatario; destinatario; ...</em></"
"li>\n"
"<li><em>dlist; indirizzo; abilitato; destinatario; destinatario; ...</em></li>\n"
"</ul>\n"
"<p>Il primo elemento di ogni riga è obbligatorio e deve essere uguale a "
"quello dei valori precedenti.</p>\n"
"<p>Il primo elemento di ogni riga è obbligatorio e deve essere uguale a quello dei valori precedenti.</p>\n"
"\n"
"<p>Come separatore puoi utilizzare un carattere diverso.</p>\n"
@@ -1558,9 +1537,8 @@ msgid "pending requests"
msgstr "richieste in sospeso"
#: extensions/amavis/general_callbacks.py:124
#, fuzzy
msgid "Pending requests"
msgstr "richieste in sospeso"
msgstr "Richieste in sospeso"
#: extensions/amavis/general_callbacks.py:134
msgid "Content filter"
@@ -1576,12 +1554,12 @@ msgid "Virus filter"
msgstr "Filtro virus"
#: extensions/amavis/models.py:94 extensions/amavis/models.py:102
#: extensions/amavis/models.py:110 extensions/sievefilters/views.py:290
#: extensions/amavis/models.py:110 extensions/sievefilters/views.py:293
msgid "yes"
msgstr "si"
#: extensions/amavis/models.py:95 extensions/amavis/models.py:103
#: extensions/amavis/models.py:111 extensions/sievefilters/views.py:294
#: extensions/amavis/models.py:111 extensions/sievefilters/views.py:297
msgid "no"
msgstr "no"
@@ -1631,7 +1609,7 @@ msgstr ""
#: extensions/amavis/sql_listing.py:31
msgid "Score"
msgstr ""
msgstr "Punteggio"
#: extensions/amavis/sql_listing.py:35
#: extensions/amavis/templatetags/amavis_tags.py:75
@@ -1708,9 +1686,8 @@ msgid "Quarantine management"
msgstr "Gestione quarantena"
#: extensions/amavis/templates/amavis/main_action_bar.html:3
#, fuzzy
msgid "Select"
msgstr "Seleziona un file"
msgstr "Seleziona"
#: extensions/amavis/templates/amavis/main_action_bar.html:9
msgid "Spam"
@@ -1721,7 +1698,6 @@ msgid "Virus"
msgstr "Virus"
#: extensions/amavis/templates/amavis/main_action_bar.html:11
#, fuzzy
msgid "Bad Header"
msgstr "Intestazione errata"
@@ -1730,9 +1706,8 @@ msgid "Bad MIME"
msgstr "MIME errato"
#: extensions/amavis/templates/amavis/main_action_bar.html:13
#, fuzzy
msgid "Banned"
msgstr "Filtro banned"
msgstr "Banditi"
#: extensions/amavis/templates/amavis/main_action_bar.html:18
#: extensions/amavis/templatetags/amavis_tags.py:19
@@ -1754,32 +1729,27 @@ msgstr "Tutti i messaggi"
#: extensions/amavis/templates/amavis/main_action_bar.html:31
msgid "All"
msgstr ""
msgstr "Tutti"
#: extensions/amavis/templates/amavis/main_action_bar.html:32
#, fuzzy
msgid "Spam only"
msgstr "Spam"
msgstr "Solo messaggi di spam"
#: extensions/amavis/templates/amavis/main_action_bar.html:33
#, fuzzy
msgid "Virus only"
msgstr "Virus"
msgstr "Solo messaggi con virus"
#: extensions/amavis/templates/amavis/main_action_bar.html:34
#, fuzzy
msgid "Bad Header only"
msgstr "Intestazione errata"
msgstr "Solo messaggi con intestazione errata"
#: extensions/amavis/templates/amavis/main_action_bar.html:35
#, fuzzy
msgid "Bad MIME only"
msgstr "MIME errato"
msgstr "Solo messaggi con struttura MIME errata"
#: extensions/amavis/templates/amavis/main_action_bar.html:36
#, fuzzy
msgid "Banned only"
msgstr "Filtro banned"
msgstr "Solo messaggi banditi"
#: extensions/amavis/templates/amavis/notifications/pending_requests.html:1
#, python-format
@@ -1954,23 +1924,21 @@ msgstr ""
"destinatario"
#: extensions/postfix_autoreply/app_settings.py:20
#, fuzzy
msgid "Default subject"
msgstr "Editor predefinito"
msgstr "Oggetto predefinito"
#: extensions/postfix_autoreply/app_settings.py:21
msgid "I'm off"
msgstr ""
msgstr "Sono via"
#: extensions/postfix_autoreply/app_settings.py:23
msgid ""
"Default subject used when an auto-reply message is created automatically"
msgstr ""
msgstr "Oggetto predefinito utilizzato nei messaggi di risposta automatica"
#: extensions/postfix_autoreply/app_settings.py:28
#, fuzzy
msgid "Default content"
msgstr "Editor predefinito"
msgstr "Corpo del messaggio predefinito"
#: extensions/postfix_autoreply/app_settings.py:30
#, python-format
@@ -1980,18 +1948,21 @@ msgid ""
"Best regards,\n"
"%(name)s\n"
msgstr ""
"Al momento non posso rispondere. Risponderò al mio ritorno.\n"
"\n"
"Distinti saluti,\n"
"%(name)s\n"
#: extensions/postfix_autoreply/app_settings.py:36
#, python-format
msgid ""
"Default content used when an auto-reply message is created automatically. "
"The '%(name)s' macro will be replaced by the user's full name."
msgstr ""
msgstr "Corpo del messaggio predefinito per i messaggi di risposta automatica. La macro '%(name)s' verrà sostituita con il nome dell'utente. "
#: extensions/postfix_autoreply/forms.py:13
#, fuzzy
msgid "Activate your auto reply from this date"
msgstr "Attiva la tua risposta automatica fino a questa data"
msgstr "Attiva la risposta automatica da questa data"
#: extensions/postfix_autoreply/forms.py:19
msgid "Until"
@@ -2007,9 +1978,8 @@ msgid "This date is over"
msgstr "Questa data è passata"
#: extensions/postfix_autoreply/forms.py:59
#, fuzzy
msgid "Must be greater than start date"
msgstr "è maggiore di"
msgstr "Dev'essere successiva alla data di inizio"
#: extensions/postfix_autoreply/general_callbacks.py:33
#: extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html:2
@@ -2018,11 +1988,11 @@ msgstr "Messaggio di risposta automatica"
#: extensions/postfix_autoreply/general_callbacks.py:118
msgid "Enable auto-reply"
msgstr ""
msgstr "Abilita la risposta automatica"
#: extensions/postfix_autoreply/general_callbacks.py:120
msgid "Enable or disable Postfix auto-reply"
msgstr ""
msgstr "Abilita o disabilita la risposta automatica di Postfix"
#: extensions/postfix_autoreply/models.py:20
msgid "subject"
@@ -2054,65 +2024,54 @@ msgstr "Definisci un messaggio da inviare automaticamente quando sei via"
#: extensions/postfix_relay_domains/__init__.py:72
msgid "Relay domains support for Postfix"
msgstr ""
msgstr "Supporto ai \"domini relè\" per Postfix"
#: extensions/postfix_relay_domains/__init__.py:89
#: extensions/postfix_relay_domains/general_callbacks.py:100
#: extensions/postfix_relay_domains/limits_callbacks.py:16
#, fuzzy
msgid "Relay domains"
msgstr "Nuovo dominio"
msgstr "Domini relè"
#: extensions/postfix_relay_domains/app_settings.py:10
msgid "Postfix's master.cf path"
msgstr ""
msgstr "Percorso del file master.cf di Postfix"
#: extensions/postfix_relay_domains/app_settings.py:12
#, fuzzy
msgid "Path to the master.cf configuration file"
msgstr "Percorso del file di log"
msgstr "Percorso del file di configurazione master.cf"
#: extensions/postfix_relay_domains/forms.py:15
#, fuzzy
msgid ""
"Alias(es) of this relay domain. Indicate only one name per input, press "
"ENTER to add a new input."
msgstr ""
"Alias per questo dominio. Inserire un solo nome alla volta, premere INVIO "
"per aggiungere un nuovo elemento"
msgstr "Alias per questo dominio relè. Inserisci un solo nome per casella, premi INVIO per aggiungere un nuovo elemento."
#: extensions/postfix_relay_domains/forms.py:51
#: extensions/postfix_relay_domains/general_callbacks.py:93
#, fuzzy
msgid "relay domain alias"
msgstr "Alias del dominio"
msgstr "Alias del dominio relè"
#: extensions/postfix_relay_domains/forms.py:69
#: extensions/postfix_relay_domains/general_callbacks.py:92
#, fuzzy
msgid "relay domain"
msgstr "Nuovo dominio"
msgstr "Dominio relè"
#: extensions/postfix_relay_domains/general_callbacks.py:35
#, fuzzy
msgid "Add relay domain"
msgstr "Aggiungi dominio"
msgstr "Aggiungi dominio relè"
#: extensions/postfix_relay_domains/general_callbacks.py:101
#, fuzzy
msgid "Maximum number of relay domains this user can create"
msgstr "Numero massimo di domini che questo utente può creare"
msgstr "Numero massimo di domini relè che questo utente può creare"
#: extensions/postfix_relay_domains/general_callbacks.py:103
#: extensions/postfix_relay_domains/limits_callbacks.py:24
#, fuzzy
msgid "Relay domain aliases"
msgstr "Alias del dominio"
msgstr "Alias del dominio relè"
#: extensions/postfix_relay_domains/general_callbacks.py:104
#, fuzzy
msgid "Maximum number of relay domain aliases this user can create"
msgstr "Numero massimo di alias di dominio che questo utente può creare"
msgstr "Numero massimo di alias di un dominio relè che questo utente può creare"
#: extensions/postfix_relay_domains/general_callbacks.py:111
msgid ""
@@ -2121,80 +2080,70 @@ msgid ""
"em></li>\n"
"<li><em>relaydomainalias; name; target; enabled</em></li>\n"
msgstr ""
"\n"
"<li><em>dominio relè; nome; host di destinazione; servizio; abilitato; verifica caselle</em></li>\n"
"<li><em> alias dominio relè; nome; destinazione; abilitato</em></li>\n"
#: extensions/postfix_relay_domains/limits_callbacks.py:19
#, fuzzy
msgid "Maximum number of allowed relay domains for a new administrator"
msgstr "Numero massimo di domini consentiti per un nuovo amministratore"
msgstr "Numero massimo di domini relè consentiti per un nuovo amministratore"
#: extensions/postfix_relay_domains/limits_callbacks.py:27
#, fuzzy
msgid "Maximum number of allowed relay domain aliases for a new administrator"
msgstr ""
"Numero massimo di alias di dominio consentiti per un nuovo amministratore"
msgstr "Numero massimo di alias di dominio relè consentiti per un nuovo amministratore"
#: extensions/postfix_relay_domains/models.py:59
#, fuzzy
msgid "The service name"
msgstr "Il nome dell'alias"
msgstr "Il nome del servizio"
#: extensions/postfix_relay_domains/models.py:80
#, fuzzy
msgid "target host"
msgstr "destinazione"
#: extensions/postfix_relay_domains/models.py:81
#, fuzzy
msgid "Remote destination of this domain"
msgstr "Seleziona per attivare questo dominio"
msgstr "Destinazione remota per questo dominio"
#: extensions/postfix_relay_domains/models.py:89
#, fuzzy
msgid "verify recipients"
msgstr "Destinatari"
msgstr "Verifica destinatari"
#: extensions/postfix_relay_domains/models.py:90
msgid "Check for valid recipients"
msgstr ""
msgstr "Verifica validità destinatari"
#: extensions/postfix_relay_domains/models.py:103
#, fuzzy
msgid "Relay Domain"
msgstr "Nuovo dominio"
msgstr "Dominio relè"
#: extensions/postfix_relay_domains/models.py:164
#, fuzzy
msgid "The relay domain this alias points to"
msgstr "Il dominio a cui punta questo alias"
msgstr "Il dominio relè a cui punta questo alias"
#: extensions/postfix_relay_domains/models.py:195
#, fuzzy, python-format
#, python-format
msgid "Relay domain %s does not exist"
msgstr "Il dominio '%s' non esiste"
msgstr "Il dominio relè '%s' non esiste"
#: extensions/postfix_relay_domains/views.py:26
#, fuzzy
msgid "Relay domain created"
msgstr "Dominio creato"
msgstr "Dominio relè creato"
#: extensions/postfix_relay_domains/views.py:31
#, fuzzy
msgid "New relay domain"
msgstr "Nuovo dominio"
msgstr "Nuovo dominio relè"
#: extensions/postfix_relay_domains/views.py:54
#, fuzzy
msgid "Relay domain modified"
msgstr "Dominio modificato"
msgstr "Dominio relè modificato"
#: extensions/postfix_relay_domains/views.py:77
#, fuzzy
msgid "Relay domain deleted"
msgstr "Dominio cancellato"
msgstr "Dominio relè cancellato"
#: extensions/postfix_relay_domains/templates/postfix_relay_domains/service_field.html:2
msgid "Scan"
msgstr ""
msgstr "Cerca"
#: extensions/sievefilters/__init__.py:12
msgid "Plugin to easily create server-side filters"
@@ -2315,53 +2264,53 @@ msgstr ""
msgid "Not enough space on server"
msgstr "Non c'è spazio sufficiente nel server"
#: extensions/sievefilters/views.py:78
#: extensions/sievefilters/views.py:80
msgid "Failed to retrieve filters set"
msgstr "Recupero delle impostazioni di filtro fallito"
#: extensions/sievefilters/views.py:138
#: extensions/sievefilters/views.py:141
#: extensions/sievefilters/templatetags/sfilters_tags.py:33
msgid "New filter"
msgstr "Nuovo filtro"
#: extensions/sievefilters/views.py:146
#: extensions/sievefilters/views.py:149
msgid "Filter created"
msgstr "Filtro creato"
#: extensions/sievefilters/views.py:160
#: extensions/sievefilters/views.py:163
#: extensions/sievefilters/templatetags/sfilters_tags.py:78
msgid "Edit filter"
msgstr "Modifica filtro"
#: extensions/sievefilters/views.py:169
#: extensions/sievefilters/views.py:172
msgid "Filter modified"
msgstr "Filtro modificato"
#: extensions/sievefilters/views.py:192
#: extensions/sievefilters/views.py:195
msgid "Filter removed"
msgstr "Filtro eliminato"
#: extensions/sievefilters/views.py:193
#: extensions/sievefilters/views.py:196
msgid "Failed to remove filter"
msgstr "Rimozione del filtro fallita"
#: extensions/sievefilters/views.py:208
#: extensions/sievefilters/views.py:211
msgid "Filters set saved"
msgstr "Gruppo filtri salvato"
#: extensions/sievefilters/views.py:224
#: extensions/sievefilters/views.py:227
msgid "Filters set created"
msgstr "Gruppo filtri creato"
#: extensions/sievefilters/views.py:228
#: extensions/sievefilters/views.py:231
msgid "Create a new filters set"
msgstr "Crea un nuovo gruppo di filtri"
#: extensions/sievefilters/views.py:247
#: extensions/sievefilters/views.py:250
msgid "Filters set deleted"
msgstr "Gruppo filtri eliminato"
#: extensions/sievefilters/views.py:260
#: extensions/sievefilters/views.py:263
msgid "Filters set activated"
msgstr "Gruppo filtri attivato"
@@ -2649,13 +2598,12 @@ msgid "Automatic folder refresh rate (in seconds)"
msgstr "Frequenza di aggiornamento automatico delle cartelle (in secondi)"
#: extensions/webmail/app_settings.py:103
#, fuzzy
msgid "Mailboxes container's width"
msgstr "Proprietario delle caselle postali"
msgstr "Larghezza del contenitore delle caselle"
#: extensions/webmail/app_settings.py:104
msgid "The width of the mailbox list container"
msgstr ""
msgstr "Larghezza della lista delle caselle"
#: extensions/webmail/app_settings.py:111
msgid "Trash folder"
@@ -2703,7 +2651,7 @@ msgstr "Signature delle e-mail personalizzato dall'utente"
#: extensions/webmail/app_settings.py:160
msgid "Value must be a positive integer (> 0)"
msgstr ""
msgstr "Il valore dev'essere un intero positivo"
#: extensions/webmail/exceptions.py:20
msgid "Server response"
@@ -2889,9 +2837,9 @@ msgid "Remove the selected mailbox"
msgstr "Elimina la cartella di posta selezionata"
#: lib/dbutils.py:36
#, fuzzy, python-format
#, python-format
msgid "Connection to database %s not configured"
msgstr "Connessione ad amavis fallita: %s"
msgstr "La connessione con il database %s non è configurata"
#: lib/exceptions.py:67
#, python-format
@@ -2997,36 +2945,3 @@ msgstr "Ricordami"
#: templates/registration/login.html:36
msgid "Log in"
msgstr "Registrati"
#~ msgid "An alias with this name already exists"
#~ msgstr "Esiste già un alias con questo nome"
#~ msgid "A domain with this name already exists"
#~ msgstr "Esiste già un dominio con questo nome"
#~ msgid "Edit domain"
#~ msgstr "Modifica dominio"
#~ msgid "%d pending requests"
#~ msgstr "%d richieste in sospeso"
#~ msgid "Content Class"
#~ msgstr "Classe dei contenuti"
#~ msgid "Nothing"
#~ msgstr "Nulla"
#~ msgid "Bad request"
#~ msgstr "Richiesta sbagliata"
#~ msgid "Undefined action"
#~ msgstr "Azione non definita"
#~ msgid "User preferences"
#~ msgstr "Preferenze utente"
#~ msgid "Per-user settings"
#~ msgstr "Impostazioni per-utente"
#~ msgid "You need a mailbox in order to define a forward"
#~ msgstr "Hai bisogno di una casella di posta per definire un inoltro"
+4 -8
View File
@@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
"PO-Revision-Date: 2013-09-27 00:07+0200\n"
"Last-Translator: Giancarlo Gaifas <lallo@artiemestieri.tn.it>\n"
"POT-Creation-Date: 2013-12-22 23:16+0100\n"
"PO-Revision-Date: 2013-12-23 00:58+0100\n"
"Last-Translator: Alberto Berti <alberto@metapensiero.it>\n"
"Language-Team: Italiano <LL@li.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
@@ -73,7 +73,7 @@ msgstr "Nascondi le intestazioni"
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
msgid "Services updated"
msgstr ""
msgstr "Servizi aggiornati"
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
msgid "Remove this filter?"
@@ -135,13 +135,9 @@ msgid "Cancel"
msgstr "Elimina"
#: static/js/global.js:314
#, fuzzy
msgid "Internal error"
msgstr "Errore interno"
#: static/js/searchbar.js:20
msgid "Search..."
msgstr "Ricerca ..."
#~ msgid "Message deleted"
#~ msgstr "Messaggio eliminato"
+116 -298
View File
@@ -1,9 +1,10 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
#
# Translators:
# ddu17 <dduponchelle@gmail.com>, 2013
# lusitan <lusitan@hushmail.com>, 2013
# mikec <miguelmclara@gmail.com>, 2012
# mikec <miguelmclara@gmail.com>, 2013
# Sandra Ribeiro <sandrarib@gmail.com>, 2012-2013
@@ -13,14 +14,13 @@ msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-12-15 10:21+0100\n"
"PO-Revision-Date: 2013-10-04 21:10+0000\n"
"Last-Translator: ddu17 <dduponchelle@gmail.com>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
"modoboa/language/pt_PT/)\n"
"Language: pt_PT\n"
"PO-Revision-Date: 2013-12-23 11:25+0000\n"
"Last-Translator: lusitan <lusitan@hushmail.com>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/modoboa/language/pt_PT/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_PT\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: core/__init__.py:52 core/__init__.py:53
@@ -55,23 +55,23 @@ msgstr "Esquema pré-definido para passwords"
#: core/app_settings.py:51
msgid "md5crypt (weak)"
msgstr ""
msgstr "md5crypt (fraco)"
#: core/app_settings.py:52
msgid "sha256 (weak)"
msgstr ""
msgstr "sha256 (fraco)"
#: core/app_settings.py:53
msgid "md5 (weak)"
msgstr ""
msgstr "md5 (fraco)"
#: core/app_settings.py:54
msgid "crypt (weak)"
msgstr ""
msgstr "crypt (fraco)"
#: core/app_settings.py:55
msgid "plain (weak)"
msgstr ""
msgstr "plain (fraco)"
#: core/app_settings.py:57
msgid "Scheme used to crypt mailbox passwords"
@@ -149,9 +149,7 @@ msgstr "\"Bind DN\""
msgid ""
"The distinguished name to use when binding to the LDAP server. Leave empty "
"for an anonymous bind"
msgstr ""
"O nome único usado para fazer o \"bind\" ao servidor LDAP. Deixar vario "
"para \"bind\" anónimo"
msgstr "O nome único usado para fazer o \"bind\" ao servidor LDAP. Deixar vario para \"bind\" anónimo"
#: core/app_settings.py:113
msgid "Bind password"
@@ -159,18 +157,15 @@ msgstr "Password para bind"
#: core/app_settings.py:115
msgid "The password to use when binding to the LDAP server (with 'Bind DN')"
msgstr ""
"A password usada para fazer o \"bind\" ao servidor LDAP (com 'Bind DN')"
msgstr "A password usada para fazer o \"bind\" ao servidor LDAP (com 'Bind DN')"
#: core/app_settings.py:121
#, fuzzy
msgid "Users search base"
msgstr "Base de pesquisa"
msgstr ""
#: core/app_settings.py:123
#, fuzzy
msgid "The distinguished name of the search base used to find users"
msgstr "O nome único para a pesquisa base"
msgstr ""
#: core/app_settings.py:128
msgid "Search filter"
@@ -180,9 +175,7 @@ msgstr "Filtro de pesquisa"
msgid ""
"An optional filter string (e.g. '(objectClass=person)'). In order to be "
"valid, it must be enclosed in parentheses."
msgstr ""
"Um filtro opcional (ex: '(objetClass=person)'). Tem de estar entre "
"parênteses para ser válido"
msgstr "Um filtro opcional (ex: '(objetClass=person)'). Tem de estar entre parênteses para ser válido"
#: core/app_settings.py:135
msgid "User DN template"
@@ -193,9 +186,7 @@ msgstr "Template DN de utilizador"
msgid ""
"The template used to construct a user's DN. It should contain one "
"placeholder (ie. %(user)s)"
msgstr ""
"O template usado para construir o DN (nome único). Deve apenas conter um "
"'placeholder' (ex: %(user)s) "
msgstr "O template usado para construir o DN (nome único). Deve apenas conter um 'placeholder' (ex: %(user)s) "
#: core/app_settings.py:142
msgid "Password attribute"
@@ -214,25 +205,22 @@ msgid "Tell if the LDAP server is an Active Directory one"
msgstr "Especificar se o servidor LDAP e um Active Directory"
#: core/app_settings.py:154
#, fuzzy
msgid "Administrator groups"
msgstr "Administradores"
msgstr ""
#: core/app_settings.py:157
msgid ""
"Members of those LDAP Posix groups will be created as domain administrators. "
"Use ';' characters to separate groups."
"Members of those LDAP Posix groups will be created as domain administrators."
" Use ';' characters to separate groups."
msgstr ""
#: core/app_settings.py:164
#, fuzzy
msgid "Groups search base"
msgstr "Base de pesquisa"
msgstr ""
#: core/app_settings.py:167
#, fuzzy
msgid "The distinguished name of the search base used to find groups"
msgstr "O nome único para a pesquisa base"
msgstr ""
#: core/app_settings.py:172
#: extensions/admin/templates/admin/editdomainform.html:7
@@ -261,9 +249,7 @@ msgstr "Redirecionamento pré-definido"
#: core/app_settings.py:190
msgid "The default redirection used when no application is specified"
msgstr ""
"O redirecionamento padrão usado quando nenhuma aplicação se encontra "
"especificada"
msgstr "O redirecionamento padrão usado quando nenhuma aplicação se encontra especificada"
#: core/app_settings.py:218
msgid "Key must be either 16, 24, or 32 bytes long"
@@ -274,9 +260,8 @@ msgid "Invalid syntax"
msgstr "Sintax inválida"
#: core/app_settings.py:233
#, fuzzy
msgid "Invalid rounds number"
msgstr "Pedido inválido"
msgstr ""
#: core/app_settings.py:256
msgid "This field is required"
@@ -326,14 +311,12 @@ msgid "Passwords mismatch"
msgstr "Passwords não coincidem"
#: core/models.py:50
#, fuzzy
msgid "password"
msgstr "Password"
msgstr ""
#: core/models.py:52
#, fuzzy
msgid "last login"
msgstr "Último login"
msgstr ""
#: core/models.py:81
msgid "You can't delete your own account"
@@ -366,9 +349,7 @@ msgstr "O simple utilizador '%s' deve ter endereço de e-mail válido"
#: core/models.py:335
#, python-format
msgid "username and email fields must not differ for '%s'"
msgstr ""
"Os campos nome de utilizador e endereço de email não deve ser diferente para "
"'%s'"
msgstr "Os campos nome de utilizador e endereço de email não deve ser diferente para '%s'"
#: core/models.py:404 extensions/admin/models/alias.py:33
#: extensions/admin/models/domain.py:32
@@ -583,31 +564,15 @@ msgid ""
"\n"
"<h3>Welcome to the online demo!</h3>\n"
"<p><b>Warning:</b> the demo's concent is resetted every two hours.</p>\n"
"<p>To try the demo, the following users are available (<i>username/password</"
"i>): </p>\n"
"<p>To try the demo, the following users are available (<i>username/password</i>): </p>\n"
"<ul>\n"
" <li>A super administrator : <b>admin/password</b></li>\n"
" <li>A domain administrator : <b>admin@demo.local/admin</b></li>\n"
" <li>A simple user : <b>user@demo.local/user</b></li>\n"
"</ul>\n"
"<p>Please report any issue or give any suggestion to \n"
" <a href=\"https://groups.google.com/forum/#!forum/modoboa-users\">the "
"mailing list</a>.</p>\n"
msgstr ""
"\n"
"<h3>Bem-vindo à demo online!</h3>\n"
"<p><b>Aviso:</b> os conteúdos desta demo são limpos a cada duas horas.</p>\n"
"<p>Para experimentar a demo, estão disponíveis os seguintes utilizadores "
"(<i>username/password</i>): </p>\n"
"<ul>\n"
" <li>Super administrador: <b>admin/password</b></li>\n"
" <li>Administrador de domínio: <b>admin@demo.local/admin</b></li>\n"
" <li>Utilizador básico: <b>user@demo.local/user</b></li>\n"
"</ul>\n"
"<p>Para reportar qualquer problema ou apresentar uma sugestão, utilize o "
"seguinte link \n"
" <a href=\"https://groups.google.com/forum/#!forum/modoboa-users\">mailing "
"list</a>.</p>\n"
" <a href=\"https://groups.google.com/forum/#!forum/modoboa-users\">the mailing list</a>.</p>\n"
msgstr "\n<h3>Bem-vindo à demo online!</h3>\n<p><b>Aviso:</b> os conteúdos desta demo são limpos a cada duas horas.</p>\n<p>Para experimentar a demo, estão disponíveis os seguintes utilizadores (<i>username/password</i>): </p>\n<ul>\n <li>Super administrador: <b>admin/password</b></li>\n <li>Administrador de domínio: <b>admin@demo.local/admin</b></li>\n <li>Utilizador básico: <b>user@demo.local/user</b></li>\n</ul>\n<p>Para reportar qualquer problema ou apresentar uma sugestão, utilize o seguinte link \n <a href=\"https://groups.google.com/forum/#!forum/modoboa-users\">mailing list</a>.</p>\n"
#: extensions/admin/__init__.py:48
msgid "Administration console"
@@ -618,9 +583,8 @@ msgid "Web based console to manage domains, accounts and aliases"
msgstr "Consola baseada na web para gestão de domínios, contas e aliases"
#: extensions/admin/__init__.py:55
#, fuzzy
msgid "Administration"
msgstr "Administradores"
msgstr ""
#: extensions/admin/__init__.py:79
#: extensions/admin/templates/admin/domains.html:2
@@ -660,9 +624,7 @@ msgstr "Lidar com caixas de correio no sistema de ficheiros"
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Renomear ou remover as caixas de correio no sistema de ficheiros quando "
"estas são renomeadas no Modoboa"
msgstr "Renomear ou remover as caixas de correio no sistema de ficheiros quando estas são renomeadas no Modoboa"
#: extensions/admin/app_settings.py:20
msgid "Mailboxes ower"
@@ -673,9 +635,8 @@ msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "A conta UNIX que detém as caixas de correio no sistema de ficheiros"
#: extensions/admin/app_settings.py:26
#, fuzzy
msgid "Default domain quota"
msgstr "Limites predefinidos"
msgstr ""
#: extensions/admin/app_settings.py:29
msgid ""
@@ -689,8 +650,7 @@ msgstr "Remoção automática de conta"
#: extensions/admin/app_settings.py:38
msgid "When a mailbox is removed, also remove the associated account"
msgstr ""
"Ao remover uma caixa de correio estará também a remover a conta associada"
msgstr "Ao remover uma caixa de correio estará também a remover a conta associada"
#: extensions/admin/app_settings.py:69 extensions/admin/forms/account.py:178
#: extensions/admin/forms/domain.py:56
@@ -698,16 +658,14 @@ msgid "Must be a positive integer"
msgstr ""
#: extensions/admin/lib.py:28
#, fuzzy
msgid "A mailbox is required"
msgstr "É necessário uma quota"
msgstr ""
#: extensions/admin/forms/account.py:21
#, fuzzy
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr "O simple utilizador '%s' deve ter endereço de e-mail válido"
msgstr ""
#: extensions/admin/forms/account.py:26
msgid "Role"
@@ -750,10 +708,7 @@ msgid ""
"Quota in MB for this mailbox. Define a custom value or use domain's default "
"one. Leave empty to define an unlimited value (not allowed for domain "
"administrators)."
msgstr ""
"Quota em MB para esta caixa de correio. Definir uma valor personalizado ou "
"usar o valor predefinido pelo domínio. Deixar em branco para definir uma "
"valor ilimitado (não permitido para administradores do domínio)."
msgstr "Quota em MB para esta caixa de correio. Definir uma valor personalizado ou usar o valor predefinido pelo domínio. Deixar em branco para definir uma valor ilimitado (não permitido para administradores do domínio)."
#: extensions/admin/forms/account.py:123 extensions/admin/forms/domain.py:26
#: extensions/admin/templates/admin/domains_table.html:6
@@ -766,10 +721,7 @@ msgid ""
"Alias(es) of this mailbox. Indicate only one address per input, press ENTER "
"to add a new input. Use the '*' character to create a 'catchall' alias (ex: "
"*@domain.tld)."
msgstr ""
"Alias(es) desta caixa de correio. Indique apenas um endereço por entrada, "
"clique ENTER para adicionar uma nova entrada. Use o '*' para criar um alias "
"'catchall' (ex: *@domain.tld)."
msgstr "Alias(es) desta caixa de correio. Indique apenas um endereço por entrada, clique ENTER para adicionar uma nova entrada. Use o '*' para criar um alias 'catchall' (ex: *@domain.tld)."
#: extensions/admin/forms/account.py:187 extensions/admin/forms/account.py:217
#: extensions/admin/forms/alias.py:65
@@ -806,9 +758,7 @@ msgstr "Endereço de e-mail"
msgid ""
"The distribution list address. Use the '*' character to create a 'catchall' "
"address (ex: *@domain.tld)."
msgstr ""
"Os endereços da lista de distribuição. Use o '*' para criar um endereço "
"'catchall' (Ex: *@domain.tld)."
msgstr "Os endereços da lista de distribuição. Use o '*' para criar um endereço 'catchall' (Ex: *@domain.tld)."
#: extensions/admin/forms/alias.py:23
msgid "Recipients"
@@ -818,9 +768,7 @@ msgstr "Destinatários"
msgid ""
"Mailbox(es) this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Caixa(s) de correio para o qual este 'alias' aponta. Indicar apenas um "
"endereço por entrada, pressionar ENTER para acrescentar uma nova entrada."
msgstr "Caixa(s) de correio para o qual este 'alias' aponta. Indicar apenas um endereço por entrada, pressionar ENTER para acrescentar uma nova entrada."
#: extensions/admin/forms/alias.py:68
msgid "You don't have access to this domain"
@@ -845,40 +793,35 @@ msgid "No recipient defined"
msgstr "Sem destinatário definido"
#: extensions/admin/forms/domain.py:20
#, fuzzy
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr "Quota em MB aplicada por defeito às caixas de correio"
msgstr ""
#: extensions/admin/forms/domain.py:29
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alias(es) deste domínio. Indicar apenas um nome por entrada, pressionar "
"ENTER para acrescentar uma nova entrada."
msgstr "Alias(es) deste domínio. Indicar apenas um nome por entrada, pressionar ENTER para acrescentar uma nova entrada."
#: extensions/admin/forms/domain.py:75
#: extensions/postfix_relay_domains/forms.py:50
#: extensions/postfix_relay_domains/forms.py:70
#, fuzzy
msgid "domain alias"
msgstr "Aliases de domínio"
msgstr ""
#: extensions/admin/forms/domain.py:82 extensions/admin/forms/domain.py:98
#: extensions/postfix_relay_domains/forms.py:58
#: extensions/postfix_relay_domains/forms.py:78
#, fuzzy, python-format
#, python-format
msgid "A %s with this name already exists"
msgstr "Já existe um 'alias' com este nome"
msgstr ""
#: extensions/admin/forms/domain.py:92
#: extensions/postfix_relay_domains/forms.py:49
#: extensions/postfix_relay_domains/forms.py:71
#, fuzzy
msgid "domain"
msgstr "Domínios"
msgstr ""
#: extensions/admin/forms/domain.py:176
msgid "Create a domain administrator"
@@ -892,9 +835,7 @@ msgstr "Criar automaticamente um administrador para este domínio"
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"O nome do administrador. Não inclua o nome do domínio já que este será "
"automaticamente anexado."
msgstr "O nome do administrador. Não inclua o nome do domínio já que este será automaticamente anexado."
#: extensions/admin/forms/domain.py:193
msgid "Create aliases"
@@ -935,16 +876,13 @@ msgstr "Conservar cópias locais"
#: extensions/admin/forms/forward.py:20
msgid "Forward messages and store copies into your local mailbox"
msgstr ""
"Reencaminhar mensagens e armazenar cópias na sua caixa de correio local"
msgstr "Reencaminhar mensagens e armazenar cópias na sua caixa de correio local"
#: extensions/admin/forms/forward.py:38
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Não é possível definir um reencaminhamento para um destino local. Por favor "
"informe-se junto de um administrador para criar um alias."
msgstr "Não é possível definir um reencaminhamento para um destino local. Por favor informe-se junto de um administrador para criar um alias."
#: extensions/admin/forms/import_.py:6 extensions/webmail/forms.py:26
msgid "Select a file"
@@ -972,11 +910,9 @@ msgstr "endereços"
#: extensions/admin/models/alias.py:18
msgid ""
"The alias address (without the domain part). For a 'catch-all' address, just "
"enter an * character."
msgstr ""
"O endereço do alias (sem a parte do domínio). Para um endereço 'cath-all', "
"digite apenas o caractere *"
"The alias address (without the domain part). For a 'catch-all' address, just"
" enter an * character."
msgstr "O endereço do alias (sem a parte do domínio). Para um endereço 'cath-all', digite apenas o caractere *"
#: extensions/admin/models/alias.py:24
msgid "mailboxes"
@@ -1037,9 +973,8 @@ msgid "Check to activate this domain"
msgstr "Marcar para ativar este domínio"
#: extensions/admin/models/domain.py:61
#, fuzzy
msgid "Domain"
msgstr "Domínios"
msgstr ""
#: extensions/admin/models/domain.py:140
#, python-format
@@ -1061,9 +996,9 @@ msgid "The domain this alias points to"
msgstr "O domínio para o qual este alias irá apontar"
#: extensions/admin/models/domain_alias.py:46
#, fuzzy, python-format
#, python-format
msgid "A domain %s already exists"
msgstr "Caixa de correio %s já existe"
msgstr ""
#: extensions/admin/models/domain_alias.py:51
#, python-format
@@ -1095,9 +1030,8 @@ msgid "MB"
msgstr "MB"
#: extensions/admin/templates/admin/domain_options_form.html:5
#, fuzzy
msgid "You can't create a new domain administrator"
msgstr "Criar um administrador de domínio"
msgstr ""
#: extensions/admin/templates/admin/domains_table.html:7
#: extensions/admin/templates/admin/identities_table.html:7
@@ -1379,7 +1313,7 @@ msgstr[0] "Conta eliminada"
msgstr[1] "Contas eliminadas"
#: extensions/admin/views/import.py:39
#, fuzzy, python-format
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
@@ -1387,15 +1321,9 @@ msgid ""
" <li><em>domainalias; name; targeted domain; enabled</em></li>\n"
" %s\n"
"</ul>\n"
"<p>The first element of each line is mandatory and must be equal to one of "
"the previous values.</p>\n"
"<p>The first element of each line is mandatory and must be equal to one of the previous values.</p>\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Forneça um ficheiro CSV cujas linhas respeitem um dos seguintes formatos:⏎ "
"<ul>⏎ <li><em>domain; name; quota; enabled</em></li>⏎ <li><em>domainalias; "
"name; targeted domain; enabled</em></li>⏎ </ul>⏎ <p>O primeiro elemento de "
"cada linha é obrigatório e deve ser igual a um dos valores anteriores.</p>⏎ "
"<p>Pode utilizar um caractere diferente como separador.</p>⏎\n"
#: extensions/admin/views/import.py:50
msgid "Import domains"
@@ -1412,33 +1340,18 @@ msgid "%d objects imported successfully"
msgstr "%d objetos importados com sucesso"
#: extensions/admin/views/import.py:153
#, fuzzy
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
"<li><em>account; loginname; password; first name; last name; enabled; group; "
"address; quota; [, domain, ...]</em></li>\n"
"<li><em>account; loginname; password; first name; last name; enabled; group; address; quota; [, domain, ...]</em></li>\n"
"<li><em>alias; address; enabled; internal recipient</em></li>\n"
"<li><em>forward; address; enabled; external recipient</em></li>\n"
"<li><em>dlist; address; enabled; recipient; recipient; ...</em></li>\n"
"</ul>\n"
"<p>The first element of each line is mandatory and must be equal to one of "
"the previous values.</p>\n"
"<p>The first element of each line is mandatory and must be equal to one of the previous values.</p>\n"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Forneça um ficheiro CSV cujas linhas respeitem um dos seguintes formatos:⏎\n"
"<ul>⏎\n"
"<li><em>account; loginname; password; first name; last name; enabled; group; "
"address[, domain, ...]</em></li>⏎\n"
"<li><em>alias; address; enabled; internal recipient</em></li>⏎\n"
"<li><em>forward; address; enabled; external recipient</em></li>⏎\n"
"<li><em>dlist; address; enabled; recipient; recipient; ...</em></li>⏎\n"
"</ul>⏎\n"
"<p>O primeiro elemento de cada linha é obrigatório e deve ser igual a um dos "
"valores anteriores..</p>⏎\n"
"⏎\n"
"<p>Pode utilizar um caractere diferente como separador.</p>⏎\n"
#: extensions/admin/views/import.py:165
msgid "Import identities"
@@ -1466,9 +1379,7 @@ msgstr "Idade maxima da messagem"
#: extensions/amavis/app_settings.py:16
msgid "Quarantine messages maximum age (in days) before deletion"
msgstr ""
"Tempo máximo que as mensagens ficam na quarentena (em dias) até serem "
"eliminadas"
msgstr "Tempo máximo que as mensagens ficam na quarentena (em dias) até serem eliminadas"
#: extensions/amavis/app_settings.py:19
msgid "Messages releasing"
@@ -1480,9 +1391,7 @@ msgstr "Remover mensagens libertadas"
#: extensions/amavis/app_settings.py:24
msgid "Remove messages marked as released while cleaning up the database"
msgstr ""
"Remova as mensagens, marcadas como enviadas, durante a limpeza da base de "
"dados"
msgstr "Remova as mensagens, marcadas como enviadas, durante a limpeza da base de dados"
#: extensions/amavis/app_settings.py:28
msgid "Amavis connection mode"
@@ -1561,9 +1470,8 @@ msgid "pending requests"
msgstr "pedidos pendentes"
#: extensions/amavis/general_callbacks.py:124
#, fuzzy
msgid "Pending requests"
msgstr "pedidos pendentes"
msgstr ""
#: extensions/amavis/general_callbacks.py:134
msgid "Content filter"
@@ -1596,9 +1504,7 @@ msgstr "predifinido"
#: extensions/amavis/models.py:98
msgid "Bypass virus checks or not. Choose 'default' to use global settings."
msgstr ""
"Ignorar a verificação de vírus ou não. Escolher 'predefinido' para utilizar "
"as definições gerais."
msgstr "Ignorar a verificação de vírus ou não. Escolher 'predefinido' para utilizar as definições gerais."
#: extensions/amavis/models.py:101
msgid "Spam filter"
@@ -1606,9 +1512,7 @@ msgstr "Filtro de spam"
#: extensions/amavis/models.py:106
msgid "Bypass spam checks or not. Choose 'default' to use global settings."
msgstr ""
"Ignorar filtros de spam ou não. Escolher 'predefinido' para utilizar as "
"definições gerais."
msgstr "Ignorar filtros de spam ou não. Escolher 'predefinido' para utilizar as definições gerais."
#: extensions/amavis/models.py:109
msgid "Banned filter"
@@ -1616,9 +1520,7 @@ msgstr "Ficheiros proibidos"
#: extensions/amavis/models.py:114
msgid "Bypass banned checks or not. Choose 'default' to use global settings."
msgstr ""
"Ignorar a verificação de ficheiros proibidos. Escolher 'predefinido' para "
"utilizar as definições gerais."
msgstr "Ignorar a verificação de ficheiros proibidos. Escolher 'predefinido' para utilizar as definições gerais."
#: extensions/amavis/models.py:144
msgid "Spam marker"
@@ -1626,11 +1528,9 @@ msgstr "Marcador spam"
#: extensions/amavis/models.py:146
msgid ""
"Modify spam subject using the specified text. Choose 'default' to use global "
"settings."
msgstr ""
"Modifique o assunto de spam usando o texto especificado. Escolha 'default' "
"para usar as configurações globais."
"Modify spam subject using the specified text. Choose 'default' to use global"
" settings."
msgstr "Modifique o assunto de spam usando o texto especificado. Escolha 'default' para usar as configurações globais."
#: extensions/amavis/sql_listing.py:31
msgid "Score"
@@ -1711,9 +1611,8 @@ msgid "Quarantine management"
msgstr "Gestão de Quarentena"
#: extensions/amavis/templates/amavis/main_action_bar.html:3
#, fuzzy
msgid "Select"
msgstr "Escolha um ficheiro"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:9
msgid "Spam"
@@ -1724,18 +1623,16 @@ msgid "Virus"
msgstr "Vírus"
#: extensions/amavis/templates/amavis/main_action_bar.html:11
#, fuzzy
msgid "Bad Header"
msgstr "Cabeçalho inválido"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:12
msgid "Bad MIME"
msgstr "Formato MIME inválido"
#: extensions/amavis/templates/amavis/main_action_bar.html:13
#, fuzzy
msgid "Banned"
msgstr "Ficheiros proibidos"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:18
#: extensions/amavis/templatetags/amavis_tags.py:19
@@ -1760,29 +1657,24 @@ msgid "All"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:32
#, fuzzy
msgid "Spam only"
msgstr "Spam"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:33
#, fuzzy
msgid "Virus only"
msgstr "Vírus"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:34
#, fuzzy
msgid "Bad Header only"
msgstr "Cabeçalho inválido"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:35
#, fuzzy
msgid "Bad MIME only"
msgstr "Formato MIME inválido"
msgstr ""
#: extensions/amavis/templates/amavis/main_action_bar.html:36
#, fuzzy
msgid "Banned only"
msgstr "Ficheiros proibidos"
msgstr ""
#: extensions/amavis/templates/amavis/notifications/pending_requests.html:1
#, python-format
@@ -1792,12 +1684,8 @@ msgid ""
msgid_plural ""
"\n"
"%(counter)s release requests are pending for action.\n"
msgstr[0] ""
"\n"
"%(counter)s pedido de libertação pendente\n"
msgstr[1] ""
"\n"
"%(counter)s pedidos de libertação pendentes\n"
msgstr[0] "\n%(counter)s pedido de libertação pendente\n"
msgstr[1] "\n%(counter)s pedidos de libertação pendentes\n"
#: extensions/amavis/templates/amavis/notifications/pending_requests.html:6
msgid "Sketch:"
@@ -1850,10 +1738,9 @@ msgid "Domain admins"
msgstr "Administradores de domínio"
#: extensions/limits/app_settings.py:15
msgid "Maximum number of allowed domain administrators for a new administrator"
msgstr ""
"Número máximo permitido de administradores de domínio para um novo "
"administrador"
msgid ""
"Maximum number of allowed domain administrators for a new administrator"
msgstr "Número máximo permitido de administradores de domínio para um novo administrador"
#: extensions/limits/app_settings.py:21
msgid "Maximum number of allowed domains for a new administrator"
@@ -1861,8 +1748,7 @@ msgstr "Número máximo permitido de domínios para novo administrador"
#: extensions/limits/app_settings.py:27
msgid "Maximum number of allowed domain aliases for a new administrator"
msgstr ""
"Número máximo permitido de aliases de domínio para um novo administrador"
msgstr "Número máximo permitido de aliases de domínio para um novo administrador"
#: extensions/limits/app_settings.py:33
msgid "Maximum number of allowed mailboxes for a new administrator"
@@ -1903,8 +1789,7 @@ msgstr "Recursos insuficientes"
#: extensions/limits/models.py:15
msgid "Maximum number of domain administrators this user can create"
msgstr ""
"Número máximo de administradores de domínio que este utilizador pode criar"
msgstr "Número máximo de administradores de domínio que este utilizador pode criar"
#: extensions/limits/models.py:18
msgid "Maximum number of domains this user can create"
@@ -1920,8 +1805,7 @@ msgstr "Número máximo de caixas de correio que este utilizador por criar"
#: extensions/limits/models.py:26
msgid "Maximum number of mailbox aliases this user can create"
msgstr ""
"Número máximo de aliases para caixas de correio que este utilizador por criar"
msgstr "Número máximo de aliases para caixas de correio que este utilizador por criar"
#: extensions/limits/models.py:136
msgid "undefined"
@@ -1949,14 +1833,11 @@ msgstr "Tempo limite de resposta automática"
#: extensions/postfix_autoreply/app_settings.py:16
msgid "Timeout in seconds between two auto-replies to the same recipient"
msgstr ""
"Tempo limite em segundos entre duas respostas automáticas para o mesmo "
"destinatário"
msgstr "Tempo limite em segundos entre duas respostas automáticas para o mesmo destinatário"
#: extensions/postfix_autoreply/app_settings.py:20
#, fuzzy
msgid "Default subject"
msgstr "Editor predefinido"
msgstr ""
#: extensions/postfix_autoreply/app_settings.py:21
msgid "I'm off"
@@ -1968,9 +1849,8 @@ msgid ""
msgstr ""
#: extensions/postfix_autoreply/app_settings.py:28
#, fuzzy
msgid "Default content"
msgstr "Editor predefinido"
msgstr ""
#: extensions/postfix_autoreply/app_settings.py:30
#, python-format
@@ -1989,9 +1869,8 @@ msgid ""
msgstr ""
#: extensions/postfix_autoreply/forms.py:13
#, fuzzy
msgid "Activate your auto reply from this date"
msgstr "Ativar o seu aviso de ausência até esta data"
msgstr ""
#: extensions/postfix_autoreply/forms.py:19
msgid "Until"
@@ -2007,9 +1886,8 @@ msgid "This date is over"
msgstr "Esta data ultrapassa"
#: extensions/postfix_autoreply/forms.py:59
#, fuzzy
msgid "Must be greater than start date"
msgstr "é maior do que"
msgstr ""
#: extensions/postfix_autoreply/general_callbacks.py:33
#: extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html:2
@@ -2059,138 +1937,113 @@ msgstr ""
#: extensions/postfix_relay_domains/__init__.py:89
#: extensions/postfix_relay_domains/general_callbacks.py:100
#: extensions/postfix_relay_domains/limits_callbacks.py:16
#, fuzzy
msgid "Relay domains"
msgstr "Novo domínio"
msgstr ""
#: extensions/postfix_relay_domains/app_settings.py:10
msgid "Postfix's master.cf path"
msgstr ""
#: extensions/postfix_relay_domains/app_settings.py:12
#, fuzzy
msgid "Path to the master.cf configuration file"
msgstr "Caminho para o ficheiro log"
msgstr ""
#: extensions/postfix_relay_domains/forms.py:15
#, fuzzy
msgid ""
"Alias(es) of this relay domain. Indicate only one name per input, press "
"ENTER to add a new input."
msgstr ""
"Alias(es) deste domínio. Indicar apenas um nome por entrada, pressionar "
"ENTER para acrescentar uma nova entrada."
#: extensions/postfix_relay_domains/forms.py:51
#: extensions/postfix_relay_domains/general_callbacks.py:93
#, fuzzy
msgid "relay domain alias"
msgstr "Aliases de domínio"
msgstr ""
#: extensions/postfix_relay_domains/forms.py:69
#: extensions/postfix_relay_domains/general_callbacks.py:92
#, fuzzy
msgid "relay domain"
msgstr "Novo domínio"
msgstr ""
#: extensions/postfix_relay_domains/general_callbacks.py:35
#, fuzzy
msgid "Add relay domain"
msgstr "Adicionar domínio"
msgstr ""
#: extensions/postfix_relay_domains/general_callbacks.py:101
#, fuzzy
msgid "Maximum number of relay domains this user can create"
msgstr "Número máximo de domínios que este utilizador pode criar"
msgstr ""
#: extensions/postfix_relay_domains/general_callbacks.py:103
#: extensions/postfix_relay_domains/limits_callbacks.py:24
#, fuzzy
msgid "Relay domain aliases"
msgstr "Aliases de domínio"
msgstr ""
#: extensions/postfix_relay_domains/general_callbacks.py:104
#, fuzzy
msgid "Maximum number of relay domain aliases this user can create"
msgstr "Número máximo de aliases de domínio que este utilizador pode criar"
msgstr ""
#: extensions/postfix_relay_domains/general_callbacks.py:111
msgid ""
"\n"
"<li><em>relaydomain; name; target host; service; enabled; verify recipients</"
"em></li>\n"
"<li><em>relaydomain; name; target host; service; enabled; verify recipients</em></li>\n"
"<li><em>relaydomainalias; name; target; enabled</em></li>\n"
msgstr ""
#: extensions/postfix_relay_domains/limits_callbacks.py:19
#, fuzzy
msgid "Maximum number of allowed relay domains for a new administrator"
msgstr "Número máximo permitido de domínios para novo administrador"
msgstr ""
#: extensions/postfix_relay_domains/limits_callbacks.py:27
#, fuzzy
msgid "Maximum number of allowed relay domain aliases for a new administrator"
msgstr ""
"Número máximo permitido de aliases de domínio para um novo administrador"
#: extensions/postfix_relay_domains/models.py:59
#, fuzzy
msgid "The service name"
msgstr "O nome do 'alias'"
msgstr ""
#: extensions/postfix_relay_domains/models.py:80
#, fuzzy
msgid "target host"
msgstr "alvo"
msgstr ""
#: extensions/postfix_relay_domains/models.py:81
#, fuzzy
msgid "Remote destination of this domain"
msgstr "Marcar para ativar este domínio"
msgstr ""
#: extensions/postfix_relay_domains/models.py:89
#, fuzzy
msgid "verify recipients"
msgstr "Destinatários"
msgstr ""
#: extensions/postfix_relay_domains/models.py:90
msgid "Check for valid recipients"
msgstr ""
#: extensions/postfix_relay_domains/models.py:103
#, fuzzy
msgid "Relay Domain"
msgstr "Novo domínio"
msgstr ""
#: extensions/postfix_relay_domains/models.py:164
#, fuzzy
msgid "The relay domain this alias points to"
msgstr "O domínio para o qual este alias irá apontar"
msgstr ""
#: extensions/postfix_relay_domains/models.py:195
#, fuzzy, python-format
#, python-format
msgid "Relay domain %s does not exist"
msgstr "O domínio '%s' não existe"
msgstr ""
#: extensions/postfix_relay_domains/views.py:26
#, fuzzy
msgid "Relay domain created"
msgstr "Domínio criado"
msgstr ""
#: extensions/postfix_relay_domains/views.py:31
#, fuzzy
msgid "New relay domain"
msgstr "Novo domínio"
msgstr ""
#: extensions/postfix_relay_domains/views.py:54
#, fuzzy
msgid "Relay domain modified"
msgstr "Domínio alterado"
msgstr ""
#: extensions/postfix_relay_domains/views.py:77
#, fuzzy
msgid "Relay domain deleted"
msgstr "Domínio criado"
msgstr ""
#: extensions/postfix_relay_domains/templates/postfix_relay_domains/service_field.html:2
msgid "Scan"
@@ -2387,8 +2240,7 @@ msgstr "ativo"
#: extensions/sievefilters/templates/sievefilters/index.html:94
msgid ""
"No filters set defined yet. You must create one in order to define filters."
msgstr ""
"Não existem grupos de filtros. Terá de criar um para poder definir filtros."
msgstr "Não existem grupos de filtros. Terá de criar um para poder definir filtros."
#: extensions/sievefilters/templatetags/sfilters_tags.py:15
msgid "New filters set"
@@ -2645,9 +2497,8 @@ msgid "Automatic folder refresh rate (in seconds)"
msgstr "Taxa de actualização automática de pastas (em segundos)"
#: extensions/webmail/app_settings.py:103
#, fuzzy
msgid "Mailboxes container's width"
msgstr "Detentor das Caixas de Correio (Owner)"
msgstr ""
#: extensions/webmail/app_settings.py:104
msgid "The width of the mailbox list container"
@@ -2885,9 +2736,9 @@ msgid "Remove the selected mailbox"
msgstr "Eliminar caixa de correio selecionada"
#: lib/dbutils.py:36
#, fuzzy, python-format
#, python-format
msgid "Connection to database %s not configured"
msgstr "Não foi possível estabelecer ligação com o amavis: %s"
msgstr ""
#: lib/exceptions.py:67
#, python-format
@@ -2993,36 +2844,3 @@ msgstr "Lembrar-me"
#: templates/registration/login.html:36
msgid "Log in"
msgstr "Entrar"
#~ msgid "An alias with this name already exists"
#~ msgstr "Já existe um alias com este nome"
#~ msgid "A domain with this name already exists"
#~ msgstr "Este nome de domínio já existe"
#~ msgid "Edit domain"
#~ msgstr "Editar domínio"
#~ msgid "%d pending requests"
#~ msgstr "%d pedidos pendentes"
#~ msgid "Content Class"
#~ msgstr "Tipo de Conteúdo"
#~ msgid "Nothing"
#~ msgstr "Nenhum"
#~ msgid "Bad request"
#~ msgstr "Pedido inválido"
#~ msgid "Undefined action"
#~ msgstr "Ação indefinida"
#~ msgid "User preferences"
#~ msgstr "Preferências do utilizador"
#~ msgid "Per-user settings"
#~ msgstr "Configurações por usuário"
#~ msgid "You need a mailbox in order to define a forward"
#~ msgstr "Precisa de uma caixa de correio para definir um reencaminhamento"
+9 -30
View File
@@ -1,8 +1,9 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
#
# Translators:
# lusitan <lusitan@hushmail.com>, 2013
# mikec <miguelmclara@gmail.com>, 2012
# Sandra Ribeiro <sandrarib@gmail.com>, 2013
msgid ""
@@ -10,14 +11,13 @@ msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-12-15 10:16+0100\n"
"PO-Revision-Date: 2013-06-03 17:44+0000\n"
"Last-Translator: Sandra Ribeiro <sandrarib@gmail.com>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
"modoboa/language/pt_PT/)\n"
"Language: pt_PT\n"
"PO-Revision-Date: 2013-12-23 11:22+0000\n"
"Last-Translator: lusitan <lusitan@hushmail.com>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/modoboa/language/pt_PT/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_PT\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: extensions/admin/static/admin/js/admin.js:32
@@ -36,9 +36,7 @@ msgstr "Esta operação irá remover TODOS os dados associados a este domínio."
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Esta operação irá remover TODOS os dados associados a este domínio, excepto "
"as contas."
msgstr "Esta operação irá remover TODOS os dados associados a este domínio, excepto as contas."
#: extensions/admin/static/admin/js/admin.js:222
msgid "No domain administrator defined"
@@ -76,7 +74,7 @@ msgstr "Esconder cabeçalho completo"
#: extensions/postfix_relay_domains/static/postfix_relay_domains/js/relay_domains.js:49
msgid "Services updated"
msgstr ""
msgstr "Atualizados os serviços"
#: extensions/sievefilters/static/sievefilters/js/lib.js:14
msgid "Remove this filter?"
@@ -138,28 +136,9 @@ msgid "Cancel"
msgstr "Cancelar"
#: static/js/global.js:314
#, fuzzy
msgid "Internal error"
msgstr "Erro Interno"
msgstr "Erro interno"
#: static/js/searchbar.js:20
msgid "Search..."
msgstr "Pesquisar..."
#~ msgid "Message deleted"
#~ msgstr "Mensagem apagada"
#~ msgid "Delete this domain?"
#~ msgstr "Apagar este domínio?"
#~ msgid "Delete this account?"
#~ msgstr "Apagar esta conta?"
#~ msgid "Delete this list?"
#~ msgstr "Apagar esta lista?"
#~ msgid "Delete this forward?"
#~ msgstr "Apagar este reencaminhamento?"
#~ msgid "Delete this alias?"
#~ msgstr "Apagar este alias?"
+2 -2
View File
@@ -49,6 +49,6 @@ pre {
margin-left: 10px;
margin-right: 20px;
font-weight: bold;
width: 6.38%;
padding: 2px 0;
width: 7.5%;
padding: 0 0 2px 0;
}
+1 -1
View File
@@ -54,7 +54,7 @@ def read(fname):
setup(
name="modoboa",
version='1.0.1',
version='1.1.0',
url='http://modoboa.org/',
license='MIT',
description="Mail hosting made simple",