Merge with admin, limits and relaydomains extensions.

WIP
This commit is contained in:
Antoine Nguyen
2015-11-09 18:25:34 +01:00
parent 7beeaaeaf4
commit 11064393a6
173 changed files with 22291 additions and 3 deletions
+1
View File
@@ -0,0 +1 @@
default_app_config = "modoboa.admin.apps.AdminConfig"
+151
View File
@@ -0,0 +1,151 @@
"""Modoboa admin settings and events."""
import os
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy
from ..lib import events
from modoboa.lib import parameters
from modoboa.lib.form_utils import YesNoField, SeparatorField
from modoboa.lib.sysutils import exec_cmd
ADMIN_EVENTS = [
"DomainCreated",
"DomainModified",
"DomainDeleted",
"DomainOwnershipRemoved",
"GetTagsForDomain",
"ExtraDomainMenuEntries",
"ExtraDomainFilters",
"ExtraDomainQsetFilters",
"ExtraDomainTypes",
"ExtraDomainWizardSteps",
"GetDomainModifyLink",
"CheckDomainName",
"DomainAliasCreated",
"DomainAliasDeleted",
"MailboxCreated",
"MailboxDeleted",
"MailboxModified",
"MailboxAliasCreated",
"MailboxAliasDeleted",
"ExtraDomainForm",
"FillDomainInstances",
"ExtraAccountForm",
"CheckExtraAccountForm",
"FillAccountInstances",
"ExtraDomainImportHelp",
"ImportObject"
]
class AdminParametersForm(parameters.AdminParametersForm):
app = "admin"
mbsep = SeparatorField(label=ugettext_lazy("Mailboxes"))
handle_mailboxes = YesNoField(
label=ugettext_lazy("Handle mailboxes on filesystem"),
initial="no",
help_text=ugettext_lazy(
"Rename or remove mailboxes on the filesystem when they get"
" renamed or removed within Modoboa"
)
)
mailboxes_owner = forms.CharField(
label=ugettext_lazy("Mailboxes owner"),
initial="vmail",
help_text=ugettext_lazy(
"The UNIX account who owns mailboxes on the filesystem"
)
)
default_domain_quota = forms.IntegerField(
label=ugettext_lazy("Default domain quota"),
initial=0,
help_text=ugettext_lazy(
"Default quota (in MB) applied to freshly created domains with no "
"value specified. A value of 0 means no quota."
)
)
auto_account_removal = YesNoField(
label=ugettext_lazy("Automatic account removal"),
initial="no",
help_text=ugettext_lazy(
"When a mailbox is removed, also remove the associated account")
)
auto_create_domain_and_mailbox = YesNoField(
label=ugettext_lazy("Automatic domain/mailbox creation"),
initial="yes",
help_text=ugettext_lazy(
"Create a domain and a mailbox when an account is automatically "
"created."
)
)
# Visibility rules
visibility_rules = {
"mailboxes_owner": "handle_mailboxes=yes",
}
def __init__(self, *args, **kwargs):
super(AdminParametersForm, self).__init__(*args, **kwargs)
self.field_widths = {
"default_domain_quota": 2
}
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:
hide_fields = True
else:
if code or not version.strip().startswith("2"):
hide_fields = True
else:
hide_fields = True
if hide_fields:
del self.fields["handle_mailboxes"]
del self.fields["mailboxes_owner"]
def clean_default_domain_quota(self):
"""Ensure quota is a positive integer."""
if self.cleaned_data['default_domain_quota'] < 0:
raise forms.ValidationError(
ugettext_lazy('Must be a positive integer')
)
return self.cleaned_data['default_domain_quota']
def load_admin_settings():
"""Load admin settings."""
from .app_settings import AdminParametersForm
parameters.register(
AdminParametersForm, ugettext_lazy("Administration"))
events.declare(ADMIN_EVENTS)
from . import callbacks
+14
View File
@@ -0,0 +1,14 @@
"""AppConfig for admin."""
from django.apps import AppConfig
class AdminConfig(AppConfig):
"""App configuration."""
name = "modoboa.admin"
verbose_name = "Modoboa admin console"
def ready(self):
from . import handlers
+256
View File
@@ -0,0 +1,256 @@
"""Event callbacks for modoboa admin."""
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib import parameters, events
from modoboa.lib.email_utils import split_mailbox
from modoboa.lib.exceptions import PermDeniedException, BadRequest, Conflict
from .models import (
Domain, DomainAlias, Mailbox, Alias
)
PERMISSIONS = {
"DomainAdmins": [
["core", "user", "add_user"],
["core", "user", "change_user"],
["core", "user", "delete_user"],
["admin", "domain", "view_domains"],
["admin", "domain", "view_domain"],
["admin", "mailbox", "add_mailbox"],
["admin", "mailbox", "change_mailbox"],
["admin", "mailbox", "delete_mailbox"],
["admin", "alias", "add_alias"],
["admin", "alias", "change_alias"],
["admin", "alias", "delete_alias"],
["admin", "mailbox", "view_mailboxes"],
["admin", "alias", "view_aliases"],
["admin", "domainalias", "view_domaliases"],
["admin", "domainalias", "add_domainalias"],
["admin", "domainalias", "change_domainalias"],
["admin", "domainalias", "delete_domainalias"]
]
}
@events.observe("GetExtraRoles")
def extra_roles(user, account):
"""Return new roles."""
filters = events.raiseQueryEvent(
"UserCanSetRole", user, "DomainAdmins", account
)
if user.has_perm("admin.add_domain") and \
(not filters or True in filters):
return [("DomainAdmins", _("Domain administrator"))]
return []
@events.observe("GetExtraRolePermissions")
def extra_permissions(rolename):
"""Return extra permissions for :kw:`rolename`."""
return PERMISSIONS.get(rolename, [])
@events.observe("ExtraUprefsRoutes")
def extra_routes():
from django.conf.urls import url
return [
url(r'^user/forward/',
'modoboa.admin.views.user.forward',
name="user_forward"),
]
@events.observe("AdminMenuDisplay")
def admin_menu(target, user):
if target != "top_menu":
return []
entries = []
if user.has_perm("admin.view_domains"):
entries += [
{"name": "domains",
"url": reverse("admin:domain_list"),
"label": _("Domains")}
]
conditions = (
user.has_perm("core.add_user"),
user.has_perm("admin.add_alias")
)
if any(conditions):
entries += [
{"name": "identities",
"url": reverse("admin:identity_list"),
"label": _("Identities")},
]
return entries
@events.observe("UserMenuDisplay")
def user_menu(target, user):
if target != "uprefs_menu":
return []
if not hasattr(user, "mailbox"):
return []
return [
{"name": "forward",
"class": "ajaxnav",
"url": "forward/",
"label": ugettext_lazy("Forward")}
]
@events.observe("RoleChanged")
def grant_access_to_all_objects(user, role):
from django.contrib.contenttypes.models import ContentType
from modoboa.lib.permissions import grant_access_to_objects
from modoboa.core.models import User
if role != "SuperAdmins":
return
grant_access_to_objects(
user, User.objects.all(),
ContentType.objects.get_for_model(User)
)
grant_access_to_objects(
user, Domain.objects.all(),
ContentType.objects.get_for_model(Domain)
)
grant_access_to_objects(
user, DomainAlias.objects.all(),
ContentType.objects.get_for_model(DomainAlias)
)
grant_access_to_objects(
user, Mailbox.objects.all(),
ContentType.objects.get_for_model(Mailbox)
)
grant_access_to_objects(
user, Alias.objects.all(),
ContentType.objects.get_for_model(Alias)
)
@events.observe("AccountExported")
def export_admin_domains(admin):
result = [admin.mailbox.quota] \
if hasattr(admin, "mailbox") else [""]
if admin.group != "DomainAdmins":
return result
return result + [dom.name for dom in Domain.objects.get_for_admin(admin)]
@events.observe("AccountImported")
def import_account_mailbox(user, account, row):
"""Handle extra fields when an account is imported.
Expected fields:
email address; quota; [domain; ...]
:param User user: user importing the account
:param User account: account being imported
:param list rom: list of fields (strings)
"""
account.email = row[0].strip()
if account.email:
account.save()
mailbox, domname = split_mailbox(account.email)
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise BadRequest(
_("Account import failed (%s): domain does not exist"
% account.username)
)
if not user.can_access(domain):
raise PermDeniedException
try:
mb = Mailbox.objects.get(address=mailbox, domain=domain)
except Mailbox.DoesNotExist:
pass
else:
raise Conflict(_("Mailbox %s already exists" % account.email))
if len(row) == 1:
quota = None
else:
try:
quota = int(row[1].strip())
except ValueError:
raise BadRequest(
_("Account import failed (%s): wrong quota value"
% account.username)
)
use_domain_quota = True if not quota else False
mb = Mailbox(address=mailbox, domain=domain,
user=account, use_domain_quota=use_domain_quota)
mb.set_quota(
quota, override_rules=user.has_perm("admin.change_domain")
)
mb.save(creator=user)
if account.group == "DomainAdmins":
for domname in row[2:]:
try:
dom = Domain.objects.get(name=domname.strip())
except Domain.DoesNotExist:
continue
dom.add_admin(account)
@events.observe("AccountAutoCreated")
def account_auto_created(user):
from modoboa.core.models import User
from modoboa.lib.permissions import grant_access_to_object
from .lib import check_if_domain_exists
if parameters.get_admin("AUTO_CREATE_DOMAIN_AND_MAILBOX") == "no":
return
localpart, domname = split_mailbox(user.username)
if user.group != 'SimpleUsers' and domname is None:
return
sadmins = User.objects.filter(is_superuser=True)
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
label = check_if_domain_exists(
domname, [(DomainAlias, _('domain alias'))])
if label is not None:
return
domain = Domain(name=domname, enabled=True, quota=0)
domain.save(creator=sadmins[0])
for su in sadmins[1:]:
grant_access_to_object(su, domain)
try:
mb = Mailbox.objects.get(domain=domain, address=localpart)
except Mailbox.DoesNotExist:
mb = Mailbox(
address=localpart, domain=domain, user=user, use_domain_quota=True
)
mb.set_quota(override_rules=True)
mb.save(creator=sadmins[0])
for su in sadmins[1:]:
grant_access_to_object(su, mb)
@events.observe("UserLogin")
def user_logged_in(request, username, password):
from modoboa.lib.cryptutils import encrypt
if hasattr(request.user, "mailbox"):
request.session["password"] = encrypt(password)
@events.observe("AccountDeleted")
def account_deleted(account, byuser, **kwargs):
"""'AccountDeleted' listener.
When an account is deleted, we also need to remove its mailbox (if
any).
"""
if not hasattr(account, "mailbox"):
return
mb = account.mailbox
if not byuser.can_access(mb):
raise PermDeniedException
keep_mb_dir = kwargs.get("keep_mb_dir", True)
mb.delete(keepdir=keep_mb_dir)
+110
View File
@@ -0,0 +1,110 @@
"""
Fixtures factories.
"""
import factory
from modoboa.core.factories import PermissionFactory, UserFactory
from . import models
class DomainFactory(PermissionFactory):
"""Factory to create Domains."""
class Meta:
model = models.Domain
django_get_or_create = ("name", )
type = "domain"
quota = 10
enabled = True
class DomainAliasFactory(PermissionFactory):
"""Factory to create DomainAlias objects."""
class Meta:
model = models.DomainAlias
django_get_or_create = ("name", )
target = factory.SubFactory(DomainFactory)
enabled = True
class MailboxFactory(PermissionFactory):
"""A factory to create Mailbox instances."""
class Meta:
model = models.Mailbox
django_get_or_create = ("address", "domain")
domain = factory.SubFactory(DomainFactory)
user = factory.SubFactory(UserFactory)
quota = 10
enabled = True
class AliasRecipientFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.AliasRecipient
class AliasFactory(PermissionFactory):
class Meta:
model = models.Alias
enabled = True
def populate_database():
"""Create test data.
2 domains, 1 domain admin, 2 users
"""
dom = DomainFactory(name="test.com")
admin = UserFactory(
username="admin@test.com", groups=('DomainAdmins', ),
password='{PLAIN}toto'
)
MailboxFactory(address='admin', domain=dom, user=admin)
account = UserFactory.create(
username="user@test.com", groups=('SimpleUsers',),
)
MailboxFactory.create(address='user', domain=dom, user=account)
al = AliasFactory.create(
address='forward@test.com', domain=dom
)
AliasRecipientFactory.create(
address='user@external.com', alias=al)
al = AliasFactory.create(
address='alias@test.com', domain=dom
)
mb = account.mailbox
AliasRecipientFactory.create(
address=mb.full_address, alias=al, r_mailbox=mb)
al = AliasFactory.create(
address='postmaster@test.com', domain=dom
)
for address in ['toto@titi.com', 'test@truc.fr']:
AliasRecipientFactory.create(address=address, alias=al)
dom.add_admin(admin)
dom2 = DomainFactory.create(name='test2.com', quota=0)
admin = UserFactory.create(
username='admin@test2.com', groups=('DomainAdmins',),
password='{PLAIN}toto'
)
MailboxFactory.create(address='admin', domain=dom2, user=admin)
u = UserFactory.create(
username='user@test2.com', groups=('SimpleUsers',)
)
MailboxFactory.create(address='user', domain=dom2, user=u)
dom2.add_admin(admin)
+19
View File
@@ -0,0 +1,19 @@
from .domain import (
DomainFormGeneral, DomainFormOptions, DomainForm, DomainWizard
)
from .account import (
AccountFormGeneral, AccountFormMail, AccountPermissionsForm,
AccountForm, AccountWizard
)
from .alias import AliasForm
from .forward import ForwardForm
from .import_ import ImportDataForm, ImportIdentitiesForm
from .export import ExportDataForm, ExportDomainsForm, ExportIdentitiesForm
__all__ = [
'DomainFormGeneral', 'DomainFormOptions', 'DomainForm',
'AccountFormGeneral', 'AccountFormMail', 'AccountPermissionsForm',
'AccountForm', 'AliasForm', 'ImportDataForm', 'ImportIdentitiesForm',
'ExportDataForm', 'ExportDomainsForm', 'ExportIdentitiesForm',
'ForwardForm', 'DomainWizard', 'AccountWizard'
]
+512
View File
@@ -0,0 +1,512 @@
"""Forms related to accounts management."""
from collections import OrderedDict
from django import forms
from django.core.urlresolvers import reverse
from django.http import QueryDict
from django.utils.translation import ugettext as _, ugettext_lazy
from passwords.fields import PasswordField
from modoboa.core.models import User
from modoboa.lib import events, parameters
from modoboa.lib.email_utils import split_mailbox
from modoboa.lib.exceptions import PermDeniedException, Conflict, NotFound
from modoboa.lib.form_utils import (
DomainNameField, DynamicForm, TabForms, WizardForm, WizardStep
)
from modoboa.lib.permissions import get_account_roles
from modoboa.lib.web_utils import render_to_json_response
from ..models import Domain, Mailbox, Alias
class AccountFormGeneral(forms.ModelForm):
"""General account form."""
username = forms.CharField(
label=ugettext_lazy("Username"),
help_text=ugettext_lazy(
"The user's name. Must be a valid e-mail address for simple users "
"or administrators with a mailbox."
)
)
role = forms.ChoiceField(
label=ugettext_lazy("Role"),
choices=[('', ugettext_lazy("Choose"))],
help_text=ugettext_lazy("What level of permission this user will have")
)
password1 = PasswordField(
label=ugettext_lazy("Password"), widget=forms.widgets.PasswordInput
)
password2 = PasswordField(
label=ugettext_lazy("Confirmation"),
widget=forms.widgets.PasswordInput,
help_text=ugettext_lazy(
"Enter the same password as above, for verification."
)
)
class Meta:
model = User
fields = (
"username", "first_name", "last_name", "role", "is_active",
"master_user"
)
def __init__(self, user, *args, **kwargs):
super(AccountFormGeneral, self).__init__(*args, **kwargs)
self.fields = OrderedDict(
(key, self.fields[key]) for key in
['role', 'username', 'first_name', 'last_name', 'password1',
'password2', 'master_user', 'is_active']
)
self.fields["is_active"].label = _("Enabled")
self.user = user
if user.group == "DomainAdmins":
self.fields["role"] = forms.CharField(
label="",
widget=forms.HiddenInput(attrs={"class": "form-control"}),
required=False
)
else:
self.fields["role"].choices = [('', ugettext_lazy("Choose"))]
self.fields["role"].choices += \
get_account_roles(user, kwargs['instance']) \
if 'instance' in kwargs else get_account_roles(user)
if not user.is_superuser:
del self.fields["master_user"]
if "instance" in kwargs:
account = kwargs["instance"]
domain_disabled = (
hasattr(account, "mailbox") and
not account.mailbox.domain.enabled
)
if domain_disabled:
self.fields["is_active"].widget.attrs['disabled'] = "disabled"
if args:
if args[0].get("password1", "") == "" \
and args[0].get("password2", "") == "":
self.fields["password1"].required = False
self.fields["password2"].required = False
if domain_disabled:
del self.fields["is_active"]
self.fields["role"].initial = account.group
if not account.is_local \
and parameters.get_admin(
"LDAP_AUTH_METHOD", app="core") == "directbind":
del self.fields["password1"]
del self.fields["password2"]
def domain_is_disabled(self):
"""Little shortcut to get the domain's state.
We need this information inside a template and the form is the
only object available...
"""
if not hasattr(self.instance, "mailbox"):
return False
return not self.instance.mailbox.domain.enabled
def clean_role(self):
if self.user.group == "DomainAdmins":
if self.instance == self.user:
return "DomainAdmins"
return "SimpleUsers"
return self.cleaned_data["role"]
def clean_username(self):
from django.core.validators import validate_email
if "role" not in self.cleaned_data:
return self.cleaned_data["username"]
if self.cleaned_data["role"] != "SimpleUsers":
return self.cleaned_data["username"]
uname = self.cleaned_data["username"].lower()
validate_email(uname)
return uname
def clean_master_user(self):
"""Check role before allowing this mode."""
conditions = (
self.cleaned_data["master_user"],
self.cleaned_data["role"] != "SuperAdmins"
)
if all(conditions):
raise forms.ValidationError(
_("Only super administrators are allowed for this mode")
)
return self.cleaned_data["master_user"]
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(
_("The two password fields didn't match."))
return password2
def save(self, commit=True):
account = super(AccountFormGeneral, self).save(commit=False)
if self.user == account and not self.cleaned_data["is_active"]:
raise PermDeniedException(_("You can't disable your own account"))
if commit:
if "password1" in self.cleaned_data \
and self.cleaned_data["password1"] != "":
account.set_password(self.cleaned_data["password1"])
account.save()
account.set_role(self.cleaned_data["role"])
return account
class AccountFormMail(forms.Form, DynamicForm):
"""Form to handle mail part."""
email = forms.EmailField(label=ugettext_lazy("E-mail"), required=False)
quota = forms.IntegerField(
label=ugettext_lazy("Quota"),
required=False,
help_text=_("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)."),
widget=forms.widgets.TextInput(attrs={"class": "form-control"})
)
quota_act = forms.BooleanField(required=False)
aliases = forms.EmailField(
label=ugettext_lazy("Alias(es)"),
required=False,
help_text=ugettext_lazy(
"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)."
)
)
def __init__(self, *args, **kwargs):
if "instance" in kwargs:
self.mb = kwargs["instance"]
del kwargs["instance"]
else:
self.mb = None
super(AccountFormMail, self).__init__(*args, **kwargs)
self.field_widths = {
"quota": 3
}
self.extra_fields = []
result = events.raiseQueryEvent('ExtraFormFields', 'mailform', self.mb)
for fname, field in result:
self.fields[fname] = field
self.extra_fields.append(fname)
if self.mb is not None:
self.fields["email"].required = True
cpt = 1
qset = self.mb.aliasrecipient_set.select_related("alias")
for ralias in qset:
if ralias.alias.recipients_count >= 2:
continue
name = "aliases_%d" % cpt
self._create_field(forms.EmailField, name, ralias.alias.address)
cpt += 1
self.fields["email"].initial = self.mb.full_address
self.fields["quota_act"].initial = self.mb.use_domain_quota
if not self.mb.use_domain_quota and self.mb.quota:
self.fields["quota"].initial = self.mb.quota
else:
self.fields["quota_act"].initial = True
if len(args) and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "aliases", forms.EmailField)
def clean_email(self):
"""Ensure lower case emails"""
return self.cleaned_data["email"].lower()
def clean(self):
"""Custom fields validation.
Check if quota is >= 0 only when the domain value is not used.
"""
super(AccountFormMail, self).clean()
if not self.cleaned_data["quota_act"] \
and self.cleaned_data['quota'] is not None:
if self.cleaned_data["quota"] < 0:
self.add_error("quota", _("Must be a positive integer"))
return self.cleaned_data
def create_mailbox(self, user, account):
"""Create a mailbox associated to :kw:`account`."""
locpart, domname = split_mailbox(self.cleaned_data["email"])
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise NotFound(_("Domain does not exist"))
if not user.can_access(domain):
raise PermDeniedException
try:
Mailbox.objects.get(address=locpart, domain=domain)
except Mailbox.DoesNotExist:
pass
else:
raise Conflict(
_("Mailbox %s already exists" % self.cleaned_data["email"])
)
events.raiseEvent("CanCreate", user, "mailboxes")
self.mb = Mailbox(address=locpart, domain=domain, user=account,
use_domain_quota=self.cleaned_data["quota_act"])
self.mb.set_quota(self.cleaned_data["quota"],
user.has_perm("admin.add_domain"))
self.mb.save(creator=user)
def update_mailbox(self, user, account):
newaddress = None
if self.cleaned_data["email"] != self.mb.full_address:
newaddress = self.cleaned_data["email"]
elif (account.group == "SimpleUsers" and
account.username != self.mb.full_address):
newaddress = account.username
if newaddress is not None:
self.mb.old_full_address = self.mb.full_address
local_part, domname = split_mailbox(newaddress)
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise NotFound(_("Domain does not exist"))
if not user.can_access(domain):
raise PermDeniedException
self.mb.rename(local_part, domain)
self.mb.use_domain_quota = self.cleaned_data["quota_act"]
override_rules = True \
if not self.mb.quota or user.has_perm("admin.add_domain") \
else False
self.mb.set_quota(self.cleaned_data["quota"], override_rules)
self.mb.save()
events.raiseEvent('MailboxModified', self.mb)
def _update_aliases(self, user, account):
"""Update mailbox aliases."""
aliases = []
for name, value in self.cleaned_data.iteritems():
if not name.startswith("aliases"):
continue
if value == "":
continue
aliases.append(value.lower())
qset = self.mb.aliasrecipient_set.select_related("alias").filter(
alias__internal=False)
for ralias in qset:
if ralias.alias.address not in aliases:
alias = ralias.alias
ralias.delete()
if alias.recipients_count >= 2:
continue
alias.delete()
else:
aliases.remove(ralias.alias.address)
if not aliases:
return
events.raiseEvent(
"CanCreate", user, "mailbox_aliases", len(aliases)
)
for alias in aliases:
if self.mb.aliasrecipient_set.select_related("alias").filter(
alias__address=alias).exists():
continue
local_part, domname = split_mailbox(alias)
al = Alias(address=alias, enabled=account.is_active)
al.domain = Domain.objects.get(name=domname)
al.save()
al.set_recipients([self.mb.full_address])
al.post_create(user)
def save(self, user, account):
"""Save or update account mailbox."""
if self.cleaned_data["email"] == "":
return None
if self.cleaned_data["quota_act"]:
self.cleaned_data["quota"] = None
if not hasattr(self, "mb") or self.mb is None:
self.create_mailbox(user, account)
else:
self.update_mailbox(user, account)
events.raiseEvent(
'SaveExtraFormFields', 'mailform', self.mb, self.cleaned_data
)
account.email = self.cleaned_data["email"]
account.save()
self._update_aliases(user, account)
return self.mb
class AccountPermissionsForm(forms.Form, DynamicForm):
domains = DomainNameField(
label=ugettext_lazy("Domain(s)"),
required=False,
help_text=ugettext_lazy("Domain(s) that user administrates")
)
def __init__(self, *args, **kwargs):
if "instance" in kwargs:
self.account = kwargs["instance"]
del kwargs["instance"]
super(AccountPermissionsForm, self).__init__(*args, **kwargs)
if not hasattr(self, "account") or self.account is None:
return
for pos, dom in enumerate(Domain.objects.get_for_admin(self.account)):
name = "domains_%d" % (pos + 1)
self._create_field(DomainNameField, name, dom.name)
if len(args) and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "domains", DomainNameField)
def save(self):
current_domains = [
dom.name for dom in Domain.objects.get_for_admin(self.account)
]
for name, value in self.cleaned_data.items():
if not name.startswith("domains"):
continue
if value in ["", None]:
continue
if value not in current_domains:
domain = Domain.objects.get(name=value)
domain.add_admin(self.account)
for domain in Domain.objects.get_for_admin(self.account):
if not filter(lambda name: self.cleaned_data[name] == domain.name,
self.cleaned_data.keys()):
domain.remove_admin(self.account)
class AccountForm(TabForms):
"""Account edition form."""
def __init__(self, request, *args, **kwargs):
self.user = request.user
self.forms = [
dict(id="general", title=_("General"),
formtpl="admin/account_general_form.html",
cls=AccountFormGeneral,
new_args=[self.user], mandatory=True),
dict(id="mail",
title=_("Mail"), formtpl="admin/mailform.html",
cls=AccountFormMail),
dict(
id="perms", title=_("Permissions"),
formtpl="admin/permsform.html",
cls=AccountPermissionsForm
)
]
cbargs = [self.user]
if "instances" in kwargs:
cbargs += [kwargs["instances"]["general"]]
self.forms += events.raiseQueryEvent("ExtraAccountForm", *cbargs)
super(AccountForm, self).__init__(request, *args, **kwargs)
def extra_context(self, context):
account = self.instances["general"]
context.update({
'title': account.username,
'formid': 'accountform',
'action': reverse("admin:account_change",
args=[account.id]),
})
def check_perms(self, account):
if account.is_superuser:
return False
return self.user.has_perm("admin.add_domain") \
and account.has_perm("core.add_user")
def _before_is_valid(self, form):
if form["id"] == "general":
return True
if hasattr(self, "check_%s" % form["id"]):
if not getattr(self, "check_%s" % form["id"])(self.account):
return False
return True
extra_forms = events.raiseQueryEvent(
"CheckExtraAccountForm", self.account, form)
if False in extra_forms:
return False
return True
def is_valid(self):
"""Two steps validation.
"""
self.instances["general"].oldgroup = self.instances["general"].group
if super(AccountForm, self).is_valid(mandatory_only=True):
self.account = self.forms[0]["instance"].save()
return super(AccountForm, self).is_valid(optional_only=True)
return False
def save(self):
"""Custom save method
As forms interact with each other, it is simpler to make
custom code to save them.
"""
events.raiseEvent(
"AccountModified", self.instances["general"], self.account
)
self.forms[1]["instance"].save(self.user, self.account)
if len(self.forms) <= 2:
return
for f in self.forms[2:]:
f["instance"].save()
def done(self):
return render_to_json_response(_("Account updated"))
class AccountWizard(WizardForm):
"""Account creation wizard."""
def __init__(self, request):
super(AccountWizard, self).__init__(request)
self.add_step(
WizardStep(
"general", AccountFormGeneral, _("General"),
new_args=[request.user]
)
)
self.add_step(
WizardStep(
"mail", AccountFormMail, _("Mail"),
"admin/mailform.html"
)
)
def extra_context(self, context):
context.update({
'title': _("New account"),
'action': reverse("admin:account_add"),
'formid': 'newaccount_form'
})
def done(self):
from modoboa.lib.web_utils import render_to_json_response
account = self.first_step.form.save()
account.post_create(self.request.user)
mailform = self.steps[1].form
mailform.save(self.request.user, account)
return render_to_json_response(_("Account created"))
+100
View File
@@ -0,0 +1,100 @@
"""Forms related to aliases management."""
from collections import OrderedDict
from django import forms
from django.http import QueryDict
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib.email_utils import split_mailbox
from modoboa.lib.form_utils import (
DynamicForm
)
from ..models import Domain, Alias
from .base import EmailField
class AliasForm(forms.ModelForm, DynamicForm):
"""A form to create/modify an alias."""
address = EmailField(
label=ugettext_lazy("Email address"),
help_text=ugettext_lazy(
"The alias address. To create a catchall alias, just enter the "
"domain name (@domain.tld)."
),
widget=forms.TextInput(attrs={"class": "form-control"})
)
recipients = EmailField(
label=ugettext_lazy("Recipients"), required=False,
help_text=ugettext_lazy(
"Addresses this alias will point to. Indicate only one address "
"per input, press ENTER to add a new input."
),
widget=forms.TextInput(attrs={"class": "form-control"})
)
class Meta:
model = Alias
fields = ("address", "enabled", )
def __init__(self, user, *args, **kwargs):
self.user = user
super(AliasForm, self).__init__(*args, **kwargs)
self.fields = OrderedDict(
(key, self.fields[key]) for key in
['address', 'recipients', 'enabled']
)
if len(args) and isinstance(args[0], QueryDict):
if "instance" in kwargs:
if not kwargs["instance"].domain.enabled:
del self.fields["enabled"]
self._load_from_qdict(args[0], "recipients", forms.EmailField)
elif "instance" in kwargs:
alias = kwargs["instance"]
if not alias.domain.enabled:
self.fields["enabled"].widget.attrs["disabled"] = "disabled"
cpt = 1
for rcpt in alias.aliasrecipient_set.filter(alias__internal=False):
name = "recipients_%d" % cpt
self._create_field(forms.EmailField, name, rcpt.address, 2)
cpt += 1
def clean_address(self):
"""Check if address points to a local domain."""
localpart, domname = split_mailbox(self.cleaned_data["address"])
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise forms.ValidationError(_("Domain does not exist"))
if not self.user.can_access(domain):
raise forms.ValidationError(
_("You don't have access to this domain")
)
return self.cleaned_data["address"].lower()
def clean(self):
"""Check it there is at least one recipient."""
super(AliasForm, self).clean()
for field, value in self.cleaned_data.items():
if field.startswith("recipients") and value:
return self.cleaned_data
self.add_error("recipients", _("No recipient defined"))
return self.cleaned_data
def save(self, commit=True):
"""Custom save method."""
alias = super(AliasForm, self).save(commit=False)
local_part, domname = split_mailbox(self.cleaned_data["address"])
alias.domain = Domain.objects.get(name=domname)
if commit:
alias.save()
address_list = [
value for field, value in self.cleaned_data.items()
if field.startswith("recipients") and value
]
alias.set_recipients(address_list)
return alias
+26
View File
@@ -0,0 +1,26 @@
"""Custom form tools."""
import re
from django.core.validators import EmailValidator
from django.forms.fields import EmailField as OriginalEmailField
class CustomEmailValidator(EmailValidator):
"""Allow empty user_part."""
user_regex = re.compile(
r"(^$"
r"|^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string
re.IGNORECASE)
validate_email = CustomEmailValidator()
class EmailField(OriginalEmailField):
"""A field that accepts email addresses."""
default_validators = [validate_email]
+405
View File
@@ -0,0 +1,405 @@
"""Forms related to domains management."""
from django import forms
from django.core.urlresolvers import reverse
from django.http import QueryDict
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.core.models import User
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import ModoboaException, Conflict
from modoboa.lib.form_utils import (
DomainNameField, YesNoField, WizardForm, WizardStep, DynamicForm, TabForms
)
from modoboa.lib.web_utils import render_to_json_response
from ..lib import check_if_domain_exists
from ..models import (
Domain, DomainAlias, Mailbox, Alias, Quota
)
DOMAIN_TYPES = [
("domain", _("Domain")),
]
DOMAIN_TYPES += events.raiseQueryEvent("ExtraDomainTypes")
class DomainFormGeneral(forms.ModelForm, DynamicForm):
"""A form to create/edit a domain."""
type = forms.ChoiceField(
label=ugettext_lazy("Type"), choices=DOMAIN_TYPES
)
quota = forms.IntegerField(
label=ugettext_lazy("Quota"),
required=False,
help_text=ugettext_lazy(
"Default quota in MB applied to mailboxes. Leave empty to use the "
"default value."
)
)
aliases = DomainNameField(
label=ugettext_lazy("Alias(es)"),
required=False,
help_text=ugettext_lazy(
"Alias(es) of this domain. Indicate only one name per input, "
"press ENTER to add a new input."
)
)
class Meta:
model = Domain
fields = ("name", "type", "quota", "aliases", "enabled")
def __init__(self, *args, **kwargs):
self.oldname = None
if "instance" in kwargs:
self.oldname = kwargs["instance"].name
super(DomainFormGeneral, self).__init__(*args, **kwargs)
self.field_widths = {
"quota": 3
}
if len(args) and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "aliases", DomainNameField)
elif "instance" in kwargs:
d = kwargs["instance"]
for pos, dalias in enumerate(d.domainalias_set.all()):
name = "aliases_%d" % (pos + 1)
self._create_field(forms.CharField, name, dalias.name, 3)
def clean_quota(self):
if self.cleaned_data['quota'] is None:
return int(parameters.get_admin('DEFAULT_DOMAIN_QUOTA'))
if self.cleaned_data['quota'] < 0:
raise forms.ValidationError(_("Must be a positive integer"))
return self.cleaned_data['quota']
def clean(self):
"""Custom fields validation.
We want to prevent duplicate names between domains and domain
aliases. Extensions have the possibility to declare other
objects (see *CheckDomainName* event).
The validation way is not very smart...
"""
cleaned_data = super(DomainFormGeneral, self).clean()
if self._errors:
return cleaned_data
name = cleaned_data["name"]
label = check_if_domain_exists(
name, [(DomainAlias, _('domain alias'))])
if label is not None:
self.add_error(
"name", _("A %s with this name already exists")
% unicode(label)
)
for k in cleaned_data.keys():
if not k.startswith("aliases"):
continue
if cleaned_data[k] == "":
del cleaned_data[k]
continue
if cleaned_data[k] == name:
self._errors[k] = self.error_class(
[_("A %s with this name already exists") % _("domain")]
)
del cleaned_data[k]
continue
label = check_if_domain_exists(
cleaned_data[k], [(Domain, _("domain"))])
if label is not None:
self.add_error(
k, _("A %s with this name already exists")
% unicode(label)
)
return cleaned_data
def update_mailbox_quotas(self, domain):
"""Update all quota records associated to this domain.
This method must be called only when a domain gets renamed. As
the primary key used for a quota is an email address, rename a
domain will change all associated email addresses, so it will
change the primary keys used for quotas. The consequence is we
can't issue regular UPDATE queries using the .save() method of
a Quota instance (it will trigger an INSERT as the primary key
has changed).
So, we use this ugly hack to bypass this behaviour. It is not
perfomant at all as it will generate one query per quota
record to update.
"""
for q in Quota.objects.filter(username__contains="@%s" % self.oldname):
username = q.username.replace(
'@%s' % self.oldname, '@%s' % domain.name)
Quota.objects.create(
username=username, bytes=q.bytes, messages=q.messages)
q.delete()
def save(self, user, commit=True, domalias_post_create=False):
"""Custom save method
Updating a domain may have consequences on other objects
(domain alias, mailbox, quota). The most tricky part concerns
quotas update.
"""
d = super(DomainFormGeneral, self).save(commit=False)
if commit:
old_mail_homes = None
if self.oldname is not None and d.name != self.oldname:
d.name = self.oldname
old_mail_homes = \
dict((mb.id, mb.mail_home) for mb in d.mailbox_set.all())
d.name = self.cleaned_data['name']
d.save()
Mailbox.objects.filter(domain=d, use_domain_quota=True) \
.update(quota=d.quota)
aliases = []
for k, v in self.cleaned_data.iteritems():
if not k.startswith("aliases"):
continue
if v in ["", None]:
continue
aliases.append(v)
for dalias in d.domainalias_set.all():
if dalias.name not in aliases:
dalias.delete()
else:
aliases.remove(dalias.name)
if aliases:
events.raiseEvent(
"CanCreate", user, "domain_aliases", len(aliases))
for alias in aliases:
try:
d.domainalias_set.get(name=alias)
except DomainAlias.DoesNotExist:
pass
else:
continue
al = DomainAlias(name=alias, target=d, enabled=d.enabled)
al.save(creator=user) if domalias_post_create else al.save()
if old_mail_homes is not None:
self.update_mailbox_quotas(d)
for mb in d.mailbox_set.all():
mb.rename_dir(old_mail_homes[mb.id])
return d
class DomainFormOptions(forms.Form):
"""A form containing options for domain creation."""
create_dom_admin = YesNoField(
label=ugettext_lazy("Create a domain administrator"),
initial="no",
help_text=ugettext_lazy(
"Automatically create an administrator for this domain"
)
)
dom_admin_username = forms.CharField(
label=ugettext_lazy("Name"),
initial="admin",
help_text=ugettext_lazy(
"The administrator's name. Don't include the domain's name here, "
"it will be automatically appended."
),
required=False
)
create_aliases = YesNoField(
label=ugettext_lazy("Create aliases"),
initial="yes",
help_text=ugettext_lazy(
"Automatically create standard aliases for this domain"
),
required=False
)
def __init__(self, user, *args, **kwargs):
super(DomainFormOptions, self).__init__(*args, **kwargs)
result = events.raiseQueryEvent('UserCanSetRole', user, 'DomainAdmins')
if False in result:
self.fields = {}
return
if args:
if args[0].get("create_dom_admin", "no") == "yes":
self.fields["dom_admin_username"].required = True
self.fields["create_aliases"].required = True
def clean_dom_admin_username(self):
"""Ensure admin username is an email address."""
if '@' in self.cleaned_data["dom_admin_username"]:
raise forms.ValidationError(_("Invalid format"))
return self.cleaned_data["dom_admin_username"]
def save(self, *args, **kwargs):
if not self.fields:
return
if self.cleaned_data["create_dom_admin"] == "no":
return
user = kwargs.pop("user")
domain = kwargs.pop("domain")
username = "%s@%s" % (
self.cleaned_data["dom_admin_username"], domain.name)
try:
da = User.objects.get(username=username)
except User.DoesNotExist:
pass
else:
raise Conflict(_("User '%s' already exists" % username))
events.raiseEvent("CanCreate", user, "mailboxes")
da = User(username=username, email=username, is_active=True)
da.set_password("password")
da.save()
da.set_role("DomainAdmins")
da.post_create(user)
mb = Mailbox(
address=self.cleaned_data["dom_admin_username"], domain=domain,
user=da, use_domain_quota=True
)
mb.set_quota(
override_rules=user.has_perm("admin.change_domain"))
mb.save(creator=user)
if domain.type == "domain" and \
self.cleaned_data["create_aliases"] == "yes":
events.raiseEvent("CanCreate", user, "mailbox_aliases")
alias = Alias(
address="postmaster@{}".format(domain.name),
domain=domain, enabled=True
)
alias.save()
alias.set_recipients([mb.full_address])
alias.post_create(user)
domain.add_admin(da)
class DomainForm(TabForms):
"""Domain edition form."""
template_name = "admin/editdomainform.html"
def __init__(self, request, *args, **kwargs):
self.user = request.user
self.forms = []
self.domain = None
if self.user.has_perm("admin.change_domain"):
self.forms.append({
"id": "general",
"title": _("General"),
"formtpl": "admin/domain_general_form.html",
"cls": DomainFormGeneral,
"mandatory": True
})
cbargs = [self.user]
if "instances" in kwargs:
self.domain = kwargs["instances"]["general"]
cbargs += [self.domain]
self.forms += events.raiseQueryEvent("ExtraDomainForm", *cbargs)
if not self.forms:
self.active_id = "admins"
super(DomainForm, self).__init__(request, *args, **kwargs)
def extra_context(self, context):
domadmins = [u for u in self.domain.admins
if self.request.user.can_access(u) and not u.is_superuser]
if not self.request.user.is_superuser:
domadmins = [u for u in domadmins if u.group == "DomainAdmins"]
context.update({
"title": self.domain.name,
"action": reverse(
"admin:domain_change", args=[self.domain.pk]),
"formid": "domform",
"domain": self.domain,
"domadmins": domadmins
})
def is_valid(self):
"""Custom validation.
We just save the current name before it is potentially
modified.
"""
if "general" in self.instances:
self.instances["general"].oldname = self.instances["general"].name
return super(DomainForm, self).is_valid()
def save(self):
"""Custom save method.
As forms interact with each other, it is easier to make custom
code to save them.
"""
if not self.forms:
return
first_form = self.forms[0]["instance"]
options = {}
if isinstance(first_form, DomainFormGeneral):
options["domalias_post_create"] = True
first_form.save(self.request.user, **options)
for f in self.forms[1:]:
f["instance"].save(self.request.user)
def done(self):
if "general" in self.instances:
events.raiseEvent("DomainModified", self.instances["general"])
return render_to_json_response(_("Domain modified"))
class DomainWizard(WizardForm):
"""Domain creation wizard."""
def __init__(self, request):
super(DomainWizard, self).__init__(request)
self.add_step(
WizardStep(
"general", DomainFormGeneral, _("General"),
"admin/domain_general_form.html"
)
)
steps = events.raiseQueryEvent("ExtraDomainWizardSteps")
for step in steps:
self.add_step(step)
self.add_step(
WizardStep(
"options", DomainFormOptions, _("Options"),
"admin/domain_options_form.html",
[self.request.user]
)
)
def extra_context(self, context):
context.update({
"title": _("New domain"),
"action": reverse("admin:domain_add"),
"formid": "domform"
})
def done(self):
genform = self.first_step.form
domain = genform.save(self.request.user)
domain.post_create(self.request.user)
for step in self.steps[1:]:
if not step.check_access(self):
continue
try:
step.form.save(user=self.request.user, domain=domain)
except ModoboaException:
from django.db import transaction
transaction.rollback()
raise
return render_to_json_response(_("Domain created"))
+45
View File
@@ -0,0 +1,45 @@
from django import forms
from django.utils.translation import ugettext_lazy
class ExportDataForm(forms.Form):
filename = forms.CharField(
label=ugettext_lazy("File name"),
max_length=100,
required=False,
widget=forms.TextInput(attrs={"class": "form-control"})
)
sepchar = forms.CharField(
label=ugettext_lazy("Separator"),
max_length=1,
required=False,
widget=forms.TextInput(attrs={"class": "form-control"})
)
def __init__(self, *args, **kwargs):
super(ExportDataForm, self).__init__(*args, **kwargs)
self.fields["sepchar"].widget.attrs = {"class": "col-md-1 form-control"}
def clean_sepchar(self):
if self.cleaned_data["sepchar"] == "":
return ";"
return str(self.cleaned_data["sepchar"])
def clean_filename(self):
if self.cleaned_data["filename"] == "":
return self.fields["filename"].initial
return str(self.cleaned_data["filename"])
class ExportDomainsForm(ExportDataForm):
def __init__(self, *args, **kwargs):
super(ExportDomainsForm, self).__init__(*args, **kwargs)
self.fields["filename"].initial = "modoboa-domains.csv"
class ExportIdentitiesForm(ExportDataForm):
def __init__(self, *args, **kwargs):
super(ExportIdentitiesForm, self).__init__(*args, **kwargs)
self.fields["filename"].initial = "modoboa-identities.csv"
+46
View File
@@ -0,0 +1,46 @@
"""Forms related to forwards management."""
from django import forms
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib.email_utils import split_mailbox
from modoboa.lib.exceptions import BadRequest, PermDeniedException
from ..models import Domain
class ForwardForm(forms.Form):
dest = forms.CharField(
label=ugettext_lazy("Recipient(s)"),
widget=forms.Textarea(attrs={"class": "form-control"}),
required=False,
help_text=ugettext_lazy(
"Indicate one or more recipients separated by a ','")
)
keepcopies = forms.BooleanField(
label=ugettext_lazy("Keep local copies"),
required=False,
help_text=ugettext_lazy(
"Forward messages and store copies into your local mailbox")
)
def get_recipients(self):
recipients = []
rawdata = self.cleaned_data["dest"].strip()
if not rawdata:
return
for rcpt in rawdata.split(","):
local_part, domname = split_mailbox(rcpt)
if not local_part or not domname:
raise BadRequest("Invalid mailbox syntax for %s" % rcpt)
try:
Domain.objects.get(name=domname)
except Domain.DoesNotExist:
recipients += [rcpt]
else:
raise PermDeniedException(
_("You can't define a forward to a local destination. "
"Please ask your administrator to create an alias "
"instead.")
)
return recipients
+43
View File
@@ -0,0 +1,43 @@
"""Forms related to import operations."""
from django import forms
from django.utils.translation import ugettext_lazy
class ImportDataForm(forms.Form):
"""Base form to import objects."""
sourcefile = forms.FileField(label=ugettext_lazy("Select a file"))
sepchar = forms.CharField(
label=ugettext_lazy("Separator"),
max_length=1,
required=False,
widget=forms.TextInput(attrs={"class": "form-control"})
)
continue_if_exists = forms.BooleanField(
label=ugettext_lazy("Continue on error"), required=False,
help_text=ugettext_lazy("Don't treat duplicated objects as error")
)
def __init__(self, *args, **kwargs):
super(ImportDataForm, self).__init__(*args, **kwargs)
self.fields["sepchar"].widget.attrs = {"class": "col-md-1 form-control"}
def clean_sepchar(self):
if self.cleaned_data["sepchar"] == "":
return ";"
return str(self.cleaned_data["sepchar"])
class ImportIdentitiesForm(ImportDataForm):
"""A form to import identities."""
crypt_password = forms.BooleanField(
label=ugettext_lazy("Crypt passwords"), required=False,
help_text=ugettext_lazy(
"Check this option if passwords contained in your file "
"are not crypted"
)
)
+76
View File
@@ -0,0 +1,76 @@
"""Django signal handlers for admin."""
from django.db.models import signals
from django.dispatch import receiver
from . import models
@receiver(signals.post_save, sender=models.DomainAlias)
def create_alias_for_domainalias(sender, instance, **kwargs):
"""Create a dedicated alias for domain alias."""
if not kwargs.get("created"):
return
alias = models.Alias.objects.create(
address="@{}".format(instance.name), enabled=True, internal=True)
models.AliasRecipient.objects.create(
address="@{}".format(instance.target.name), alias=alias)
@receiver(signals.post_delete, sender=models.DomainAlias)
def remove_alias_for_domainalias(sender, instance, **kwargs):
"""Remove the alias associated to domain alias."""
models.Alias.objects.filter(
address="@{}".format(instance.name)).delete()
@receiver(signals.post_save, sender=models.Mailbox)
def manage_alias_for_mailbox(sender, instance, **kwargs):
"""Create or update a "self alias" for mailbox (catchall)."""
if kwargs.get("created"):
alias, created = models.Alias.objects.get_or_create(
address=instance.full_address, domain=instance.domain,
internal=True)
models.AliasRecipient.objects.create(
address=instance.full_address, alias=alias, r_mailbox=instance)
return
old_address = getattr(instance, "old_full_address", None)
if old_address is None or old_address == instance.full_address:
return
alr = models.AliasRecipient.objects.select_related("alias").get(
alias__address=old_address, address=old_address,
r_mailbox=instance, alias__internal=True)
alr.address = instance.full_address
alr.save()
alr.alias.address = instance.full_address
alr.alias.save()
@receiver(signals.pre_delete, sender=models.Mailbox)
def mailbox_deleted_handler(sender, **kwargs):
"""``Mailbox`` pre_delete signal receiver
In order to properly handle deletions (ie. we don't want to leave
orphan records into the db), we define this custom receiver.
It manually removes the mailbox from the aliases it is linked to
and then remove all empty aliases.
"""
from modoboa.lib import events
from modoboa.lib.permissions import ungrant_access_to_object
mb = kwargs['instance']
events.raiseEvent("MailboxDeleted", mb)
ungrant_access_to_object(mb)
for ralias in mb.aliasrecipient_set.select_related("alias"):
alias = ralias.alias
ralias.delete()
if not alias.aliasrecipient_set.exists():
alias.delete()
@receiver(signals.post_delete, sender=models.Mailbox)
def remove_alias_for_mailbox(sender, instance, **kwargs):
"""Remove "self alias" for this mailbox."""
models.Alias.objects.filter(
address=instance.full_address).delete()
+155
View File
@@ -0,0 +1,155 @@
# coding: utf-8
"""Internal library for admin."""
from functools import wraps
from itertools import chain
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.translation import ugettext as _
from modoboa.core.models import User
from modoboa.lib import events
from modoboa.lib.exceptions import PermDeniedException
from .models import Domain, DomainAlias, Alias
def needs_mailbox():
"""Check if the current user owns at least one mailbox
Some applications (the webmail for example) need a mailbox to
work.
"""
def decorator(f):
@wraps(f)
def wrapped_f(request, *args, **kwargs):
if hasattr(request.user, "mailbox"):
return f(request, *args, **kwargs)
raise PermDeniedException(_("A mailbox is required"))
return wrapped_f
return decorator
def get_identities(user, searchquery=None, idtfilter=None, grpfilter=None):
"""Return all the identities owned by a user.
:param user: the desired user
:param str searchquery: search pattern
:param list idtfilter: identity type filters
:param list grpfilter: group names filters
:return: a queryset
"""
accounts = []
if idtfilter is None or not idtfilter or idtfilter == "account":
ids = user.objectaccess_set \
.filter(content_type=ContentType.objects.get_for_model(user)) \
.values_list('object_id', flat=True)
q = Q(pk__in=ids)
if searchquery is not None:
q &= Q(username__icontains=searchquery) \
| Q(email__icontains=searchquery)
if grpfilter is not None and grpfilter:
if grpfilter == "SuperAdmins":
q &= Q(is_superuser=True)
else:
q &= Q(groups__name=grpfilter)
accounts = User.objects.select_related().filter(q)
aliases = []
if idtfilter is None or not idtfilter \
or (idtfilter in ["alias", "forward", "dlist"]):
alct = ContentType.objects.get_for_model(Alias)
ids = user.objectaccess_set.filter(content_type=alct) \
.values_list('object_id', flat=True)
q = Q(pk__in=ids)
if searchquery is not None:
q &= (
Q(address__icontains=searchquery) |
Q(domain__name__icontains=searchquery)
)
aliases = Alias.objects.select_related().filter(q)
if idtfilter is not None and idtfilter:
aliases = [al for al in aliases if al.type == idtfilter]
return chain(accounts, aliases)
def get_domains(user, domfilter=None, searchquery=None, **extrafilters):
"""Return all the domains the user can access.
:param ``User`` user: user object
:param str searchquery: filter
:rtype: list
:return: a list of domains and/or relay domains
"""
domains = Domain.objects.get_for_admin(user)
if domfilter:
domains = domains.filter(type=domfilter)
if searchquery is not None:
q = Q(name__contains=searchquery)
q |= Q(domainalias__name__contains=searchquery)
domains = domains.filter(q).distinct()
qset_filters = events.raiseDictEvent(
"ExtraDomainQsetFilters", domfilter, extrafilters
)
if qset_filters:
domains = domains.filter(**qset_filters)
return domains
def check_if_domain_exists(name, extra_checks=None):
"""Check if a domain already exists.
We not only look for domains, we also look for every object that
could conflict with a domain (domain alias, etc.)
"""
dtypes = events.raiseQueryEvent('CheckDomainName')
if extra_checks is not None:
dtypes = extra_checks + dtypes
for dtype, label in dtypes:
if dtype.objects.filter(name=name).exists():
return label
return None
def import_domain(user, row, formopts):
"""Specific code for domains import"""
if not user.has_perm("admin.add_domain"):
raise PermDeniedException(_("You are not allowed to import domains"))
dom = Domain()
dom.from_csv(user, row)
def import_domainalias(user, row, formopts):
"""Specific code for domain aliases import"""
if not user.has_perm("admin.add_domainalias"):
raise PermDeniedException(
_("You are not allowed to import domain aliases."))
domalias = DomainAlias()
domalias.from_csv(user, row)
def import_account(user, row, formopts):
"""Specific code for accounts import"""
account = User()
account.from_csv(user, row, formopts["crypt_password"])
def _import_alias(user, row, **kwargs):
"""Specific code for aliases import"""
alias = Alias()
alias.from_csv(user, row, **kwargs)
def import_alias(user, row, formopts):
_import_alias(user, row, expected_elements=4)
def import_forward(user, row, formopts):
_import_alias(user, row, expected_elements=4)
def import_dlist(user, row, formopts):
_import_alias(user, row)
@@ -0,0 +1,890 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Miroslav Abrahám <miris@kunago.com>, 2013-2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2015-01-06 10:05+0000\n"
"Last-Translator: Miroslav Abrahám <miris@kunago.com>\n"
"Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/"
"modoboa/language/cs_CZ/)\n"
"Language: cs_CZ\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Schránky"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Spravovat schránky v souborovém systému"
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Přejmenovat nebo odstranit schránky v souborovém systému, pokud jsou "
"přejmenovány nebo odstraněny v Modoboa"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Majitel schránky"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "Účet UNIXu, který vlastní schránky v souborovém systému"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Výchozí kvóta domény"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Výchozí kvóta (v MB), které se použije u nově vytvořených domén bez uvedené "
"hodnoty kvóty. Hodnota 0 znamená, že se kvóta nepoužívá."
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Automatické odstranění účtu"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr "Když je schránka odstraněná, odstranit také přiřazený účet uživatele"
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Automatické vytvoření domény/schránky"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr "Při založení účtu automaticky vytvořit doménu a schránku."
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Musí se jednat o kladné číslo"
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Správa"
#: admin/callbacks.py:46
#, fuzzy
#| msgid "Create a domain administrator"
msgid "Domain administrator"
msgstr "Vytvořit správce domény"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domény"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identity"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Přeposlat"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Import účtu nebyl úspěšný (%s): doména neexistuje"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "Schránka %s již existuje"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Import účtu selhal (%s): chybná hodnota kvóty"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "doménový alias"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Uživatelské jméno"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
"Název uživatele. U běžných uživatelů musí být uvedena platná e-mailová "
"adresa nebo u administrátorů schránka."
#: admin/forms/account.py:37
msgid "Role"
msgstr "Role"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Vybrat"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Jaká úroveň oprávnění bude tomuto uživateli nastavena"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Heslo"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Potvrzení"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Vložte stejné heslo jako v poli výše, pro jeho ověření."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Povoleno"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr ""
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Obě pole s heslem se neshodují."
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Nemůžete zakázat váš vlastní účet"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-mail"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Kvóta"
#: admin/forms/account.py:174
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 ""
"Kvóta pro tuto chránku v MB. Definujte vlastní hodnotu nebo použijte výchozí "
"hodnotu domény. Ponechejte prázdné pro neomezenou kvótu (není povoleno "
"správcům domény)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias(y)"
#: admin/forms/account.py:185
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(y) této schránky. Vložte pouze jeden alias na řádek, stiskněte ENTER "
"pro vložení dalšího aliasu. Pro vytvoření doménového koše vložte '*' (např. "
"*@domena.cz)."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Doména neexistuje"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Doména(y)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Doména(y) pod správou uživatele"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Obecné"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Mail"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Oprávnění"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Účet aktualizován"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nový účet"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Účet vytvořen"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Emailová adresa"
#: admin/forms/alias.py:25
#, fuzzy
#| msgid ""
#| "The alias address (without the domain part). For a 'catch-all' address, "
#| "just enter an * character."
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"Alias adresy (bez části označující doménu). Pro vytvoření doménového koše "
"vložte znak '*'."
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Příjemci"
#: admin/forms/alias.py:33
#, fuzzy
#| msgid ""
#| "Mailbox(es) this alias will point to. Indicate only one address per "
#| "input, press ENTER to add a new input."
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Schránka/Schránky, na kterou/které bude tento alias odkazovat. Uveďte na "
"řádek pouze jednu adresu; pro vložení další adresy stiskněte po vložení "
"ENTER."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Nemáte oprávnění k přístupu k této doméně"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Nebyl určen příjemce"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Doména"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Typ"
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
"Výchozí kvóta v MB pro schránky. Ponechejte prázdné pro výchozí nastavení "
"kvóty."
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alias(y) této domény. Uveďte na řádek pouze jeden alias; pro vložení dalšího "
"aliasu stiskněte po vložení ENTER."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "%s s tímto názvem již existuje"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "doména"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Vytvořit správce domény"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Automaticky vytvořit správce této domény"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Jméno"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"Jméno správce. Nevkládejte sem název domény, název bude automaticky doplněn."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Vytvořit aliasy"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Automaticky vytvořit běžné aliasy pro tuto doménu"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Neplatný formát"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "Uživatel '%s' již existuje"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Doména upravena"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Možnosti"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Nová doména"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Doména vytvořena"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Název souboru"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Oddělovač"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Příjemce(i)"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Uveďte jednoho nebo více příjemců oddělených pomocí ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Ponechat místní kopie"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr "Přeposílat zprávy a ukládat kopie do místní schránky"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Nemůžete nastavit přeposílání na místní adresu. Požádejte správce o "
"vytvoření aliasu."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Vybrat soubor"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Při výskytu chyby pokračovat"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Nepovažovat duplikáty objektů za chybu"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Zašifrovat hesla"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr "Zatrhněte toto pole, pokud hesla ve vašem souboru nejsou zašifrována"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "Schránka je vyžadována"
#: admin/lib.py:120
#, fuzzy
#| msgid "You can't delete your own domain"
msgid "You are not allowed to import domains"
msgstr "Nemůžete smazat vlastní doménu"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "adresa"
#: admin/models/alias.py:28
#, fuzzy
#| msgid "The alias name"
msgid "The alias address."
msgstr "Název aliasu"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "povoleno"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Zatrhněte pole pro povolení tohoto aliasu"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "distribuční seznam"
#: admin/models/alias.py:80
msgid "forward"
msgstr "přeposlat"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
#, fuzzy
#| msgid "Invalid request"
msgid "Invalid address"
msgstr "Neplatný požadavek"
#: admin/models/alias.py:133
#, fuzzy
#| msgid "Local recipient %s not found"
msgid "Local recipient {}@{} not found"
msgstr "Místní příjemce %s nebyl nalezen"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Neplatný řádek: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "Doména '%s' neexistuje"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "název"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Doménové jméno"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Zatrhněte pole pro povolení této domény"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Neplatný řádek"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Neplatná hodnota kvóty u domény '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Název aliasu"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "cíl"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "Doména, na kterou tento alias odkazuje"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Neznámá doména %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Adresa schránky (bez části @doména.tld)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Nebylo možné zjistit umístění schránky (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "Zvolená kvóta je větší než povolené omezení domény (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "Kvóta je vyžadována"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "Doména je zakázána"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "Nemůžete vytvořit nového správce domény"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Statistiky"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domény a identity"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Globální počitadla"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Počet"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Doménové aliasy"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "Počitadla dle domén"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Značky"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Akce"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "zákázáno"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Správci"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Různé"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr " Nebyl určen správce domény"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Datum vytvoření"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Datum poslední změny"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Aliasy schránek"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Automaticky přesměrovat emaily na jinou adresu"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Aktualizovat"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Celé jméno/Příjemce"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Nápověda"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Importuji..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Zvolte hodnotu"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Použít výchozí hodnotu domény"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Adresa"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Hodnota"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Omezení"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Využití (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Hledat"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "Aktivovaný"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "Aktivovaná"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Seznam domén"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Přidat doménu"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Import"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Export"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Seznam identit"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Seznam kvót"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Přidat účet"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Přidat alias"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Přidat přeposílání"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Přidat distribuční seznam"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Zobrazit seznam identit domény"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Smazat %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Odstranit toto oprávnění"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Alias s tímto názvem již existuje"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Vytvořit"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Nový distribuční seznam"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Distribuční seznam vytvořen"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Nový alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias vytvořen"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Nové přeposílání"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Přeposílání vytvořeno"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Distribuční seznam upraven"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Přeposílání upraveno"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias upraven"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Nemůžete smazat vlastní doménu"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Doména smazána"
msgstr[1] "Domény smazány"
msgstr[2] "Domén smazáno"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Exportovat identity"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Exportovat domény"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Neplatný požadavek"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Účet smazán"
msgstr[1] "Účty smazány"
msgstr[2] "Účtů smazáno"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "Objekt již existuje: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d objektů úspěšně importováno"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
"Nahrajte CSV soubor, který bude mít jednotlivé řádky v některém z uvedených "
"formátů:\n"
"<ul>\n"
"<li><em>domain; název; kvóta, povoleno</em></li>\n"
"<li><em>domainalias; název; cílová doména; povoleno</em></li>\n"
"%s\n"
"</ul>\n"
"<p>První element každého řádku je povinný a musí být shodný s některou z "
"předchozích hodnot.</p>\n"
"<p>Můžete také použít jiný oddělovač hodnot.</p>\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importovat domény"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Nahrajte CSV soubor, který bude mít jednotlivé řádky v některém z uvedených "
"formátů:\n"
"<ul>\n"
"<li><em>account; přihlašovací jméno; heslo; křestní jméno; příjmení; "
"povoleno; skupina; adresa; kvóta; [, doména, ...]</em></li>\n"
"<li><em>alias; adresa; povoleno; interní příjemce</em></li>\n"
"<li><em>forward; adresa; povoleno; externí příjemce</em></li>\n"
"<li><em>dlist; adresa; povoleno; příjemce; příjemce; ...</em></li>\n"
"</ul>\n"
"<p>První element každého řádku je povinný a musí být shodný s některou z "
"předchozích hodnot.</p>\n"
"<p>Můžete také použít jiný oddělovač hodnot.</p>\n"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Importovat identity"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Přeposílání aktualizováno"
@@ -0,0 +1,55 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Miroslav Abrahám <miris@kunago.com>, 2013,2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2015-01-06 10:05+0000\n"
"Last-Translator: Miroslav Abrahám <miris@kunago.com>\n"
"Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/"
"modoboa/language/cs_CZ/)\n"
"Language: cs_CZ\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Žádná další doména k zobrazení"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Nemazat adresář domény"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Tento krok odstraní VŠECHNA data související s touto doménou."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Tento krok odstraní VŠECHNA data související s touto doménou, vyjma účtů."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Není určen správce domény"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Žádná další identita k zobrazení"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Nemazat adresář schránky"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Žádná další kvóta k zobrazení"
@@ -0,0 +1,876 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Arvedui <arvedui@posteo.de>, 2014
# controlc.de, 2015
# Patrick Koetter <p@state-of-mind.de>, 2010
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2015-09-09 09:33+0000\n"
"Last-Translator: controlc.de\n"
"Language-Team: German (http://www.transifex.com/tonio/modoboa/language/de/)\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Postfächer"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Postfächer im Dateisystem verwalten"
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Postfächer auch in Dateisystem umbenennen oder entfernen wenn sie in Modoboa "
"umbenannt oder entfernt werden"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Postfach-Besitzer"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr ""
"UID des virtuellen Benutzers, der Besitzer der Domains, Mailboxen und "
"Nachrichten im Dateisystem ist."
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Standard-Domainquota"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Standardquota (in MB) für neu erstelle Domains ohne explizite Wertangabe. "
"Der Wert 0 bedeutet „Kein Quota“"
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Automatische Konto-Entfernung"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr "Beim Löschen des Postfachs auch das Konto entfernen."
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Automatische Domain- /Postfach-Erstellung"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
"Erstelle eine Domain und ein Postfach, wenn ein Konto automatisch erstellt "
"wurde."
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Muss eine positive Ganzzahl sein"
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Verwaltung"
#: admin/callbacks.py:46
msgid "Domain administrator"
msgstr "Domain Administrator"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domains"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identitäten"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Weiterleiten"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Kann Konto nicht importieren (%s): Die Domain existiert nicht"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "Postfach %s existiert bereits"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Konto Import fehlgeschlagen (%s): ungültiger Quota-Wert"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "Domain-Alias"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Benutzername"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
"Der Benutzername. Muss eine gültige E-Mail-Adresse für 'Normale Benutzer' "
"oder Administratoren mit einer Mailbox sein."
#: admin/forms/account.py:37
msgid "Role"
msgstr "Rolle"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Auswählen"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Rechte, die der Benutzer haben wird"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Passwort"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Bestätigung"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Passwort bestätigen"
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Aktiviert"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr "Nur Super-Administratoren dürfen diesen Modus benutzen."
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Die angegebenen Passwörter stimmen nicht überein"
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Sie können Ihr eigenes Konto nicht deaktivieren"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-Mail"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Quota"
#: admin/forms/account.py:174
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 in MB für dieses Postfach. Geben Sie einen individuellen Wert an oder "
"nutzen Sie den Standardwert der Domain. Lassen Sie das Feld leer für "
"unbegrenztes Quota (Domain-Administratoren ist dies nicht gestattet)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias(e)"
#: admin/forms/account.py:185
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(e) für dieses Postfach. Geben Sie jeweils nur eine Adresse pro Eingabe "
"an. Bestätigen Sie die Eingabe mit ENTER. Geben Sie '*' an, um einen "
"'catchall'-Alias (Bsp: *@example.com) zu erstellen."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Domain existiert nicht"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Domain(s)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Domain(s), die dieser Benutzer administriert"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Allgemein"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "E-Mail"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Berechtigungen"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Konto aktualisiert"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Neues Konto"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Konto erstellt"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "E-Mail Adresse"
#: admin/forms/alias.py:25
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"Die Alias-Adresse. Geben Sie nur den Domainnamen (@domain.tld) für eine "
"'catch-all' Alias an."
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Empfänger"
#: admin/forms/alias.py:33
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Adressen, auf die der Alias verweist. Geben Sie jeweils nur eine Adresse ein "
"und bestätigen Sie mit ENTER."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Der Zugriff auf diese Domain ist Ihnen nicht gestattet"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Keine Empfänger angegeben"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Domain"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Typ"
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
"Standardquota in MB für Mailboxen. Bei leerem Feld wird der Domain-Standard "
"genutzt."
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alias(e) für diese Domain. Geben Sie jeweils nur eine Adresse pro "
"Eingabefeld an. Bestätigen Sie mit ENTER, um ein neues Eingabefeld zu "
"erhalten."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "Ein %s mit diesem Namen existiert bereits"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "Domain"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Domain-Administrator erstellen"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Automatisch Administrator für diese Domain anlegen"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Name"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"Name des Administrators. Geben Sie nicht den Domainnamen mit an. Er wird "
"automatisch angehängt."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Aliase erstellen"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Automatisch Standard-Aliase für Domain anlegen"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Ungültiges Format"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "Benutzer %s existiert bereits"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Domain verändert"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Optionen"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Neue Domain"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Domain erstellt."
#: admin/forms/export.py:7
msgid "File name"
msgstr "Dateiname"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Separator"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Empfänger"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Geben sie einen oder mehrere, durch ',' separierte Empfänger an"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Lokale Kopien behalten"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr "Nachricht weiterleiten und Kopie in Mailbox ablegen"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Sie können keine Weiterleitung auf ein lokales Ziel anlegen. Bitten Sie "
"Ihren Administrator stattdessen einen Alias anzulegen."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Wählen Sie eine Datei"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Bei Fehler fortfahren"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Doppelte Objekte nicht als Fehler behandeln"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Passwörter verschlüsseln"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr "Auswählen, wenn Kennwörter in Datei nicht verschlüsselt sind"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "Ein Postfach ist erforderlich"
#: admin/lib.py:120
msgid "You are not allowed to import domains"
msgstr "Sie verfügen nicht über ausreichend Rechte, um Domains zu importieren."
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
"Sie verfügen nicht über ausreichend Rechte, um Domain Aliase zu importieren."
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "Adresse"
#: admin/models/alias.py:28
msgid "The alias address."
msgstr "Die Alias-Adresse"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "aktiviert"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Auswählen, um den Alias zu aktivieren"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "Verteilerliste"
#: admin/models/alias.py:80
msgid "forward"
msgstr "Weiterleitung"
#: admin/models/alias.py:81
msgid "alias"
msgstr "Alias"
#: admin/models/alias.py:112
msgid "Invalid address"
msgstr "Ungültige Adresse"
#: admin/models/alias.py:133
msgid "Local recipient {}@{} not found"
msgstr "Lokaler Empfänger {}@{} nicht gefunden"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Ungültige Zeile: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "Domain '%s' existiert nicht"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "Name"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Name der Domain"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Auswählen, um diese Domain zu aktivieren"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Ungültige Zeile"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Ungültiger Quota-Wert für Domain '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Der Alias-Name"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "Ziel"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "Domain, auf die dieser Alias verweist"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Unbekannte Domain %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Postfach-Adresse (ohne den '@domain.tld' Teil)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Der Speicherort des Postfachs (%s) konnte nicht bestimmt werden"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "Das Quota ist größer als das maximale Quota der Domain (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "Ein Quota ist erforderlich"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "Domain ist deaktiviert"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "Sie können keinen neuen Domain-Administrator erstellen"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Statistiken"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domains und Identitäten"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Zentrale Zähler"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Anzahl"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Domain-Aliase"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "pro Domain"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Tags"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Aktionen"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "deaktiviert"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Administratoren"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Verschiedenes"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Kein Domain-Administrator definiert"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Erstellungsdatum"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Datum der letzten Änderung"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Postfach-Aliase"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Nachrichten automatisch an andere Adresse weiterleiten"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Aktualisieren"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Name/Empfänger"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Hilfe"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Importiere…"
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Wert auswählen"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Standardwert der Domain verwenden"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Adresse"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Wert"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Limit"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Verwendung (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Suchen"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "aktiviert_m"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "aktiviert_f"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Domain-Liste"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Domain hinzufügen"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Import"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Export"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Identitäten-Liste"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Quota-Liste"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Konto hinzufügen"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Alias hinzufügen"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Weiterleitung hinzufügen"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Verteilerliste hinzufügen"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Domain-Identitäten anzeigen"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "%s löschen?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Diese Berechtigung entfernen"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Ein Alias mit diesem Namen existiert bereits"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Erstellen"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Neue Verteilerliste"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Verteilerliste erstellt"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Neuer Alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias erstellt"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Neue Weiterleitung"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Weiterleitung erstellt"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Verteilerliste verändert"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Weiterleitung verändert"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias verändert"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Sie können Ihre eigene Domain nicht löschen"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Domain gelöscht"
msgstr[1] "Domains gelöscht"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Identitäten exportieren"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Domains exportieren"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Ungültige Anfrage"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Konto gelöscht"
msgstr[1] "Konten gelöscht"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "Objekt existiert bereits: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d Objekte erfolgreich importiert"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
"Verwenden sie eine CSV-Datei mit einem der folgenden Formate:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <li><em>domainalias; name; targeted domain; enabled</em></li>\n"
" %s\n"
"</ul>\n"
"<p>Das erste Element jeder Zeile ist zwingend und muss gleich zum "
"vorhergehenden sein.</p>\n"
"<p>Ein anderes Zeichen als Trenner ist gestattet.</p>\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Domains importieren"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Verwenden sie eine CSV-Datei in einem der folgenden Formate:\n"
"<ul>\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>Das erste Element einer Zeile ist zwingend und muss gleich mit einem der "
"vorhergehenden sein.</p>\n"
"\n"
"<p>Ein anderes Zeichen als Separator ist gestattet.</p>\n"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Identitäten importieren"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Weiterleitung aktualisiert"
@@ -0,0 +1,57 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Arvedui <arvedui@posteo.de>, 2014
# Patrick Koetter <p@state-of-mind.de>, 2010
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2014-12-01 12:12+0000\n"
"Last-Translator: Arvedui <arvedui@posteo.de>\n"
"Language-Team: German (http://www.transifex.com/projects/p/modoboa/language/"
"de/)\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Keine weiteren Domains"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Verzeichnis der Domain nicht löschen"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Diese Aktion wird ALLE mit der Domain verbundenen Daten löschen."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Diese Aktion wird ALLE mit der Domain verbundenen Daten, außer Mailkonten, "
"löschen."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Domain-Administrator fehlt"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Keine weiteren Identitäten"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Verzeichnis der Mailbox nicht löschen"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Keine weiteren Quotas"
@@ -0,0 +1,822 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr ""
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr ""
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr ""
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr ""
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr ""
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr ""
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr ""
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr ""
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr ""
#: admin/app_settings.py:149
msgid "Administration"
msgstr ""
#: admin/callbacks.py:46
msgid "Domain administrator"
msgstr ""
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr ""
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr ""
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr ""
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr ""
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr ""
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr ""
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr ""
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr ""
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
#: admin/forms/account.py:37
msgid "Role"
msgstr ""
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr ""
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr ""
#: admin/forms/account.py:42
msgid "Password"
msgstr ""
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr ""
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr ""
#: admin/forms/account.py:66
msgid "Enabled"
msgstr ""
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr ""
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr ""
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr ""
#: admin/forms/account.py:170
msgid "E-mail"
msgstr ""
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr ""
#: admin/forms/account.py:174
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 ""
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr ""
#: admin/forms/account.py:185
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 ""
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr ""
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr ""
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr ""
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr ""
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr ""
#: admin/forms/account.py:409
msgid "Permissions"
msgstr ""
#: admin/forms/account.py:476
msgid "Account updated"
msgstr ""
#: admin/forms/account.py:500
msgid "New account"
msgstr ""
#: admin/forms/account.py:512
msgid "Account created"
msgstr ""
#: admin/forms/alias.py:23
msgid "Email address"
msgstr ""
#: admin/forms/alias.py:25
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr ""
#: admin/forms/alias.py:33
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr ""
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr ""
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr ""
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr ""
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr ""
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr ""
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr ""
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr ""
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr ""
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr ""
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr ""
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr ""
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr ""
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr ""
#: admin/forms/domain.py:379
msgid "Options"
msgstr ""
#: admin/forms/domain.py:387
msgid "New domain"
msgstr ""
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr ""
#: admin/forms/export.py:7
msgid "File name"
msgstr ""
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr ""
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr ""
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr ""
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr ""
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr ""
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr ""
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr ""
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr ""
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr ""
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr ""
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr ""
#: admin/lib.py:120
msgid "You are not allowed to import domains"
msgstr ""
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr ""
#: admin/models/alias.py:28
msgid "The alias address."
msgstr ""
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr ""
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr ""
#: admin/models/alias.py:79
msgid "distribution list"
msgstr ""
#: admin/models/alias.py:80
msgid "forward"
msgstr ""
#: admin/models/alias.py:81
msgid "alias"
msgstr ""
#: admin/models/alias.py:112
msgid "Invalid address"
msgstr ""
#: admin/models/alias.py:133
msgid "Local recipient {}@{} not found"
msgstr ""
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr ""
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr ""
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr ""
#: admin/models/domain.py:34
msgid "The domain name"
msgstr ""
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr ""
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr ""
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr ""
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr ""
#: admin/models/domain_alias.py:20
msgid "target"
msgstr ""
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr ""
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr ""
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr ""
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr ""
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr ""
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr ""
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr ""
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr ""
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr ""
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr ""
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr ""
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr ""
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr ""
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr ""
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr ""
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr ""
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr ""
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr ""
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr ""
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr ""
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr ""
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr ""
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr ""
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr ""
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr ""
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr ""
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr ""
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr ""
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr ""
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr ""
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr ""
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr ""
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr ""
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr ""
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr ""
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr ""
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr ""
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr ""
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr ""
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr ""
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr ""
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr ""
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr ""
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr ""
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr ""
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr ""
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr ""
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr ""
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr ""
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr ""
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr ""
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr ""
#: admin/views/alias.py:53
msgid "Create"
msgstr ""
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr ""
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr ""
#: admin/views/alias.py:76
msgid "New alias"
msgstr ""
#: admin/views/alias.py:77
msgid "Alias created"
msgstr ""
#: admin/views/alias.py:86
msgid "New forward"
msgstr ""
#: admin/views/alias.py:87
msgid "Forward created"
msgstr ""
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr ""
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr ""
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr ""
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr ""
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] ""
msgstr[1] ""
#: admin/views/export.py:40
msgid "Export identities"
msgstr ""
#: admin/views/export.py:68
msgid "Export domains"
msgstr ""
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr ""
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] ""
msgstr[1] ""
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr ""
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr ""
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
#: admin/views/import_.py:98
msgid "Import domains"
msgstr ""
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
#: admin/views/import_.py:132
msgid "Import identities"
msgstr ""
#: admin/views/user.py:34
msgid "Forward updated"
msgstr ""
@@ -0,0 +1,52 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr ""
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr ""
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr ""
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr ""
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr ""
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr ""
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr ""
@@ -0,0 +1,899 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Pedro Gracia <lasarux@neuroomante.com>, 2011-2013
# Pedro Gracia <pedro.gracia@impulzia.com>, 2010-2011
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2013-10-01 09:56+0000\n"
"Last-Translator: Pedro Gracia <lasarux@neuroomante.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/modoboa/language/"
"es/)\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Mailboxes"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Manejar mailboxes en el sistema de fichero"
#: admin/app_settings.py:60
#, fuzzy
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Renombrar o eliminar mailboxes en el sistema de ficheros cuando sean "
"renombrados o eliminados dentro de Modoboa"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Propietario de los mailboxes"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "Cuenta de UNIX que posee los mailboxes en el sistema de ficheros"
#: admin/app_settings.py:74
#, fuzzy
msgid "Default domain quota"
msgstr "Límites por defecto"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Elminar cuentas automáticamente"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr "Cuando un mailbox es eliminado, también lo es su cuenta asociada."
#: admin/app_settings.py:90
#, fuzzy
msgid "Automatic domain/mailbox creation"
msgstr "Dominio creado"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr ""
#: admin/app_settings.py:149
#, fuzzy
msgid "Administration"
msgstr "Administradores"
#: admin/callbacks.py:46
#, fuzzy
#| msgid "Create a domain administrator"
msgid "Domain administrator"
msgstr "Crear un administrador del dominio"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Dominios"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identidades"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Siguiente"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Falló la importación de la cuenta (%s): el dominio no existe"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "El mailbox %s ya existe"
#: admin/callbacks.py:181
#, fuzzy, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Falló la importación de la cuenta (%s): el dominio no existe"
#: admin/callbacks.py:216 admin/forms/domain.py:94
#, fuzzy
msgid "domain alias"
msgstr "Alias del dominio"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Nombre de usuario"
#: admin/forms/account.py:32
#, fuzzy
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr "El usuario normal '%s' debe tener una dirección de email válida"
#: admin/forms/account.py:37
msgid "Role"
msgstr "Rol"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Elija"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Qué nivel de permiso tendrá este usuario"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Contraseña"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Confirmación"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Introduzca la misma contraseña que arriba, para verificación."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Activado"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr ""
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Las contraseñas no concuerdan"
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "No puede deshabilitar su propia cuenta"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "Email"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Cuota"
#: admin/forms/account.py:174
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 ""
"Cuota en MB para este mailbox. Defina un valor por defecto o use el del "
"dominio por defecto. Déjelo vacío para definir un valor ilimitado (no "
"permitido para los administradores del dominio)"
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias"
#: admin/forms/account.py:185
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 de este mailbox. Indique sólo una dirección por entrada, presione "
"ENTER para añadir una nueva entrada. Use el carácter '*' para crear una "
"dirección 'catchall' (recogerlos todos, ej. *@domain.tld)"
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "El dominio no existe"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Dominio(s)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Dominio(s) que el usuario administra"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "General"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Correo"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Permisos"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Cuenta actualizada"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nueva cuenta"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Cuenta creada"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Dirección de email"
#: admin/forms/alias.py:25
#, fuzzy
#| msgid ""
#| "The alias address (without the domain part). For a 'catch-all' address, "
#| "just enter an * character."
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"La dirección del alias (sin la parte del dominio). Para una dirección que lo "
"recoja todo, ponga un asterisco (*)."
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Destinatarios"
#: admin/forms/alias.py:33
#, fuzzy
#| msgid ""
#| "Mailbox(es) this alias will point to. Indicate only one address per "
#| "input, press ENTER to add a new input."
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Mailbox(es) a los que apuntará este alias. Indique únicamente una dirección "
"por entrada, presione ENTER para añadir una nueva entrada."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "No tienes acceso a este dominio"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "No ha puesto destinatario"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
#, fuzzy
msgid "Domain"
msgstr "Dominios"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr ""
#: admin/forms/domain.py:38
#, fuzzy
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr "Cuota por defecto en MB que se aplicará a los mailboxes"
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alia(s) de este dominio. Indique únicamente un nombre por entrada, presione "
"ENTER para añadir una nueva entrada."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, fuzzy, python-format
msgid "A %s with this name already exists"
msgstr "Ya existe un alias con este nombre"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
#, fuzzy
msgid "domain"
msgstr "Dominios"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Crear un administrador del dominio"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Automáticamente crear una administrador para este dominio"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Nombre"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"El nombre del administrador. No incluya el nombre del dominio aquí, será "
"añadido automáticamente."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Crear alias"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Automáticamente crear alias estándar para este dominio"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Formato inválido"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "El usuario '%s' ya existe"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Dominio modificado"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Opciones"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Nuevo dominio"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Dominio creado"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Nombre del fichero"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Separador"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Destinatario(s)"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Indique uno o más destinatarios separados por una ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Mantener copias locales"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr "Reenvía mensajes y guarda copias en su mailbox local"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"No puede definir un reenvío a un destino local. Por favor, pregunte a su "
"administrador para crear un alias en su lugar."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Seleccione un fichero"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Continúe en error."
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "No tratar objetos duplicados como error"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Contraseñas cifradas"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr ""
"Marque esta opción si las contraseñas que contiene su fichero no están "
"cifradas"
#: admin/lib.py:30
#, fuzzy
msgid "A mailbox is required"
msgstr "Una cuota es obligatoria"
#: admin/lib.py:120
#, fuzzy
#| msgid "You can't delete your own domain"
msgid "You are not allowed to import domains"
msgstr "No puedo eliminar su propio dominio"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "dirección"
#: admin/models/alias.py:28
#, fuzzy
#| msgid "The alias name"
msgid "The alias address."
msgstr "Nombre del alias"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "activado"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Marcar para activar este alias"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "Lista de distribución"
#: admin/models/alias.py:80
msgid "forward"
msgstr "reenvío"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
#, fuzzy
#| msgid "Invalid request"
msgid "Invalid address"
msgstr "Petición inválida"
#: admin/models/alias.py:133
#, fuzzy
#| msgid "Local recipient %s not found"
msgid "Local recipient {}@{} not found"
msgstr "Destinatario local %s no encontrado"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Línea inválida: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "El dominio '%s' no existe"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "nombre"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Nombre del dominio"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Marcar para activar este dominio"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Línea inválida"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr ""
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Nombre del alias"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "objetivo"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "El dominio al que este alias apunta"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Dominio desconocido %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Dirección del mailbox (sin la parte @dominio.tld)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Fallo al recuperar la localización del mailbox (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "La cuota es mayor que el límite permitido para el dominio (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "Una cuota es obligatoria"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
#, fuzzy
msgid "Domain is disabled"
msgstr "Dominio eliminado"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
#, fuzzy
msgid "You can't create a new domain administrator"
msgstr "Crear un administrador del dominio"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Estadísticas"
#: admin/templates/admin/domain_statistics.html:15
#, fuzzy
msgid "Domains and identities"
msgstr "Listar identidades"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr ""
#: admin/templates/admin/domain_statistics.html:27
#, fuzzy
msgid "Quantity"
msgstr "Cuarentena"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Alias del dominio"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr ""
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Tags"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Acciones"
#: admin/templates/admin/domains_table.html:4
#, fuzzy
msgid "disabled"
msgstr "activado"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Administradores"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Otros"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "No hay un dominio de administrador definido "
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Fecha de creación"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Última fecha de modificación"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Alias del mailbox"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Reenvía automáticamente los correos a otra dirección"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Actualizar"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Nombre completo/Destinatario"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Ayuda"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Importando..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Elija un valor"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Usar el valor por defecto del dominio"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Dirección"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Valor"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Límite"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Uso (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Buscar"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "Activado"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "Activado"
#: admin/templatetags/admin_tags.py:40
#, fuzzy
msgid "List domains"
msgstr "Editar el dominio"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Añadir dominio"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Importar"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Exportar"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Listar identidades"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Listar cuotas"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Añadir cuenta"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Añadir alias"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Añadir reenvío"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Añadir lista de distribución"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Ver las identidades del dominio"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "¿Eliminar %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Eliminar este permiso"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Ya existe un alias con este nombre"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Crear"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Nueva lista de distribución"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Lista de distribución creada"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Nuevo alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias creado"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Nuevo reenvío"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Reenvío creado"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Lista de distribución modificada"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Reenvío modificado"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias modificado"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "No puedo eliminar su propio dominio"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Dominio eliminado"
msgstr[1] "Dominios eliminados"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Exportar identidades"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Exportar dominios"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Petición inválida"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Cuenta eliminada"
msgstr[1] "Cuentas eliminadas"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "El objeto ya existe: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d objetos importados con éxito"
#: admin/views/import_.py:86
#, fuzzy, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
"Provea un fichero CSV dónde las líneas respetan uno de los siguientes "
"formatos:⏎ <ul>⏎ <li><em>dominio; nombre; cuota; activado</em></li>⏎ "
"<li><em>aliasdeldominio; nombre; dominio objetivo; activado</em></li>⏎ </"
"ul>⏎ <p>El primer elemento de cada línea es obligatorio y debe ser igual a "
"uno de los valores anteriores.</p>⏎ <p>Puede usar un carácter diferente como "
"separador.</p>⏎\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importar dominios"
#: admin/views/import_.py:120
#, 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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Importe un fichero CSV dónde las líneas respetan uno de los siguientes "
"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>El primer elemento de cada línea es obligatorio y debe ser igual al de "
"los valores previos.</p>\n"
"\n"
"<p>Puede usar un carácter diferente como separador.</p>\n"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Importar identidades"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Reenvío actualizado"
@@ -0,0 +1,57 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Pedro Gracia <lasarux@neuroomante.com>, 2011,2013
# Pedro Gracia <pedro.gracia@impulzia.com>, 2010, 2011
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2013-06-03 16:32+0000\n"
"Last-Translator: Pedro Gracia <lasarux@neuroomante.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/modoboa/language/"
"es/)\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr ""
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "No eleminar el directorio del dominio"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Esta operación eliminará TODOS los datos asociados a este dominio."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Esta operación eliminará todos los datos asociados a este dominio, excepto "
"las cuentas."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "No hay un administrador de dominio definido"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr ""
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "No eliminar el directorio del mailbox"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr ""
@@ -0,0 +1,874 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Antoine Nguyen <tonio@ngyn.org>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2015-09-09 08:33+0000\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: French (http://www.transifex.com/tonio/modoboa/language/fr/)\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Boites"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Gérer les boites sur le système de fichiers"
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Renommer ou supprimer les boites sur le système de fichiers quand elles sont "
"renommées ou supprimées dans Modoboa"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Propriétaire des boites"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "Le compte UNIX qui possède les boites sur le système de fichiers"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Quota de domaine par défaut"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Quota par défaut (en Mo) appliqué aux domaines fraichement créés et ne "
"spécifiant pas de valeur. 0 signifie qu'aucun quota ne sera appliqué."
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Suppression automatique de compte"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr ""
"Lorsqu'une boite aux lettres est supprimée, supprimer le compte associé en "
"même temps"
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Création automatique de domaine/boite aux lettres"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
"Créer un domaine et une boite aux lettres quand un compte est créé "
"automatiquement."
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Doit être un entier positif"
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Administration"
#: admin/callbacks.py:46
msgid "Domain administrator"
msgstr "Administrateur de domaine"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domaines"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identités"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Transfert"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "L'import du compte a échoué (%s): le domaine n'existe pas"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "La boite %s existe déjà"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "L'import du compte a échoué (%s): mauvais quota"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "alias de domaine"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Nom d'utilisateur"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
"Le nom de l'utilisateur. Doit être une adresse e-mail valide pour les "
"utilisateurs simples ou les administrateurs avec une boite aux lettres."
#: admin/forms/account.py:37
msgid "Role"
msgstr "Rôle"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Choisir"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Quel niveau de permission cet utilisateur aura"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Mot de passe"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Confirmation"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Entrer le même mot de passe, pour vérification."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Activé"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr "Seuls les super administrateurs sont autorisés pour ce mode"
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Les deux mots de passe ne correspondent pas."
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Vous ne pouvez pas désactiver votre propre compte"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-mail"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Quota"
#: admin/forms/account.py:174
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 en Mo pour cette boite. Définir une valeur spécifique ou utiliser la "
"valeur par défaut du domaine. Laisser vide pour définir une valeur illimitée "
"(interdit pour les admins. de domaine)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias"
#: admin/forms/account.py:185
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 de cette boite. Indiquer seulement une adresse par champ, appuyer sur "
"ENTREE pour ajouter un nouveau champ. Utiliser le caractère '*' pour créer "
"une adresse 'attrape tout' (ex: *@domain.tld)."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Domaine inexistant"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Domaine(s)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Domaine(s) que cet utilisateur administre"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Général"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Courrier"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Permissions"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Compte mis à jour"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nouveau compte"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Compte créé"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Adresse email"
#: admin/forms/alias.py:25
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"L'adresse de l'alias. Pour créer un alias \"fourre-tout\", saisissez "
"uniquement le nom du domaine (@domain.tld)."
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Destinataires"
#: admin/forms/alias.py:33
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Les adresses vers lesquelles cet alias pointera. Indiquer une adresse par "
"champ, appuyer sur ENTREE pour ajouter un nouveau champ."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Vous n'avez pas accès à ce domaine"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Aucune destinataire défini"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Domaine"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Type"
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
"Quota par défaut en Mo appliqué aux boites aux lettres. Laisser vide pour "
"utiliser la valeur par défaut."
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alias de ce domaine. Indiquer seulement un nom par champ, appuyer sur ENTREE "
"pour ajouter un nouveau champ."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "Un %s de ce nom existe déjà"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "domaine"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Créer un administrateur de domaine"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Créer automatiquement un administrateur pour ce domaine"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Nom"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"Le nom de l'administrateur. Ne pas include un nom de domaine ici, il sera "
"ajouté automatiquement."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Créer des alias"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Créer automatiquement des alias standard pour ce domaine"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Format invalide"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "L'utilisateur '%s' existe déjà"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Domaine modifié"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Options"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Nouveau domaine"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Domaine créé"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Nom du fichier"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Séparateur"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Destinataire(s)"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Indiquer un ou plusieurs destinataires séparés par une ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Conserver des copies locales"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr "Transférer les messages et conserver une copie dans votre boite locale"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Vous ne pouvez pas définir un transfert vers une destination locale. "
"Veuillez plutôt demander à votre administrateur de créer un alias"
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Sélectionner un fichier"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Continuer en cas d'erreur"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Ne pas traiter les objets dupliqués comme des erreurs"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Chiffrer les mots de passe"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr ""
"Cocher cette option si les mots de passe contenus dans votre fichier ne sont "
"pas chiffrés"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "Une boite aux lettres est requise"
#: admin/lib.py:120
msgid "You are not allowed to import domains"
msgstr "Vous n'êtes pas autorisé à importer des domaines"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr "Vous n'êtes pas autorisé à importer des alias de domaine."
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "Adresse"
#: admin/models/alias.py:28
msgid "The alias address."
msgstr "L'adresse de l'alias"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "Activé"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Cocher pour activer cet alias"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "liste de distribution"
#: admin/models/alias.py:80
msgid "forward"
msgstr "transfert"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
msgid "Invalid address"
msgstr "Adresse invalide"
#: admin/models/alias.py:133
msgid "Local recipient {}@{} not found"
msgstr "Destinataire local {}@{} introuvable"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Ligne invalide : %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "Domaine '%s' inexistant"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "Nom"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Le nom du domaine"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Cocher pour activer ce domaine"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Ligne invalide"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Quota invalide pour le domaine '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Le nom de l'alias"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "cible"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "Le domaine vers lequel cet alias pointe"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Domaine %s inconnu"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Adresse de la boite aux lettres (sans la partie @domain.tld)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Impossible de récuperer l'emplacement de la boite (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "Le quota est supérieur à la limité autorisée pour le domaine (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "Un quota est requis"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "Le domaine est désactivé"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "Mo"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "Vous ne pouvez pas créer de nouvel administrateur de domaine"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Statistiques"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domaines et identités"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Compteurs globaux"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Quantité"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Alias de domaine"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "Compteurs par domaine"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Tags"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Actions"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "Desactivé"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Administrateurs"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Divers"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Aucun administrateur de domaine défini"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Date de création"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Date de dernière modification"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Alias de boite."
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Transférer automatiquement les messages vers une autre adresse"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Mettre à jour"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Nom complet/Destinataire"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Aide"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Import en cours..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Choisir une valeur"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Utiliser la valeur par défaut du domaine"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Adresse"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Valeur"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Limite"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Utilisation (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Chercher"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "Activé"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "Activée"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Lister les domaines"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Ajouter un domaine"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Importer"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Exporter"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Lister les identités"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Lister les quotas"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Ajouter un compte"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Ajouter un alias"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Ajouter un transfert"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Ajouter une liste de distribution"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Voir les identités du domaine"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Supprimer %s ?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Supprimer cette permission"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Un alias de ce nom existe déjà"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Créer"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Nouvelle liste de distribution"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Liste de distribution créée"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Nouvel alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias créé"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Nouveau transfert"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Transfert créé"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Liste de distribution modifiée"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Transfert modifié"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias modifié"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Vous ne pouvez pas supprimer votre propre domaine"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Domaine supprimé"
msgstr[1] "Domaines supprimés"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Export d'identités"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Export de domaines"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Requête invalide"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Compte supprimé"
msgstr[1] "Comptes supprimés"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "L'objet existe déjà : %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d objets importés avec succès"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
"Fournir un fichier CSV ou chaque ligne respècte un des formats suivants :\n"
"<ul>\n"
" <li><em>domain; nom; quota; activé</em></li>\n"
" <li><em>domainalias; nom; domaine cible; activé</em></li>\n"
" %s\n"
"</ul>\n"
"<p>Le premier élément de chaque ligne est obligatoire et doit être égal à "
"l'une des valeurs précédentes.</p>\n"
"<p>Vous pouvez utiliser un caractère différent comme séparateur.</p>\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importer des domaines"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Fournir un fichier CSV ou chaque ligne respècte un des formats suivants :\n"
"<ul>\n"
"<li><em>account; identifiant; mot de passe; prénom; nom; activé, groupe; "
"addresse; quota; [, domaine, ...]</em></li>\n"
"<li><em>alias; addresse; activé; destinataire interne</em></li>\n"
"<li><em>forward; addresse; activé; destinataire externe</em></li>\n"
"<li><em>dlist; addresse; activée; destinataire; destinataire; ...</em></li>\n"
"</ul>\n"
"<p>Le premier élément de chaque ligne est obligatoire et doit être égal à "
"l'une des valeurs précédentes.</p>\n"
"\n"
"<p>Vous pouvez utiliser un caractère différent comme séparateur.</p>\n"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Importer des identités"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Transfert mis à jour"
@@ -0,0 +1,54 @@
# Modoboa french translation
# Copyright (C) 2009-2013
# This file is distributed under the same license as the Modoboa package.
# Antoine Nguyen <tonio@ngyn.org>, 2009-2013.
#
msgid ""
msgstr ""
"Project-Id-Version: modoboa-1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2011-01-07 10:00+0100\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: FR <tonio@ngyn.org>\n"
"Language: Français\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Plus de domaine à afficher"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Ne pas supprimer le répertoire du domaine"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Cette opération supprimera TOUTES les données associées à ce domaine."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Cette opération supprimera toutes les données associées à ce domaine à "
"l'excéption des comptes."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Aucun administrateur de domaine défini"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Plus d'identité à afficher"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Ne pas supprimer le répertoire de la boite aux lettres"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Plus de quota à afficher"
@@ -0,0 +1,878 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Giancarlo Gaifas <lallo@artiemestieri.tn.it>, 2013
# Rocco <mors10@hotmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2015-10-11 11:48+0000\n"
"Last-Translator: Rocco <mors10@hotmail.com>\n"
"Language-Team: Italian (http://www.transifex.com/tonio/modoboa/language/"
"it/)\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Caselle di posta"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Gestione delle caselle di posta nel filsystem"
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Rinomina o rimuovi le caselle di posta nel filesystem quando vengono "
"rinominate o cancellate in Modoboa "
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Proprietario delle caselle postali"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "L'account UNIX a cui appartengono le caselle postali nel filsystem"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Quota predefinita per il domini"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Quota predefinita (in MB) applicata ai nuovi domini che non la specificano. "
"Un valore 0 implica nessuna quota."
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Rimozione automatica dell'account"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr ""
"Quando una casella postale viene rimossa rimuovi anche l'account associato"
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Creazione automatica del domini/casella di posta elettronica"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
"Crea un dominio ed una casella di posta elettronica quando si crea un "
"account utente"
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Dev'essere un intero positivo"
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Amministrazione"
#: admin/callbacks.py:46
msgid "Domain administrator"
msgstr "Amministratore Dominio"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domini"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identità"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Inoltra"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Importazione dell'account fallito (%s): il dominio non esiste"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "La casella di posta %s esiste già"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Importazione account fallito(%s): valore della quota errato"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "alias del dominio"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Nome utente"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
"Il nome dell'utente. Dev'essere un indirizzo e-mail per gli utenti semplici "
"o gli amministratori che posseggono una casella"
#: admin/forms/account.py:37
msgid "Role"
msgstr "Ruolo"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Scegli"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Il livello di permessi che avrà questo utente"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Password"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Conferma"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr ""
"Inserisci la medesima password che hai inserito sopra, per la verifica."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Abilitato"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr "Solo i super amministratori possono operare in questa modalità"
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Le password nei due campi non corrispondono"
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Tu non puoi disabilitare il tuo account"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-mail"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Quota"
#: admin/forms/account.py:174
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 in MB per questo account. Stabilire un valore personalizzato o usare "
"quello predefinito. Lasciare vuoto per non impostare limiti (non consentito "
"per gli amministratori di dominio)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias"
#: admin/forms/account.py:185
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 per questa casella di posta. Inserire un solo indirizzo alla volta, "
"premere INVIO per aggiungere un nuovo elemento. Utilizzare il carattere '*' "
"per creare un alias 'prenditutto' (p.es.: *@dominio.org)."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Il dominio non esiste"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Dominio(i)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Dominio(i) amministrato(i) dall'utente"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Generale"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Posta"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Permessi"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Account aggiornato"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nuovo account"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Account creato"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Indirizzo email"
#: admin/forms/alias.py:25
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"Indirizzi email alias. Per creare un alias catchall specifica il nome del "
"dominio (@domain.tld)."
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Destinatari"
#: admin/forms/alias.py:33
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Indirizzi verso il quale questo alias punterà . Indicare un solo indirizzo "
"per ogni ingresso , premere INVIO per aggiungere un nuovo input ."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Non hai accesso a questo dominio"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Non è stato definito un destinatario"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Dominio"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Tipo"
#: admin/forms/domain.py:38
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. Lascialo vuoto se "
"vuoi applicare il valore predefinito."
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this 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"
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "Un %s con questo nome esiste già"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "dominio"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Crea un amministratore di dominio"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Crea automaticamente un amministratore per questo dominio"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Nome"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"Il nome dell'amministratore. Non includere il nome del dominio, verrà "
"aggiunto automaticamente."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Crea alias"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Crea automaticamente gli alias standard per questo dominio"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Formato non valido"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "L'utente '%s' esiste già"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Dominio modificato"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Opzioni"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Nuovo dominio"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Dominio creato"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Nome del file"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Separatore"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Destinatario(i)"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Indicare uno o più destinatari separati da ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Mantieni copie locali"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr ""
"Inoltra i messaggi e memorizza le copie nella tua casella di posta locale"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Non puoi definire l'inoltro con destinazione una cartella locale. Chiedi "
"piuttosto all'amministratore di creare un alias."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Seleziona un file"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Continuare in caso di errore"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Non considerare errore gli oggetti duplicati"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Cripta le password"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr ""
"Seleziona questa opzione se le password contenute nel file non sono criptate"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "É necessaria una casella"
#: admin/lib.py:120
msgid "You are not allowed to import domains"
msgstr "Non hai i permessi di importare domini"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr "Non hai i permessi di importare gli alias dominio"
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "indirizzo"
#: admin/models/alias.py:28
msgid "The alias address."
msgstr "Indirizzi Alias."
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "Attivato"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Seleziona per attivare questo alias"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "elenco di smistamento"
#: admin/models/alias.py:80
msgid "forward"
msgstr "inoltra"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
msgid "Invalid address"
msgstr "Indirizzo non valido"
#: admin/models/alias.py:133
msgid "Local recipient {}@{} not found"
msgstr "Destinatario locale {}@{} non trovato"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Riga non valida: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "Il dominio '%s' non esiste"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "nome"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Il nome del dominio"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Seleziona per attivare questo dominio"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Riga non valida"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Valore della quota non valido per il dominio '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Il nome dell'alias"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "destinazione"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "Il dominio a cui punta questo alias"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Dominio sconosciuto %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Indirizzo della casella di posta (senza la parte @dominio.org)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Impossibile recuperare la posizione della casella di posta (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "La quota è maggiore del limite stabilito per il dominio (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "È richiesta una quota"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "il Dominio è disabilitato"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "Non ti è possibile creare un amministratore di dominio"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Statistiche"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domini e identità"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Contatori globali"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Quantità"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Alias del dominio"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "Contatore per dominio"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Tag"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Azioni"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "Disabilitato"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Amministratori"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Miscellanea"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Non è stato definito alcun amministratore di dominio"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Data di creazione"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Data dell'ultima modifica"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Alias delle caselle di posta"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Inoltra automaticamente le e-mail ad un altro indirizzo"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Aggiorna"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Nome completo/Destinatario"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Aiuto"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Importazione..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Scegli un valore"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Usa il valore predefinito"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Indirizzo"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Valore"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Limite"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Utilizzo (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Ricerca"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "Abilitato"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "Abilitata"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Lista domini"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Aggiungi dominio"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Importa"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Esporta"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Elenco identità"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Elenco quote"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Aggiungi account"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Aggiungi alias"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Aggiungi inoltro"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Aggiungi elenco di smistamento"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Visualizza le identita del dominio"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Eliminare %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Rimuovi questo permesso"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Un alias con questo nome esiste già"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Crea"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Nuovo elenco di smistamento"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Elenco di smistamento creato"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Nuovo alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias creato"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Nuovo inoltro"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Inoltro creato"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Elenco di smistamento modificato"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Inoltro modificato"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias modificato"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Non puoi cancellare il tuo dominio"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Dominio cancellato"
msgstr[1] "Domini cancellati"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Identità di esportazione"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Esporta domini"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Richiesta non valida"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Account cancellato"
msgstr[1] "Account cancellati"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "L'oggetto esiste già: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d oggetti importati correttamente"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
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 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"
"\n"
"<p>Come separatore puoi utilizzare un carattere diverso.</p>\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importa domini"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Fornire un file CSV le cui righe rispettino uno dei seguenti formati:\n"
"<ul>\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"
"</ul>\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"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Identità di importazione"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Inoltro aggiornato"
@@ -0,0 +1,56 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Rocco <mors10@hotmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2015-10-11 11:19+0000\n"
"Last-Translator: Rocco <mors10@hotmail.com>\n"
"Language-Team: Italian (http://www.transifex.com/tonio/modoboa/language/"
"it/)\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Non ci sono altri domini da mostrare"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Non eliminare la cartella dei domini"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Questa operazione eliminerà TUTTI i dati associati a questo dominio."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Questa operazione eliminerà tutti i dati associati a questo dominio tranne "
"gli account"
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Non è stato definito un amministratore di dominio"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Non ci sono altre identità da mostrare"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Non eliminare la cartella della casella posta"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Non ci sono altre quote da mostrare"
@@ -0,0 +1,897 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Andrew <buddha@cryptolab.net>, 2014
# TuxBrother <vrijwilligerswerk@wij.rs>, 2014
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2014-12-31 11:37+0000\n"
"Last-Translator: TuxBrother <vrijwilligerswerk@wij.rs>\n"
"Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/"
"modoboa/language/nl_NL/)\n"
"Language: nl_NL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Mailboxen"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Pas mailboxen op het bestandssysteem aan"
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Hernoem of verwijder mailboxen op het bestandssysteem wanner ze hernoemd of "
"verwijderd worden in Modoboa."
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Eigenaar van de mailboxen"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr ""
"De UNIX account die de rechten heeft tot de mailboxen op het bestandssysteem"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Standaard domein quota"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Standaard quota (in MB) om op nieuw aangemaakte domeinen zonder waarde toe "
"te passen. Een waarde van 0 betekend geen quota."
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Automatish een account verwijderen"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr ""
"Als een mailbox word verwijderd, verwijder dan ook de bijbehorende account"
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Domein en mailbox automatisch aanmaken"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
"Maak een domein en mailbox aan wanneer een account automatisch is aangemaakt."
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Dit moet een positive integer zijn."
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Administratie"
#: admin/callbacks.py:46
#, fuzzy
#| msgid "Create a domain administrator"
msgid "Domain administrator"
msgstr "Maak een domein administrator aan"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domeinen"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identiteiten"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Doorsturen"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Accounts importeren mislukt (%s): domein bestaat niet"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "Mailbox %s bestaat al"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Account import is mislukt (%s): verkeerd quotum waarde"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "domein alias"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Gebruikersnaam"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
"De naam van de gebruiker. Moet een geldig e-mail adres zijn voor gebruikers "
"of administrators met een mailbox."
#: admin/forms/account.py:37
msgid "Role"
msgstr "Regel"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Kiezen"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Welk niveau van rechten deze gebruiker zal hebben"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Wachtwoord"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Bevestiging"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Herhaal wachtwoord (voor verificatie)"
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Actief"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr ""
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "De wachtwoorden komen niet overeen."
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "U kunt uw eigen account niet uitschakelen"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-mail"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Quota"
#: admin/forms/account.py:174
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 in MB voor deze mailbox. Geef een aangepaste waarde of gebruik de "
"stanaard waarde van het domein. Laat leeg om een ongelimiteerde waarde te "
"definieren (niet toegestaan voor domein administrators)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias(sen)"
#: admin/forms/account.py:185
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(en) van deze mailbox. Geef slechts een adres per regel op, en toets "
"ENTER om een nieuwe regel te krijgen. Gebruik een wildcard '*' om een 'catch-"
"all' adres te maken (bijvoorbeeld: *@domein.tld)."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Domein bestaat niet"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Domein(en)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Domein(en) die gebruiker mag beheren"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Algemeen"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Mail"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Rechten"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Account aangepast"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nieuwe account"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Account aangemaakt"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Email adres"
#: admin/forms/alias.py:25
#, fuzzy
#| msgid ""
#| "The alias address (without the domain part). For a 'catch-all' address, "
#| "just enter an * character."
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"Het alias adres (zonder het domein deel). Voor een 'catch-all' adres, vul "
"een * in."
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Ontvangers"
#: admin/forms/alias.py:33
#, fuzzy
#| msgid ""
#| "Mailbox(es) this alias will point to. Indicate only one address per "
#| "input, press ENTER to add a new input."
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Mailbox(en) waarnaar deze alias zal verwijzen. Geef slechts een adres per "
"regel op, en toets ENTER om een nieuwe regel te krijgen."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "U heeft geen toegang tot dit domein"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Geen ontvanger opgegeven"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Domein"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Type"
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
"Standaard quota in MB toegepast op mailboxen. Laat leeg om de standaard "
"waarde te gebruiken."
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alias(sen) van dit domein. Geef slechts een adres per regel op, en toets "
"ENTER om een nieuwe regel te krijgen."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "Een %s met deze naam bestaat al"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "domein"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Maak een domein administrator aan"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Automatisch een administrator aanmaken voor dit domein"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Naam"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"De naam van de administrator. De domeinnaam word automatisch toegevoegd."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Aliassen aanmaken"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Automatisch een standaard alias voor dit domein aanmaken"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Verkeerd formaat"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "Gebruiker '%s' bestaat al"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Domein gewijzigd"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Opties"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Nieuw domein"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Domein aangemaakt"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Bestandsnaam"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Scheidingsteken"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Ontvanger(s)"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Geef een of meer ontvangers op, gescheiden door een komma"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Lokale kopie behouden"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr "Stuur berichten door en sla een kopie op in uw mailbox"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"U kunt mail niet doorsturen naar een lokaal adres. Vraag uw systeembeheerder "
"om een alias voor u aan te maken."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Bestand selecteren"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Doorgaan als er een fout optreed"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Zie duplicaten niet als fout"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Versleutel wachtwoorden"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr ""
"Vink deze optie aan als wachtwoorden in uw bestand niet versleuteld zijn"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "Een mailbox is benodigd"
#: admin/lib.py:120
#, fuzzy
#| msgid "You can't delete your own domain"
msgid "You are not allowed to import domains"
msgstr "U kunt uw eigen domein niet verwijderen"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "adres"
#: admin/models/alias.py:28
#, fuzzy
#| msgid "The alias name"
msgid "The alias address."
msgstr "De alias naam"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "actief"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Aanvinken om deze alias te activeren"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "distributie lijst"
#: admin/models/alias.py:80
msgid "forward"
msgstr "doorsturen"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
#, fuzzy
#| msgid "Invalid request"
msgid "Invalid address"
msgstr "Ongeldig verzoek"
#: admin/models/alias.py:133
#, fuzzy
#| msgid "Local recipient %s not found"
msgid "Local recipient {}@{} not found"
msgstr "Lokale ontvanger %s niet gevonden"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Ongeldige regel: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "Domein '%s' bestaat niet"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "naam"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "De domeinnaam"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Aanvinken om dit domein te activeren"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Verkeerde regel"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Verkeerde quota waarde voor domein '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "De alias naam"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "doel"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "De domein alias verwijst naar"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Onbekend domein %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Mailbox adres (zonder het @domein.naam deel)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Mislukt om de mailbox locatie te verkijgen (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "Quota is groter dan de maximaal toegestane domein limiet (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "Een quota is vereist"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "Domein is gedeactiveerd"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "U kunt geen nieuwe domein administrator aanmaken"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Statistieken"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domeinen en identiteiten"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Globale statistieken"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Hoeveelheid"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Domein aliassen"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "Statistieken per domein"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Labels"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Acties"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "uitgeschakeld"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Administrators"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Diversen"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Geen domein administrator toegewezen"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Datum van aanmaken"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Laatst gewijzigd"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Mailbox aliassen"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Automatisch mails doorsturen naar een ander adres"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Toepassen"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Volledige naam/Ontvanger"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Help"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Importeren..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Kies een waarde"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Gebruik de standaard waarde van dit domein"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Adres"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Waarde"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Limiet"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Gebruik (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Zoeken"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "geactiveerd_m"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "geactiveerd_f"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Weergeef domeinen"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Domein toevoegen"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Importeren"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Exporteren"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Weergeef identiteiten"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Weergeef quota's"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Account aanmaken"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Alias toevoegen"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Doorsuurregel toevoegen"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Distributielijst toevoegen"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Weergeef de identiteiten van dit domein"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Verwijder %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Deze bevoegdheid verwijderen"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Een alias met deze naam bestaat al"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Aanmaken"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Nieuwe distributie lijst"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Distributielijst aangemaakt"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Nieuwe alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias aangemaakt"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Nieuwe doorstuurregel"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Doorstuurregel aangemaakt"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Distributielijst aangepast"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Doorstuurregel aangepast"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias aangepast"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "U kunt uw eigen domein niet verwijderen"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Domeinen verwijderd"
msgstr[1] "Domeinen verwijderd"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Identiteiten exporteren"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Exporteer domeinen"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Ongeldig verzoek"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Account verwijderd"
msgstr[1] "Accounts verwijderd"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "Object bestaat al: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d objecten successvol geimporteerd"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
"Lever een CSV bestand en zorg dat de regels voldoen aan een van de volgende "
"formaten:\n"
"<ul>\n"
"<li><em>domain; name; quota; enabled</em></li>\n"
"<li><em>domainalias; name; targeted domain; enabled</em></li>\n"
"%s\n"
"</ul>\n"
"<p>Het eerste element van elke regel is verplicht en moet gelijk zijn aan "
"een van de vorige waarden.</p>\n"
"<p>U kunt een eigen scheidingsteken gebruiken.</p>\n"
"<p>Let op! Zorg dat het 'line-encoding format' Unix is - anders gaat het "
"importeren mis</p>\n"
"\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importeer domeinen"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Lever een CSV bestand en zorg dat de regels voldoen aan een van de volgende "
"formaten:\n"
"<ul>\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>Het eerste element van elke regel is verplicht en moet gelijk zijn aan "
"een van de vorige waarden.</p>\n"
"<p>U kunt een eigen scheidingsteken gebruiken.</p>\n"
"<p>Let op! Zorg dat het 'line-encoding format' Unix is - anders gaat het "
"importeren mis</p>\n"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Identiteiten importeren"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Doorstuuradres aangepast"
@@ -0,0 +1,59 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# TuxBrother <vrijwilligerswerk@wij.rs>, 2014
# TuxBrother <vrijwilligerswerk@wij.rs>, 2014
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2014-12-31 11:42+0000\n"
"Last-Translator: TuxBrother <vrijwilligerswerk@wij.rs>\n"
"Language-Team: Dutch (Netherlands) (http://www.transifex.com/projects/p/"
"modoboa/language/nl_NL/)\n"
"Language: nl_NL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Geen domein meer om te weergeven"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "De map van dit domein niet verwijderen (Zal de data ervan behouden)"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr ""
"Deze actie zal ALLE data verwijderen die bij dit domein hoort. Wilt u "
"doorgaan?"
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Deze actie zal alle data die bij dit domein hoort verwijderen, behalve de "
"gebruikers."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Geen administrator toegewezen"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Geen identiteiten meer om te weergeven"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Verwijder de map van deze mailbox niet (zal de data ervan behouden)"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Geen quota meer om te weergeven"
@@ -0,0 +1,871 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# csmk, 2014
# Paulino Michelazzo <paulino@michelazzo.com.br>, 2014
# Rafael Barretto Alves <ch3yd@hotmail.com>, 2013,2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2015-09-09 08:30+0000\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/tonio/modoboa/"
"language/pt_BR/)\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Caixas de correios"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Tratar caixas de mensagens no sistema de arquivos."
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Renomeia ou remove caixas de mensagens do sistema de arquivos quando elas "
"são renomeadas ou removidas dentro do Modoboa"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Proprietário de caixas de mensagens"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "A conta UNIX que possui caixas de mensagens no sistema de arquivos"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Cota padrão do domínio"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Cota padrão (em MB) aplicada para novos domínios sem nenhum valor "
"especificado. Um valor de 0 indica que não existe cota."
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Remoção automática da conta"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr ""
"Quando uma caixa de correio é removida, também remove a conta associada"
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Criação automática de domínio/caixa de correio"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
"Criar um domínio e uma caixa de correio quando uma conta é automaticamente "
"criada."
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Deve ser um inteiro positivo"
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Administração"
#: admin/callbacks.py:46
msgid "Domain administrator"
msgstr "Administrador de domínio"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domínios"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identidades"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Encaminhar"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Falha ao importar a conta (%s): domínio não existe"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "A caixa %s já existe"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Falha na importação de conta (%s): valor de cota errado"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "alias de domínio"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Nome de usuário"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
"O nome de usuário. Deve ser um endereço de e-mail válido para usuários "
"simples ou administradores com uma caixa de mensagens."
#: admin/forms/account.py:37
msgid "Role"
msgstr "Papel"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Escolher"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Qual nível de permissão terá esse usuário"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Senha"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Confirmação"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Para verificação, digite a mesma senha que a anterior."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Ativado"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr "Apenas super administradores estão liberados para este modo"
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Os dois campos de senha não coincidiram."
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Você não pode desabilitar seu própria conta"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-mail"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Cota"
#: admin/forms/account.py:174
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 ""
"Cota em MB para esta caixa de mensagens. Defina um valor customizado ou use "
"o padrão do domínio. Deixe em branco para definir um valor ilimitado (não "
"permitido para administradores de domínios)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias(es)"
#: admin/forms/account.py:185
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) para essa caixa de correio. Indique apenas um endereço por "
"entrada, pressione ENTER para adicionar uma nova entrada. Use o caractere "
"'*' para criar um alias 'pega-todos' (ex: *@exemplo.com)."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Domínio não existe"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Domínio(s)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Domínio(s) que o usuário administra"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Geral"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Correio"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Permissões"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Conta atualizada"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nova conta"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Conta criada"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Endereço de e-mail"
#: admin/forms/alias.py:25
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Destinatários"
#: admin/forms/alias.py:33
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Você não tem acesso a esse domínio"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Nenhum destinatário definido"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Domínio"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Tipo"
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
"Cota padrão em MB aplicada para caixas de mensagens. Deixe em branco para "
"usar o valor padrão."
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alias(es) para esse domínio. Indique apenas um nome por entrada, pressione "
"ENTER para adicionar uma nova entrada."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "Já existe um %s com este nome"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "domínio"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Criar um administrador de domínio"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Criar automaticamente um administrador para este domínio"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Nome"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"O nome de administrador. Não inclua aqui o nome de domínio. Ele será "
"automaticamente adicionado."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Criar alias"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Criar automaticamente aliases padrão para este domínio"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Formato inválido"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "O usuário '%s' já existe"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Domínio alterado"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Opções"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Novo domínio"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Domínio criado"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Nome do arquivo"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Separador"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Destinatário(s)"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Indique um ou mais destinatários separados por uma ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Manter cópias locais"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr "Encaminhar mensagens e armazenar cópias em sua caixa de correio local"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Você não pode definir um encaminhamento para um local de destino. Por favor, "
"ao invés peça ao seu administrador para criar um alias."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Selecione um arquivo"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Continuar em caso de erro"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Não tratar como erro objetos duplicados"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Criptografar senhas"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr ""
"Marcar essa opção se as senhas contidas no seu arquivo estão criptografadas"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "Um caixa de mensagens é obrigatória"
#: admin/lib.py:120
msgid "You are not allowed to import domains"
msgstr "Você não tem permissão para importar domínios"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr "Você não tem permissão para importar aliases de domínio."
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "endereço"
#: admin/models/alias.py:28
msgid "The alias address."
msgstr ""
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "ativado"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Verificar para ativar o alias"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "lista de distribuição"
#: admin/models/alias.py:80
msgid "forward"
msgstr "encaminhar"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
msgid "Invalid address"
msgstr ""
#: admin/models/alias.py:133
msgid "Local recipient {}@{} not found"
msgstr "Destinatário local {}@{} não encontrado"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Linha inválida: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "O domínio '%s' não existe"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "nome"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Nome do domínio"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Marque para ativar esse domínio"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Linha inválida"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Valor de cota inválido para o domínio '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Nome de alias"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "alvo"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "Esse alias aponta para o domínio"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Domínio desconhecido %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Endereço da caixa de correio (sem a parte @meu.dominio)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Falha ao recuperar a localização da caixa de mensagens (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "A cota é maior que o permitido pelo limite do domínio (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "Uma cota é requerida"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "Domínio está desabilitado"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "Você não pode criar um novo administrador de domínio"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Estatísticas"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domínios e identidades"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Contadores globais"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Quantidade"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Alias de domínios"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "Contadores por domínios"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Tags"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Ações"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "desabilitado"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Administradores"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Diversos"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Nenhum administrador de domínio definido"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Data de criação"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Data da última modificação"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Alias de caixas de correio"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Encaminhar e-mails automaticamente para outro endereço"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Atualizar"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Nome completo/Destinatário"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Ajuda"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Importando..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Escolher um valor"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Usar valor padrão do domínio"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Endereço"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Valor"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Limite"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Uso (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Busca"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "Ativado"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "Ativada"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Listardomínios"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Adicionar domínio"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Importar"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Exportar"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Listar identidades"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Listar quotas"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Adicionar conta"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Adicionar alias"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Adicionar encaminhamento"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Adicionar lista de distribuição"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Ver as identidades de domínios"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Remover %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Remover essa persmissão"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Um alias para esse nome já existe"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Criar"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Nova lista de distribuição"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Lista de distribuição criada"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Novo alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias criado"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Novo encaminhamento"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Encaminhamento criado"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Lista de distribuição modificada"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Encaminhamento modificado"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias modificado"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Você não pode deletar seu próprio domínio"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Domínio deletado"
msgstr[1] "Domínios deletados"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Exportar identidades"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Exportar domínios"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Solicitação inválida"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Conta deletada"
msgstr[1] "Contas deletadas"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "O objeto já existe: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d objetos importados com sucesso"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
"Forneça um arquivo CSV onde as linhas respeitam um dos seguintes formatos:\n"
"<ul>\n"
" <li><em>domain; name; cota; enabled</em></li>\n"
" <li><em>domainalias; name; targeted domain; enabled</em></li>\n"
" %s\n"
"</ul>\n"
"<p>O primeiro elemento de cada linha é mandatório e deve ser igual a um dos "
"valores indicados.</p>\n"
"<p>Você pode usar um caractere diferente como separador.</p>\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importar domínios"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Forneça um arquivo CSV onde as linhas respeitam um dos seguintes formatos:\n"
"<ul>\n"
"<li><em>account; loginname; password; first name; last name; enabled; group; "
"address; cota; [, 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 é mandatório e deve ser igual a um dos "
"valores indicados.</p>\n"
"\n"
"<p>Você pode usar um caractere diferente como separador.</p>\n"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Importar identidades"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Encaminhamento atualizado"
@@ -0,0 +1,56 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Paulino Michelazzo <paulino@michelazzo.com.br>, 2014
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2015-08-10 20:19+0000\n"
"Last-Translator: Rafael Barretto Alves <ch3yd@hotmail.com>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/tonio/modoboa/"
"language/pt_BR/)\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Nenhum domínio a mais para mostrar"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Não remova o diretório de domínio"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Esta operação removerá TODOS os dados associados com este domínio."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Esta operação removerá todos os dados associados com este domínio, exceto "
"contas."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Nenhum administrador de domínio definido"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Nenhuma identidade a mais para mostrar"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Não remova o diretório de caixas de mensagens"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Nenhuma quota a mais para mostrar"
@@ -0,0 +1,873 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Damien Duponchelle <dduponchelle@gmail.com>, 2013
# lusitan <lusitan@hushmail.com>, 2013
# Mike C. <miguelmclara@gmail.com>, 2012
# Mike C. <miguelmclara@gmail.com>, 2013-2014
# Sandra Ribeiro <sandrarib@gmail.com>, 2012-2013
# Sandra Ribeiro <sandrarib@gmail.com>, 2012
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2014-12-29 12:51+0000\n"
"Last-Translator: Mike C. <miguelmclara@gmail.com>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
"modoboa/language/pt_PT/)\n"
"Language: pt_PT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Caixas de Correio"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Lidar com caixas de correio no sistema de ficheiros"
#: admin/app_settings.py:60
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"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Dono das caixas de correio"
#: admin/app_settings.py:69
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"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Quota pré-definida para o domínio"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Quota (em MB) a aplicar a domínios recentemente criados sem valor "
"especificado. Um valor de 0 implica sem quota."
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Remoção automática de conta"
#: admin/app_settings.py:86
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"
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Criação automatica de domino/caixa de correio"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
"Criar um dominio e uma caixa de correio quando uma conta é criada "
"automátiamente."
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Tem de ser um inteiro, positivo"
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Administração"
#: admin/callbacks.py:46
#, fuzzy
#| msgid "Create a domain administrator"
msgid "Domain administrator"
msgstr "Criar um administrador de domínio"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domínios"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Identidades"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Reencaminhar"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Importação da conta falhou (%s): domínio não existe"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "Caixa de correio %s já existe"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Importação da conta falhou (%s): valor para quota inválido"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "'Alias' de domínio"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Nome de Utilizador"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
#: admin/forms/account.py:37
msgid "Role"
msgstr "Papel"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Escolher"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Que nível de permissões terá o utilizador"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Password"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Confirmação"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Repita a password acima, para verificação."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Ativo"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr ""
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Os dois campos de password não são idênticos."
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Não pode desativar a sua própria conta"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-mail"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Quota"
#: admin/forms/account.py:174
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)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias(es)"
#: admin/forms/account.py:185
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)."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "O domínio não existe"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Domínio(s)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Domínio(s) administrado pelo utilizador"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Geral"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Correio"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Permissões"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Conta actualizada"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nova conta"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Conta criada"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Endereço de e-mail"
#: admin/forms/alias.py:25
#, fuzzy
#| msgid ""
#| "The alias address (without the domain part). For a 'catch-all' address, "
#| "just enter an * character."
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"O endereço do alias (sem a parte do domínio). Para um endereço 'cath-all', "
"digite apenas o caractere *"
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Destinatários"
#: admin/forms/alias.py:33
#, fuzzy
#| msgid ""
#| "Mailbox(es) this alias will point to. Indicate only one address per "
#| "input, press ENTER to add a new input."
msgid ""
"Addresses 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."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Não tem acesso a este domínio"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Sem destinatário definido"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Domínio"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Tipo"
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
"Quota (em MB) aplicada às caixas de correio. Deixe em branco para manter o "
"valor pré-definido."
#: admin/forms/domain.py:46
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."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "Já existe um(a) %s com este nome. "
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "domínio"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Criar um administrador de domínio"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Criar automaticamente um administrador para este domínio"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Nome"
#: admin/forms/domain.py:211
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."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr " Criar aliases"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Criar automaticamente aliases para este domínio"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Formato inválido"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "O utilizador '%s' já existe"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Domínio alterado"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Opções"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Novo domínio"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Domínio criado"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Nome do ficheiro"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Separador"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Destinatário(s)"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Indicar um ou mais destinatários separados por ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Conservar cópias locais"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr ""
"Reencaminhar mensagens e armazenar cópias na sua caixa de correio local"
#: admin/forms/forward.py:42
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."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Escolha um ficheiro"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Continua com erro"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Não tratar objetos duplicados como erro"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Passwords encriptadas"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr "Marque esta opção se as senhas contidas no arquivo não são encriptadas"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "É necessária uma caixa de correio"
#: admin/lib.py:120
#, fuzzy
#| msgid "You can't delete your own domain"
msgid "You are not allowed to import domains"
msgstr "Não é possível eliminar o seu próprio domínio"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "endereços"
#: admin/models/alias.py:28
#, fuzzy
#| msgid "The alias name"
msgid "The alias address."
msgstr "O nome do 'alias'"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "ativo"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Marcar para ativar este alias"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "lista de distribuição"
#: admin/models/alias.py:80
msgid "forward"
msgstr "reencaminhar"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
#, fuzzy
#| msgid "Invalid request"
msgid "Invalid address"
msgstr "Pedido inválido"
#: admin/models/alias.py:133
#, fuzzy
#| msgid "Local recipient %s not found"
msgid "Local recipient {}@{} not found"
msgstr "O destinatário local %s não foi encontrado"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Linha inválida: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "O domínio '%s' não existe"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "nome"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "O nome do domínio"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Marcar para ativar este domínio"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Linha inválida"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Valor de quota inválido para o dominio '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "O nome do 'alias'"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "alvo"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "O domínio para o qual este alias irá apontar"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Domínio desconhecido: %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Endereços das caixas de correio (sem a parte @domain.tld)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Falha ao recuperar a localização da caixa de correio (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "A quota excede o limite permitido pelo domínio (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "É necessário uma quota"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "O dominio está inativo"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "Não pode criar administradores de domínio"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Estatísticas"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domínios e identidades"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Contadores globais"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Quantidade"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Aliases de domínio"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "Contadores por-domínio"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Tags"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Ações"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "inativo"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Administradores"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Vários"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Nenhum administrador de domínio definido"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Data de criação"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Data da última modificação"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Aliases para caixas de correio"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Reenviar automaticamente e-mails para outro endereço"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Actualizar"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Nome Completo / Destinatário"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Ajuda"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "A importar..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Escolha um valor"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Usar um valor padrão de domínio"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Endereço"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Valor"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Limite"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Utilização (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Procurar"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "enabled_m"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "enabled_f"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Listar domínios"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Adicionar domínio"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Importar"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Exportar"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Listar identidades"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Listar as quotas"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Adicionar conta"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Adicionar 'alias'"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Adicionar reencaminhamento"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Adicionar lista de distribuição"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Visualizar as identidades do domínio"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Eliminar %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Remover esta permissão"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Já existe um 'alias' com este nome"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Criar"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Nova lista de distribuição"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "A lista de distribuição foi criada"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Novo 'alias'"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias criado"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Novo reencaminhamento"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Reencaminhamento criado"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Lista de distribuição modificada"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Reencaminhamento modificado"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "'Alias' modificado"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Não é possível eliminar o seu próprio domínio"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Domínio criado"
msgstr[1] "Domínios eliminados"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Exportar identidades"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Exportar domínios"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Pedido inválido"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Conta eliminada"
msgstr[1] "Contas eliminadas"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "Este objeto já existe: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d objetos importados com sucesso"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importar domínios"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Importar identidades"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Reencaminhamento atualizado"
@@ -0,0 +1,61 @@
# 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
# lusitan <lusitan@hushmail.com>, 2013
# Mike C. <miguelmclara@gmail.com>, 2012
# Mike C. <miguelmclara@gmail.com>, 2012
# Mike C. <miguelmclara@gmail.com>, 2014
# Sandra Ribeiro <sandrarib@gmail.com>, 2013
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2014-12-29 12:08+0000\n"
"Last-Translator: Mike C. <miguelmclara@gmail.com>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/"
"modoboa/language/pt_PT/)\n"
"Language: pt_PT\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Não existem mais dominios para mostrar"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Não excluir o diretório do domínio"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Esta operação irá remover TODOS os dados associados a este domínio."
#: static/admin/js/admin.js:148
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."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Não está definido nenhum administrador para o domínio"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Não existem mais identidades para mostrar"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Não excluir o diretório da caixa de correio"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Não existem mais quotas para mostrar"
@@ -0,0 +1,871 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2013-12-03 09:35+0000\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/modoboa/language/"
"ru/)\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Почтовые ящики"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Обрабатывать почтовые ящики на диске"
#: admin/app_settings.py:60
#, fuzzy
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Удалять или переименовывать почтовые ящики на диске, если они удалены или "
"переименованы в Modoboa"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Почтовые ящики закончились"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "UNIX аккаунт владельца почтового ящика на диске"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr ""
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Автоматическое удаление аккаунта"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr "При удалении почтового ящика, так же удалять и аккаунт владельца"
#: admin/app_settings.py:90
#, fuzzy
msgid "Automatic domain/mailbox creation"
msgstr "Домен создан"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr ""
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr ""
#: admin/app_settings.py:149
msgid "Administration"
msgstr ""
#: admin/callbacks.py:46
#, fuzzy
#| msgid "Create a domain administrator"
msgid "Domain administrator"
msgstr "Создание администратора домена"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Домены"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Пользователи"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Перенаправленые"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Ошибка импорта аккаунта (%s): домен должен существовать "
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "Почтовый ящик %s существует"
#: admin/callbacks.py:181
#, fuzzy, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Ошибка импорта аккаунта (%s): домен должен существовать "
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr ""
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Логин пользователя"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
#: admin/forms/account.py:37
msgid "Role"
msgstr "Роль"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Выбрать"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Разрешения для этого пользователя"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Пароль"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Подтверждение"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Введите такой же пароль как выше. Для проверки."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Разрешено"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr ""
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Пароли не совпадают"
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Вы не можете запретить собственный аккаунт"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "Е-майл"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Квота"
#: admin/forms/account.py:174
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 ""
"Квота в мегабайтах для этого почтового ящика. Определите собственное "
"значение или используйте значение по умолчанию для родительского домена. "
"Оставьте пустым для неограниченного значения. (Не разрешено администраторам "
"домена)"
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Синоним(ы)"
#: admin/forms/account.py:185
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 ""
"Синонимы этого почтового ящика. Вводите один адрес за раз. Нажмите ENTER для "
"добавления. Используйте '*' для создания синонима 'для всех' (*@domain.tld)"
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Домен не существует"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Домен(ы)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Домен(ы) администрируемые пользователем"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Основные"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Почта"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Права"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "аккаунт изменен"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Новый аккаунт"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Аккаунт создан"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "Е-майл адрес"
#: admin/forms/alias.py:25
#, fuzzy
#| msgid ""
#| "The alias address (without the domain part). For a 'catch-all' address, "
#| "just enter an * character."
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"Адреса синонимов (без доменной части). Для указания адреса 'для всех' "
"введите символ *"
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Получатели"
#: admin/forms/alias.py:33
#, fuzzy
#| msgid ""
#| "Mailbox(es) this alias will point to. Indicate only one address per "
#| "input, press ENTER to add a new input."
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Этот синоним почтового ящика. Вводите один адрес за раз. Нажмите ENTER для "
"добавления."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Вы не имеете права доступа к этому домену"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Получатель не определен"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr ""
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr ""
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Синоним(ы) этого домена. Вводите одно имя за раз. Нажмите ENTER для "
"добавления."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr ""
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr ""
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Создание администратора домена"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Автоматически создаст администратора для этого домена"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Логин"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"Имя администратора. Не включайте имя домена оно будет добавлено "
"автоматически."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Создание синонимов"
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Автоматически создает стандартные синонимы для этого домена"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Неверный формат"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "Пользователь '%s' уже существует"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Домен изменен"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Опции"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Новый домен"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Домен создан"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Имя файла"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Разделитель"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Получатели"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Укажите одного или нескольких получателей, разделенных запятой ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Сохранить локальную копию"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr ""
"Перенаправление сообщения, и сохранение копии в вашем локальном почтовом "
"ящике"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Вы не можете определить перенаправление на локальный адрес. Пожалуйста, "
"попросите администратора создать синоним"
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Выберите файл"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Продолжать при ошибке"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Не исправлять дублирующийся объект при ошибке"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Шифровать пароль"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr "Выберите эту опцию если пароль в вашем файле не зашифрован "
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr ""
#: admin/lib.py:120
#, fuzzy
#| msgid "You can't delete your own domain"
msgid "You are not allowed to import domains"
msgstr "Вы не можете удалить собственный домен"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "адрес"
#: admin/models/alias.py:28
#, fuzzy
#| msgid "The alias name"
msgid "The alias address."
msgstr "Имя синонима"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "Разрешен"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Отметить для активации синонима"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "Список рассылки"
#: admin/models/alias.py:80
msgid "forward"
msgstr "Перенаправление"
#: admin/models/alias.py:81
msgid "alias"
msgstr "Синоним"
#: admin/models/alias.py:112
#, fuzzy
#| msgid "Invalid request"
msgid "Invalid address"
msgstr "Ошибочный запрос"
#: admin/models/alias.py:133
#, fuzzy
#| msgid "Local recipient %s not found"
msgid "Local recipient {}@{} not found"
msgstr "Локальный получатель %s не найден"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Неверная строка %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "Домен '%s' не существует"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "имя"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Имя домена"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Отметить для активации этого домена"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Ошибочная строка"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr ""
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Имя синонима"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "адресат"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "Домен этого синонима указывает на"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Неизвестный домен %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Адрес почтового ящика (без @domain.tld)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Ошибка получения места находжения почтового ящика (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "Квота больше чем разрешено на уровне домена (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "Требуется квота"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
#, fuzzy
msgid "Domain is disabled"
msgstr "Домен удален"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "Мб"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr ""
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Статистика"
#: admin/templates/admin/domain_statistics.html:15
#, fuzzy
msgid "Domains and identities"
msgstr "Список пользователей"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr ""
#: admin/templates/admin/domain_statistics.html:27
#, fuzzy
msgid "Quantity"
msgstr "Карантин"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Синонимы доменов"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr ""
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Теги"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Действие"
#: admin/templates/admin/domains_table.html:4
#, fuzzy
msgid "disabled"
msgstr "Разрешен"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Администраторы"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Прочее"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Администратор домена не определен"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Дата создания"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Дата последней модификации"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Синонимы почтового ящика"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Автоматически перенаправлять письма на другой адрес"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Обновление"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Полное имя/получатель"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Подсказка"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Импорт..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Выбрать значение"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Используются значения домена по умолчанию "
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Адрес"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Значение"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Лимит"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "использование (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Поиск"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "enabled_m"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "enabled_f"
#: admin/templatetags/admin_tags.py:40
#, fuzzy
msgid "List domains"
msgstr "Список квот"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Добавить домен"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Импорт"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Экспорт"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Список пользователей"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Список квот"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Добавить аккаунт"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Добавить синоним"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Добавить пересылку"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Добавить список рассылки"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Информация о домене"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Удалить %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Удалить это разрешение"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Синоним с таким именем уже есть"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Создать"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Новый список рассылки"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Список рассылки создан"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Новый синоним"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Синоним создан"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Новая пересылка"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Пересылка создана"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Список рассылки изменен"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Пересылка изменена"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Синоним изменен"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Вы не можете удалить собственный домен"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Домен удален"
msgstr[1] "Домена удалено"
msgstr[2] "Доменов удалено"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Экспорт пользователя"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Экспорт доменов"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Ошибочный запрос"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Аккаунт удален"
msgstr[1] "Аккаунта удалено"
msgstr[2] "Аккаунтов удалено"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "Объект уже существует: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d объектов импортировано"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Импорт доменов"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Импорт информации о пользователях"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Перенаправление обновлено"
@@ -0,0 +1,56 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2013-12-03 09:35+0000\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/modoboa/language/"
"ru/)\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr ""
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Не удаляйте каталог домена"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Эта операция удалит ВСЕ данные, связанные с этим доменом."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Эта операция удалит ВСЕ данные, связанные с этим доменом, за исключением "
"аккаунтов."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Не указан администратор домена"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr ""
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Не удаляйте каталог почтовых ящиков"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr ""
@@ -0,0 +1,889 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Martin Persson <martin.jens.persson@gmail.com>, 2014
# Olle Gustafsson <olle@dalnix.se>, 2013
# Olle Gustafsson <olle@dalnix.se>, 2013,2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 18:24+0100\n"
"PO-Revision-Date: 2015-01-13 15:18+0000\n"
"Last-Translator: Olle Gustafsson <olle@dalnix.se>\n"
"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/"
"sv/)\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin/app_settings.py:54 admin/templates/admin/editdomainform.html:25
msgid "Mailboxes"
msgstr "Brevlådor"
#: admin/app_settings.py:57
msgid "Handle mailboxes on filesystem"
msgstr "Hantera brevlådor på filsystem"
#: admin/app_settings.py:60
msgid ""
"Rename or remove mailboxes on the filesystem when they get renamed or "
"removed within Modoboa"
msgstr ""
"Byt namn på eller ta bort brevlådor på filsystemet när de kommer byta namn "
"eller tas bort inom Modoboa"
#: admin/app_settings.py:66
msgid "Mailboxes owner"
msgstr "Brevlådans ägare"
#: admin/app_settings.py:69
msgid "The UNIX account who owns mailboxes on the filesystem"
msgstr "Den UNIX-konto som äger brevlådor på filsystemet"
#: admin/app_settings.py:74
msgid "Default domain quota"
msgstr "Standard domän kvota"
#: admin/app_settings.py:77
msgid ""
"Default quota (in MB) applied to freshly created domains with no value "
"specified. A value of 0 means no quota."
msgstr ""
"Standard kvota (i MB) applicerad på nyskapade domäner som inte har något "
"värde specificerat. Ett värde på 0 betyder ingen kvota."
#: admin/app_settings.py:83
msgid "Automatic account removal"
msgstr "Automatisk konto borttagning"
#: admin/app_settings.py:86
msgid "When a mailbox is removed, also remove the associated account"
msgstr "När en brevlåda raderas, radera också tillhörande konto"
#: admin/app_settings.py:90
msgid "Automatic domain/mailbox creation"
msgstr "Automatisk skapande av domän/brevlåda"
#: admin/app_settings.py:93
msgid "Create a domain and a mailbox when an account is automatically created."
msgstr "Skapa en domän och en brevlåda när ett konto skapas automatiskt."
#: admin/app_settings.py:139 admin/forms/account.py:239
#: admin/forms/domain.py:77
msgid "Must be a positive integer"
msgstr "Måste vara ett positivt heltal"
#: admin/app_settings.py:149
msgid "Administration"
msgstr "Administration"
#: admin/callbacks.py:46
#, fuzzy
#| msgid "Create a domain administrator"
msgid "Domain administrator"
msgstr "Skapa en domän administratör"
#: admin/callbacks.py:75 admin/templates/admin/domain_statistics.html:30
#: admin/templates/admin/domains.html:3
msgid "Domains"
msgstr "Domäner"
#: admin/callbacks.py:85 admin/templates/admin/domain_statistics.html:36
#: admin/templates/admin/domain_statistics.html:50
#: admin/templates/admin/identities.html:5
msgid "Identities"
msgstr "Konton"
#: admin/callbacks.py:100 admin/templates/admin/forward.html:2
msgid "Forward"
msgstr "Vidarebefordra"
#: admin/callbacks.py:163
#, python-format
msgid "Account import failed (%s): domain does not exist"
msgstr "Konto import misslyckades (%s): domänen existerar inte"
#: admin/callbacks.py:173 admin/forms/account.py:257
#, python-format
msgid "Mailbox %s already exists"
msgstr "Brevlåda %s finns redan"
#: admin/callbacks.py:181
#, python-format
msgid "Account import failed (%s): wrong quota value"
msgstr "Importering av konto misslyckades (%s): Fel kvota värde"
#: admin/callbacks.py:216 admin/forms/domain.py:94
msgid "domain alias"
msgstr "domän alias"
#: admin/forms/account.py:30 admin/templates/admin/editdomainform.html:13
msgid "Username"
msgstr "Användarnamn"
#: admin/forms/account.py:32
msgid ""
"The user's name. Must be a valid e-mail address for simple users or "
"administrators with a mailbox."
msgstr ""
"Användarens namn. Måste vara en giltig e-postadress för vanliga användare "
"eller för administratörer med brevlåda."
#: admin/forms/account.py:37
msgid "Role"
msgstr "Roll"
#: admin/forms/account.py:38 admin/forms/account.py:75
msgid "Choose"
msgstr "Välj"
#: admin/forms/account.py:39
msgid "What level of permission this user will have"
msgstr "Vilken nivå av behörighet den här användaren har"
#: admin/forms/account.py:42
msgid "Password"
msgstr "Lösenord"
#: admin/forms/account.py:45
msgid "Confirmation"
msgstr "Bekräftelse"
#: admin/forms/account.py:48
msgid "Enter the same password as above, for verification."
msgstr "Ange samma lösenord som ovan för verifiering."
#: admin/forms/account.py:66
msgid "Enabled"
msgstr "Aktiverad"
#: admin/forms/account.py:141
msgid "Only super administrators are allowed for this mode"
msgstr ""
#: admin/forms/account.py:150
msgid "The two password fields didn't match."
msgstr "Dessa två lösnordsfält matchar inte."
#: admin/forms/account.py:156
msgid "You can't disable your own account"
msgstr "Du kan inte inaktivera ditt eget konto"
#: admin/forms/account.py:170
msgid "E-mail"
msgstr "E-post"
#: admin/forms/account.py:172 admin/forms/domain.py:35
msgid "Quota"
msgstr "Kvot"
#: admin/forms/account.py:174
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 ""
"Kvot i MB för denna brevlåda. Definiera ett anpassat värde eller använda "
"domänens förvalda. Lämna tomt för att definiera ett obegränsat värde (ej "
"tillåtet för domänadministratörer)."
#: admin/forms/account.py:182 admin/forms/domain.py:43
#: admin/templates/admin/domains.html:26
msgid "Alias(es)"
msgstr "Alias"
#: admin/forms/account.py:185
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 för denna brevlåda. Ange endast en adress per rad, tryck ENTER för att "
"lägga till ny rad. Använd '*' för att skapa ett 'catchall' alias (ex: "
"*@domain.tld)."
#: admin/forms/account.py:248 admin/forms/account.py:279
#: admin/forms/alias.py:72
msgid "Domain does not exist"
msgstr "Domän existerar inte"
#: admin/forms/account.py:355
msgid "Domain(s)"
msgstr "Domän(er)"
#: admin/forms/account.py:357
msgid "Domain(s) that user administrates"
msgstr "Domän(er) användaren administrerar"
#: admin/forms/account.py:401 admin/forms/account.py:487
#: admin/forms/domain.py:300 admin/forms/domain.py:370
msgid "General"
msgstr "Generellt"
#: admin/forms/account.py:406 admin/forms/account.py:493
msgid "Mail"
msgstr "Post"
#: admin/forms/account.py:409
msgid "Permissions"
msgstr "Rättigheter"
#: admin/forms/account.py:476
msgid "Account updated"
msgstr "Konto uppdaterat"
#: admin/forms/account.py:500
msgid "New account"
msgstr "Nytt konto"
#: admin/forms/account.py:512
msgid "Account created"
msgstr "Konto skapat"
#: admin/forms/alias.py:23
msgid "Email address"
msgstr "E-post adress"
#: admin/forms/alias.py:25
#, fuzzy
#| msgid ""
#| "The alias address (without the domain part). For a 'catch-all' address, "
#| "just enter an * character."
msgid ""
"The alias address. To create a catchall alias, just enter the domain name "
"(@domain.tld)."
msgstr ""
"Alias adress (utan domän delen). För en \"catch-all\"-adress, ange bara "
"asterisk *."
#: admin/forms/alias.py:31
msgid "Recipients"
msgstr "Mottagare"
#: admin/forms/alias.py:33
#, fuzzy
#| msgid ""
#| "Mailbox(es) this alias will point to. Indicate only one address per "
#| "input, press ENTER to add a new input."
msgid ""
"Addresses this alias will point to. Indicate only one address per input, "
"press ENTER to add a new input."
msgstr ""
"Brevlåda/or detta alias kommer att peka på. Ange endast en adress per "
"inlägg, tryck ENTER för att lägga till en ny inlägg."
#: admin/forms/alias.py:75
msgid "You don't have access to this domain"
msgstr "Du har inte åtkomst till denna domän"
#: admin/forms/alias.py:85
msgid "No recipient defined"
msgstr "Ingen mottagare definierad"
#: admin/forms/domain.py:22 admin/models/domain.py:76
#: admin/templates/admin/domain_statistics.html:49
msgid "Domain"
msgstr "Domän"
#: admin/forms/domain.py:32 admin/templates/admin/domain_statistics.html:26
msgid "Type"
msgstr "Typ"
#: admin/forms/domain.py:38
msgid ""
"Default quota in MB applied to mailboxes. Leave empty to use the default "
"value."
msgstr ""
"Standard kvota i MB tillämpad på nya brevlådor. Lämna tomt för att använda "
"standardvärdet."
#: admin/forms/domain.py:46
msgid ""
"Alias(es) of this domain. Indicate only one name per input, press ENTER to "
"add a new input."
msgstr ""
"Alias för denna domän. Ange endast ett namn per rad, tryck ENTER för att "
"lägga till en rad."
#: admin/forms/domain.py:97 admin/forms/domain.py:108
#: admin/forms/domain.py:116
#, python-format
msgid "A %s with this name already exists"
msgstr "En %s med detta namn finns redan"
#: admin/forms/domain.py:108 admin/forms/domain.py:113
msgid "domain"
msgstr "domän"
#: admin/forms/domain.py:200
msgid "Create a domain administrator"
msgstr "Skapa en domän administratör"
#: admin/forms/domain.py:203
msgid "Automatically create an administrator for this domain"
msgstr "Skapa en domän administratör automatiskt för denna domän"
#: admin/forms/domain.py:208 admin/templates/admin/domains.html:25
#: admin/templates/admin/editdomainform.html:13
#: admin/templates/admin/identity_headers.html:2
msgid "Name"
msgstr "Namn"
#: admin/forms/domain.py:211
msgid ""
"The administrator's name. Don't include the domain's name here, it will be "
"automatically appended."
msgstr ""
"Administratörens namn. Inkludera inte domänens namn här, den kommer att "
"läggas till automatiskt."
#: admin/forms/domain.py:218
msgid "Create aliases"
msgstr "Skapa alias."
#: admin/forms/domain.py:221
msgid "Automatically create standard aliases for this domain"
msgstr "Skapa automatiskt standard alias för den här domänen"
#: admin/forms/domain.py:240
msgid "Invalid format"
msgstr "Ogiltigt format"
#: admin/forms/domain.py:257
#, python-format
msgid "User '%s' already exists"
msgstr "Användare '%s' finns redan"
#: admin/forms/domain.py:359
msgid "Domain modified"
msgstr "Domän ändrad"
#: admin/forms/domain.py:379
msgid "Options"
msgstr "Inställningar"
#: admin/forms/domain.py:387
msgid "New domain"
msgstr "Ny domän"
#: admin/forms/domain.py:405
msgid "Domain created"
msgstr "Domän skapad"
#: admin/forms/export.py:7
msgid "File name"
msgstr "Filnamn"
#: admin/forms/export.py:13 admin/forms/import_.py:13
msgid "Separator"
msgstr "Separator"
#: admin/forms/forward.py:14
msgid "Recipient(s)"
msgstr "Mottagare"
#: admin/forms/forward.py:18
msgid "Indicate one or more recipients separated by a ','"
msgstr "Ange en eller flera mottagare separerade med ett ','"
#: admin/forms/forward.py:21
msgid "Keep local copies"
msgstr "Behåll lokal kopia"
#: admin/forms/forward.py:24
msgid "Forward messages and store copies into your local mailbox"
msgstr "Vidarebefordra meddelande och spara kopia i din lokala brevlåda"
#: admin/forms/forward.py:42
msgid ""
"You can't define a forward to a local destination. Please ask your "
"administrator to create an alias instead."
msgstr ""
"Du kan inte definiera en vidarebefordring till en lokal destination. Be din "
"administratör att skapa ett alias istället."
#: admin/forms/import_.py:11
msgid "Select a file"
msgstr "Välj en fil"
#: admin/forms/import_.py:19
msgid "Continue on error"
msgstr "Fortsätt vid fel"
#: admin/forms/import_.py:20
msgid "Don't treat duplicated objects as error"
msgstr "Behandla inte dubletter som fel"
#: admin/forms/import_.py:38
msgid "Crypt passwords"
msgstr "Krypterade lösenord"
#: admin/forms/import_.py:40
msgid "Check this option if passwords contained in your file are not crypted"
msgstr "Markera det här alternativet om lösenord i filen inte är krypterad"
#: admin/lib.py:30
msgid "A mailbox is required"
msgstr "En brevlåda krävs"
#: admin/lib.py:120
#, fuzzy
#| msgid "You can't delete your own domain"
msgid "You are not allowed to import domains"
msgstr "Du kan inte radera din egen domän"
#: admin/lib.py:129
msgid "You are not allowed to import domain aliases."
msgstr ""
#: admin/models/alias.py:26 admin/models/mailbox.py:77
msgid "address"
msgstr "adress"
#: admin/models/alias.py:28
#, fuzzy
#| msgid "The alias name"
msgid "The alias address."
msgstr "Alias namn"
#: admin/models/alias.py:33 admin/models/domain.py:37
#: admin/models/domain_alias.py:24
msgid "enabled"
msgstr "aktiverad"
#: admin/models/alias.py:34 admin/models/domain_alias.py:25
msgid "Check to activate this alias"
msgstr "Markera för aktivera detta alias"
#: admin/models/alias.py:79
msgid "distribution list"
msgstr "distributionslista"
#: admin/models/alias.py:80
msgid "forward"
msgstr "vidarebefordra"
#: admin/models/alias.py:81
msgid "alias"
msgstr "alias"
#: admin/models/alias.py:112
#, fuzzy
#| msgid "Invalid request"
msgid "Invalid address"
msgstr "Ogiltig förfrågan"
#: admin/models/alias.py:133
#, fuzzy
#| msgid "Local recipient %s not found"
msgid "Local recipient {}@{} not found"
msgstr "Lokal mottagare %s kunde inte hittas"
#: admin/models/alias.py:162
#, python-format
msgid "Invalid line: %s"
msgstr "Ogiltig rad: %s"
#: admin/models/alias.py:168
#, python-format
msgid "Domain '%s' does not exist"
msgstr "Domänen '%s' existerar inte"
#: admin/models/domain.py:33 admin/models/domain_alias.py:17
msgid "name"
msgstr "namn"
#: admin/models/domain.py:34
msgid "The domain name"
msgstr "Domännamnet"
#: admin/models/domain.py:38
msgid "Check to activate this domain"
msgstr "Markera för att aktivera denna domän"
#: admin/models/domain.py:160 admin/models/domain_alias.py:47
msgid "Invalid line"
msgstr "Ogiltig rad"
#: admin/models/domain.py:168
#, python-format
msgid "Invalid quota value for domain '%s'"
msgstr "Ogiltig kvota värde för domänen '%s'"
#: admin/models/domain_alias.py:18
msgid "The alias name"
msgstr "Alias namn"
#: admin/models/domain_alias.py:20
msgid "target"
msgstr "mål"
#: admin/models/domain_alias.py:21
msgid "The domain this alias points to"
msgstr "Domän dit detta alias pekar mot"
#: admin/models/domain_alias.py:56
#, python-format
msgid "Unknown domain %s"
msgstr "Okänt kommando %s"
#: admin/models/mailbox.py:79
msgid "Mailbox address (without the @domain.tld part)"
msgstr "Brevlåde adress (utan @domain.tld)"
#: admin/models/mailbox.py:144
#, python-format
msgid "Failed to retrieve mailbox location (%s)"
msgstr "Kunde inte hitta brevlådans sökväg (%s)"
#: admin/models/mailbox.py:233
#, python-format
msgid "Quota is greater than the allowed domain's limit (%dM)"
msgstr "Kvotan är större än domänens gräns (%dM)"
#: admin/models/mailbox.py:239
msgid "A quota is required"
msgstr "En kvota krävs"
#: admin/templates/admin/account_general_form.html:11
#: admin/templates/admin/aliasform.html:8
msgid "Domain is disabled"
msgstr "Domänen är inaktiverad"
#: admin/templates/admin/domain_general_form.html:4
#: admin/templates/admin/mailform.html:3
#: admin/templates/admin/newdomainform.html:3
msgid "MB"
msgstr "MB"
#: admin/templates/admin/domain_options_form.html:7
msgid "You can't create a new domain administrator"
msgstr "Du kan inte skapa en ny domänadministratör"
#: admin/templates/admin/domain_statistics.html:15
#: admin/templatetags/admin_tags.py:45
msgid "Statistics"
msgstr "Statistik"
#: admin/templates/admin/domain_statistics.html:15
msgid "Domains and identities"
msgstr "Domäner och konton"
#: admin/templates/admin/domain_statistics.html:22
msgid "Global counters"
msgstr "Globala räknare"
#: admin/templates/admin/domain_statistics.html:27
msgid "Quantity"
msgstr "Mängd"
#: admin/templates/admin/domain_statistics.html:33
#: admin/templates/admin/editdomainform.html:24
msgid "Domain aliases"
msgstr "Domän alias"
#: admin/templates/admin/domain_statistics.html:45
msgid "Per-domain counters"
msgstr "Per domän räknare"
#: admin/templates/admin/domains.html:27
#: admin/templates/admin/identity_headers.html:4
msgid "Tags"
msgstr "Tagg"
#: admin/templates/admin/domains.html:28
#: admin/templates/admin/identity_headers.html:5
msgid "Actions"
msgstr "Åtgärder"
#: admin/templates/admin/domains_table.html:4
msgid "disabled"
msgstr "inaktiverad"
#: admin/templates/admin/editdomainform.html:4
msgid "Administrators"
msgstr "Administratörer"
#: admin/templates/admin/editdomainform.html:7
msgid "Miscellaneous"
msgstr "Övrigt"
#: admin/templates/admin/editdomainform.html:19
msgid "No domain administrator defined"
msgstr "Ingen domän administratör definierad"
#: admin/templates/admin/editdomainform.html:22
msgid "Creation date"
msgstr "Skapelsedatum"
#: admin/templates/admin/editdomainform.html:23
msgid "Last modification date"
msgstr "Senast ändrad"
#: admin/templates/admin/editdomainform.html:26
msgid "Mailbox aliases"
msgstr "Brevlåde alias"
#: admin/templates/admin/forward.html:2
msgid "Automatically forward emails to another address"
msgstr "Vidarebefordra automatiskt e-post till en annan adress"
#: admin/templates/admin/forward.html:9 admin/views/alias.py:113
msgid "Update"
msgstr "Uppdatera"
#: admin/templates/admin/identity_headers.html:3
msgid "Fullname/Recipient"
msgstr "Fullständigt namn/Mottagare"
#: admin/templates/admin/importform.html:6
msgid "Help"
msgstr "Hjälp"
#: admin/templates/admin/importform.html:13
msgid "Importing..."
msgstr "Importerar..."
#: admin/templates/admin/mailboxes_raw.html:3
msgid "Choose a value"
msgstr "Välj ett värde"
#: admin/templates/admin/mailform.html:3
msgid "Use domain default value"
msgstr "Använd domänens standardvärde"
#: admin/templates/admin/quota_headers.html:2
msgid "Address"
msgstr "Adress"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
msgstr "Värde"
#: admin/templates/admin/quota_headers.html:4
msgid "Limit"
msgstr "Gräns"
#: admin/templates/admin/quota_headers.html:5
#, python-format
msgid "Usage (%%)"
msgstr "Användning (%%)"
#: admin/templates/admin/searchform.html:6
msgid "Search"
msgstr "Sök"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_m"
msgstr "enabled_m"
#: admin/templatetags/admin_tags.py:19
msgid "enabled_f"
msgstr "enabled_f"
#: admin/templatetags/admin_tags.py:40
msgid "List domains"
msgstr "Lista domäner"
#: admin/templatetags/admin_tags.py:53
msgid "Add domain"
msgstr "Lägg till domän"
#: admin/templatetags/admin_tags.py:62 admin/templatetags/admin_tags.py:126
#: admin/views/import_.py:99 admin/views/import_.py:133
msgid "Import"
msgstr "Importera"
#: admin/templatetags/admin_tags.py:68 admin/templatetags/admin_tags.py:132
#: admin/views/export.py:41 admin/views/export.py:69
msgid "Export"
msgstr "Exportera"
#: admin/templatetags/admin_tags.py:92
msgid "List identities"
msgstr "Lista konton"
#: admin/templatetags/admin_tags.py:97
msgid "List quotas"
msgstr "Lista kvoter"
#: admin/templatetags/admin_tags.py:102
msgid "Add account"
msgstr "Lägg till konto"
#: admin/templatetags/admin_tags.py:108
msgid "Add alias"
msgstr "Lägg till alias"
#: admin/templatetags/admin_tags.py:114
msgid "Add forward"
msgstr "Lägg till vidarebefodring"
#: admin/templatetags/admin_tags.py:120
msgid "Add distribution list"
msgstr "Lägg till distributionslista"
#: admin/templatetags/admin_tags.py:152
msgid "View the domain's identities"
msgstr "Visa domänens konton"
#: admin/templatetags/admin_tags.py:159 admin/templatetags/admin_tags.py:182
#: admin/templatetags/admin_tags.py:190
#, python-format
msgid "Delete %s?"
msgstr "Radera %s?"
#: admin/templatetags/admin_tags.py:267
msgid "Remove this permission"
msgstr "Radera detta tillstånd"
#: admin/views/alias.py:29
msgid "Alias with this name already exists"
msgstr "Alias med detta namn existerar redan"
#: admin/views/alias.py:53
msgid "Create"
msgstr "Skapa"
#: admin/views/alias.py:65
msgid "New distribution list"
msgstr "Ny distributionslista"
#: admin/views/alias.py:67
msgid "Distribution list created"
msgstr "Distributionslista skapad"
#: admin/views/alias.py:76
msgid "New alias"
msgstr "Nytt alias"
#: admin/views/alias.py:77
msgid "Alias created"
msgstr "Alias skapat"
#: admin/views/alias.py:86
msgid "New forward"
msgstr "Ny vidarebefodring"
#: admin/views/alias.py:87
msgid "Forward created"
msgstr "Vidarebefodring skapad"
#: admin/views/alias.py:101
msgid "Distribution list modified"
msgstr "Distributionslista ändrad"
#: admin/views/alias.py:103
msgid "Forward modified"
msgstr "Vidarebefodring ändrad"
#: admin/views/alias.py:105
msgid "Alias modified"
msgstr "Alias ändrat"
#: admin/views/domain.py:132
msgid "You can't delete your own domain"
msgstr "Du kan inte radera din egen domän"
#: admin/views/domain.py:135
msgid "Domain deleted"
msgid_plural "Domains deleted"
msgstr[0] "Domän raderad"
msgstr[1] "Domäner raderade"
#: admin/views/export.py:40
msgid "Export identities"
msgstr "Exportera konton"
#: admin/views/export.py:68
msgid "Export domains"
msgstr "Exportera domäner"
#: admin/views/identity.py:108 admin/views/identity.py:200
#: admin/views/identity.py:205
msgid "Invalid request"
msgstr "Ogiltig förfrågan"
#: admin/views/identity.py:190
msgid "Account deleted"
msgid_plural "Accounts deleted"
msgstr[0] "Konto raderat"
msgstr[1] "Konton raderade"
#: admin/views/import_.py:64
#, python-format
msgid "Object already exists: %s"
msgstr "Objektet existerar redan: %s"
#: admin/views/import_.py:68
#, python-format
msgid "%d objects imported successfully"
msgstr "%d objekt importerade"
#: admin/views/import_.py:86
#, python-format
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
" <li><em>domain; name; quota; enabled</em></li>\n"
" <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>You can use a different character as separator.</p>\n"
msgstr ""
"Tillhandahåll en CSV fil där raderna har följande format:\n"
"<ul>\n"
"<li><em>domain; name; quota; enabled</em></li>\n"
"<li><em>domainalias; name; targeted domain; enabled</em></li>\n"
"%s\n"
"</ul>\n"
"<p>det första elementet i varje rad är obligatoriskt och måste vara lika med "
"en av de tidigare värdena.</ p>\n"
"<p>Du kan använda ett annat tecken som separator.</ p>\n"
#: admin/views/import_.py:98
msgid "Import domains"
msgstr "Importera domäner"
#: admin/views/import_.py:120
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>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"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Tillhandahåll en CSV-fil där raderna har följande format:\n"
"<ul>\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>Det första elementet i varje rad är obligatorisk och måste vara lika med "
"en av de tidigare värdena.</p>\n"
"\n"
"<p>Du kan använda ett annat tecken separator.</p>\n"
#: admin/views/import_.py:132
msgid "Import identities"
msgstr "Importera konton"
#: admin/views/user.py:34
msgid "Forward updated"
msgstr "Vidarebefordring uppdaterad"
@@ -0,0 +1,57 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Olle Gustafsson <olle@dalnix.se>, 2013
# Olle Gustafsson <olle@dalnix.se>, 2013,2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-11-09 08:20+0100\n"
"PO-Revision-Date: 2015-01-13 15:21+0000\n"
"Last-Translator: Olle Gustafsson <olle@dalnix.se>\n"
"Language-Team: Swedish (http://www.transifex.com/projects/p/modoboa/language/"
"sv/)\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/admin/js/admin.js:126
msgid "No more domain to show"
msgstr "Inga fler domäner att visa"
#: static/admin/js/admin.js:145
msgid "Do not delete domain directory"
msgstr "Radera inte domän katalog"
#: static/admin/js/admin.js:147
msgid "This operation will remove ALL data associated to this domain."
msgstr "Denna åtgärd kommer radera ALL data associerad med den här domänen."
#: static/admin/js/admin.js:148
msgid ""
"This operation will remove all data associated to this domain, excepting "
"accounts."
msgstr ""
"Den här operationen raderar all data som är associerad med denna domän, "
"förutom konton."
#: static/admin/js/admin.js:231
msgid "No domain administrator defined"
msgstr "Ingen domänadministratör definierad"
#: static/admin/js/admin.js:286 static/admin/js/admin.js.c:354
msgid "No more identity to show"
msgstr "Inga fler konton att visa"
#: static/admin/js/admin.js:328
msgid "Do not delete mailbox directory"
msgstr "Radera inte brevlådans katalog"
#: static/admin/js/admin.js:357
msgid "No more quota to show"
msgstr "Ingen mer kvota att visa"
@@ -0,0 +1,53 @@
import os.path
import sys
import csv
from progressbar import ProgressBar, Percentage, Bar, ETA
from modoboa.core.models import User
from modoboa.lib import events
from modoboa.lib.exceptions import Conflict
from ... import lib
def import_csv(filename, options):
"""Import objects from a CSV file."""
superadmin = User.objects.filter(is_superuser=True).first()
if not os.path.exists(filename):
print('File not found')
sys.exit(1)
num_lines = sum(1 for line in open(filename))
pbar = ProgressBar(
widgets=[Percentage(), Bar(), ETA()], maxval=num_lines
).start()
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=';')
i = 0
for row in reader:
i += 1
pbar.update(i)
if not row:
continue
try:
fct = getattr(lib, "import_%s" % row[0].strip())
except AttributeError:
fct = events.raiseQueryEvent(
'ImportObject', row[0].strip())
if not fct:
continue
fct = fct[0]
try:
fct(superadmin, row, options)
except Conflict:
if options['continue_if_exists']:
continue
raise Conflict(
"Object already exists: %s"
% options['sepchar'].join(row[:2])
)
pbar.finish()
@@ -0,0 +1,28 @@
import sys
import csv
from optparse import make_option
from django.core.management.base import BaseCommand
from modoboa.core import load_core_settings
from modoboa.core.extensions import exts_pool
from modoboa.core.management.commands import CloseConnectionMixin
from ...models import Domain
class Command(BaseCommand, CloseConnectionMixin):
help = 'Export domains and domain aliases to a csv'
option_list = BaseCommand.option_list + (
make_option(
'--sepchar', action='store_true', dest='sepchar', default=';'
),
)
def handle(self, *args, **kwargs):
exts_pool.load_all()
load_core_settings()
csvwriter = csv.writer(sys.stdout, delimiter=kwargs['sepchar'])
for dom in Domain.objects.all():
dom.to_csv(csvwriter)
@@ -0,0 +1,32 @@
import sys
import csv
from optparse import make_option
from django.core.management.base import BaseCommand
from modoboa.core import load_core_settings
from modoboa.core.models import User
from modoboa.core.extensions import exts_pool
from modoboa.core.management.commands import CloseConnectionMixin
from ...models import Alias
class Command(BaseCommand, CloseConnectionMixin):
help = 'Export identities (mailbox and aliases) to a csv'
option_list = BaseCommand.option_list + (
make_option(
'--sepchar', action='store_true', dest='sepchar', default=';'
),
)
def handle(self, *args, **kwargs):
exts_pool.load_all()
load_core_settings()
csvwriter = csv.writer(sys.stdout, delimiter=kwargs['sepchar'])
for u in User.objects.all():
u.to_csv(csvwriter)
for a in Alias.objects.prefetch_related('mboxes', 'aliases'):
a.to_csv(csvwriter)
@@ -0,0 +1,97 @@
import logging
from optparse import make_option
import os
from django.core.management.base import BaseCommand
from modoboa.core.management.commands import CloseConnectionMixin
from modoboa.lib import parameters
from modoboa.lib.sysutils import exec_cmd
from modoboa.lib.exceptions import InternalError
from ...models import MailboxOperation
from ...modo_extension import AdminConsole
class OperationError(Exception):
pass
class Command(BaseCommand, CloseConnectionMixin):
help = 'Handles rename and delete operations on mailboxes'
option_list = BaseCommand.option_list + (
make_option(
'--pidfile', default='/tmp/handle_mailbox_operations.pid',
help='Path to the file that will contain the PID of this process'
),
)
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
self.logger = logging.getLogger('modoboa.admin')
def rename_mailbox(self, operation):
if not os.path.exists(operation.argument):
return
new_mail_home = operation.mailbox.mail_home
dirname = os.path.dirname(new_mail_home)
if not os.path.exists(dirname):
try:
os.makedirs(dirname)
except os.error as e:
raise OperationError(str(e))
code, output = exec_cmd(
"mv %s %s" % (operation.argument, new_mail_home)
)
if code:
raise OperationError(output)
def delete_mailbox(self, operation):
if not os.path.exists(operation.argument):
return
code, output = exec_cmd(
"rm -r %s" % operation.argument
)
if code:
raise OperationError(output)
def check_pidfile(self, path):
"""Check if this command is already running
:param str path: path to the file containing the PID
:return: a boolean, True means we can go further
"""
if os.path.exists(path):
with open(path) as fp:
pid = fp.read().strip()
code, output = exec_cmd(
"grep handle_mailbox_operations /proc/%s/cmdline" % pid
)
if not code:
return False
with open(path, 'w') as fp:
print >> fp, os.getpid()
return True
def handle(self, *args, **options):
AdminConsole().load()
if parameters.get_admin("HANDLE_MAILBOXES") == 'no':
return
if not self.check_pidfile(options['pidfile']):
return
for ope in MailboxOperation.objects.all():
try:
f = getattr(self, '%s_mailbox' % ope.type)
except AttributeError:
continue
try:
f(ope)
except (OperationError, InternalError) as e:
self.logger.critical('%s failed (reason: %s)',
ope, str(e).encode('utf-8'))
else:
self.logger.info('%s succeed', ope)
ope.delete()
os.unlink(options['pidfile'])
@@ -0,0 +1,32 @@
from optparse import make_option
from django.core.management.base import BaseCommand
from modoboa.core import load_core_settings
from modoboa.core.extensions import exts_pool
from modoboa.core.management.commands import CloseConnectionMixin
from ._import import import_csv
class Command(BaseCommand, CloseConnectionMixin):
args = 'csvfile'
help = 'Import domains and domain aliases from a csv file'
option_list = BaseCommand.option_list + (
make_option(
'--sepchar', action='store_true', dest='sepchar', default=';'
),
make_option(
'--continue-if-exists', action='store_true',
dest='continue_if_exists', default=True
)
)
def handle(self, *args, **kwargs):
exts_pool.load_all()
load_core_settings()
for filename in args:
import_csv(filename, kwargs)
@@ -0,0 +1,35 @@
from optparse import make_option
from django.core.management.base import BaseCommand
from modoboa.core import load_core_settings
from modoboa.core.extensions import exts_pool
from modoboa.core.management.commands import CloseConnectionMixin
from ._import import import_csv
class Command(BaseCommand, CloseConnectionMixin):
args = 'csvfile'
help = 'Import identities from a csv file'
option_list = BaseCommand.option_list + (
make_option(
'--sepchar', action='store_true', dest='sepchar', default=';'
),
make_option(
'--continue-if-exists', action='store_true',
dest='continue_if_exists', default=True
),
make_option(
'--crypt-password', action='store_true', dest='crypt_password',
default=False
)
)
def handle(self, *args, **kwargs):
exts_pool.load_all()
load_core_settings()
for filename in args:
import_csv(filename, kwargs)
+178
View File
@@ -0,0 +1,178 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Alias',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('address', models.CharField(help_text='The alias address.', max_length=254, verbose_name='address')),
('enabled', models.BooleanField(default=True, help_text='Check to activate this alias', verbose_name='enabled')),
('internal', models.BooleanField(default=False)),
],
options={
'ordering': ['address'],
'permissions': (('view_aliases', 'View aliases'),),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='AliasRecipient',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('address', models.EmailField(max_length=75)),
('alias', models.ForeignKey(to='admin.Alias')),
('r_alias', models.ForeignKey(related_name='alias_recipient_aliases', blank=True, to='admin.Alias', null=True)),
],
options={
'db_table': 'modoboa_admin_aliasrecipient',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Domain',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(help_text='The domain name', unique=True, max_length=100, verbose_name='name')),
('quota', models.IntegerField()),
('enabled', models.BooleanField(default=True, help_text='Check to activate this domain', verbose_name='enabled')),
('type', models.CharField(default=b'domain', max_length=20)),
],
options={
'ordering': ['name'],
'permissions': (('view_domain', 'View domain'), ('view_domains', 'View domains')),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='DomainAlias',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(help_text='The alias name', unique=True, max_length=100, verbose_name='name')),
('enabled', models.BooleanField(default=True, help_text='Check to activate this alias', verbose_name='enabled')),
],
options={
'permissions': (('view_domaliases', 'View domain aliases'),),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Mailbox',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('address', models.CharField(help_text='Mailbox address (without the @domain.tld part)', max_length=252, verbose_name='address')),
('quota', models.PositiveIntegerField()),
('use_domain_quota', models.BooleanField(default=False)),
],
options={
'permissions': (('view_mailboxes', 'View mailboxes'),),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='MailboxOperation',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('type', models.CharField(max_length=20, choices=[(b'rename', b'rename'), (b'delete', b'delete')])),
('argument', models.TextField()),
('mailbox', models.ForeignKey(blank=True, to='admin.Mailbox', null=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='ObjectDates',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('creation', models.DateTimeField(auto_now_add=True)),
('last_modification', models.DateTimeField(auto_now=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Quota',
fields=[
('username', models.EmailField(max_length=254, serialize=False, primary_key=True)),
('bytes', models.BigIntegerField(default=0)),
('messages', models.IntegerField(default=0)),
],
options={
},
bases=(models.Model,),
),
migrations.AddField(
model_name='mailbox',
name='dates',
field=models.ForeignKey(to='admin.ObjectDates'),
preserve_default=True,
),
migrations.AddField(
model_name='mailbox',
name='domain',
field=models.ForeignKey(to='admin.Domain'),
preserve_default=True,
),
migrations.AddField(
model_name='mailbox',
name='user',
field=models.OneToOneField(to=settings.AUTH_USER_MODEL),
preserve_default=True,
),
migrations.AddField(
model_name='domainalias',
name='dates',
field=models.ForeignKey(to='admin.ObjectDates'),
preserve_default=True,
),
migrations.AddField(
model_name='domainalias',
name='target',
field=models.ForeignKey(verbose_name='target', to='admin.Domain', help_text='The domain this alias points to'),
preserve_default=True,
),
migrations.AddField(
model_name='domain',
name='dates',
field=models.ForeignKey(to='admin.ObjectDates'),
preserve_default=True,
),
migrations.AddField(
model_name='aliasrecipient',
name='r_mailbox',
field=models.ForeignKey(blank=True, to='admin.Mailbox', null=True),
preserve_default=True,
),
migrations.AlterUniqueTogether(
name='aliasrecipient',
unique_together=set([('alias', 'r_alias'), ('alias', 'r_mailbox')]),
),
migrations.AddField(
model_name='alias',
name='dates',
field=models.ForeignKey(to='admin.ObjectDates'),
preserve_default=True,
),
migrations.AddField(
model_name='alias',
name='domain',
field=models.ForeignKey(to='admin.Domain', null=True),
preserve_default=True,
),
migrations.AlterUniqueTogether(
name='alias',
unique_together=set([('address', 'internal')]),
),
]
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
def rename_content_types(apps, schema_editor):
"""Rename old content types if necessary."""
ContentType = apps.get_model("contenttypes", "ContentType")
for ct in ContentType.objects.filter(app_label="admin"):
try:
old_ct = ContentType.objects.get(
app_label="modoboa_admin", model=ct.model)
except ContentType.DoesNotExist:
continue
old_ct.app_label = "admin"
ct.delete()
old_ct.save()
class Migration(migrations.Migration):
dependencies = [
('admin', '0001_initial'),
]
operations = [
migrations.RunPython(rename_content_types),
]
+10
View File
@@ -0,0 +1,10 @@
from .base import AdminObject
from .domain import Domain
from .domain_alias import DomainAlias
from .mailbox import Mailbox, Quota, MailboxOperation
from .alias import Alias, AliasRecipient
__all__ = [
'Domain', 'DomainAlias', 'Mailbox', 'Quota', 'Alias',
'MailboxOperation', 'AdminObject', 'AliasRecipient'
]
+214
View File
@@ -0,0 +1,214 @@
"""Models related to aliases management."""
from django.db import models
from django.utils.encoding import python_2_unicode_compatible, smart_text
from django.utils.translation import ugettext as _, ugettext_lazy
import reversion
from modoboa.lib.email_utils import split_mailbox
from modoboa.lib.exceptions import (
PermDeniedException, BadRequest, Conflict, NotFound
)
from .base import AdminObject
from .domain import Domain
from .mailbox import Mailbox
from .. import signals
@python_2_unicode_compatible
class Alias(AdminObject):
"""Mailbox alias."""
address = models.CharField(
ugettext_lazy('address'), max_length=254,
help_text=ugettext_lazy(
"The alias address."
)
)
domain = models.ForeignKey(Domain, null=True)
enabled = models.BooleanField(
ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to activate this alias"),
default=True
)
internal = models.BooleanField(default=False)
_objectname = 'MailboxAlias'
class Meta:
permissions = (
("view_aliases", "View aliases"),
)
ordering = ["address"]
unique_together = (("address", "internal"), )
app_label = "admin"
def __str__(self):
return smart_text(self.address)
@property
def identity(self):
return self.address
@property
def name_or_rcpt(self):
rcpts_count = self.recipients_count
if not rcpts_count:
return "---"
rcpts = self.recipients
if rcpts_count > 1:
return "%s, ..." % rcpts[0]
return rcpts[0]
@property
def type(self):
cpt = self.recipients_count
if cpt > 1:
return "dlist"
qset = self.aliasrecipient_set.filter(
r_mailbox__isnull=True, r_alias__isnull=True)
if qset.exists():
return "forward"
return "alias"
@property
def tags(self):
labels = {
"dlist": _("distribution list"),
"forward": _("forward"),
"alias": _("alias")
}
altype = self.type
return [{"name": altype, "label": labels[altype], "type": "idt"}]
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
super(Alias, self).post_create(creator)
if creator.is_superuser:
for admin in self.domain.admins:
grant_access_to_object(admin, self)
def set_recipients(self, address_list):
"""Set recipients for this alias. Special recipients:
* local mailbox + extension: r_mailbox will be set to local mailbox
* alias address == recipient address: valid only to keep local copies
(when a forward is defined) and to create exceptions when a catchall
is defined on the associated domain
"""
to_create = []
for address in set(address_list):
if not address:
continue
if self.aliasrecipient_set.filter(address=address).exists():
continue
local_part, domname, extension = (
split_mailbox(address, return_extension=True))
if domname is None:
raise BadRequest(
u"%s %s" % (_("Invalid address"), address)
)
domain = Domain.objects.filter(name=domname).first()
kwargs = {"address": address, "alias": self}
if (
(domain is not None) and
(
any(
r[1] for r in signals.use_external_recipients.send(
self, recipients=address)
) is False
)
):
rcpt = Mailbox.objects.filter(
domain=domain, address=local_part).first()
if rcpt is None:
rcpt = Alias.objects.filter(
address='%s@%s' % (local_part, domname)
).first()
if rcpt is None:
raise NotFound(
_("Local recipient {}@{} not found")
.format(local_part, domname)
)
if rcpt.address == self.address:
raise Conflict
kwargs["r_alias"] = rcpt
else:
kwargs["r_mailbox"] = rcpt
to_create.append(AliasRecipient(**kwargs))
AliasRecipient.objects.bulk_create(to_create)
# Remove old recipients
self.aliasrecipient_set.exclude(
address__in=address_list).delete()
@property
def recipients(self):
"""Return the recipient list."""
return self.aliasrecipient_set.values_list("address", flat=True)
@property
def recipients_count(self):
"""Return the number of recipients of this alias."""
return self.aliasrecipient_set.count()
def from_csv(self, user, row, expected_elements=5):
"""Create a new alias from a CSV file entry
"""
if len(row) < expected_elements:
raise BadRequest(_("Invalid line: %s" % row))
address = row[1].strip()
localpart, domname = split_mailbox(address)
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise BadRequest(_("Domain '%s' does not exist" % domname))
if not user.can_access(domain):
raise PermDeniedException
if Alias.objects.filter(address=address).exists():
raise Conflict
self.address = address
self.domain = domain
self.enabled = (row[2].strip() in ["True", "1", "yes", "y"])
self.save()
self.set_recipients([raddress.strip() for raddress in row[3:]])
self.post_create(user)
def to_csv(self, csvwriter):
row = [self.type, self.address.encode("utf-8"), self.enabled]
row += self.recipients
csvwriter.writerow(row)
reversion.register(Alias)
@python_2_unicode_compatible
class AliasRecipient(models.Model):
"""An alias recipient."""
address = models.EmailField()
alias = models.ForeignKey(Alias)
# if recipient is a local mailbox
r_mailbox = models.ForeignKey(Mailbox, blank=True, null=True)
# if recipient is a local alias
r_alias = models.ForeignKey(
Alias, related_name="alias_recipient_aliases", blank=True, null=True)
class Meta:
app_label = "admin"
db_table = "modoboa_admin_aliasrecipient"
unique_together = [
("alias", "r_mailbox"),
("alias", "r_alias")
]
def __str__(self):
"""Return alias and recipient."""
return smart_text(
"{} -> {}".format(self.alias.address, self.alias)
)
+82
View File
@@ -0,0 +1,82 @@
from django.db import models
from modoboa.lib import events
from modoboa.lib.permissions import (
grant_access_to_object, ungrant_access_to_object
)
class ObjectDates(models.Model):
"""Dates recording for admin objects
This table keeps creation and last modification dates for Domains,
domain aliases, mailboxes and aliases objects.
"""
creation = models.DateTimeField(auto_now_add=True)
last_modification = models.DateTimeField(auto_now=True)
class Meta:
app_label = "admin"
@staticmethod
def set_for_object(obj):
"""Initialize or update dates for a given object.
:param obj: an admin object (Domain, Mailbox, etc)
"""
try:
dates = getattr(obj, "dates")
except ObjectDates.DoesNotExist:
dates = ObjectDates()
dates.save()
obj.dates = dates
class AdminObject(models.Model):
"""Abstract model to support dates
Inherit from this model to automatically add the "dates" feature
to another model. It defines the appropriate field and handles
saves.
"""
dates = models.ForeignKey(ObjectDates)
_objectname = None
class Meta:
abstract = True
@property
def creation(self):
return self.dates.creation
@property
def last_modification(self):
return self.dates.last_modification
@property
def objectname(self):
if self._objectname is None:
return self.__class__.__name__
return self._objectname
def post_create(self, creator):
grant_access_to_object(creator, self, is_owner=True)
events.raiseEvent("%sCreated" % self.objectname, creator, self)
def save(self, *args, **kwargs):
ObjectDates.set_for_object(self)
if "creator" in kwargs:
creator = kwargs["creator"]
del kwargs["creator"]
else:
creator = None
super(AdminObject, self).save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
def delete(self):
events.raiseEvent("%sDeleted" % self.objectname, self)
ungrant_access_to_object(self)
super(AdminObject, self).delete()
+186
View File
@@ -0,0 +1,186 @@
"""Models related to domains management."""
from django.contrib.contenttypes import generic
from django.db import models
from django.db.models.manager import Manager
from django.utils.translation import ugettext as _, ugettext_lazy
import reversion
from .base import AdminObject
from modoboa.core.models import User, ObjectAccess
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import BadRequest, Conflict
class DomainManager(Manager):
def get_for_admin(self, admin):
"""Return the domains belonging to this admin
The result is a ``QuerySet`` object, so this function can be used
to fill ``ModelChoiceField`` objects.
"""
if admin.is_superuser:
return self.get_query_set()
return self.get_query_set().filter(owners__user=admin)
class Domain(AdminObject):
"""Mail domain."""
name = models.CharField(ugettext_lazy('name'), max_length=100, unique=True,
help_text=ugettext_lazy("The domain name"))
quota = models.IntegerField()
enabled = models.BooleanField(
ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to activate this domain"),
default=True
)
owners = generic.GenericRelation(ObjectAccess)
type = models.CharField(default="domain", max_length=20)
objects = DomainManager()
class Meta:
permissions = (
("view_domain", "View domain"),
("view_domains", "View domains"),
)
ordering = ["name"]
app_label = "admin"
@property
def domainalias_count(self):
return self.domainalias_set.count()
@property
def mailbox_count(self):
return self.mailbox_set.count()
@property
def mbalias_count(self):
return self.alias_set.count()
@property
def identities_count(self):
"""Total number of identities in this domain."""
return (
self.mailbox_set.count() +
self.alias_set.filter(internal=False).count())
@property
def tags(self):
if self.type == "domain":
return [{"name": "domain", "label": _("Domain"), "type": "dom"}]
return events.raiseQueryEvent("GetTagsForDomain", self)
@property
def admins(self):
"""Return the domain administrators of this domain
:return: a list of User objects
"""
return [oa.user for oa in self.owners.filter(user__is_superuser=False)]
@property
def aliases(self):
return self.domainalias_set
def add_admin(self, account):
"""Add a new administrator for this domain
:param User account: the administrotor to add
"""
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(account, self)
for mb in self.mailbox_set.all():
if mb.user.has_perm("admin.add_domain"):
continue
grant_access_to_object(account, mb)
grant_access_to_object(account, mb.user)
for al in self.alias_set.all():
grant_access_to_object(account, al)
def remove_admin(self, account):
"""Remove an administrator of this domain.
:param User account: administrator to remove
"""
from modoboa.lib.permissions import \
ungrant_access_to_object, get_object_owner
if get_object_owner(self) == account:
events.raiseEvent('DomainOwnershipRemoved', account, self)
ungrant_access_to_object(self, account)
for mb in self.mailbox_set.all():
if mb.user.has_perm("admin.add_domain"):
continue
ungrant_access_to_object(mb, account)
ungrant_access_to_object(mb.user, account)
for al in self.alias_set.all():
ungrant_access_to_object(al, account)
def delete(self, fromuser, keepdir=False):
"""Custom delete method.
"""
from modoboa.lib.permissions import ungrant_access_to_objects
from .mailbox import Quota
if self.domainalias_set.count():
events.raiseEvent("DomainAliasDeleted", self.domainalias_set.all())
ungrant_access_to_objects(self.domainalias_set.all())
if self.alias_set.count():
events.raiseEvent("MailboxAliasDeleted", self.alias_set.all())
ungrant_access_to_objects(self.alias_set.all())
if parameters.get_admin("AUTO_ACCOUNT_REMOVAL") == "yes":
for account in User.objects.filter(mailbox__domain__name=self.name):
account.delete(fromuser, keepdir)
elif self.mailbox_set.count():
Quota.objects.filter(username__contains='@%s' % self.name).delete()
events.raiseEvent("MailboxDeleted", self.mailbox_set.all())
ungrant_access_to_objects(self.mailbox_set.all())
super(Domain, self).delete()
def __str__(self):
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()
if Domain.objects.filter(name=self.name).exists():
raise Conflict
try:
self.quota = int(row[2].strip())
except ValueError:
raise BadRequest(
_("Invalid quota value for domain '%s'") % self.name)
self.enabled = (row[3].strip() in ["True", "1", "yes", "y"])
self.save(creator=user)
def to_csv(self, csvwriter):
csvwriter.writerow(["domain", self.name, self.quota, self.enabled])
for dalias in self.domainalias_set.all():
dalias.to_csv(csvwriter)
def post_create(self, creator):
"""Post creation actions.
:param ``User`` creator: user whos created this domain
"""
super(Domain, self).post_create(creator)
for domalias in self.domainalias_set.all():
domalias.post_create(creator)
reversion.register(Domain)
+68
View File
@@ -0,0 +1,68 @@
"""Models related to domain aliases management."""
from django.db import models
from django.utils.translation import ugettext as _, ugettext_lazy
import reversion
from .base import AdminObject
from .domain import Domain
from modoboa.lib.exceptions import BadRequest, Conflict
class DomainAlias(AdminObject):
"""Domain aliases."""
name = models.CharField(ugettext_lazy("name"), max_length=100, unique=True,
help_text=ugettext_lazy("The alias name"))
target = models.ForeignKey(
Domain, verbose_name=ugettext_lazy('target'),
help_text=ugettext_lazy("The domain this alias points to")
)
enabled = models.BooleanField(
ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to activate this alias"),
default=True
)
class Meta:
permissions = (
("view_domaliases", "View domain aliases"),
)
app_label = "admin"
def __unicode__(self):
return self.name
def from_csv(self, user, row):
"""Create a domain alias from a CSV row
Expected format: ["domainalias", domain alias name, targeted domain, enabled]
:param user: a ``User`` object
:param row: a list containing the alias definition
"""
if len(row) < 4:
raise BadRequest(_("Invalid line"))
self.name = row[1].strip()
for model in [DomainAlias, Domain]:
if model.objects.filter(name=self.name).exists():
raise Conflict
domname = row[2].strip()
try:
self.target = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise BadRequest(_("Unknown domain %s") % domname)
self.enabled = row[3].strip() in ["True", "1", "yes", "y"]
self.save(creator=user)
def to_csv(self, csvwriter):
"""Export a domain alias using CSV format
:param csvwriter: a ``csv.writer`` object
"""
csvwriter.writerow(["domainalias", self.name,
self.target.name, self.enabled])
reversion.register(DomainAlias)
+320
View File
@@ -0,0 +1,320 @@
"""Models related to mailboxes management."""
import os
import pwd
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q
from django.db.models.manager import Manager
from django.utils.translation import ugettext as _, ugettext_lazy
import reversion
from .base import AdminObject
from .domain import Domain
from modoboa.core.models import User
from modoboa.lib import parameters
from modoboa.lib.exceptions import BadRequest, InternalError
from modoboa.lib.sysutils import exec_cmd
class Quota(models.Model):
"""Keeps track of Mailbox current quota."""
username = models.EmailField(primary_key=True, max_length=254)
bytes = models.BigIntegerField(default=0)
messages = models.IntegerField(default=0)
class Meta:
app_label = "admin"
class MailboxManager(Manager):
"""Custom manager for Mailbox."""
def get_for_admin(self, admin, squery=None):
"""Return the mailboxes that belong to this admin.
The result will contain the mailboxes defined for each domain that
user can see.
:param string squery: a search query
:return: a list of ``Mailbox`` objects
"""
qf = None
if squery is not None:
if '@' in squery:
parts = squery.split('@')
addrfilter = '@'.join(parts[:-1])
domfilter = parts[-1]
qf = (
Q(address__contains=addrfilter) &
Q(domain__name__contains=domfilter)
)
else:
qf = (
Q(address__contains=squery) |
Q(domain__name__contains=squery)
)
ids = admin.objectaccess_set \
.filter(content_type=ContentType.objects.get_for_model(Mailbox)) \
.values_list('object_id', flat=True)
if qf is not None:
qf = Q(pk__in=ids) & qf
else:
qf = Q(pk__in=ids)
return self.get_query_set().select_related().filter(qf)
class Mailbox(AdminObject):
"""User mailbox."""
address = models.CharField(
ugettext_lazy('address'), max_length=252,
help_text=ugettext_lazy(
"Mailbox address (without the @domain.tld part)")
)
quota = models.PositiveIntegerField()
use_domain_quota = models.BooleanField(default=False)
domain = models.ForeignKey(Domain)
user = models.OneToOneField(User)
objects = MailboxManager()
class Meta:
permissions = (
("view_mailboxes", "View mailboxes"),
)
app_label = "admin"
def __init__(self, *args, **kwargs):
super(Mailbox, self).__init__(*args, **kwargs)
self.__mail_home = None
def __str__(self):
return self.full_address
def __full_address(self, localpart):
return "%s@%s" % (localpart, self.domain.name)
@property
def full_address(self):
return self.__full_address(self.address)
@property
def enabled(self):
return self.user.is_active
@property
def alias_count(self):
return (
self.recipientalias_set.select_related("alias")
.filter(alias__internal=False).count()
)
@property
def mail_home(self):
"""Retrieve the home directory of this mailbox.
The home directory refers to the place on the file system
where the mailbox data is stored.
We ask dovecot to give us this information because there are
several patterns to understand and we don't want to implement
them.
"""
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
if hm is None or hm == "no":
return None
if self.__mail_home is None:
curuser = pwd.getpwuid(os.getuid()).pw_name
mbowner = parameters.get_admin("MAILBOXES_OWNER")
options = {}
if curuser != mbowner:
options['sudo_user'] = mbowner
code, output = exec_cmd(
"doveadm user %s -f home" % self.full_address, **options
)
if code:
raise InternalError(
_("Failed to retrieve mailbox location (%s)" % output))
self.__mail_home = output.strip()
return self.__mail_home
@property
def alias_addresses(self):
"""Return all alias address of this mailbox.
:rtype: list of string
"""
aliases = []
qset = (
self.aliasrecipient_set.select_related("alias")
.filter(alias__internal=False)
)
for alr in qset:
aliases += [alr.alias.address]
return aliases
@property
def quota_value(self):
"""Retrieve the ``Quota`` instance associated to this mailbox."""
if not hasattr(self, "_quota_value"):
try:
self._quota_value = Quota.objects.get(
username=self.full_address)
except Quota.DoesNotExist:
return None
return self._quota_value
@quota_value.setter
def quota_value(self, instance):
"""Set the ``Quota`` for this mailbox."""
self._quota_value = instance
def rename_dir(self, old_mail_home):
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
if hm is None or hm == "no":
return
MailboxOperation.objects.create(
mailbox=self, type='rename', argument=old_mail_home
)
def rename(self, address, domain):
"""Rename the mailbox.
To update the associated Quota record, we must create a new
one first, update the foreign key and then we can delete the
original record!
:param string address: the new mailbox's address (local part)
:param Domain domain: the new mailbox's domain
"""
old_mail_home = self.mail_home
old_qvalue = self.quota_value
self.address = address
self.domain = domain
self.quota_value = Quota.objects.create(
username=self.full_address, bytes=old_qvalue.bytes,
messages=old_qvalue.messages
)
old_qvalue.delete()
self.rename_dir(old_mail_home)
def delete_dir(self):
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
if hm is None or hm == "no":
return
MailboxOperation.objects.create(type='delete', argument=self.mail_home)
def set_quota(self, value=None, override_rules=False):
"""Set or update quota's value for this mailbox.
A value equal to 0 means the mailbox won't have any quota. The
following cases allow people to define such behaviour:
* The domain has no quota
* :keyword:`override_rules` is True
:param integer value: the quota's value
:param bool override_rules: allow to override defined quota rules
"""
if value is None:
if self.use_domain_quota:
self.quota = self.domain.quota
else:
self.quota = 0
elif int(value) > self.domain.quota and not override_rules:
raise BadRequest(
_("Quota is greater than the allowed domain's limit (%dM)"
% self.domain.quota)
)
else:
self.quota = value
if not self.quota and self.domain.quota and not override_rules:
raise BadRequest(_("A quota is required"))
def get_quota(self):
"""Get quota limit.
:rtype: int
"""
return int(self.quota_value.bytes / 1048576)
def get_quota_in_percent(self):
"""Get current quota usage.
:rtype: int
"""
if not self.quota:
return 0
return int(
self.quota_value.bytes / float(self.quota * 1048576) * 100
)
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
super(Mailbox, self).post_create(creator)
conditions = (
creator.is_superuser,
not self.user.has_perm("admin.add_domain")
)
if all(conditions):
# A super user is creating a new mailbox. Give
# access to that mailbox (and the associated
# account) to the appropriate domain admins,
# except if the new account has a more important
# role (SuperAdmin, Reseller)
for admin in self.domain.admins:
grant_access_to_object(admin, self)
grant_access_to_object(admin, self.user)
def save(self, *args, **kwargs):
"""Custom save.
We just make sure a quota record is defined for this mailbox.
"""
if self.quota_value is None:
self.quota_value, created = Quota.objects.get_or_create(
username=self.full_address)
super(Mailbox, self).save(*args, **kwargs)
def delete(self, keepdir=False):
"""Custom delete method
We try to delete the associated quota in the same time (it may
has already been removed if we're deleting a domain).
:param bool keepdir: delete the mailbox home dir on the
filesystem or not
"""
Quota.objects.filter(username=self.full_address).delete()
if not keepdir:
self.delete_dir()
super(Mailbox, self).delete()
reversion.register(Mailbox)
class MailboxOperation(models.Model):
"""An operation on a mailbox."""
mailbox = models.ForeignKey(Mailbox, blank=True, null=True)
type = models.CharField(
max_length=20, choices=(('rename', 'rename'), ('delete', 'delete'))
)
argument = models.TextField()
class Meta:
app_label = "admin"
def __str__(self):
if self.type == 'rename':
return 'Rename %s -> %s' % (self.argument, self.mailbox.mail_home)
return 'Delete %s' % self.argument
+143
View File
@@ -0,0 +1,143 @@
"""Map file definitions for postfix."""
from modoboa.core.commands.postfix_maps import registry
class DomainsMap(object):
"""Map to list all domains."""
filename = 'sql-domains.cf'
mysql = (
"SELECT name FROM admin_domain "
"WHERE name='%s' AND type='domain' AND enabled=1"
)
postgres = (
"SELECT name FROM admin_domain "
"WHERE name='%s' AND type='domain' AND enabled"
)
sqlite = (
"SELECT name FROM admin_domain "
"WHERE name='%s' AND type='domain' AND enabled=1"
)
class DomainsAliasesMap(object):
"""Map to list all domain aliases."""
filename = 'sql-domain-aliases.cf'
mysql = (
"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"
)
postgres = (
"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 AND dom.enabled"
)
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 AliasesMap(object):
"""A map to list all mailbox aliases."""
filename = 'sql-aliases.cf'
mysql = (
"SELECT alr.address FROM admin_aliasrecipient AS alr "
"INNER JOIN admin_alias AS al ON alr.alias_id=al.id "
"WHERE al.enabled=1 AND al.address='%s'"
)
postgres = (
"SELECT alr.address FROM admin_aliasrecipient AS alr "
"INNER JOIN admin_alias AS al ON alr.alias_id=al.id "
"WHERE al.enabled AND al.address='%s'"
)
sqlite = (
"SELECT alr.address FROM admin_aliasrecipient AS alr "
"INNER JOIN admin_alias AS al ON alr.alias_id=al.id "
"WHERE al.enabled=1 AND al.address='%s'"
)
class MaintainMap(object):
"""Map files to list non available mailboxes."""
filename = 'sql-maintain.cf'
mysql = (
"SELECT '450 Requested mail action not taken: mailbox unavailable' "
"FROM admin_mailbox mb INNER JOIN admin_domain dom "
"ON mb.domain_id=dom.id INNER JOIN admin_mailboxoperation mbop "
"ON mbop.mailbox_id=mb.id WHERE dom.name='%d' AND mb.address='%u' "
"LIMIT 1"
)
postgres = (
"SELECT '450 Requested mail action not taken: mailbox unavailable' "
"FROM admin_mailbox mb INNER JOIN admin_domain dom "
"ON mb.domain_id=dom.id INNER JOIN admin_mailboxoperation mbop "
"ON mbop.mailbox_id=mb.id WHERE dom.name='%d' AND mb.address='%u' "
"LIMIT 1"
)
sqlite = (
"SELECT '450 Requested mail action not taken: mailbox unavailable' "
"FROM admin_mailbox mb INNER JOIN admin_domain dom "
"ON mb.domain_id=dom.id INNER JOIN admin_mailboxoperation mbop "
"ON mbop.mailbox_id=mb.id WHERE dom.name='%d' AND mb.address='%u' "
"LIMIT 1"
)
class SenderLoginMailboxMap(object):
"""Map file to list authorized sender addresses (from mailboxes)."""
filename = "sql-sender-login-mailboxes.cf"
mysql = (
"SELECT email FROM core_user WHERE email='%s' AND is_active=1 "
)
postgres = (
"SELECT email FROM core_user WHERE email='%s' AND is_active"
)
sqlite = (
"SELECT email FROM core_user WHERE email='%s' AND is_active=1"
)
class SenderLoginAliasMap(object):
"""Map file to list authorized sender addresses (from aliases)."""
filename = "sql-sender-login-aliases.cf"
mysql = (
"SELECT concat(mb.address, '@', dom.name) FROM admin_mailbox mb "
"INNER JOIN admin_aliasrecipient alr ON alr.r_mailbox_id=mb.id"
" INNER JOIN admin_domain dom ON dom.id=mb.domain_id"
" INNER JOIN admin_alias al ON alr.alias_id=al.id "
"WHERE al.enabled=1 AND al.address='%s'"
)
postgres = (
"SELECT mb.address || '@' || dom.name FROM admin_mailbox mb "
"INNER JOIN admin_aliasrecipient alr ON alr.r_mailbox_id=mb.id"
" INNER JOIN admin_domain dom ON dom.id=mb.domain_id"
" INNER JOIN admin_alias al ON alr.alias_id=al.id "
"WHERE al.enabled AND al.address='%s'"
)
sqlite = (
"SELECT mb.address || '@' || dom.name FROM admin_mailbox mb "
"INNER JOIN admin_aliasrecipient alr ON alr.r_mailbox_id=mb.id"
" INNER JOIN admin_domain dom ON dom.id=mb.domain_id"
" INNER JOIN admin_alias al ON alr.alias_id=al.id "
"WHERE al.enabled=1 AND al.address='%s'"
)
registry.add_files([
DomainsMap, DomainsAliasesMap, AliasesMap, MaintainMap,
SenderLoginAliasMap, SenderLoginMailboxMap
])
+7
View File
@@ -0,0 +1,7 @@
"""Modoboa admin signals."""
import django.dispatch
use_external_recipients = django.dispatch.Signal(providing_args=['recipients'])
extra_domain_actions = django.dispatch.Signal(
providing_args=["user", "domain"])
+33
View File
@@ -0,0 +1,33 @@
#searchform {
margin-bottom: 10px;
}
span.title {
margin-left: 40px;
margin-bottom: 10px;
float: left;
font-weight: bold;
width: 180px;
}
table th#actions {
text-align: right;
}
table td[name="actions"] {
text-align: right;
}
table .progress {
margin-bottom: 0 !important;
}
table tbody tr:hover {
background-color: #F9F9F9;
}
.search {
width: 85%;
-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;
padding-left: 14px;
}
@@ -0,0 +1,9 @@
#settings_content .form-horizontal .control-label {
margin-left: 20px;
text-align: left;
}
#settings_content > .form-horizontal .controls {
margin-left: 240px;
}
+573
View File
@@ -0,0 +1,573 @@
/**
* Creates an instance of Admin.
*
* @constructor
* @param {Object} options - instance options
* @classdesc This class contains utility methods used by the admin
* application.
*/
var Admin = function(options) {
Listing.call(this, options);
};
Admin.prototype = {
defaults: {
deflocation: "list/",
squery: null
},
initialize: function(options) {
this.options = $.extend({}, this.defaults, options);
this.options.defcallback = $.proxy(this.list_cb, this);
Listing.prototype.initialize.call(this, this.options);
this.listen();
},
/**
* Callback used when the initial content of the listing is
* received.
*
* @this Admin
* @param {Object} data - response of the ajax call (JSON)
*/
list_cb: function(data) {
if (data.rows) {
$("#objects_table tbody").html(data.rows);
} else {
$("#objects_table tbody").html("");
}
this.update_listing(data);
if (data.length === 0) {
this.get_load_page_args();
this.end_of_list_reached();
}
},
/**
* Children must override this method.
*/
listen: function() {
},
/**
* Load a new page using an AJAX request.
*
* @this Admin
* @param {Object} e - event object
*/
page_loader: function(e) {
var $link = get_target(e);
e.preventDefault();
if ($link.hasClass("navigation")) {
$(".sidebar li.active").removeClass("active");
$link.parent().addClass("active");
}
this.navobj.baseurl($link.attr("href")).update();
},
importform_cb: function() {
$(".submit").one('click', function(e) {
e.preventDefault();
if ($("#id_sourcefile").val() === "") {
return;
}
$("#import_status").css("display", "block");
$("#import_result").html("").removeClass("alert alert-danger");
$("#importform").submit();
});
},
importdone: function(status, msg) {
$("#import_status").css("display", "none");
if (status === "ok") {
$("#modalbox").modal('hide');
this.reload_listing(msg);
} else {
$("#import_result").addClass("alert alert-danger");
$("#import_result").html(msg);
this.importform_cb();
}
},
exportform_cb: function() {
$(".submit").one('click', function(e) {
e.preventDefault();
$("#exportform").submit();
$("#modalbox").modal('hide');
});
},
reload_listing: function(data) {
this.navobj.update(true);
if (data) {
$("body").notify("success", data, 2000);
}
}
};
Admin.prototype = $.extend({}, Listing.prototype, Admin.prototype);
/**
* Creates an instance of Domains.
*
* @constructor
* @param {Object} options - instance options
* @classdesc This class extends the Admin one by adding methods
* specific to domains management.
*/
var Domains = function(options) {
Admin.call(this, options);
};
Domains.prototype = {
initialize: function(options) {
Admin.prototype.initialize.call(this, options);
this.options.navigation_params.push("domfilter", "srvfilter");
this.options.eor_message = gettext("No more domain to show");
this.register_tag_handler("dom");
},
/**
* Custom callbacks declaration.
*/
listen: function() {
Admin.prototype.listen.call(this);
$(document).on("click", "a.ajaxnav", $.proxy(this.page_loader, this));
},
/**
* Initialize the domain links embedded within a page.
*
* @param {Object} data - options
*/
init_domain_links: function(data) {
var deloptions = (data.handle_mailboxes === "yes") ?
{keepdir: gettext("Do not delete domain directory")} : {};
var warnmsg = (data.auto_account_removal && data.auto_account_removal === "yes")
? gettext("This operation will remove ALL data associated to this domain.")
: gettext("This operation will remove all data associated to this domain, excepting accounts.");
$("a[name=deldomain]").confirm({
question: function() { return this.$element.attr('title'); },
method: "POST",
warning: warnmsg,
checkboxes: deloptions,
success_cb: $.proxy(this.reload_listing, this)
});
$(document).trigger('domain_listing_refresh');
},
/**
* Navigation callback: default.
*
* @param {Object} data - response of the ajax call (JSON)
*/
list_cb: function(data) {
Admin.prototype.list_cb.call(this, data);
this.init_domain_links(data);
},
/**
* A new page has been received, inject it.
*
* @param {Object} data - page content
* @param {string} direction -
*/
add_new_page: function(data, direction) {
Admin.prototype.add_new_page.call(this, data, direction);
this.init_domain_links(data);
},
change_inputs_state: function(value) {
$("#id_dom_admin_username").attr("disabled", value);
$("input[name=create_aliases]").attr("disabled", value);
},
create_dom_admin_changed: function(e) {
var $target = $(e.target);
this.change_inputs_state(($target.val() === "yes") ? false : true);
},
/**
* Initialize the main form contained in the domain edition modal.
*
* @this Domains
*/
generalform_init: function() {
$('input:text:visible:first').focus();
$("#original_aliases").dynamic_input({
input_added: function($row) {
$row.find("label").html("");
},
input_removed: function($input) {
$input.parents(".form-group").remove();
return true;
}
});
},
optionsform_init: function() {
$("input[name=create_dom_admin]").click(
$.proxy(this.create_dom_admin_changed, this));
this.change_inputs_state(
$("input[name=create_dom_admin]:checked").val() === "yes" ? false : true
);
this.optionsform_prefill();
},
optionsform_prefill: function() {
var $span = $("#id_dom_admin_username").next("span");
$span.html("@" + $("#id_name").val());
},
domadminsform_init: function() {
$("a[name=removeperm]").click(function(e) {
var $tr = $(this).parents('tr');
simple_ajax_request.apply(this, [e, {
ok_cb: function(resp) {
$tr.remove();
if (!$("#domadmins").find("tr").length) {
$("#admins").html('<div class="alert alert-info">'
+ gettext("No domain administrator defined") + "</div>");
}
}
}]);
});
},
newdomain_cb: function() {
this.generalform_init();
this.optionsform_init();
$("#wizard").cwizard({
formid: "domform",
transition_callbacks: {
"options": this.optionsform_prefill
},
error_callbacks: {
"options": $.proxy(this.optionsform_init, this)
},
success_callback: $.proxy(this.reload_listing, this)
});
},
domainform_cb: function() {
this.generalform_init();
this.domadminsform_init();
$(".submit").one('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
formid: "domform",
error_cb: $.proxy(this.domainform_cb, this),
reload_on_success: false,
success_cb: $.proxy(this.reload_listing, this)
});
}, this));
$(document).trigger('domform_init');
}
};
Domains.prototype = $.extend({}, Admin.prototype, Domains.prototype);
/**
* Creates an instance of Identities.
*
* @constructor
* @param {Object} options - instance options
* @classdesc This class extends the Admin one by adding methods
* specific to identities management.
*/
var Identities = function(options) {
Admin.call(this, options);
};
Identities.prototype = {
initialize: function(options) {
Admin.prototype.initialize.call(this, options);
this.options.navigation_params.push("idtfilter", "grpfilter");
this.options.eor_message = gettext("No more identity to show");
this.domain_list = [];
this.register_tag_handler("idt");
this.register_tag_handler("grp", this.grp_tag_handler);
},
listen: function() {
Admin.prototype.listen.call(this);
$(document).on("click", "a.ajaxnav", $.proxy(this.page_loader, this));
$(document).on("click", "a.ajaxcall", $.proxy(this.send_call, this));
},
/**
* Send an ajax call.
*/
send_call: function(evt) {
var $link = get_target(evt, "a");
var $this = this;
var method = $link.attr("data-call_method");
if (method === undefined) {
method = "GET";
}
evt.preventDefault();
$.ajax({
url: $link.attr("href"),
type: method,
dataType: "json"
}).done(function(data) {
$this.reload_listing(data.respmsg);
});
},
/**
* Initialize the links embedded within a page.
*
* @param {Object} data - options
*/
init_identity_links: function(data) {
var deloptions = {};
if (data.handle_mailboxes === "yes") {
deloptions = {keepdir: gettext("Do not delete mailbox directory")};
}
$("a[name=delaccount]").confirm({
question: function() { return this.$element.attr('title'); },
method: "POST",
checkboxes: deloptions,
success_cb: $.proxy(this.reload_listing, this)
});
$("a[name=delalias]").confirm({
question: function() { return this.$element.attr('title'); },
method: "DELETE",
success_cb: $.proxy(this.reload_listing, this)
});
},
/**
* Add a type to let the server know what kind of object we expect.
*
* @this Listing
*/
get_load_page_args: function() {
var args = Admin.prototype.get_load_page_args.call(this);
if (this.navobj.getbaseurl() === "list") {
args.objtype = "identity";
this.options.eor_message = gettext("No more identity to show");
} else {
args.objtype = "quota";
this.options.eor_message = gettext("No more quota to show");
}
return args;
},
/**
* A new page has been received, inject it.
*
* @param {Object} data - page content
* @param {string} direction -
*/
add_new_page: function(data, direction) {
Admin.prototype.add_new_page.call(this, data, direction);
this.init_identity_links(data);
},
/**
* Navigation callback: default.
*
* @param {Object} data - response of the ajax call (JSON)
*/
list_cb: function(data) {
if ($(".sidebar li.active").length === 0) {
var menu = this.navobj.getbaseurl();
if (menu === "list") {
menu = "identities";
}
$("a[name={0}]".format(menu)).parent().addClass("active");
}
$("#objects_table thead tr").html(data.headers);
Admin.prototype.list_cb.call(this, data);
this.init_identity_links(data);
},
grp_tag_handler: function(tag, $link) {
if (this.navobj.getparam(tag + "filter") === undefined &&
$link.hasClass(tag)) {
var text = $link.attr("name");
this.navobj
.setparam("idtfilter", "account")
.setparam(tag + "filter", text)
.update();
if ($("a[name=idt]").length === 0) {
$("#taglist").append(this.make_tag("account", "idt"));
}
$("#taglist").append(this.make_tag(text, tag));
return true;
}
return false;
},
/**
* Retrieve a list of domain from the server.
*
* @this Identities
* @return {Array} a list of domain names
*/
get_domain_list: function() {
if (!this.domain_list.length) {
$.ajax({
url: this.options.domain_list_url,
dataType: "json",
async: false
}).done($.proxy(function(data) {
this.domain_list = data;
}, this));
}
return this.domain_list;
},
simpleuser_mode: function() {
$("#id_username").autocompleter({
from_character: "@",
choices: $.proxy(this.get_domain_list, this)
});
$("#id_email").addClass("disabled")
.attr("readonly", "")
.autocompleter("unbind");
},
normal_mode: function(group) {
$("#id_email").removeClass("disabled")
.attr("readonly", null)
.autocompleter("listen");
this.toggle_master_user(group);
},
/**
* Hide or show the master user checkbox according to desired role.
*
* @param {string} group - The desired role (or group)
*/
toggle_master_user: function(group) {
var $target = $("#id_master_user").parents(".form-group");
if (group !== "SuperAdmins") {
$target.hide();
} else {
$target.show();
}
},
generalform_init: function(notrigger) {
$("#id_role").change($.proxy(function(e) {
var $this = $(e.target);
var value = $this.val();
if (value == "SimpleUsers" || value === "") {
this.simpleuser_mode();
} else {
this.normal_mode(value);
}
}, this));
this.toggle_master_user($("#id_role").val());
if (notrigger !== undefined && notrigger) {
return;
}
$("#id_role").trigger("change");
},
mailform_init: function() {
$("#id_aliases").autocompleter({
from_character: "@",
choices: $.proxy(this.get_domain_list, this)
});
$("#original_aliases").dynamic_input({
input_added: function($row) {
$row.find("label").html("");
},
input_removed: function($input) {
$input.parents(".form-group").remove();
return true;
}
});
$("#id_email").autocompleter({
from_character: "@",
choices: $.proxy(this.get_domain_list, this)
});
if ($("#id_role").length) {
$("#id_role").trigger("change");
} else {
this.simpleuser_mode();
}
$("#id_domains").autocompleter({
choices: $.proxy(this.get_domain_list, this)
});
$("#original_domains").dynamic_input({
input_added: function($row) {
$row.find("label").html("");
},
input_removed: function($input) {
$input.parents(".form-group").remove();
return true;
}
});
activate_widget.call($("#id_quota_act"));
},
accountform_init: function() {
this.generalform_init(true);
this.mailform_init();
},
mailform_prefill: function() {
var $role = $("#id_role");
if (!$role.length || $role.val() === "" || $role.val() === "SimpleUsers") {
$("#id_email").val($("#id_username").val());
}
},
newaccount_cb: function() {
this.accountform_init();
$("#wizard").cwizard({
formid: "newaccount_form",
transition_callbacks: {
"mail": this.mailform_prefill
},
success_callback: $.proxy(this.reload_listing, this)
});
},
editaccount_cb: function() {
this.accountform_init();
$('.submit').on('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
formid: "accountform",
reload_on_success: false,
success_cb: $.proxy(this.reload_listing, this)
});
}, this));
},
aliasform_cb: function() {
$("#id_address").autocompleter({
from_character: "@",
choices: $.proxy(this.get_domain_list, this)
});
$("#original_recipients").dynamic_input({
input_added: function($row) {
$row.find("label").html("");
},
input_removed: function($input) {
$input.parents(".form-group").remove();
return true;
}
});
$(".submit").on('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
formid: "aliasform",
reload_on_success: false,
success_cb: $.proxy(this.reload_listing, this)
});
}, this));
}
};
Identities.prototype = $.extend({}, Admin.prototype, Identities.prototype);
@@ -0,0 +1,14 @@
{% load form_tags %}
{% render_field form.role %}
{% render_field form.username %}
{% render_field form.first_name %}
{% render_field form.last_name %}
{% render_field form.password1 %}
{% render_field form.password2 %}
{% if form.master_user %}{% render_field form.master_user %}{% endif %}
{% if form.domain_is_disabled %}
{% include "common/generic_field.html" with field=form.is_active label_width="col-sm-4" inline_message=_("Domain is disabled") %}
{% else %}
{% render_field form.is_active %}
{% endif %}
@@ -0,0 +1,10 @@
{% extends "common/generic_modal_form.html" %}{% load form_tags %}
{% block formbody %}
{% render_field form.address %}
{% render_fields_group form "recipients" %}
{% if not form.instance.pk or form.instance.domain.enabled %}
{% render_field form.enabled %}
{% else %}
{% include "common/generic_field.html" with field=form.enabled label_width="col-sm-4" inline_message=_("Domain is disabled") extra_classes="info" %}
{% endif %}
{% endblock %}
@@ -0,0 +1,6 @@
{% load i18n form_tags %}
{% render_field form.name %}
{% render_field form.type %}
{% render_field form.quota appended_text=_("MB") %}
{% render_fields_group form "aliases" %}
{% render_field form.enabled %}
@@ -0,0 +1,8 @@
{% load i18n form_tags %}
{% if form.fields %}
{% render_field form.create_dom_admin %}
{% render_field form.dom_admin_username appended_text="@" %}
{% render_field form.create_aliases %}
{% else %}
<div class="alert alert-warning">{% trans "You can't create a new domain administrator" %}</div>
{% endif %}
@@ -0,0 +1,63 @@
{% extends "admin/header.html" %}
{% load i18n %}
{% load admin_tags %}
{% block leftcol %}
<ul class="nav nav-sidebar">
{% domains_menu "statistics" user False %}
</ul>
{% get_extra_admin_content user "leftcol" selection %}
{% endblock %}
{% block apparea %}
<h2>{% trans "Statistics" %} <small>{% trans "Domains and identities" %}</small></h2>
<hr>
<div class="row">
{% if user.group == "SuperAdmins" %}
<div class="col-xs-12 col-sm-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">{% trans "Global counters" %}</h3></div>
<div class="panel-body"></div>
<table class="table">
<tr>
<th>{% trans "Type" %}</th>
<th>{% trans "Quantity" %}</th>
</tr>
<tr>
<td>{% trans "Domains" %}</td><td>{{ domains_counter }}</td>
</tr>
<tr>
<td>{% trans "Domain aliases" %}</td><td>{{ domain_aliases_counter }}</td>
</tr>
<tr>
<td>{% trans "Identities" %}</td><td>{{ identities_counter }}</td>
</tr>
</table>
</div>
</div>
{% endif %}
<div class="col-xs-12 col-sm-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">{% trans "Per-domain counters" %}</h3></div>
<div class="panel-body"></div>
<table class="table">
<tr>
<th>{% trans "Domain" %}</th>
<th>{% trans "Identities" %}</th>
</tr>
{% for domain in domains %}
<tr>
<td>{{ domain.name }}</td><td>{{ domain.identities_count }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,29 @@
{% extends "admin/listing.html" %}{% load i18n admin_tags lib_tags %}
{% block pagetitle %}{% trans "Domains" %}{% endblock %}
{% block extra_css %}
{{ block.super }}
{% extra_static_content "domains" "css" user %}
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script type="text/javascript">
$(document).ready(function() {
admin = new Domains({
load_page_url: "{% url 'admin:_domain_list' %}"
});
});
</script>
{% extra_static_content "domains" "js" user %}
{% endblock %}
{% block actions %}{% domains_menu "domains" user %}{% endblock %}
{% block table_header %}
<th id="name" class="sortable" data-sort_order="name" width="">{% trans "Name" %}</th>
<th id="aliases" width="">{% trans "Alias(es)" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
<th id="actions" width="">{% trans "Actions" %}</th>
{% endblock %}
@@ -0,0 +1,9 @@
{% load i18n admin_tags lib_tags %}
{% for domain in domains %}
<tr id="{{ domain.id }}" class="{% if not domain.enabled %}text-muted{% endif %}" name="{{ forloop.counter }}">
<td name="name">{% domain_modify_link domain %}{% if not domain.enabled %} ({% trans "disabled" %}){% endif %}</td>
<td name="aliases">{% domain_aliases domain %} </td>
<td name="tags">{% render_tags domain.tags %}</td>
<td name="actions">{% domain_actions user domain %}</td>
</tr>
{% endfor %}
@@ -0,0 +1,28 @@
{% extends "common/tabforms.html" %}{% load i18n lib_tags admin_tags %}
{% block extratabs %}
<li class="{% if tabs.active_id == 'admins' %}active{% endif %}">
<a href="#admins" data-toggle="tab">{% trans "Administrators" %}</a>
</li>
<li class="{% if tabs.active_id == 'misc' %}active{% endif %}">
<a href="#misc" data-toggle="tab">{% trans "Miscellaneous" %}</a>
</li>
{% endblock %}
{% block extratabs_content %}
<div class="tab-pane{% if tabs.active_id == 'admins' %} active{% endif %}" id="admins">{% if domadmins %}
<table id="domadmins" class="table table-condensed">
<tr><th>{% trans "Username" %}</th><th>{% trans "Name" %}</th><th></th></tr>
{% for da in domadmins %}<tr>
<td>{{ da.username }}</td>
<td>{{ da.fullname }}</td>
<td name="actions">{% if perms.admin.change_domain %}{% domadmin_actions da.id domain.id %}{% endif %}</td>
</tr>{% endfor %}
</table>{% else %}<div class="alert alert-info">{% trans "No domain administrator defined" %}</div>{% endif %}
</div>
<div class="tab-pane{% if tabs.active_id == 'misc' %} active{% endif %}" id="misc">
<div class="row"><span class="title">{% trans "Creation date" %}</span><span>{{ domain.creation }}</span></div>
<div class="row"><span class="title">{% trans "Last modification date" %}</span><span>{{ domain.last_modification }}</span></div>
<div class="row"><span class="title">{% trans "Domain aliases" %}</span><span>{{ domain.domainalias_count }}</span></div>
<div class="row"><span class="title">{% trans "Mailboxes" %}</span><span>{{ domain.mailbox_count }}</span></div>
<div class="row"><span class="title">{% trans "Mailbox aliases" %}</span><span>{{ domain.mbalias_count }}</span></div>
</div>
{% endblock %}
@@ -0,0 +1,11 @@
{% load i18n form_tags %}{% load url from future %}
<h2>{% trans "Forward" %} <small>{% trans "Automatically forward emails to another address" %}</small></h2>
<hr>
<form class="form-horizontal" method="POST" action="{% url 'user_forward' %}"
id="uprefs_form">
{% csrf_token %}
{% render_form form %}
<div class="form-actions">
<button type="submit" class="btn btn-primary col-lg-offset-8 col-md-offset-8 col-sm-offset-7 col-lg-1 col-md-1 col-sm-2" id="update" name="update">{% trans 'Update' %}</button>
</div>
</form>
+12
View File
@@ -0,0 +1,12 @@
{% extends "twocols.html" %}
{% load static %}
{% block extra_css %}
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/admin.css' %}" />
{% endblock %}
{% block extra_js %}
<script type="text/javascript" src="{% static 'js/history.js' %}"></script>
<script type="text/javascript" src="{% static 'js/listing.js' %}"></script>
<script type="text/javascript" src="{% static 'admin/js/admin.js' %}"></script>
{% endblock %}
@@ -0,0 +1,26 @@
{% extends "admin/listing.html" %}
{% load admin_tags lib_tags i18n %}{% load url from future %}
{% load static %}
{% block pagetitle %}{% trans "Identities" %}{% endblock %}
{% block extra_css %}
{{ block.super }}
{% extra_static_content "identities" "css" user %}
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script type="text/javascript" src="{% static 'js/autocompleter.js' %}"></script>
<script type="text/javascript">
$(document).ready(function() {
admin = new Identities({
domain_list_url: "{% url 'admin:domain_flat_list' %}",
load_page_url: "{% url 'admin:identity_page' %}"
});
});
</script>
{% extra_static_content "identities" "js" user %}
{% endblock %}
{% block actions %}{% identities_menu user "identities" %}{% endblock %}
@@ -0,0 +1,10 @@
{% load i18n admin_tags lib_tags %}
{% for ident in identities %}
{% check_identity_status ident as identity_is_enabled %}
<tr id="{{ ident.id }}" class="{% if not identity_is_enabled %}text-muted{% endif %}" name="{{ forloop.counter }}">
<td name="identity">{% identity_modify_link ident %}{% if not identity_is_enabled %} (disabled){% endif %}</td>
<td name="name_or_rcpt">{{ ident.name_or_rcpt }}</td>
<td name="tags">{% render_tags ident.tags %}</td>
<td name="actions">{% identity_actions user ident %}</td>
</tr>
{% endfor %}
@@ -0,0 +1,5 @@
{% load i18n %}
<th id="identity" class="sortable" data-sort_order="identity" width="">{% trans "Name" %}</th>
<th id="name_or_rcpt" class="sortable" data-sort_order="name_or_rcpt" width="">{% trans "Fullname/Recipient" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
<th id="actions" width="">{% trans "Actions" %}</th>
@@ -0,0 +1,11 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head profile="http://www.w3.org/2005/10/profile">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
parent.admin.importdone("{{ status }}", "{{ msg|escapejs }}");
</script>
</head>
<body></body>
</html>
@@ -0,0 +1,16 @@
{% extends "common/generic_modal_form.html" %}
{% load form_tags i18n %}
{% load static %}
{% block formbody %}
<div class="alert alert-info">
<h4>{% trans "Help" %}</h4>
{{ helptext|safe }}
</div>
{% render_form form %}
<iframe id="import_target" name="import_target" src="#"
style="width:0;height:0;border:0px solid #fff;"></iframe>
<div id="import_status" style="text-align: center;display:none">
<img src="{% static 'css/spinner.gif' %}" /> {% trans "Importing..." %}
</div>
<div id="import_result"></div>
{% endblock %}
@@ -0,0 +1,47 @@
{% extends "admin/header.html" %}{% load i18n admin_tags %}
{% load static %}
{% block extra_css %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'css/jquery.sortable.css' %}" />
{% endblock %}
{% block extra_js %}
{{ block.super }}
<script type="text/javascript" src="{% static 'js/creation_wizard.js' %}"></script>
<script type="text/javascript" src="{% static 'js/dynamic_input.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery.sortable.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery.infinitescroll.js' %}"></script>
<script type="text/javascript">
var admin;
</script>
{% endblock %}
{% block leftcol %}
<ul class="nav nav-sidebar">
{% block actions %}{% endblock %}
</ul>
{% get_extra_admin_content user "leftcol" selection %}
{% endblock %}
{% block apparea %}
<div class="row">
<div class="col-sm-5">
{% include "admin/searchform.html" %}
</div>
<div id="taglist" class="col-sm-5"> </div>
</div>
<div id="listing">
<table id="objects_table" class="table {{ styles }}">
<thead>
<tr>
{% block table_header %}{% endblock %}
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
{% endblock %}
@@ -0,0 +1,11 @@
{% load i18n %}
{% if target == "permissions" %}
<option value="">{% trans "Choose a value" %}</option>
{% endif %}
{% for mb in mailboxes %}
{% if target == "permissions" %}
<option value='{{ mb.id }}'>{{ mb.name }}</option>
{% else %}
<option value='{{ mb.id }}'>{{ mb.full_address }}</option>
{% endif %}
{% endfor %}
@@ -0,0 +1,5 @@
{% load form_tags %}
{% render_field form.email %}
{% include "common/generic_field.html" with field=form.quota help_display_mode="tooltip" label_width="col-sm-4" appended_text=_("MB") activator=True activator_value=form.quota_act.value deactivate_if_empty=False activator_label=_("Use domain default value") activator_size="col-sm-5" %}
{% render_extra_fields form %}
{% render_fields_group form "aliases" %}
@@ -0,0 +1,6 @@
{% extends "common/generic_modal_form.html" %}{% load form_tags %}{% block formbody %}
{% render_field form.name %}
{% render_field form.quota appended_text=_("MB") %}
{% render_fields_group form "aliases" %}
{% render_field form.enabled %}
{% endblock %}
@@ -0,0 +1,2 @@
{% load form_tags %}
{% render_fields_group form "domains" %}
@@ -0,0 +1,5 @@
{% load i18n %}
<th width="50%" class="sortable" data-sort_order="address">{% trans "Address" %}</th>
<th class="sortable" data-sort_order="quota_value__bytes">{% trans "Value" %}</th>
<th class="sortable" data-sort_order="quota">{% trans "Limit" %}</th>
<th class="sortable" data-sort_order="quota_usage" id="quotabar">{% trans "Usage (%)" %}</th>
+15
View File
@@ -0,0 +1,15 @@
{% load lib_tags admin_tags %}
{% for mbox in mboxes %}
<tr>
<td>{% identity_modify_link mbox.user "mail" %}</td>
<td>{{ mbox.get_quota }}M</td>
<td>{{ mbox.quota }}M</td>
<td name="quota">
<div id="quotabar" rel="tooltip" title="{{ mbox.get_quota_in_percent }}%"
class="progress">
<div class="{% progress_color mbox.get_quota_in_percent %}" style="width: {{ mbox.get_quota_in_percent }}%"></div>
</div>
</td>
</tr>
{% endfor %}
@@ -0,0 +1,11 @@
{% load i18n %}
<form id="searchform" class="form-horizontal" role="form">
<div class="form-group has-feedback">
<div class="col-sm-12">
<input class="form-control" type="text"
value={% if squery %}"{{ squery }}"{% else %}"{% trans 'Search' %}"{% endif %}
id="searchquery" name="searchquery" />
<span class="fa fa-search form-control-feedback"></span>
</div>
</div>
</form>
+286
View File
@@ -0,0 +1,286 @@
"""
Admin extension tags.
"""
from django import template
from django.core.urlresolvers import reverse
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib import events
from modoboa.lib.templatetags.lib_tags import render_link
from modoboa.lib.web_utils import render_actions
from .. import signals
register = template.Library()
genders = {
"Enabled": (ugettext_lazy("enabled_m"), ugettext_lazy("enabled_f"))
}
@register.simple_tag
def domains_menu(selection, user, ajax_mode=True):
"""Specific menu for domain related operations.
Corresponds to the menu visible on the left column when you go to
*Domains*.
:param str selection: menu entry currently selected
:param ``User`` user: connected user
:rtype: str
:return: rendered menu (as HTML)
"""
domain_list_url = (
"list/" if ajax_mode else reverse("admin:domain_list")
)
entries = [
{"name": "domains",
"label": _("List domains"),
"img": "fa fa-user",
"class": "ajaxnav navigation",
"url": domain_list_url},
{"name": "statistics",
"label": _("Statistics"),
"img": "fa fa-line-chart",
"class": "navigation",
"url": reverse("admin:domain_statistics")}
]
if user.has_perm("admin.add_domain"):
entries += [
{"name": "newdomain",
"label": _("Add domain"),
"img": "fa fa-plus",
"modal": True,
"modalcb": "admin.newdomain_cb",
"url": reverse("admin:domain_add")},
]
entries += events.raiseQueryEvent("ExtraDomainMenuEntries", user)
entries += [
{"name": "import",
"label": _("Import"),
"img": "fa fa-folder-open",
"url": reverse("admin:domain_import"),
"modal": True,
"modalcb": "admin.importform_cb"},
{"name": "export",
"label": _("Export"),
"img": "fa fa-share-alt",
"url": reverse("admin:domain_export"),
"modal": True,
"modalcb": "admin.exportform_cb"}
]
return render_to_string('common/menulist.html', {
"entries": entries,
"selection": selection,
"user": user
})
@register.simple_tag
def identities_menu(user, selection=None):
"""Menu specific to the Identities page.
:param ``User`` user: the connecter user
:rtype: str
:return: the rendered menu
"""
entries = [
{"name": "identities",
"label": _("List identities"),
"img": "fa fa-user",
"class": "ajaxnav navigation",
"url": "list/"},
{"name": "quotas",
"label": _("List quotas"),
"img": "fa fa-hdd-o",
"class": "ajaxnav navigation",
"url": "quotas/"},
{"name": "newaccount",
"label": _("Add account"),
"img": "fa fa-plus",
"modal": True,
"modalcb": "admin.newaccount_cb",
"url": reverse("admin:account_add")},
{"name": "newalias",
"label": _("Add alias"),
"img": "fa fa-plus",
"modal": True,
"modalcb": "admin.aliasform_cb",
"url": reverse("admin:alias_add")},
{"name": "newforward",
"label": _("Add forward"),
"img": "fa fa-plus",
"modal": True,
"modalcb": "admin.aliasform_cb",
"url": reverse("admin:forward_add")},
{"name": "newdlist",
"label": _("Add distribution list"),
"img": "fa fa-plus",
"modal": True,
"modalcb": "admin.aliasform_cb",
"url": reverse("admin:dlist_add")},
{"name": "import",
"label": _("Import"),
"img": "fa fa-folder-open",
"url": reverse("admin:identity_import"),
"modal": True,
"modalcb": "admin.importform_cb"},
{"name": "export",
"label": _("Export"),
"img": "fa fa-share-alt",
"url": reverse("admin:identity_export"),
"modal": True,
"modalcb": "admin.exportform_cb"
}
]
return render_to_string('common/menulist.html', {
"entries": entries,
"user": user
})
@register.simple_tag
def domain_actions(user, domain):
actions = [
{"name": "listidentities",
"url": "{0}#list/?searchquery=@{1}".format(
reverse("admin:identity_list"), domain.name),
"title": _("View the domain's identities"),
"img": "fa fa-user"}
]
if user.has_perm("admin.delete_domain"):
actions.append({
"name": "deldomain",
"url": reverse("admin:domain_delete", args=[domain.id]),
"title": _("Delete %s?" % domain.name),
"img": "fa fa-trash"
})
responses = signals.extra_domain_actions.send(
sender=None, user=user, domain=domain)
for receiver, response in responses:
if response:
actions += response
return render_actions(actions)
@register.simple_tag
def identity_actions(user, ident):
name = ident.__class__.__name__
objid = ident.id
if name == "User":
actions = events.raiseQueryEvent("ExtraAccountActions", ident)
actions += [
{"name": "delaccount",
"url": reverse("admin:account_delete", args=[objid]),
"img": "fa fa-trash",
"title": _("Delete %s?" % ident.username)},
]
else:
actions = [
{"name": "delalias",
"url": "{}?selection={}".format(
reverse("admin:alias_delete"), objid),
"img": "fa fa-trash",
"title": _("Delete %s?" % ident.address)},
]
return render_actions(actions)
@register.assignment_tag
def check_identity_status(identity):
"""Check if identity is enabled or not."""
if identity.__class__.__name__ == "User":
if hasattr(identity, "mailbox") \
and not identity.mailbox.domain.enabled:
return False
elif not identity.is_active:
return False
elif not identity.enabled or not identity.domain.enabled:
return False
return True
@register.simple_tag
def domain_modify_link(domain):
linkdef = {"label": domain.name, "modal": True}
if domain.__class__.__name__ == "Domain":
linkdef["url"] = reverse(
"admin:domain_change", args=[domain.id])
linkdef["modalcb"] = "admin.domainform_cb"
else:
tmp = events.raiseDictEvent('GetDomainModifyLink', domain)
for key in ['url', 'modalcb']:
linkdef[key] = tmp[key]
return render_link(linkdef)
@register.simple_tag
def domain_aliases(domain):
"""Display domain aliases of this domain.
:param domain:
:rtype: str
"""
if not domain.aliases.count():
return '---'
res = ''
for alias in domain.aliases.all():
res += '%s<br/>' % alias.name
return res
@register.simple_tag
def identity_modify_link(identity, active_tab='default'):
"""Return the appropriate modification link.
According to the identity type, a specific modification link (URL)
must be used.
:param identity: a ``User`` or ``Alias`` instance
:param str active_tab: the tab to display
:rtype: str
"""
linkdef = {"label": identity.identity, "modal": True}
if identity.__class__.__name__ == "User":
linkdef["url"] = reverse("admin:account_change", args=[identity.id])
linkdef["url"] += "?active_tab=%s" % active_tab
linkdef["modalcb"] = "admin.editaccount_cb"
else:
linkdef["url"] = reverse("admin:alias_change", args=[identity.id])
linkdef["modalcb"] = "admin.aliasform_cb"
return render_link(linkdef)
@register.simple_tag
def domadmin_actions(daid, domid):
actions = [{
"name": "removeperm",
"url": "{0}?domid={1}&daid={2}".format(
reverse("admin:permission_remove"), domid, daid),
"img": "fa fa-trash",
"title": _("Remove this permission")
}]
return render_actions(actions)
@register.filter
def gender(value, target):
if value in genders:
trans = target == "m" and genders[value][0] or genders[value][1]
if trans.find("_") == -1:
return trans
return value
@register.simple_tag
def get_extra_admin_content(user, target, currentpage):
res = events.raiseQueryEvent(
"ExtraAdminContent", user, target, currentpage
)
return "".join(res)
+24
View File
@@ -0,0 +1,24 @@
"""modoboa-admin unit tests."""
from .account import AccountTestCase, PermissionsTestCase
from .alias import AliasTestCase
from .domain import DomainTestCase
from .domain_alias import DomainAliasTestCase
from .export import ExportTestCase
from .import_ import ImportTestCase
from .mapfiles import MapFilesTestCase
from .password_schemes import PasswordSchemesTestCase
from .user import ForwardTestCase
__all__ = [
'AccountTestCase',
'AliasTestCase',
'DomainTestCase',
'DomainAliasTestCase',
'ExportTestCase',
'ForwardTestCase',
'ImportTestCase',
'MapFilesTestCase',
'PasswordSchemesTestCase',
'PermissionsTestCase',
]
+249
View File
@@ -0,0 +1,249 @@
from django.core.urlresolvers import reverse
from modoboa.core.models import User
from modoboa.lib.tests import ModoTestCase
from .. import factories
from .. import models
class AccountTestCase(ModoTestCase):
def setUp(self):
super(AccountTestCase, self).setUp()
factories.populate_database()
def test_crud(self):
values = dict(
username="tester@test.com", first_name="Tester", last_name="Toto",
password1="Toto1234", password2="Toto1234", role="SimpleUsers",
quota_act=True,
is_active=True, email="tester@test.com", stepid='step2'
)
self.ajax_post(reverse("admin:account_add"), values)
account = User.objects.get(username="tester@test.com")
mb = account.mailbox
self.assertEqual(mb.full_address, "tester@test.com")
self.assertEqual(mb.quota, 10)
self.assertTrue(mb.enabled)
self.assertEqual(mb.quota_value.username, "tester@test.com")
self.assertEqual(account.username, mb.full_address)
self.assertTrue(account.check_password("Toto1234"))
self.assertEqual(account.first_name, "Tester")
self.assertEqual(account.last_name, "Toto")
self.assertEqual(mb.domain.mailbox_count, 3)
# Check if self alias has been created
self.assertTrue(
models.AliasRecipient.objects.select_related("alias").filter(
alias__address=mb.full_address, address=mb.full_address,
alias__internal=True).exists()
)
values["username"] = "pouet@test.com"
self.ajax_post(
reverse("admin:account_change", args=[account.id]), values
)
mb = models.Mailbox.objects.get(pk=mb.id)
self.assertEqual(mb.full_address, "pouet@test.com")
self.assertEqual(mb.quota_value.username, "pouet@test.com")
# Check if self alias has been updated
self.assertTrue(
models.AliasRecipient.objects.select_related("alias").filter(
alias__address=mb.full_address, address=mb.full_address,
alias__internal=True).exists()
)
self.ajax_post(
reverse("admin:account_delete", args=[account.id]), {}
)
# Check if self alias has been deleted
self.assertFalse(
models.AliasRecipient.objects.select_related("alias").filter(
alias__address=mb.full_address, address=mb.full_address,
alias__internal=True).exists()
)
def _set_quota(self, email, value, expected_status=200):
account = User.objects.get(username=email)
values = {
"username": email, "role": "SimpleUsers", "quota_act": False,
"is_active": True, "quota": value, "email": email
}
self.ajax_post(
reverse("admin:account_change", args=[account.id]),
values, status=expected_status
)
def test_set_nul_quota_as_superadmin(self):
self._set_quota("user@test.com", 0)
def test_set_nul_quota_as_domainadmin(self):
self.clt.logout()
self.assertTrue(
self.clt.login(username="admin@test.com", password="toto")
)
self._set_quota("user@test.com", 0, 400)
self.clt.logout()
self.assertTrue(
self.clt.login(username="admin@test2.com", password="toto")
)
self._set_quota("user@test2.com", 0)
def test_master_user(self):
"""Validate the master user mode."""
values = {
"username": "masteruser", "role": "SuperAdmins",
"quota_act": False,
"is_active": True, "master_user": True, "stepid": "step2",
"password1": "Toto1234", "password2": "Toto1234"
}
self.ajax_post(
reverse("admin:account_add"), values
)
self.assertTrue(User.objects.get(username="masteruser").master_user)
values = {
"username": "testuser", "role": "DomainAdmins",
"quota_act": False,
"is_active": True, "master_user": True, "stepid": "step2",
"password1": "Toto1234", "password2": "Toto1234"
}
self.ajax_post(
reverse("admin:account_add"), values, status=400
)
class PermissionsTestCase(ModoTestCase):
def setUp(self):
super(PermissionsTestCase, self).setUp()
factories.populate_database()
self.user = User.objects.get(username='user@test.com')
self.values = dict(
username=self.user.username, role="DomainAdmins",
is_active=self.user.is_active, email="user@test.com",
quota_act=True
)
def tearDown(self):
self.clt.logout()
def test_domain_admins(self):
self.ajax_post(
reverse("admin:account_change", args=[self.user.id]),
self.values
)
self.assertEqual(self.user.group, "DomainAdmins")
self.values["role"] = "SimpleUsers"
self.ajax_post(
reverse("admin:account_change", args=[self.user.id]),
self.values
)
self.assertNotEqual(self.user.group, 'DomainAdmins')
def test_superusers(self):
self.values["role"] = "SuperAdmins"
self.ajax_post(
reverse("admin:account_change", args=[self.user.id]),
self.values
)
self.assertEqual(
User.objects.get(username="user@test.com").is_superuser, True
)
self.values["role"] = "SimpleUsers"
self.ajax_post(
reverse("admin:account_change", args=[self.user.id]),
self.values
)
self.assertEqual(User.objects.get(
username="user@test.com").is_superuser, False)
def test_self_modif(self):
self.clt.logout()
self.assertTrue(
self.clt.login(username="admin@test.com", password="toto")
)
admin = User.objects.get(username="admin@test.com")
values = dict(
username="admin@test.com", first_name="Admin",
password1="", password2="",
quota=10, is_active=True, email="admin@test.com"
)
self.ajax_post(
reverse("admin:account_change", args=[admin.id]),
values
)
self.assertEqual(admin.group, "DomainAdmins")
self.assertTrue(admin.can_access(
models.Domain.objects.get(name="test.com")))
values["role"] = "SuperAdmins"
self.ajax_post(
reverse("admin:account_change", args=[admin.id]),
values
)
admin = User.objects.get(username="admin@test.com")
self.assertEqual(admin.group, "DomainAdmins")
def test_domadmin_access(self):
self.clt.logout()
self.assertEqual(
self.clt.login(username="admin@test.com", password="toto"),
True)
response = self.clt.get(reverse("admin:domain_list"))
self.assertEqual(response.status_code, 200)
response = self.clt.get(
reverse("admin:account_change", args=[self.user.id]),
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertNotEqual(response["Content-Type"], "application/json")
def test_domainadmin_deletes_superadmin(self):
"""Check domain admins restrictions about super admins
When a super admin owns a mailbox and a domain admin exists
for the associated domain, this domain admin must not be able
to access the super admin.
"""
values = dict(
username="superadmin2@test.com", first_name="Super",
last_name="Admin", password1="Toto1234", password2="Toto1234",
role="SuperAdmins", is_active=True,
email="superadmin2@test.com", stepid='step2'
)
self.ajax_post(
reverse("admin:account_add"),
values
)
account = User.objects.get(username="superadmin2@test.com")
self.clt.logout()
self.clt.login(username="admin@test.com", password="toto")
self.ajax_post(
reverse("admin:account_delete", args=[account.id]), {}, 403
)
def test_domainadmin_dlist_local_domain_not_owned(self):
"""Check if a domain admin can use a local mailbox he can't
access as a recipient in a distribution list"""
values = dict(
address="all@test.com",
recipients="user@test.com",
recipients_1="user@test2.com",
enabled=True
)
self.ajax_post(reverse("admin:dlist_add"), values)
def test_domainadmin_master_user(self):
"""Check domain administrator is not allowed to access this feature."""
values = dict(
username="user10@test.com", first_name="Test",
last_name="Test", password1="Toto1234", password2="Toto1234",
role="SimpleUsers", is_active=True, master_user=True,
email="user10@test.com", stepid='step2'
)
self.ajax_post(
reverse("admin:account_add"),
values, status=400
)
+202
View File
@@ -0,0 +1,202 @@
# coding: utf-8
"""Admin test cases."""
from django.core.urlresolvers import reverse
from modoboa.core.models import User
from modoboa.lib.tests import ModoTestCase
from .. import factories
from ..models import Alias, AliasRecipient
class AliasTestCase(ModoTestCase):
def setUp(self):
super(AliasTestCase, self).setUp()
factories.populate_database()
def test_alias(self):
user = User.objects.get(username="user@test.com")
values = dict(
username="user@test.com", role=user.group,
is_active=user.is_active, email="user@test.com",
aliases="toto@test.com", aliases_1="titi@test.com"
)
self.ajax_post(
reverse("admin:account_change", args=[user.id]),
values
)
self.assertEqual(
AliasRecipient.objects.filter
(alias__internal=False, r_mailbox=user.mailbox)
.count(), 2
)
del values["aliases_1"]
self.ajax_post(
reverse("admin:account_change", args=[user.id]),
values
)
self.assertEqual(
AliasRecipient.objects.filter(
alias__internal=False, r_mailbox=user.mailbox)
.count(), 1
)
def test_alias_with_duplicated_recipient(self):
"""Check for duplicates."""
values = {
"address": "badalias@test.com",
"recipients": "user@test.com",
"recipients_1": "user@test.com",
"enabled": True
}
self.ajax_post(
reverse("admin:dlist_add"), values
)
alias = Alias.objects.get(address="badalias@test.com")
self.assertEqual(alias.recipients_count, 1)
def test_upper_case_alias(self):
"""Try to create an upper case alias."""
user = User.objects.get(username="user@test.com")
values = dict(
username="user@test.com", role=user.group,
is_active=user.is_active, email="user@test.com",
aliases="Toto@test.com"
)
self.ajax_post(
reverse("admin:account_change", args=[user.id]),
values
)
mb = user.mailbox
self.assertEqual(
AliasRecipient.objects
.filter(alias__internal=False, r_mailbox=mb).count(), 1)
self.assertEqual(
AliasRecipient.objects.get(
alias__internal=False, r_mailbox=mb).alias.address,
"toto@test.com"
)
values = {
"address": "Titi@test.com", "recipients": "user@test.com",
"enabled": True
}
self.ajax_post(reverse("admin:alias_add"), values)
self.assertTrue(
Alias.objects.filter(address="titi@test.com").exists())
def test_append_alias_with_tag(self):
"""Try to create a alias with tag in recipient address"""
user = User.objects.get(username="user@test.com")
values = dict(
username="user@test.com", role=user.group,
is_active=user.is_active, email="user@test.com"
)
self.ajax_post(
reverse("admin:account_change", args=[user.id]),
values
)
values = {
"address": "foobar@test.com", "recipients": "user+spam@test.com",
"enabled": True
}
self.ajax_post(reverse("admin:alias_add"), values)
alias = Alias.objects.get(address="foobar@test.com")
self.assertTrue(
alias.aliasrecipient_set.filter(
address="user+spam@test.com", r_mailbox__isnull=False,
r_alias__isnull=True).exists()
)
def test_dlist(self):
values = dict(address="all@test.com",
recipients="user@test.com",
recipients_1="admin@test.com",
recipients_2="ext@titi.com",
enabled=True)
self.ajax_post(
reverse("admin:dlist_add"), values
)
user = User.objects.get(username="user@test.com")
mb = user.mailbox
self.assertEqual(
AliasRecipient.objects.filter(
alias__internal=False, r_mailbox=mb).count(), 2)
admin = User.objects.get(username="admin@test.com")
mb = admin.mailbox
self.assertEqual(
AliasRecipient.objects.filter(
alias__internal=False, r_mailbox=mb).count(), 1)
dlist = Alias.objects.get(address="all@test.com")
self.assertEqual(dlist.recipients_count, 3)
del values["recipients_1"]
self.ajax_post(
reverse("admin:alias_change", args=[dlist.id]),
values
)
self.assertEqual(dlist.recipients_count, 2)
self.ajax_post(
"{}?selection={}".format(
reverse("admin:alias_delete"), dlist.id),
{}
)
self.assertRaises(
Alias.DoesNotExist, Alias.objects.get, address="all@test.com")
def test_forward(self):
values = dict(address="forward2@test.com", recipients="rcpt@dest.com")
self.ajax_post(
reverse("admin:forward_add"), values
)
fwd = Alias.objects.get(address="forward2@test.com")
self.assertEqual(fwd.recipients_count, 1)
values["recipients"] = "rcpt2@dest.com"
self.ajax_post(
reverse("admin:alias_change",
args=[fwd.id]),
values
)
self.assertEqual(fwd.recipients_count, 1)
self.ajax_post(
reverse("admin:alias_delete") + "?selection=%d"
% fwd.id, {}
)
self.assertRaises(
Alias.DoesNotExist, Alias.objects.get, address="forward2@test.com")
def test_forward_and_local_copies(self):
values = dict(address="user@test.com", recipients="rcpt@dest.com")
self.ajax_post(
reverse("admin:forward_add"), values
)
fwd = Alias.objects.get(address="user@test.com", internal=False)
self.assertEqual(fwd.recipients_count, 1)
values["recipients"] = "rcpt@dest.com"
values["recipients_1"] = "user@test.com"
self.ajax_post(
reverse("admin:alias_change", args=[fwd.id]),
values
)
fwd = Alias.objects.get(pk=fwd.pk)
self.assertEqual(fwd.aliasrecipient_set.count(), 2)
self.assertEqual(
fwd.aliasrecipient_set.filter(r_alias__isnull=True).count(), 2)
def test_wildcard_alias(self):
"""Test creation of a wildcard alias."""
values = {
"address": "@test.com",
"recipients": "user@test.com",
"enabled": True
}
self.ajax_post(
reverse("admin:dlist_add"), values
)
+131
View File
@@ -0,0 +1,131 @@
"""Domain related test cases."""
import json
from django.core.urlresolvers import reverse
from modoboa.core.models import User
from modoboa.lib import parameters
from modoboa.lib.tests import ModoTestCase
from .. import factories
from ..models import Domain, Alias
class DomainTestCase(ModoTestCase):
"""Test case for Domain."""
def setUp(self):
super(DomainTestCase, self).setUp()
factories.populate_database()
def test_create(self):
"""Test the creation of a domain."""
values = {
"name": "pouet.com", "quota": 100, "create_dom_admin": "no",
"type": "domain", "stepid": "step3"
}
self.ajax_post(reverse("admin:domain_add"), values)
dom = Domain.objects.get(name="pouet.com")
self.assertEqual(dom.name, "pouet.com")
self.assertEqual(dom.quota, 100)
self.assertEqual(dom.enabled, False)
self.assertFalse(dom.admins)
def test_create_with_template(self):
"""Test the creation of a domain with a template
"""
values = {
"name": "pouet.com", "quota": 100, "create_dom_admin": "yes",
"dom_admin_username": "toto", "create_aliases": "yes",
"type": "domain", "stepid": 'step3'
}
self.ajax_post(
reverse("admin:domain_add"),
values
)
dom = Domain.objects.get(name="pouet.com")
da = User.objects.get(username="toto@pouet.com")
self.assertIn(da, dom.admins)
al = Alias.objects.get(address="postmaster@pouet.com")
self.assertTrue(
al.aliasrecipient_set.filter(r_mailbox=da.mailbox)
.exists()
)
self.assertTrue(da.can_access(al))
def test_create_with_template_and_empty_quota(self):
"""Test the creation of a domain with a template and no quota"""
values = {
"name": "pouet.com", "quota": 0, "create_dom_admin": "yes",
"dom_admin_username": "toto", "create_aliases": "yes",
"type": "domain", "stepid": 'step3'
}
self.ajax_post(
reverse("admin:domain_add"),
values
)
dom = Domain.objects.get(name="pouet.com")
da = User.objects.get(username="toto@pouet.com")
self.assertIn(da, dom.admins)
al = Alias.objects.get(address="postmaster@pouet.com")
self.assertTrue(
al.aliasrecipient_set.filter(r_mailbox=da.mailbox)
.exists()
)
self.assertTrue(da.can_access(al))
def test_create_using_default_quota(self):
parameters.save_admin('DEFAULT_DOMAIN_QUOTA', 50, app='admin')
values = {
"name": "pouet.com", "create_dom_admin": "yes",
"dom_admin_username": "toto", "create_aliases": "yes",
"type": "domain", "stepid": 'step3'
}
self.ajax_post(
reverse("admin:domain_add"),
values
)
dom = Domain.objects.get(name="pouet.com")
self.assertEqual(dom.quota, 50)
da = User.objects.get(username="toto@pouet.com")
self.assertEqual(da.mailbox.quota, 50)
def test_modify(self):
"""Test the modification of a domain
Rename 'test.com' domain to 'pouet.com'
"""
values = {
"name": "pouet.com", "quota": 100, "type": "domain",
"enabled": True
}
dom = Domain.objects.get(name="test.com")
self.ajax_post(
reverse("admin:domain_change", args=[dom.id]),
values
)
dom = Domain.objects.get(name="pouet.com")
self.assertEqual(dom.name, "pouet.com")
self.assertTrue(dom.enabled)
def test_delete(self):
"""Test the removal of a domain
"""
dom = Domain.objects.get(name="test.com")
self.ajax_post(
reverse("admin:domain_delete", args=[dom.id]),
{}
)
with self.assertRaises(Domain.DoesNotExist):
Domain.objects.get(pk=1)
def test_domain_flat_list(self):
"""Test the 'domain_flat_list' view."""
response = self.clt.get(reverse("admin:domain_flat_list"))
self.assertEqual(response.status_code, 200)
content = json.loads(response.content)
self.assertIn("test.com", content)
self.assertIn("test2.com", content)
+55
View File
@@ -0,0 +1,55 @@
from django.core.urlresolvers import reverse
from modoboa.lib.tests import ModoTestCase
from .. import factories
from ..models import Domain, DomainAlias, Alias
class DomainAliasTestCase(ModoTestCase):
def setUp(self):
super(DomainAliasTestCase, self).setUp()
factories.populate_database()
self.dom = Domain.objects.get(name='test.com')
def test_model(self):
dom = Domain.objects.get(name="test.com")
domal = DomainAlias()
domal.name = "domalias.net"
domal.target = dom
domal.save()
self.assertEqual(dom.domainalias_count, 1)
self.assertTrue(
Alias.objects.filter(
address="@{}".format(domal.name)).exists())
domal.name = "domalias.org"
domal.save()
domal.delete()
self.assertFalse(
Alias.objects.filter(
address="@{}".format(domal.name)).exists())
def test_form(self):
dom = Domain.objects.get(name="test.com")
values = dict(
name=dom.name, quota=dom.quota, enabled=dom.enabled,
aliases="domalias.net", aliases_1="domalias.com",
type="domain"
)
self.ajax_post(
reverse("admin:domain_change",
args=[dom.id]),
values
)
self.assertEqual(dom.domainalias_set.count(), 2)
del values["aliases_1"]
self.ajax_post(
reverse("admin:domain_change",
args=[dom.id]),
values
)
self.assertEqual(dom.domainalias_set.count(), 1)
+94
View File
@@ -0,0 +1,94 @@
"""Export related test cases."""
from django.core.urlresolvers import reverse
from modoboa.lib.tests import ModoTestCase
from .. import factories
class ExportTestCase(ModoTestCase):
"""Test case for export operations."""
def setUp(self):
super(ExportTestCase, self).setUp()
factories.populate_database()
def __export_identities(self, idtfilter="", grpfilter=""):
self.clt.get(
reverse("admin:_identity_list")
+ "?grpfilter=%s&idtfilter=%s" % (grpfilter, idtfilter)
)
return self.clt.post(
reverse("admin:identity_export"),
{"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.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\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.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):
"""A test to validate we only export 1 super admin.
The password is removed since it is hashed using SHA512-CRYPT.
"""
response = self.__export_identities(
idtfilter="account", grpfilter="SuperAdmins"
)
elements = response.content.strip().split(";")
self.assertEqual(len(elements), 9)
elements[2] = ""
self.assertEqual(
";".join(elements), "account;admin;;;;True;SuperAdmins;;"
)
def test_export_domainadmins(self):
response = self.__export_identities(
idtfilter="account", grpfilter="DomainAdmins"
)
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):
response = self.__export_identities(idtfilter="alias")
self.assertEqual(
response.content.strip(),
"alias;alias@test.com;True;user@test.com"
)
def test_export_forwards(self):
response = self.__export_identities(idtfilter="forward")
self.assertEqual(
response.content.strip(),
"forward;forward@test.com;True;user@external.com"
)
def test_export_dlists(self):
response = self.__export_identities(idtfilter="dlist")
self.assertEqual(
response.content.strip(),
"dlist;postmaster@test.com;True;toto@titi.com;test@truc.fr"
)
+237
View File
@@ -0,0 +1,237 @@
# coding: utf-8
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
from modoboa.core.models import User
from modoboa.lib.tests import ModoTestCase
from .. import factories
from ..models import Domain, Alias, DomainAlias
class ImportTestCase(ModoTestCase):
def setUp(self):
super(ImportTestCase, self).setUp()
factories.populate_database()
def test_domains_import(self):
f = ContentFile(b"""domain; domain1.com; 100; True
domain; domain2.com; 200; False
domainalias; domalias1.com; domain1.com; True
""", name="domains.csv")
self.clt.post(
reverse("admin:domain_import"), {
"sourcefile": f
}
)
admin = User.objects.get(username="admin")
dom = Domain.objects.get(name="domain1.com")
self.assertEqual(dom.quota, 100)
self.assertTrue(dom.enabled)
self.assertTrue(admin.is_owner(dom))
domalias = DomainAlias.objects.get(name="domalias1.com")
self.assertEqual(domalias.target, dom)
self.assertTrue(dom.enabled)
self.assertTrue(admin.is_owner(domalias))
dom = Domain.objects.get(name="domain2.com")
self.assertEqual(dom.quota, 200)
self.assertFalse(dom.enabled)
self.assertTrue(admin.is_owner(dom))
def test_import_domains_with_conflict(self):
f = ContentFile(b"""domain;test.alias;10;True
domainalias;test.alias;test.com;True
""", name="domains.csv")
resp = self.clt.post(
reverse("admin:domain_import"), {
"sourcefile": f
}
)
self.assertIn('Object already exists: domainalias', resp.content)
def test_identities_import(self):
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 0
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 5; test.com
alias; alias1@test.com; True; user1@test.com
forward; alias2@test.com; True; user1+ext@test.com
forward; fwd1@test.com; True; user@extdomain.com
dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
""", name="identities.csv")
self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True}
)
admin = User.objects.get(username="admin")
u1 = User.objects.get(username="user1@test.com")
mb1 = u1.mailbox
self.assertTrue(admin.is_owner(u1))
self.assertEqual(u1.email, "user1@test.com")
self.assertEqual(u1.first_name, "User")
self.assertEqual(u1.last_name, "One")
self.assertTrue(u1.is_active)
self.assertEqual(u1.group, "SimpleUsers")
self.assertTrue(mb1.use_domain_quota)
self.assertEqual(mb1.quota, 0)
self.assertTrue(admin.is_owner(mb1))
self.assertEqual(mb1.full_address, "user1@test.com")
self.assertTrue(
self.clt.login(username="user1@test.com", password="toto")
)
da = User.objects.get(username="truc@test.com")
damb = da.mailbox
self.assertEqual(da.first_name, u"René")
self.assertEqual(da.group, "DomainAdmins")
self.assertEqual(damb.quota, 5)
self.assertFalse(damb.use_domain_quota)
self.assertEqual(damb.full_address, "truc@test.com")
dom = Domain.objects.get(name="test.com")
self.assertIn(da, dom.admins)
u = User.objects.get(username="user@test.com")
self.assertTrue(da.can_access(u))
al = Alias.objects.get(address="alias1@test.com")
self.assertTrue(
al.aliasrecipient_set
.filter(r_mailbox=u1.mailbox).exists()
)
self.assertTrue(admin.is_owner(al))
fwd = Alias.objects.get(address="fwd1@test.com")
self.assertTrue(
fwd.aliasrecipient_set
.filter(
address="user@extdomain.com", r_mailbox__isnull=True,
r_alias__isnull=True)
.exists()
)
self.assertTrue(admin.is_owner(fwd))
dlist = Alias.objects.get(address="dlist@test.com")
self.assertTrue(
dlist.aliasrecipient_set
.filter(r_mailbox=u1.mailbox).exists()
)
self.assertTrue(
dlist.aliasrecipient_set.filter(address="user@extdomain.com")
.exists()
)
self.assertTrue(admin.is_owner(dlist))
def test_import_for_nonlocal_domain(self):
"""Try to import an account for nonlocal domain."""
f = ContentFile(b"""
account; user1@nonlocal.com; toto; User; One; True; SimpleUsers; user1@nonlocal.com; 0
""", name="identities.csv")
self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True}
)
self.assertFalse(
User.objects.filter(username="user1@nonlocal.com").exists())
def test_import_invalid_quota(self):
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; ; test.com
""", name="identities.csv")
resp = self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True}
)
self.assertIn('wrong quota value', resp.content)
def test_import_domain_by_domainadmin(self):
"""Check if a domain admin is not allowed to import a domain."""
self.clt.logout()
self.clt.login(username="admin@test.com", password="toto")
f = ContentFile(b"""
domain; domain2.com; 200; False
""", name="identities.csv")
resp = self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True}
)
self.assertIn("You are not allowed to import domains", resp.content)
f = ContentFile(b"""
domainalias; domalias1.com; test.com; True
""", name="identities.csv")
resp = self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True}
)
self.assertIn(
"You are not allowed to import domain aliases", resp.content)
def test_import_quota_too_big(self):
self.clt.logout()
self.clt.login(username="admin@test.com", password="toto")
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 20
""", name="identities.csv")
resp = self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True}
)
self.assertIn('Quota is greater than the allowed', resp.content)
def test_import_missing_quota(self):
f = ContentFile(b"""
account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com
""", name="identities.csv")
resp = self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True}
)
account = User.objects.get(username="user1@test.com")
self.assertEqual(
account.mailbox.quota,
account.mailbox.domain.quota
)
def test_import_duplicate(self):
f = ContentFile(b"""
account; admin@test.com; toto; Admin; ; True; DomainAdmins; admin@test.com; 0; test.com
account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 0; test.com
""", name="identities.csv")
self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True,
"continue_if_exists": True}
)
admin = User.objects.get(username="admin")
u1 = User.objects.get(username="truc@test.com")
self.assertTrue(admin.is_owner(u1))
def test_import_superadmin(self):
"""Check if a domain admin can import a superadmin
Expected result: no
"""
self.clt.logout()
self.assertTrue(
self.clt.login(username="admin@test.com", password="toto")
)
f = ContentFile(b"""
account; sa@test.com; toto; Super; Admin; True; SuperAdmins; superadmin@test.com; 50
""", name="identities.csv")
self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True,
"continue_if_exists": True}
)
with self.assertRaises(User.DoesNotExist):
User.objects.get(username="sa@test.com")
def test_import_alias_with_empty_values(self):
f = ContentFile(b"""
alias;user.alias@test.com;True;user@test.com;;;;;;;;;;;;;;;;
""", name="identities.csv")
self.clt.post(
reverse("admin:identity_import"),
{"sourcefile": f, "crypt_password": True,
"continue_if_exists": True}
)
alias = Alias.objects.get(address="user.alias@test.com")
self.assertEqual(alias.type, "alias")
+17
View File
@@ -0,0 +1,17 @@
"""Test map files generation."""
from django.test import TestCase
from modoboa.lib.test_utils import MapFilesTestCaseMixin
class MapFilesTestCase(MapFilesTestCaseMixin, TestCase):
"""Test case for admin."""
extension = "admin"
MAP_FILES = [
"sql-domains.cf", "sql-domain-aliases.cf", "sql-aliases.cf",
"sql-maintain.cf"
]
+57
View File
@@ -0,0 +1,57 @@
from django.core.urlresolvers import reverse
from modoboa.core import load_core_settings
from modoboa.core.models import User
from modoboa.lib.tests import ModoTestCase
from modoboa.lib import parameters
from .. import factories
class PasswordSchemesTestCase(ModoTestCase):
fixtures = ['initial_users.json']
def setUp(self):
super(PasswordSchemesTestCase, self).setUp()
factories.populate_database()
load_core_settings()
def _create_account(self):
values = dict(
username="tester@test.com", first_name="Tester", last_name="Toto",
password1="Toto1234", password2="Toto1234", role="SimpleUsers",
quota_act=True,
is_active=True, email="tester@test.com", stepid='step2'
)
self.ajax_post(
reverse("admin:account_add"),
values
)
def _test_scheme(self, name, startpattern):
parameters.save_admin('PASSWORD_SCHEME', name, app='core')
self._create_account()
account = User.objects.get(username='tester@test.com')
self.assertTrue(account.password.startswith(startpattern))
self.assertTrue(account.check_password('Toto1234'))
def test_sha512crypt_scheme(self):
self._test_scheme('sha512crypt', '{SHA512-CRYPT}')
def test_sha256crypt_scheme(self):
self._test_scheme('sha256crypt', '{SHA256-CRYPT}')
def test_md5crypt_scheme(self):
self._test_scheme('md5crypt', '{MD5-CRYPT}')
def test_sha256_scheme(self):
self._test_scheme('sha256', '{SHA256}')
def test_md5_scheme(self):
self._test_scheme('md5', '{MD5}')
def test_crypt(self):
self._test_scheme('crypt', '{CRYPT}')
def test_plain(self):
self._test_scheme('plain', '{PLAIN}')
+27
View File
@@ -0,0 +1,27 @@
from django.core.urlresolvers import reverse
from modoboa.core.models import User
from modoboa.lib.tests import ModoTestCase
from ..factories import populate_database
from ..models import Alias
class ForwardTestCase(ModoTestCase):
def setUp(self):
super(ForwardTestCase, self).setUp()
populate_database()
def test_forward_permissions(self):
self.clt.logout()
self.clt.login(username='user@test.com', password='toto')
self.ajax_post(
reverse('user_forward'),
{'dest': 'user@extdomain.com', 'keepcopies': True}
)
forward = Alias.objects.get(address='user@test.com', internal=False)
sadmin = User.objects.get(username='admin')
self.assertTrue(sadmin.can_access(forward))
domadmin = User.objects.get(username='admin@test.com')
self.assertTrue(domadmin.can_access(forward))
+61
View File
@@ -0,0 +1,61 @@
from django.conf.urls import patterns, url
urlpatterns = patterns(
'modoboa.admin.views.domain',
url(r'^$', 'index', name="index"),
url(r'^domains/$', 'domains', name="domain_list"),
url(r'^domains/list/$', '_domains', name="_domain_list"),
url(r'^domains/flatlist/$', 'domains_list', name="domain_flat_list"),
url(r'^domains/stats/$', 'domain_statistics', name="domain_statistics"),
url(r'^domains/new/', 'newdomain', name='domain_add'),
url(r'^domains/(?P<dom_id>\d+)/edit/$', 'editdomain',
name="domain_change"),
url(r'^domains/(?P<dom_id>\d+)/delete/$', 'deldomain',
name="domain_delete"),
)
urlpatterns += patterns(
'modoboa.admin.views.identity',
url(r'^permissions/remove/$', 'remove_permission',
name="permission_remove"),
url(r'^identities/$', 'identities', name="identity_list"),
url(r'^identities/list/$', '_identities', name="_identity_list"),
url(r'^identities/quotas/$', 'list_quotas', name="quota_list"),
url(r'^identities/page/$', 'get_next_page', name="identity_page"),
url(r'^accounts/list/$', 'accounts_list', name="account_list"),
url(r'^accounts/new/$', 'newaccount', name="account_add"),
url(r'^accounts/edit/(?P<accountid>\d+)/$', 'editaccount',
name="account_change"),
url(r'^accounts/delete/(?P<accountid>\d+)/$', 'delaccount',
name="account_delete"),
)
urlpatterns += patterns(
'modoboa.admin.views.alias',
url(r'^distriblists/new/$', 'newdlist', name="dlist_add"),
url(r'^forwards/new/$', 'newforward', name="forward_add"),
url(r'^aliases/new/$', 'newalias', name="alias_add"),
url(r'^aliases/edit/(?P<alid>\d+)/$', 'editalias', name="alias_change"),
url(r'^aliases/delete/$', 'delalias', name="alias_delete"),
)
urlpatterns += patterns(
'modoboa.admin.views.import_',
url(r'^domains/import/$', 'import_domains', name="domain_import"),
url(r'^identities/import/$', 'import_identities', name="identity_import"),
)
urlpatterns += patterns(
'modoboa.admin.views.export',
url(r'^domains/export/$', 'export_domains', name="domain_export"),
url(r'^identities/export/$', 'export_identities', name="identity_export"),
)
View File
+140
View File
@@ -0,0 +1,140 @@
from django.contrib.auth.decorators import (
login_required, permission_required
)
from django.core.urlresolvers import reverse
from django.db import IntegrityError
from django.shortcuts import render
from django.utils.translation import ugettext as _, ungettext
import reversion
from modoboa.lib import events
from modoboa.lib.exceptions import PermDeniedException, Conflict
from modoboa.lib.web_utils import render_to_json_response
from ..forms import AliasForm
from ..models import Alias
def _validate_alias(request, form, successmsg, callback=None):
"""Alias validation
Common function shared between creation and modification actions.
"""
if form.is_valid():
try:
alias = form.save()
except IntegrityError:
raise Conflict(_("Alias with this name already exists"))
if callback:
callback(request.user, alias)
return render_to_json_response(successmsg)
return render_to_json_response({'form_errors': form.errors}, status=400)
def _new_alias(request, title, action, successmsg,
tplname="admin/aliasform.html"):
events.raiseEvent("CanCreate", request.user, "mailbox_aliases")
if request.method == "POST":
def callback(user, alias):
alias.post_create(user)
form = AliasForm(request.user, request.POST)
return _validate_alias(
request, form, successmsg, callback
)
ctx = {
"title": title,
"action": action,
"formid": "aliasform",
"action_label": _("Create"),
"action_classes": "submit",
"form": AliasForm(request.user)
}
return render(request, tplname, ctx)
@login_required
@permission_required("admin.add_alias")
@reversion.create_revision()
def newdlist(request):
return _new_alias(
request, _("New distribution list"),
reverse("admin:dlist_add"),
_("Distribution list created")
)
@login_required
@permission_required("admin.add_alias")
@reversion.create_revision()
def newalias(request):
return _new_alias(
request, _("New alias"), reverse("admin:alias_add"),
_("Alias created")
)
@login_required
@permission_required("admin.add_alias")
@reversion.create_revision()
def newforward(request):
return _new_alias(
request, _("New forward"), reverse("admin:forward_add"),
_("Forward created")
)
@login_required
@permission_required("admin.change_alias")
@reversion.create_revision()
def editalias(request, alid, tplname="admin/aliasform.html"):
alias = Alias.objects.get(pk=alid)
if not request.user.can_access(alias):
raise PermDeniedException
if request.method == "POST":
altype = alias.type
if altype == "dlist":
successmsg = _("Distribution list modified")
elif altype == "forward":
successmsg = _("Forward modified")
else:
successmsg = _("Alias modified")
form = AliasForm(request.user, request.POST, instance=alias)
return _validate_alias(request, form, successmsg)
ctx = {
'action': reverse("admin:alias_change", args=[alias.id]),
'formid': 'aliasform',
'title': alias.address,
'action_label': _('Update'),
'action_classes': 'submit',
'form': AliasForm(request.user, instance=alias)
}
return render(request, tplname, ctx)
@login_required
@permission_required("admin.delete_alias")
def delalias(request):
selection = request.GET["selection"].split(",")
for alid in selection:
alias = Alias.objects.get(pk=alid)
if not request.user.can_access(alias):
raise PermDeniedException
if alias.type == 'dlist':
msg = "Distribution list deleted"
msgs = "Distribution lists deleted"
elif alias.type == 'forward':
msg = "Forward deleted"
msgs = "Forwards deleted"
else:
msg = "Alias deleted"
msgs = "Aliases deleted"
alias.delete()
msg = ungettext(msg, msgs, len(selection))
return render_to_json_response(msg)
+153
View File
@@ -0,0 +1,153 @@
"""
Domain related views.
"""
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext as _, ungettext
from django.core.urlresolvers import reverse
from django.shortcuts import render
from django.views.decorators.csrf import ensure_csrf_cookie
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
import reversion
from modoboa.core.models import User
from modoboa.lib import parameters, events
from modoboa.lib.web_utils import (
_render_to_string, render_to_json_response
)
from modoboa.lib.exceptions import (
PermDeniedException
)
from modoboa.lib.listing import get_sort_order, get_listing_page
from ..forms import DomainForm, DomainWizard
from ..lib import get_domains
from ..models import (
Domain, DomainAlias, Mailbox, Alias
)
@login_required
def index(request):
return HttpResponseRedirect(reverse("admin:domain_list"))
@login_required
@user_passes_test(
lambda u: u.has_perm("admin.view_domains")
or u.has_perm("admin.view_mailboxes")
)
def _domains(request):
sort_order, sort_dir = get_sort_order(request.GET, "name")
filters = dict(
(flt, request.GET.get(flt, None))
for flt in ['domfilter', 'searchquery']
+ events.raiseQueryEvent('ExtraDomainFilters')
)
request.session['domains_filters'] = filters
domainlist = get_domains(request.user, **filters)
if sort_order == 'name':
domainlist = sorted(
domainlist,
key=lambda d: getattr(d, sort_order), reverse=sort_dir == '-'
)
else:
domainlist = sorted(domainlist, key=lambda d: d.tags[0],
reverse=sort_dir == '-')
context = {
"handle_mailboxes": parameters.get_admin(
"HANDLE_MAILBOXES", raise_error=False),
"auto_account_removal": parameters.get_admin("AUTO_ACCOUNT_REMOVAL")
}
page = get_listing_page(domainlist, request.GET.get("page", 1))
if page is None:
context["length"] = 0
else:
context["rows"] = _render_to_string(
request, "admin/domains_table.html", {
'domains': page.object_list,
}
)
context["pages"] = [page.number]
return render_to_json_response(context)
@login_required
@ensure_csrf_cookie
def domains(request, tplname="admin/domains.html"):
if not request.user.has_perm("admin.view_domains"):
if request.user.has_perm("admin.view_mailboxes"):
return HttpResponseRedirect(
reverse("admin:identity_list")
)
return HttpResponseRedirect(reverse("core:user_index"))
return render(request, tplname, {"selection": "domains"})
@login_required
@permission_required("core.add_user")
def domains_list(request):
doms = [dom.name for dom in Domain.objects.get_for_admin(request.user)]
return render_to_json_response(doms)
@login_required
@permission_required("admin.add_domain")
@reversion.create_revision()
def newdomain(request):
events.raiseEvent("CanCreate", request.user, "domains")
return DomainWizard(request).process()
@login_required
@permission_required("admin.view_domains")
@reversion.create_revision()
def editdomain(request, dom_id):
"""Edit domain view."""
domain = Domain.objects.get(pk=dom_id)
if not request.user.can_access(domain):
raise PermDeniedException
instances = dict(general=domain)
events.raiseEvent("FillDomainInstances", request.user, domain, instances)
return DomainForm(request, instances=instances).process()
@login_required
@permission_required("admin.delete_domain")
def deldomain(request, dom_id):
keepdir = True if request.POST.get("keepdir", "false") == "true" else False
try:
mb = Mailbox.objects.get(user__id=request.user.id)
except Mailbox.DoesNotExist:
mb = None
dom = Domain.objects.get(pk=dom_id)
if not request.user.can_access(dom):
raise PermDeniedException
if mb and mb.domain == dom:
raise PermDeniedException(_("You can't delete your own domain"))
dom.delete(request.user, keepdir)
msg = ungettext("Domain deleted", "Domains deleted", 1)
return render_to_json_response(msg)
@login_required
@permission_required("admin.view_domains")
def domain_statistics(request):
"""A simple page to present domain statistics."""
context = {}
if request.user.group == "SuperAdmins":
context.update({
"domains_counter": Domain.objects.count(),
"domain_aliases_counter": DomainAlias.objects.count(),
"identities_counter": (
User.objects.count() +
Alias.objects.filter(internal=False).count()),
})
context.update({"domains": Domain.objects.get_for_admin(request.user)})
return render(request, "admin/domain_statistics.html", context)
+88
View File
@@ -0,0 +1,88 @@
"""Export related views."""
import cStringIO
import csv
from rfc6266 import build_header
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.shortcuts import render
from django.utils.translation import ugettext as _
from ..forms import ExportIdentitiesForm, ExportDomainsForm
from ..lib import get_domains, get_identities
def _export(content, filename):
"""Export a csv file's content
:param content: the content to export (string)
:param filename: the name that will appear into the response
:return: an ``HttpResponse`` object
"""
resp = HttpResponse(content)
resp["Content-Type"] = "text/csv"
resp["Content-Length"] = len(content)
resp["Content-Disposition"] = build_header(filename)
return resp
@login_required
@user_passes_test(
lambda u: u.has_perm("core.add_user") or u.has_perm("admin.add_alias")
)
def export_identities(request):
ctx = {
"title": _("Export identities"),
"action_label": _("Export"),
"action_classes": "submit",
"formid": "exportform",
"action": reverse("admin:identity_export"),
}
if request.method == "POST":
form = ExportIdentitiesForm(request.POST)
form.is_valid()
fp = cStringIO.StringIO()
csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"])
identities = get_identities(
request.user, **request.session['identities_filters'])
for ident in identities:
ident.to_csv(csvwriter)
content = fp.getvalue()
fp.close()
return _export(content, form.cleaned_data["filename"])
ctx["form"] = ExportIdentitiesForm()
return render(request, "common/generic_modal_form.html", ctx)
@login_required
@permission_required("admin.add_domain")
def export_domains(request):
ctx = {
"title": _("Export domains"),
"action_label": _("Export"),
"action_classes": "submit",
"formid": "exportform",
"action": reverse("admin:domain_export"),
}
if request.method == "POST":
form = ExportDomainsForm(request.POST)
form.is_valid()
fp = cStringIO.StringIO()
csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"])
for dom in get_domains(request.user,
**request.session['domains_filters']):
dom.to_csv(csvwriter)
content = fp.getvalue()
fp.close()
return _export(content, form.cleaned_data["filename"])
ctx["form"] = ExportDomainsForm()
return render(request, "common/generic_modal_form.html", ctx)
+210
View File
@@ -0,0 +1,210 @@
"""Identity related views."""
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
from django.shortcuts import render
from django.utils.translation import ugettext as _, ungettext
from django.views.decorators.csrf import ensure_csrf_cookie
import reversion
from modoboa.core.models import User
from modoboa.lib import parameters, events
from modoboa.lib.exceptions import (
PermDeniedException, BadRequest
)
from modoboa.lib.listing import (
get_sort_order, get_listing_page
)
from modoboa.lib.web_utils import (
_render_to_string, render_to_json_response
)
from ..forms import AccountForm, AccountWizard
from ..lib import get_identities
from ..models import Mailbox, Domain
@login_required
@user_passes_test(
lambda u: u.has_perm("core.add_user") or
u.has_perm("admin.add_alias")
)
def _identities(request):
filters = dict((fname, request.GET.get(fname, None))
for fname in ['searchquery', 'idtfilter', 'grpfilter'])
request.session['identities_filters'] = filters
idents_list = get_identities(request.user, **filters)
sort_order, sort_dir = get_sort_order(request.GET, "identity",
["identity", "name_or_rcpt", "tags"])
if sort_order in ["identity", "name_or_rcpt"]:
objects = sorted(idents_list, key=lambda o: getattr(o, sort_order),
reverse=sort_dir == '-')
else:
objects = sorted(idents_list, key=lambda o: o.tags[0],
reverse=sort_dir == '-')
context = {
"handle_mailboxes": parameters.get_admin(
"HANDLE_MAILBOXES", raise_error=False)
}
page = get_listing_page(objects, request.GET.get("page", 1))
if page is None:
context["length"] = 0
else:
context["headers"] = _render_to_string(
request, "admin/identity_headers.html", {})
context["rows"] = _render_to_string(
request, "admin/identities_table.html", {
"identities": page.object_list
}
)
context["pages"] = [page.number]
return render_to_json_response(context)
@login_required
@permission_required("admin.add_mailbox")
def list_quotas(request):
from modoboa.lib.db_utils import db_type
sort_order, sort_dir = get_sort_order(request.GET, "address")
mboxes = Mailbox.objects.get_for_admin(
request.user, request.GET.get("searchquery", None)
)
mboxes = mboxes.exclude(quota=0)
if sort_order in ["address", "quota"]:
mboxes = mboxes.order_by("%s%s" % (sort_dir, sort_order))
elif sort_order == "quota_value__bytes":
where = "admin_mailbox.address||'@'||admin_domain.name"
mboxes = mboxes.extra(
select={"quota_value__bytes": "admin_quota.bytes"},
where=["admin_quota.username=%s" % where],
tables=["admin_quota", "admin_domain"],
order_by=["%s%s" % (sort_dir, sort_order)]
)
elif sort_order == "quota_usage":
where = "admin_mailbox.address||'@'||admin_domain.name"
db_type = db_type()
if db_type == "postgres":
select = (
"(admin_quota.bytes::float / (CAST(admin_mailbox.quota "
"AS BIGINT) * 1048576)) * 100"
)
else:
select = (
"admin_quota.bytes / (admin_mailbox.quota "
"* 1048576) * 100"
)
if db_type == "mysql":
where = "CONCAT(admin_mailbox.address,'@',admin_domain.name)"
mboxes = mboxes.extra(
select={'quota_usage': select},
where=["admin_quota.username=%s" % where],
tables=["admin_quota", "admin_domain"],
order_by=["%s%s" % (sort_dir, sort_order)]
)
else:
raise BadRequest(_("Invalid request"))
page = get_listing_page(mboxes, request.GET.get("page", 1))
context = {
"headers": _render_to_string(
request, "admin/quota_headers.html", {}
)
}
if page is None:
context["length"] = 0
else:
context["rows"] = _render_to_string(
request, "admin/quotas.html", {
"mboxes": page
}
)
context["pages"] = [page.number]
return render_to_json_response(context)
@login_required
@user_passes_test(
lambda u: u.has_perm("admin.add_user") or
u.has_perm("admin.add_alias") or
u.has_perm("admin.add_mailbox")
)
def get_next_page(request):
"""Return the next page of the identity list."""
if request.GET.get("objtype", "identity") == "identity":
return _identities(request)
return list_quotas(request)
@login_required
@user_passes_test(
lambda u: u.has_perm("admin.add_user") or
u.has_perm("admin.add_alias")
)
@ensure_csrf_cookie
def identities(request, tplname="admin/identities.html"):
return render(request, tplname, {
"selection": "identities",
"deflocation": "list/"
})
@login_required
@permission_required("core.add_user")
def accounts_list(request):
accs = User.objects.filter(is_superuser=False) \
.exclude(groups__name='SimpleUsers')
res = [a.username for a in accs.all()]
return render_to_json_response(res)
@login_required
@permission_required("core.add_user")
@reversion.create_revision()
def newaccount(request):
"""Create a new account."""
return AccountWizard(request).process()
@login_required
@permission_required("core.change_user")
@reversion.create_revision()
def editaccount(request, accountid, tplname="common/tabforms.html"):
account = User.objects.get(pk=accountid)
if not request.user.can_access(account):
raise PermDeniedException
mb = account.mailbox if hasattr(account, "mailbox") else None
instances = dict(general=account, mail=mb, perms=account)
events.raiseEvent("FillAccountInstances", request.user, account, instances)
return AccountForm(request, instances=instances).process()
@login_required
@permission_required("core.delete_user")
def delaccount(request, accountid):
keepdir = True if request.POST.get("keepdir", "false") == "true" else False
User.objects.get(pk=accountid).delete(request.user, keep_mb_dir=keepdir)
return render_to_json_response(
ungettext("Account deleted", "Accounts deleted", 1)
)
@login_required
@permission_required("admin.add_domain")
def remove_permission(request):
domid = request.GET.get("domid", None)
daid = request.GET.get("daid", None)
if domid is None or daid is None:
raise BadRequest(_("Invalid request"))
try:
account = User.objects.get(pk=daid)
domain = Domain.objects.get(pk=domid)
except (User.DoesNotExist, Domain.DoesNotExist):
raise BadRequest(_("Invalid request"))
if not request.user.can_access(account) or \
not request.user.can_access(domain):
raise PermDeniedException
domain.remove_admin(account)
return render_to_json_response({})

Some files were not shown because too many files have changed in this diff Show More