This commit is contained in:
Christian Wiegand
2016-10-16 10:46:32 +02:00
17 changed files with 137 additions and 217 deletions
+1 -4
View File
@@ -2,7 +2,7 @@
`Modoboa <http://modoboa.org/>`_
################################
|travis| |codecov| |landscape| |latest-version| |downloads|
|travis| |codecov| |landscape| |latest-version|
Modoboa is a mail hosting and management platform including a modern
and simplified Web User Interface. It provides useful components such
@@ -58,9 +58,6 @@ The following external libraries are provided with Modoboa:
.. |landscape| image:: https://landscape.io/github/tonioo/modoboa/master/landscape.svg?style=flat
:target: https://landscape.io/github/tonioo/modoboa/master
:alt: Code Health
.. |downloads| image:: https://img.shields.io/pypi/dm/modoboa.svg
:target: https://pypi.python.org/pypi/modoboa/
:alt: Downloads from Pypi
.. |travis| image:: https://travis-ci.org/tonioo/modoboa.png?branch=master
:target: https://travis-ci.org/tonioo/modoboa
.. |codecov| image:: http://codecov.io/github/tonioo/modoboa/coverage.svg?branch=master
+1 -1
View File
@@ -52,7 +52,7 @@ copyright = u'2016, Antoine Nguyen'
# The short X.Y version.
version = '1.6'
# The full version, including alpha/beta/rc tags.
release = '1.6.0'
release = '1.6.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
+11 -1
View File
@@ -55,7 +55,7 @@ Specific upgrade instructions
An interesting feature brougth by this version is the capability to
make different checks about MX records. For example, Modoboa can
query main `DNSBL <https://en.wikipedia.org/wiki/DNSBL>`_ providers
for every defined domain. With this, you will quickly now if one the
for every defined domain. With this, you will quickly know if one the
domains you manage is listed or not. To activate it, add the
following line to your crontab::
@@ -73,6 +73,16 @@ configuration as follows::
MODOBOA_API_URL = 'https://api.modoboa.org/1/'
Finally, it is now possible to declare additional sender addresses on
a per-account basis. You need to update your postfix configuration in
order to use this functionality. Just edit the :file:`main.cf` file
and change the following parameter::
smtpd_sender_login_maps =
<driver>:/etc/postfix/sql-sender-login-mailboxes.cf
<driver>:/etc/postfix/sql-sender-login-aliases.cf
<driver>:/etc/postfix/sql-sender-login-mailboxes-extra.cf
1.5.0
=====
+6 -3
View File
@@ -56,9 +56,12 @@ class AccountViewSet(viewsets.ModelViewSet):
def get_serializer_class(self):
"""Return a serializer."""
if self.request.method == "GET":
return serializers.AccountSerializer
return serializers.WritableAccountSerializer
action_dict = {
"list": serializers.AccountSerializer,
"retrive": serializers.AccountSerializer
}
return action_dict.get(
self.action, serializers.WritableAccountSerializer)
def get_queryset(self):
"""Filter queryset based on current user."""
-1
View File
@@ -21,7 +21,6 @@ DNSBL_PROVIDERS = [
"dnsbl-3.uceprotect.net",
"dnsbl.cyberlogic.net",
"dnsbl.inps.de",
"dnsbl.njabl.org",
"dnsbl.sorbs.net",
"drone.abuse.ch",
"duinv.aupads.org",
+2 -2
View File
@@ -206,8 +206,8 @@ class AccountFormMail(forms.Form, DynamicForm):
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)."
"press ENTER to add a new input. To create a catchall alias, just "
"enter the domain name (@domain.tld)."
)
)
senderaddress = lib_fields.UTF8AndEmptyUserEmailField(
+36 -110
View File
@@ -1,7 +1,7 @@
# 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-2016
msgid ""
@@ -9,13 +9,13 @@ msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-06 13:36+0200\n"
"PO-Revision-Date: 2016-09-16 13:56+0000\n"
"PO-Revision-Date: 2016-10-08 07:09+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"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: admin/app_settings.py:54 admin/callbacks.py:25
@@ -38,11 +38,9 @@ msgstr "MX(s) valide(s)"
#: admin/app_settings.py:68
msgid ""
"IP(s) or network(s) of the MX that should match with domains' MX records. On "
"per line."
msgstr ""
"IP(s) ou réseau(x) avec lesquels les enregistrements MX des domaines doivent "
"correspondre. Un par ligne."
"IP(s) or network(s) of the MX that should match with domains' MX records. On"
" per line."
msgstr "IP(s) ou réseau(x) avec lesquels les enregistrements MX des domaines doivent correspondre. Un par ligne."
#: admin/app_settings.py:77
msgid "Enable DNSBL checks"
@@ -64,9 +62,7 @@ msgstr "Gérer les boites sur le système de fichiers"
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"
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:96
msgid "Mailboxes owner"
@@ -84,9 +80,7 @@ msgstr "Quota de domaine par défaut"
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é."
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:113
msgid "Automatic account removal"
@@ -94,19 +88,16 @@ msgstr "Suppression automatique de compte"
#: admin/app_settings.py:116
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"
msgstr "Lorsqu'une boite aux lettres est supprimée, supprimer le compte associé en même temps"
#: admin/app_settings.py:120
msgid "Automatic domain/mailbox creation"
msgstr "Création automatique de domaine/boite aux lettres"
#: admin/app_settings.py:123
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."
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:170 admin/forms/account.py:292
#: admin/forms/domain.py:81
@@ -153,9 +144,7 @@ msgstr "Nom d'utilisateur"
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."
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:40 admin/templates/admin/account_detail.html:29
msgid "Role"
@@ -211,10 +200,7 @@ msgid ""
"Quota in MB for this mailbox. Define a custom value or use domain's default "
"one. Leave empty to define an unlimited value (not allowed for domain "
"administrators)."
msgstr ""
"Quota 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)."
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:205 admin/forms/domain.py:44
#: admin/templates/admin/domains.html:35
@@ -226,10 +212,7 @@ msgid ""
"Alias(es) of this mailbox. Indicate only one address per input, press ENTER "
"to add a new input. Use the '*' character to create a 'catchall' alias (ex: "
"*@domain.tld)."
msgstr ""
"Alias 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)."
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:214
msgid "Sender addresses"
@@ -240,11 +223,7 @@ msgid ""
"Additional sender address(es) for this account. The user will be allowed to "
"send emails using this address, even if it does not exist locally. Indicate "
"one address per input. Press ENTER to add a new input."
msgstr ""
"Adresse(s) d'envoi supplémentaire(s) pour ce compte. L'utilisateur sera "
"autorisé à envoyer des e-mails en utilisant la/les adresse(s) déclarée(s), "
"même si elle(s) n'existe(nt) pas localement. Indiquer une adresse par champ. "
"Appuyer sur ENTREE pour ajouter un nouveau champ."
msgstr "Adresse(s) d'envoi supplémentaire(s) pour ce compte. L'utilisateur sera autorisé à envoyer des e-mails en utilisant la/les adresse(s) déclarée(s), même si elle(s) n'existe(nt) pas localement. Indiquer une adresse par champ. Appuyer sur ENTREE pour ajouter un nouveau champ."
#: admin/forms/account.py:271 admin/forms/alias.py:92
#: admin/models/mailbox.py:293
@@ -300,9 +279,7 @@ msgstr "Adresse email"
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)."
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:35 admin/templates/admin/alias_detail.html:37
msgid "Recipients"
@@ -312,9 +289,7 @@ msgstr "Destinataires"
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."
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:47 admin/forms/domain.py:24 admin/models/domain.py:90
#: admin/templates/admin/domain_statistics.html:49
@@ -341,17 +316,13 @@ msgstr "Type"
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."
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:47
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."
msgstr "Alias de ce domaine. Indiquer seulement un nom par champ, appuyer sur ENTREE pour ajouter un nouveau champ."
#: admin/forms/domain.py:101 admin/forms/domain.py:113
#: admin/forms/domain.py:121
@@ -382,9 +353,7 @@ msgstr "Nom"
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."
msgstr "Le nom de l'administrateur. Ne pas include un nom de domaine ici, il sera ajouté automatiquement."
#: admin/forms/domain.py:184
msgid "Create aliases"
@@ -447,9 +416,7 @@ msgstr "Transférer les messages et conserver une copie dans votre boite locale"
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"
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"
@@ -469,9 +436,7 @@ 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"
msgstr "Cocher cette option si les mots de passe contenus dans votre fichier ne sont pas chiffrés"
#: admin/lib.py:31
msgid "A mailbox is required"
@@ -490,15 +455,13 @@ msgid "[modoboa] DNSBL issue(s) for domain {}"
msgstr "[modoboa] Problème(s) DNSBL pour le domaine {}"
#: admin/management/commands/subcommands/_mx.py:180
#, fuzzy
#| msgid "Domain {} as no MX record"
msgid "Domain {} has no MX record"
msgstr "Le domaine {} n'a aucun enregistrement MX"
msgstr "Le domaine {} n'a pas d'enregistrement MX"
#: admin/management/commands/subcommands/_mx.py:191
#, python-brace-format
msgid "MX record for domain {0} is invalid: {1}"
msgstr ""
msgstr "Enregistrement MX invalide pour le domaine {0} : {1}"
#: admin/management/commands/subcommands/_mx.py:207
msgid "[modoboa] MX issue(s) for domain {}"
@@ -614,9 +577,7 @@ msgstr "Permission refusée."
#: admin/serializers.py:82
msgid ""
"List of administered domains (resellers and domain administrators only)."
msgstr ""
"Liste des domaines administrés (revendeurs et administrateurs de domaine "
"seulement)."
msgstr "Liste des domaines administrés (revendeurs et administrateurs de domaine seulement)."
#: admin/serializers.py:189
msgid "Not allowed for this role."
@@ -745,7 +706,7 @@ msgstr "Statut DNS"
#: admin/templates/admin/domain_detail.html:28
#: admin/templates/admin/domains_table.html:12
msgid "Awaiting checks"
msgstr ""
msgstr "En attente de vérification"
#: admin/templates/admin/domain_detail.html:40
msgid "Creation date"
@@ -866,9 +827,7 @@ msgstr "Mis à jour"
#: admin/templates/admin/mx_domain_detail.html:23
msgid "This IP does not match any of the valid MXs defined in parameters"
msgstr ""
"Cette IP ne correspond à un aucun des MX valides déclarés dans les "
"paramètres."
msgstr "Cette IP ne correspond à un aucun des MX valides déclarés dans les paramètres."
#: admin/templates/admin/mx_domain_detail.html:36
msgid "No MX record found for this domain."
@@ -879,26 +838,19 @@ msgstr "Aucun enregistrement MX trouvé pour ce domaine."
msgid ""
"Modoboa detected that domain %(domain)s is listed by the following DNSBL "
"providers:"
msgstr ""
"Modoboa a détecté que le domaine %(domain)s est listé par les fournisseurs "
"de DNSBL suivants :"
msgstr "Modoboa a détecté que le domaine %(domain)s est listé par les fournisseurs de DNSBL suivants :"
#: admin/templates/admin/notifications/domain_in_dnsbl.html:11
msgid ""
"The domain's reputation will be affected and there is a chance that emails "
"coming from it are considered as spam. You should contact those providers "
"and ask them to unlist detected IP address(es)."
msgstr ""
"La réputation de ce domaine sera affectée et il est probable que les emails "
"venant de lui soient considérés comme du spam. Vous devriez contacter ces "
"fournisseurs pour leur demander de délister la/les adresse(s) détectée(s)."
msgstr "La réputation de ce domaine sera affectée et il est probable que les emails venant de lui soient considérés comme du spam. Vous devriez contacter ces fournisseurs pour leur demander de délister la/les adresse(s) détectée(s)."
#: admin/templates/admin/notifications/domain_invalid_mx.html:3
#, python-format
msgid "Modoboa detected that domain %(domain)s has no valid MX record:"
msgstr ""
"Modoboa a détecté que le domain %(domain)s n'a aucun enregistrement MX "
"valide :"
msgstr "Modoboa a détecté que le domain %(domain)s n'a aucun enregistrement MX valide :"
#: admin/templates/admin/quota_headers.html:3
msgid "Value"
@@ -1044,19 +996,9 @@ msgid ""
" <li><em>domainalias; name; targeted domain; enabled</em></li>\n"
" %s\n"
"</ul>\n"
"<p>The first element of each line is mandatory and must be equal to one of "
"the previous values.</p>\n"
"<p>The first element of each line is mandatory and must be equal to one of the previous values.</p>\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"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"
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"
@@ -1066,26 +1008,13 @@ msgstr "Importer des domaines"
msgid ""
"Provide a CSV file where lines respect one of the following formats:\n"
"<ul>\n"
"<li><em>account; loginname; password; first name; last name; enabled; group; "
"address; quota; [, domain, ...]</em></li>\n"
"<li><em>account; loginname; password; first name; last name; enabled; group; address; quota; [, domain, ...]</em></li>\n"
"<li><em>alias; address; enabled; recipient; recipient; ...</em></li>\n"
"</ul>\n"
"<p>The first element of each line is mandatory and must be equal to one of "
"the previous values.</p>\n"
"<p>The first element of each line is mandatory and must be equal to one of the previous values.</p>\n"
"\n"
"<p>You can use a different character as separator.</p>\n"
msgstr ""
"Fournir un fichier CSV dans lequel les lignes respectent un des formats "
"suivants :\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; recipient; recipient; ...</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"
msgstr "Fournir un fichier CSV dans lequel les lignes respectent un des formats suivants :\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; recipient; recipient; ...</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:130
msgid "Import identities"
@@ -1094,6 +1023,3 @@ msgstr "Importer des identités"
#: admin/views/user.py:35
msgid "Forward updated"
msgstr "Transfert mis à jour"
#~ msgid "Domain {0} got an invalid MX record. Got {1}"
#~ msgstr "L'enregistrement MX du domaine {0} n'est pas bon : {1}."
@@ -114,7 +114,7 @@ class CheckMXRecords(BaseCommand):
results = {}
for mx in mx_list:
reverse = ".".join(reversed(mx.address.split(".")))
pattern = "{}.{}".format(reverse, provider)
pattern = "{}.{}.".format(reverse, provider)
try:
results[mx] = socket.gethostbyname(pattern)
except socket.gaierror:
+2 -2
View File
@@ -125,13 +125,13 @@ class SenderLoginMailboxExtraMap(object):
"WHERE sad.address='%s'"
)
postgres = (
"SELECT mb.address || '@' || dom.name) FROM admin_mailbox mb "
"SELECT mb.address || '@' || dom.name FROM admin_mailbox mb "
"INNER JOIN admin_senderaddress sad ON sad.mailbox_id=mb.id "
"INNER JOIN admin_domain dom ON dom.id=mb.domain_id "
"WHERE sad.address='%s'"
)
sqlite = (
"SELECT mb.address || '@' || dom.name) FROM admin_mailbox mb "
"SELECT mb.address || '@' || dom.name FROM admin_mailbox mb "
"INNER JOIN admin_senderaddress sad ON sad.mailbox_id=mb.id "
"INNER JOIN admin_domain dom ON dom.id=mb.domain_id "
"WHERE sad.address='%s'"
+6 -2
View File
@@ -8,6 +8,7 @@ from rest_framework import serializers
from passwords import validators
from modoboa.admin import models as admin_models
from modoboa.core import constants as core_constants
from modoboa.core import models as core_models
from modoboa.core import signals as core_signals
from modoboa.lib import exceptions as lib_exceptions
@@ -154,6 +155,8 @@ class AccountPasswordSerializer(serializers.ModelSerializer):
class WritableAccountSerializer(AccountSerializer):
"""Serializer to create account."""
role = serializers.ChoiceField(choices=core_constants.ROLES)
class Meta(AccountSerializer.Meta):
fields = AccountSerializer.Meta.fields + (
"password", )
@@ -184,11 +187,12 @@ class WritableAccountSerializer(AccountSerializer):
def validate(self, data):
"""Check constraints."""
master_user = data.get("master_user", False)
if master_user and data["role"] != "SuperAdmins":
role = data.get("role")
if master_user and role != "SuperAdmins":
raise serializers.ValidationError({
"master_user": _("Not allowed for this role.")
})
if data["role"] == "SimpleUsers":
if role == "SimpleUsers":
mailbox = data.get("mailbox")
if mailbox is None:
data["mailbox"] = {
+19 -1
View File
@@ -74,6 +74,24 @@ class AccountTestCase(ModoTestCase):
alias__internal=True).exists()
)
def test_delete_default_superadmin(self):
"""Delete default superadmin."""
sadmin2 = core_factories.UserFactory(
username="admin2", is_superuser=True)
sadmin = User.objects.get(username="admin")
self.client.force_login(sadmin2)
self.ajax_post(
reverse("admin:account_delete", args=[sadmin.pk]), {}
)
values = {
"username": "user@test.com", "role": "DomainAdmins",
"is_active": True, "email": "user@test.com"
}
account = User.objects.get(username="user@test.com")
self.ajax_post(
reverse("admin:account_change", args=[account.pk]), values
)
def test_sender_address(self):
"""Check if sender addresses are saved."""
account = User.objects.get(username="user@test.com")
@@ -198,7 +216,7 @@ class AccountTestCase(ModoTestCase):
self.assertIn("Resources usage", response.content)
@skipIf(NO_LDAP, 'No ldap module installed')
@skipIf(NO_LDAP, "No ldap module installed")
@override_settings(AUTHENTICATION_BACKENDS=(
'modoboa.lib.authbackends.LDAPBackend',
'modoboa.lib.authbackends.SimpleBackend',
@@ -167,8 +167,12 @@ SWAGGER_SETTINGS = {
"exclude_namespaces": [],
"info": {
"contact": "contact@modoboa.com",
"description": ("Modoboa API, requires a valid token."),
"title": "Modoboa API",
"description": (
"Modoboa API, requires a valid token. To obtain a token, login "
"as super administrator, go to 'User settings > API' and enable "
"API access. Click on 'Update' and wait for page refresh."
),
"title": "Modoboa API documentation",
}
}
+3 -10
View File
@@ -118,6 +118,9 @@ class User(PermissionsMixin):
raise PermDeniedException
owner = get_object_owner(self)
if owner == self:
# The default admin is being removed...
owner = fromuser
for ooentry in self.objectaccess_set.filter(is_owner=True):
if ooentry.content_object is not None:
grant_access_to_object(owner, ooentry.content_object, True)
@@ -239,16 +242,6 @@ class User(PermissionsMixin):
return "%s %s" % (self.first_name, self.last_name)
return "----"
@property
def group(self):
"""FIXME: DEPRECATED"""
if self.is_superuser:
return "SuperAdmins"
try:
return self.groups.all()[0].name
except IndexError:
return "---"
@property
def enabled(self):
return self.is_active
+6 -10
View File
@@ -1,12 +1,9 @@
#!/usr/bin/env python
# coding: utf-8
"""
u2u_decode.py
unstructured rfc2047 header to unicode
"""
import re
"""Unstructured rfc2047 header to unicode."""
from email.header import decode_header, make_header
import re
# check spaces between encoded_words (and strip them)
sre = re.compile(r'\?=[ \t]+=\?')
@@ -15,7 +12,7 @@ mre = re.compile(r'=\?[^?]*?\?[bq]\?[^?\t]*?\?=', re.I)
def clean_spaces(m):
"""Replace unencoded spaces in string
"""Replace unencoded spaces in string.
:param str m: a match object
:return: the cleaned string
@@ -24,12 +21,11 @@ def clean_spaces(m):
def decode_mime(m):
"""substitute matching encoded_word with unicode equiv.
"""
"""Substitute matching encoded_word with unicode equiv."""
h = decode_header(clean_spaces(m))
try:
u = unicode(make_header(h))
except UnicodeDecodeError:
except (LookupError, UnicodeDecodeError):
return m.group(0)
return u
+23 -53
View File
@@ -1,7 +1,7 @@
# 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>, 2016
msgid ""
@@ -9,13 +9,13 @@ msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-06 13:36+0200\n"
"PO-Revision-Date: 2016-09-16 14:04+0000\n"
"PO-Revision-Date: 2016-10-08 07:07+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"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: core/__init__.py:63
@@ -64,8 +64,7 @@ msgstr "plain (faible)"
#: core/app_settings.py:61
msgid "Scheme used to crypt mailbox passwords"
msgstr ""
"Méthode utilisée pour chiffrer les mots de passe des boites aux lettres"
msgstr "Méthode utilisée pour chiffrer les mots de passe des boites aux lettres"
#: core/app_settings.py:66
msgid "Rounds"
@@ -75,9 +74,7 @@ msgstr "Tours"
msgid ""
"Number of rounds to use (only used by sha256crypt and sha512crypt). Must be "
"between 1000 and 999999999, inclusive."
msgstr ""
"Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit "
"être compris entre 1000 et 999999999 inclue."
msgstr "Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit être compris entre 1000 et 999999999 inclue."
#: core/app_settings.py:76
msgid "Secret key"
@@ -139,9 +136,7 @@ msgstr "ND de connexion"
msgid ""
"The distinguished name to use when binding to the LDAP server. Leave empty "
"for an anonymous bind"
msgstr ""
"Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser "
"vide pour une connexion anonyme"
msgstr "Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser vide pour une connexion anonyme"
#: core/app_settings.py:128
msgid "Bind password"
@@ -149,9 +144,7 @@ msgstr "Mot de passe de connexion"
#: core/app_settings.py:131
msgid "The password to use when binding to the LDAP server (with 'Bind DN')"
msgstr ""
"Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de "
"connexion')"
msgstr "Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de connexion')"
#: core/app_settings.py:140
msgid "Users search base"
@@ -159,9 +152,7 @@ msgstr "Base de recherche des utilisateurs"
#: core/app_settings.py:143
msgid "The distinguished name of the search base used to find users"
msgstr ""
"Le nom distinctif de la base de recherche utilisée pour rechercher les "
"utilisateurs"
msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les utilisateurs"
#: core/app_settings.py:150
msgid "Search filter"
@@ -171,9 +162,7 @@ msgstr "Filtre de recherche"
msgid ""
"An optional filter string (e.g. '(objectClass=person)'). In order to be "
"valid, it must be enclosed in parentheses."
msgstr ""
"Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être "
"valide, il doit être entouré de parenthèses."
msgstr "Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être valide, il doit être entouré de parenthèses."
#: core/app_settings.py:161
msgid "User DN template"
@@ -184,9 +173,7 @@ msgstr "Modèle de nom distinctif utilisateur"
msgid ""
"The template used to construct a user's DN. It should contain one "
"placeholder (ie. %(user)s)"
msgstr ""
"Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il "
"doit contenir un espace réservé (ex. %(user)s)"
msgstr "Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il doit contenir un espace réservé (ex. %(user)s)"
#: core/app_settings.py:172
msgid "Password attribute"
@@ -210,12 +197,9 @@ msgstr "Groupes administrateurs"
#: core/app_settings.py:189
msgid ""
"Members of those LDAP Posix groups will be created as domain administrators. "
"Use ';' characters to separate groups."
msgstr ""
"Les membres de ces groupes LDAP (Posix) seront créés en tant "
"qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les "
"groupes."
"Members of those LDAP Posix groups will be created as domain administrators."
" Use ';' characters to separate groups."
msgstr "Les membres de ces groupes LDAP (Posix) seront créés en tant qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les groupes."
#: core/app_settings.py:196
msgid "Group type"
@@ -231,9 +215,7 @@ msgstr "Base de recherche des groupes"
#: core/app_settings.py:208
msgid "The distinguished name of the search base used to find groups"
msgstr ""
"Le nom distinctif de la base de recherche utilisée pour rechercher les "
"groupes"
msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les groupes"
#: core/app_settings.py:213
msgid "Notifications"
@@ -273,9 +255,7 @@ msgstr "Envoyer des statistiques"
#: core/app_settings.py:243
msgid "Send statistics to Modoboa public API (counters and used extensions)"
msgstr ""
"Envoyer des statistiques à l'API publique de Modoboa (compteurs et "
"extensions utilisées)"
msgstr "Envoyer des statistiques à l'API publique de Modoboa (compteurs et extensions utilisées)"
#: core/app_settings.py:247
msgid "Miscellaneous"
@@ -311,8 +291,7 @@ msgstr "Redirection par défaut"
#: core/app_settings.py:277
msgid "The default redirection used when no application is specified"
msgstr ""
"La redirection appliquée par défaut quand aucune application n'est spécifiée"
msgstr "La redirection appliquée par défaut quand aucune application n'est spécifiée"
#: core/app_settings.py:310
msgid "Key must be either 16, 24, or 32 bytes long"
@@ -445,8 +424,7 @@ msgstr "Vous ne pouvez pas supprimer votre propre compte"
#: core/models.py:164
msgid "Failed to update password: LDAP module not installed"
msgstr ""
"Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé"
msgstr "Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé"
#: core/models.py:222
msgid "account"
@@ -504,9 +482,7 @@ msgstr "Dernières nouvelles"
msgid ""
"Visit the <a href=\"https://modoboa.org/blog/\" target=\"_blank\">official "
"weblog</a> for more information."
msgstr ""
"Visitez le <a href=\"https://modoboa.org/blog/\" target=\"_blank\">blog "
"officiel</a> pour plus d'information."
msgstr "Visitez le <a href=\"https://modoboa.org/blog/\" target=\"_blank\">blog officiel</a> pour plus d'information."
#: core/templates/core/information.html:2 core/templatetags/core_tags.py:53
msgid "Information"
@@ -522,8 +498,7 @@ msgstr "Une ou plusieurs mises à jour sont disponibles!"
#: core/templates/core/information.html:6
msgid "Check the following list to find the component(s) that can be updated."
msgstr ""
"Consulter la liste suivante pour trouver le(s) composant(s) concerné(s)."
msgstr "Consulter la liste suivante pour trouver le(s) composant(s) concerné(s)."
#: core/templates/core/information.html:10
msgid "Applications"
@@ -552,7 +527,7 @@ msgstr "Utilisateurs actifs"
#: core/templates/core/information.html:37
msgid "Role"
msgstr ""
msgstr "Rôle"
#: core/templates/core/information.html:38
msgid "Last login"
@@ -651,9 +626,7 @@ msgstr "Utiliseur '%s' connecté avec succès"
#: core/views/auth.py:48
msgid "Your username and password didn't match. Please try again."
msgstr ""
"Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez "
"réessayer."
msgstr "Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez réessayer."
#: core/views/auth.py:76
#, python-format
@@ -716,11 +689,11 @@ msgstr "Impossible de mettre à jour le mot de passe : {}"
#: lib/templatetags/lib_tags.py:103
msgid "From"
msgstr ""
msgstr "De"
#: lib/templatetags/lib_tags.py:103
msgid "To"
msgstr ""
msgstr "À"
#: lib/templatetags/lib_tags.py:103 templates/common/email_searchbar.html:17
msgid "Subject"
@@ -784,6 +757,3 @@ msgstr "Se souvenir de moi"
#: templates/registration/login.html:43
msgid "Log in"
msgstr "Se connecter"
#~ msgid "Group name"
#~ msgstr "Nom du groupe"
+1 -1
View File
@@ -12,7 +12,7 @@ django-xforwardedfor-middleware
django-passwords==0.3.10
rfc6266
progressbar==2.3
djangorestframework==3.3.1
djangorestframework==3.3.3
django-rest-swagger==0.3.5
django-subcommand2==0.1.1
gevent==1.1.1
+13 -13
View File
@@ -54,24 +54,24 @@ def read(fname):
setup(
name="modoboa",
version='1.6.0',
url='http://modoboa.org/',
license='MIT',
version="1.6.1",
url="http://modoboa.org/",
license="ISC",
description="Mail hosting made simple",
long_description=read('README.rst'),
author='Antoine Nguyen',
author_email='tonio@ngyn.org',
long_description=read("README.rst"),
author="Antoine Nguyen",
author_email="tonio@ngyn.org",
packages=find_packages(),
include_package_data=True,
scripts=["bin/modoboa-admin.py"],
zip_safe=False,
install_requires=parse_requirements(PIP_REQUIRES),
dependency_links=parse_dependency_links(PIP_REQUIRES),
classifiers=['Development Status :: 5 - Production/Stable',
'Framework :: Django',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP']
classifiers=["Development Status :: 5 - Production/Stable",
"Framework :: Django",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Topic :: Internet :: WWW/HTTP"]
)