From 8d621f247fe7c2a5a8c1da4adc720cbb996c1afb Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Fri, 6 Feb 2015 09:20:16 +0100 Subject: [PATCH] Remove admin extension from this repo. --- .../core/commands/templates/settings.py.tpl | 1 - modoboa/core/views/auth.py | 3 +- modoboa/extensions/admin/__init__.py | 0 modoboa/extensions/admin/app_settings.py | 100 ---- modoboa/extensions/admin/factories.py | 79 --- modoboa/extensions/admin/fixtures/demo.json | 310 ---------- modoboa/extensions/admin/forms/__init__.py | 19 - modoboa/extensions/admin/forms/account.py | 478 --------------- modoboa/extensions/admin/forms/alias.py | 152 ----- modoboa/extensions/admin/forms/domain.py | 362 ------------ modoboa/extensions/admin/forms/export.py | 45 -- modoboa/extensions/admin/forms/forward.py | 46 -- modoboa/extensions/admin/forms/import_.py | 43 -- modoboa/extensions/admin/lib.py | 114 ---- .../extensions/admin/management/__init__.py | 0 .../admin/management/commands/__init__.py | 0 .../commands/handle_mailbox_operations.py | 97 --- .../admin/migrations/0001_initial.py | 161 ----- .../extensions/admin/migrations/__init__.py | 0 modoboa/extensions/admin/models/__init__.py | 10 - modoboa/extensions/admin/models/alias.py | 210 ------- modoboa/extensions/admin/models/base.py | 81 --- modoboa/extensions/admin/models/domain.py | 185 ------ .../extensions/admin/models/domain_alias.py | 72 --- modoboa/extensions/admin/models/mailbox.py | 334 ----------- modoboa/extensions/admin/modo_extension.py | 285 --------- .../admin/static/admin/css/admin.css | 33 -- .../admin/static/admin/css/settings.css | 9 - .../extensions/admin/static/admin/js/admin.js | 556 ------------------ modoboa/extensions/admin/tables.py | 15 - .../templates/admin/account_general_form.html | 12 - .../admin/templates/admin/aliasform.html | 10 - .../templates/admin/domain_general_form.html | 5 - .../templates/admin/domain_options_form.html | 8 - .../templates/admin/domain_statistics.html | 63 -- .../admin/templates/admin/domains.html | 29 - .../admin/templates/admin/domains_table.html | 9 - .../admin/templates/admin/editdomainform.html | 28 - .../admin/templates/admin/forward.html | 11 - .../admin/templates/admin/header.html | 12 - .../admin/templates/admin/identities.html | 26 - .../templates/admin/identities_table.html | 10 - .../templates/admin/identity_headers.html | 5 - .../admin/templates/admin/import_done.html | 11 - .../admin/templates/admin/importform.html | 16 - .../admin/templates/admin/listing.html | 47 -- .../admin/templates/admin/mailboxes_raw.html | 11 - .../admin/templates/admin/mailform.html | 5 - .../admin/templates/admin/newdomainform.html | 6 - .../admin/templates/admin/permsform.html | 2 - .../admin/templates/admin/quota_headers.html | 5 - .../admin/templates/admin/quotas.html | 15 - .../admin/templates/admin/searchform.html | 11 - .../extensions/admin/templatetags/__init__.py | 0 .../admin/templatetags/admin_tags.py | 280 --------- modoboa/extensions/admin/tests/__init__.py | 14 - modoboa/extensions/admin/tests/account.py | 197 ------- modoboa/extensions/admin/tests/alias.py | 130 ---- modoboa/extensions/admin/tests/domain.py | 112 ---- .../extensions/admin/tests/domain_alias.py | 47 -- modoboa/extensions/admin/tests/export.py | 84 --- modoboa/extensions/admin/tests/import_.py | 191 ------ .../admin/tests/password_schemes.py | 55 -- modoboa/extensions/admin/tests/user.py | 26 - modoboa/extensions/admin/urls.py | 60 -- modoboa/extensions/admin/views/__init__.py | 0 modoboa/extensions/admin/views/alias.py | 135 ----- modoboa/extensions/admin/views/domain.py | 154 ----- modoboa/extensions/admin/views/export.py | 90 --- modoboa/extensions/admin/views/identity.py | 211 ------- modoboa/extensions/admin/views/import.py | 177 ------ modoboa/extensions/admin/views/user.py | 57 -- modoboa/lib/web_utils.py | 3 +- 73 files changed, 4 insertions(+), 6176 deletions(-) delete mode 100644 modoboa/extensions/admin/__init__.py delete mode 100644 modoboa/extensions/admin/app_settings.py delete mode 100644 modoboa/extensions/admin/factories.py delete mode 100644 modoboa/extensions/admin/fixtures/demo.json delete mode 100644 modoboa/extensions/admin/forms/__init__.py delete mode 100644 modoboa/extensions/admin/forms/account.py delete mode 100644 modoboa/extensions/admin/forms/alias.py delete mode 100644 modoboa/extensions/admin/forms/domain.py delete mode 100644 modoboa/extensions/admin/forms/export.py delete mode 100644 modoboa/extensions/admin/forms/forward.py delete mode 100644 modoboa/extensions/admin/forms/import_.py delete mode 100644 modoboa/extensions/admin/lib.py delete mode 100644 modoboa/extensions/admin/management/__init__.py delete mode 100644 modoboa/extensions/admin/management/commands/__init__.py delete mode 100644 modoboa/extensions/admin/management/commands/handle_mailbox_operations.py delete mode 100644 modoboa/extensions/admin/migrations/0001_initial.py delete mode 100644 modoboa/extensions/admin/migrations/__init__.py delete mode 100644 modoboa/extensions/admin/models/__init__.py delete mode 100644 modoboa/extensions/admin/models/alias.py delete mode 100644 modoboa/extensions/admin/models/base.py delete mode 100644 modoboa/extensions/admin/models/domain.py delete mode 100644 modoboa/extensions/admin/models/domain_alias.py delete mode 100644 modoboa/extensions/admin/models/mailbox.py delete mode 100644 modoboa/extensions/admin/modo_extension.py delete mode 100644 modoboa/extensions/admin/static/admin/css/admin.css delete mode 100644 modoboa/extensions/admin/static/admin/css/settings.css delete mode 100644 modoboa/extensions/admin/static/admin/js/admin.js delete mode 100644 modoboa/extensions/admin/tables.py delete mode 100644 modoboa/extensions/admin/templates/admin/account_general_form.html delete mode 100644 modoboa/extensions/admin/templates/admin/aliasform.html delete mode 100644 modoboa/extensions/admin/templates/admin/domain_general_form.html delete mode 100644 modoboa/extensions/admin/templates/admin/domain_options_form.html delete mode 100644 modoboa/extensions/admin/templates/admin/domain_statistics.html delete mode 100644 modoboa/extensions/admin/templates/admin/domains.html delete mode 100644 modoboa/extensions/admin/templates/admin/domains_table.html delete mode 100644 modoboa/extensions/admin/templates/admin/editdomainform.html delete mode 100644 modoboa/extensions/admin/templates/admin/forward.html delete mode 100644 modoboa/extensions/admin/templates/admin/header.html delete mode 100644 modoboa/extensions/admin/templates/admin/identities.html delete mode 100644 modoboa/extensions/admin/templates/admin/identities_table.html delete mode 100644 modoboa/extensions/admin/templates/admin/identity_headers.html delete mode 100644 modoboa/extensions/admin/templates/admin/import_done.html delete mode 100644 modoboa/extensions/admin/templates/admin/importform.html delete mode 100644 modoboa/extensions/admin/templates/admin/listing.html delete mode 100644 modoboa/extensions/admin/templates/admin/mailboxes_raw.html delete mode 100644 modoboa/extensions/admin/templates/admin/mailform.html delete mode 100644 modoboa/extensions/admin/templates/admin/newdomainform.html delete mode 100644 modoboa/extensions/admin/templates/admin/permsform.html delete mode 100644 modoboa/extensions/admin/templates/admin/quota_headers.html delete mode 100644 modoboa/extensions/admin/templates/admin/quotas.html delete mode 100644 modoboa/extensions/admin/templates/admin/searchform.html delete mode 100644 modoboa/extensions/admin/templatetags/__init__.py delete mode 100644 modoboa/extensions/admin/templatetags/admin_tags.py delete mode 100644 modoboa/extensions/admin/tests/__init__.py delete mode 100644 modoboa/extensions/admin/tests/account.py delete mode 100644 modoboa/extensions/admin/tests/alias.py delete mode 100644 modoboa/extensions/admin/tests/domain.py delete mode 100644 modoboa/extensions/admin/tests/domain_alias.py delete mode 100644 modoboa/extensions/admin/tests/export.py delete mode 100644 modoboa/extensions/admin/tests/import_.py delete mode 100644 modoboa/extensions/admin/tests/password_schemes.py delete mode 100644 modoboa/extensions/admin/tests/user.py delete mode 100644 modoboa/extensions/admin/urls.py delete mode 100644 modoboa/extensions/admin/views/__init__.py delete mode 100644 modoboa/extensions/admin/views/alias.py delete mode 100644 modoboa/extensions/admin/views/domain.py delete mode 100644 modoboa/extensions/admin/views/export.py delete mode 100644 modoboa/extensions/admin/views/identity.py delete mode 100644 modoboa/extensions/admin/views/import.py delete mode 100644 modoboa/extensions/admin/views/user.py diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index 2ba6dc86b..c19db186e 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -57,7 +57,6 @@ MODOBOA_APPS = ( 'modoboa.core', 'modoboa.lib', # Modoboa extensions here. - 'modoboa.extensions.admin', 'modoboa.extensions.postfix_relay_domains', 'modoboa.extensions.limits', 'modoboa.extensions.postfix_autoreply', diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 44325a366..e515cb61f 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -41,7 +41,8 @@ def dologin(request): if user.group == "SimpleUsers": nextlocation = reverse("topredirection") else: - nextlocation = reverse("admin:domain_list") + # FIXME + nextlocation = reverse("modoboa_admin:domain_list") return HttpResponseRedirect(nextlocation) error = _( "Your username and password didn't match. Please try again.") diff --git a/modoboa/extensions/admin/__init__.py b/modoboa/extensions/admin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/extensions/admin/app_settings.py b/modoboa/extensions/admin/app_settings.py deleted file mode 100644 index 8638bce9e..000000000 --- a/modoboa/extensions/admin/app_settings.py +++ /dev/null @@ -1,100 +0,0 @@ -from django import forms -from django.conf import settings -from django.utils.translation import ugettext_lazy -from modoboa.lib.form_utils import YesNoField, SeparatorField -from modoboa.lib.sysutils import exec_cmd -from modoboa.lib import parameters -import os - - -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'] diff --git a/modoboa/extensions/admin/factories.py b/modoboa/extensions/admin/factories.py deleted file mode 100644 index e49956e83..000000000 --- a/modoboa/extensions/admin/factories.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -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", ) - - quota = 10 - enabled = True - - -class MailboxFactory(PermissionFactory): - - class Meta: - model = models.Mailbox - - domain = factory.SubFactory(DomainFactory) - user = factory.SubFactory(UserFactory) - quota = 10 - enabled = True - - -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) - - fwd = AliasFactory.create( - address='forward', domain=dom, extmboxes='user@external.com' - ) - al = AliasFactory.create( - address='alias', domain=dom - ) - al.mboxes.add(account.mailbox_set.all()[0]) - AliasFactory.create( - address='postmaster', domain=dom, extmboxes='toto@titi.com,test@truc.fr' - ) - 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) diff --git a/modoboa/extensions/admin/fixtures/demo.json b/modoboa/extensions/admin/fixtures/demo.json deleted file mode 100644 index d3691219b..000000000 --- a/modoboa/extensions/admin/fixtures/demo.json +++ /dev/null @@ -1,310 +0,0 @@ -[ - { - "pk": 1, - "model": "core.user", - "fields": { - "username": "admin", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": true, - "is_staff": false, - "last_login": "2012-05-03T16:58:51+01:00", - "groups": [], - "user_permissions": [], - "password": "{CRYPT}dTTsGDkA5ZHKg", - "email": "", - "date_joined": "2012-01-26T15:19:25+01:00" - } - }, - { - "pk": 2, - "model": "core.user", - "fields": { - "username": "admin@demo.local", - "first_name": "Demo", - "last_name": "Administrator", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-05-24T05:00:12+01:00", - "groups": [1], - "user_permissions": [], - "password": "{CRYPT}SJ2bQWwabfH/6", - "email": "admin@demo.local", - "date_joined": "2012-05-03T16:50:15+01:00" - } - }, - { - "pk": 4, - "model": "core.user", - "fields": { - "username": "user@demo.local", - "first_name": "Demo", "last_name": "User", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-05-03T16:56:50+01:00", - "groups": [2], - "user_permissions": [], - "password": "{CRYPT}2iAFXtxUA6.7Y", - "email": "user@demo.local", - "date_joined": "2012-05-03T16:56:50+01:00" - } - }, - { - "pk": 2, - "model": "core.objectaccess", - "fields": { - "is_owner": true, - "user": 1, - "content_type": ["admin", "domain"], - "object_id": 1 - } - }, - { - "pk": 3, - "model": "core.objectaccess", - "fields": { - "is_owner": true, - "user": 1, - "content_type": ["core", "user"], - "object_id": 2 - } - }, - { - "pk": 4, - "model": "core.objectaccess", - "fields": { - "is_owner": true, - "user": 1, - "content_type": ["admin", "mailbox"], - "object_id": 1 - } - }, - { - "pk": 5, - "model": "core.objectaccess", - "fields": { - "is_owner": true, - "user": 1, - "content_type": ["admin", "alias"], - "object_id": 1 - } - }, - { - "pk": 6, - "model": "core.objectaccess", - "fields": { - "is_owner": false, - "user": 2, - "content_type": ["admin", "domain"], - "object_id": 1 - } - }, - { - "pk": 7, - "model": "core.objectaccess", - "fields": { - "is_owner": false, - "user": 2, - "content_type": ["admin", "mailbox"], - "object_id": 1 - } - }, - { - "pk": 8, - "model": "core.objectaccess", - "fields": { - "is_owner": false, - "user": 2, - "content_type": ["core", "user"], - "object_id": 2 - } - }, - { - "pk": 9, - "model": "core.objectaccess", - "fields": { - "is_owner": false, - "user": 2, - "content_type": ["admin", - "alias"], - "object_id": 1}}, - { - "pk": 13, - "model": "core.objectaccess", - "fields": { - "is_owner": true, - "user": 1, - "content_type": ["core", - "user"], - "object_id": 4}}, - { - "pk": 14, - "model": "core.objectaccess", - "fields": { - "is_owner": true, - "user": 1, - "content_type": ["admin", - "mailbox"], - "object_id": 3}}, - { - "pk": 15, - "model": "core.objectaccess", - "fields": { - "is_owner": false, - "user": 2, - "content_type": ["admin", - "mailbox"], - "object_id": 3}}, - { - "pk": 16, - "model": "core.objectaccess", - "fields": { - "is_owner": false, - "user": 2, - "content_type": ["core", - "user"], - "object_id": 4}}, - { - "pk": 1, - "model": "admin.objectdates", - "fields": { - "creation": "2012-05-03T16:49:40+01:00", - "last_modification": "2012-05-03T16:49:40+01:00"}}, - { - "pk": 2, - "model": "admin.objectdates", - "fields": { - "creation": "2012-05-03T16:50:15+01:00", - "last_modification": "2012-05-03T16:50:36+01:00"}}, - { - "pk": 3, - "model": "admin.objectdates", - "fields": { - "creation": "2012-05-03T16:50:15+01:00", - "last_modification": "2012-05-03T16:50:15+01:00"}}, - { - "pk": 4, - "model": "admin.objectdates", - "fields": { - "creation": "2012-05-03T16:51:04+01:00", - "last_modification": "2012-05-03T16:51:04+01:00"}}, - { - "pk": 5, - "model": "admin.objectdates", - "fields": { - "creation": "2012-05-03T16:56:50+01:00", - "last_modification": "2012-05-03T16:56:50+01:00"}}, - { - "pk": 1, - "model": "admin.domain", - "fields": { - "dates": 1, - "enabled": true, - "name": "demo.local", - "quota": 10}}, - { - "pk": 1, - "model": "admin.mailbox", - "fields": { - "dates": 2, - "quota": 10, - "user": 2, - "domain": 1, - "address": "admin" - } - }, - { - "pk": 3, - "model": "admin.mailbox", - "fields": { - "dates": 5, - "quota": 10, - "user": 4, - "domain": 1, - "address": "user" - } - }, - { - "pk": 1, - "model": "admin.alias", - "fields": { - "dates": 3, - "enabled": true, - "domain": 1, - "mboxes": [1], - "address": "postmaster", - "extmboxes": ""}}, - { - "pk": 1, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "limits"}}, - { - "pk": 2, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "postfix_autoreply"}}, - { - "pk": 3, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "stats"}}, - { - "pk": 4, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "amavis"}}, - { - "pk": 5, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "webmail"}}, - { - "pk": 6, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "sievefilters"}}, - { - "pk": 7, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "postfix_relay_domains" - } - }, - { - "pk": 8, - "model": "core.extension", - "fields": { - "enabled": true, - "name": "radicale" - } - }, - { - "pk": "admin@demo.local", - "model": "admin.quota", - "fields": { - "username": "admin@demo.local", - "bytes": 0, - "messages": 0 - } - }, - { - "pk": "user@demo.local", - "model": "admin.quota", - "fields": { - "username": "user@demo.local", - "bytes": 0, - "messages": 0 - } - } -] diff --git a/modoboa/extensions/admin/forms/__init__.py b/modoboa/extensions/admin/forms/__init__.py deleted file mode 100644 index dbb5a36ef..000000000 --- a/modoboa/extensions/admin/forms/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -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' -] diff --git a/modoboa/extensions/admin/forms/account.py b/modoboa/extensions/admin/forms/account.py deleted file mode 100644 index 3d929f697..000000000 --- a/modoboa/extensions/admin/forms/account.py +++ /dev/null @@ -1,478 +0,0 @@ -"""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 modoboa.core.models import User -from modoboa.extensions.admin.models import ( - Domain, Mailbox, Alias -) -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 -) -from modoboa.lib.permissions import get_account_roles -from modoboa.lib.web_utils import render_to_json_response - - -class AccountFormGeneral(forms.ModelForm): - 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 = forms.CharField( - label=ugettext_lazy("Password"), widget=forms.widgets.PasswordInput - ) - password2 = forms.CharField( - 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") - - 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', '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 "instance" in kwargs: - account = kwargs["instance"] - domain_disabled = account.mailbox_set.count() and \ - not account.mailbox_set.all()[0].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 self.instance.mailbox_set.count(): - return False - return not self.instance.mailbox_set.all()[0].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_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 - for alias in self.mb.alias_set.all(): - if len(alias.get_recipients()) >= 2: - continue - name = "aliases_%d" % cpt - self._create_field(forms.EmailField, name, alias.full_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()) - - for alias in self.mb.alias_set.all(): - if alias.full_address not in aliases: - if len(alias.get_recipients()) >= 2: - continue - alias.delete() - else: - aliases.remove(alias.full_address) - if not aliases: - return - events.raiseEvent( - "CanCreate", user, "mailbox_aliases", len(aliases) - ) - for alias in aliases: - local_part, domname = split_mailbox(alias) - try: - self.mb.alias_set.get(address=local_part, domain__name=domname) - except Alias.DoesNotExist: - pass - else: - continue - al = Alias(address=local_part, enabled=account.is_active) - al.domain = Domain.objects.get(name=domname) - al.save(int_rcpts=[self.mb]) - 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( - AccountFormGeneral, _("General"), new_args=[request.user] - ) - self.add_step( - AccountFormMail, _("Mail"), formtpl="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")) diff --git a/modoboa/extensions/admin/forms/alias.py b/modoboa/extensions/admin/forms/alias.py deleted file mode 100644 index 12485eebc..000000000 --- a/modoboa/extensions/admin/forms/alias.py +++ /dev/null @@ -1,152 +0,0 @@ -"""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.extensions.admin.models import ( - Domain, Mailbox, Alias -) -from modoboa.lib.email_utils import split_mailbox -from modoboa.lib.exceptions import BadRequest, NotFound, Conflict -from modoboa.lib.form_utils import ( - DynamicForm -) - - -class AliasForm(forms.ModelForm, DynamicForm): - email = forms.EmailField( - label=ugettext_lazy("Email address"), - help_text=ugettext_lazy( - "The distribution list address. Use the '*' character to create a " - "'catchall' address (ex: *@domain.tld)." - ), - widget=forms.TextInput(attrs={"class": "form-control"}) - ) - recipients = forms.EmailField( - label=ugettext_lazy("Recipients"), required=False, - help_text=ugettext_lazy( - "Mailbox(es) 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 = ("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 - ['email', '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: - dlist = kwargs["instance"] - self.fields["email"].initial = dlist.full_address - if not dlist.domain.enabled: - self.fields["enabled"].widget.attrs["disabled"] = "disabled" - cpt = 1 - for al in dlist.aliases.all(): - name = "recipients_%d" % cpt - self._create_field(forms.EmailField, name, al.full_address, 2) - cpt += 1 - for mb in dlist.mboxes.all(): - name = "recipients_%d" % (cpt) - self._create_field(forms.EmailField, name, mb.full_address, 2) - cpt += 1 - for addr in dlist.extmboxes.split(','): - if addr == "": - continue - name = "recipients_%d" % (cpt) - self._create_field(forms.EmailField, name, addr, 2) - cpt += 1 - - def clean_email(self): - localpart, domname = split_mailbox(self.cleaned_data["email"]) - 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["email"].lower() - - def set_recipients(self): - """Recipients dispatching - - We make a difference between 'local' recipients (the ones hosted - by Modoboa) and 'external' recipients. - """ - self.ext_rcpts = [] - self.int_rcpts = [] - total = 0 - - for k, v in self.cleaned_data.items(): - if not k.startswith("recipients"): - continue - if v == "": - continue - local_part, domname = split_mailbox(v) - if domname is None: - raise BadRequest( - u"%s %s" % (_("Invalid mailbox"), v) - ) - try: - domain = Domain.objects.get(name=domname) - except Domain.DoesNotExist: - domain = None - if domain is not None: - try: - rcpt = Alias.objects.get(domain=domain, address=local_part) - if rcpt.full_address == self.cleaned_data["email"]: - rcpt = None - except Alias.DoesNotExist: - rcpt = None - if rcpt is None: - try: - rcpt = Mailbox.objects.get( - domain=domain, address=local_part) - except Mailbox.DoesNotExist: - raise NotFound( - _("Local recipient %s not found" % v) - ) - if rcpt in self.int_rcpts: - raise Conflict( - _("Recipient %s already present" % v) - ) - self.int_rcpts += [rcpt] - total += 1 - continue - - if v in self.ext_rcpts: - raise Conflict( - _("Recipient %s already present" % v) - ) - self.ext_rcpts += [v] - total += 1 - - if total == 0: - raise BadRequest(_("No recipient defined")) - - def save(self, commit=True): - alias = super(AliasForm, self).save(commit=False) - localpart, domname = split_mailbox(self.cleaned_data["email"]) - alias.address = localpart - alias.domain = Domain.objects.get(name=domname) - if commit: - alias.save(int_rcpts=self.int_rcpts, ext_rcpts=self.ext_rcpts) - self.save_m2m() - return alias diff --git a/modoboa/extensions/admin/forms/domain.py b/modoboa/extensions/admin/forms/domain.py deleted file mode 100644 index 8fc8afb15..000000000 --- a/modoboa/extensions/admin/forms/domain.py +++ /dev/null @@ -1,362 +0,0 @@ -"""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.extensions.admin.lib import check_if_domain_exists -from modoboa.extensions.admin.models import ( - Domain, DomainAlias, Mailbox, Alias, Quota -) -from modoboa.lib import events, parameters -from modoboa.lib.exceptions import ModoboaException, Conflict -from modoboa.lib.form_utils import ( - DomainNameField, YesNoField, WizardForm, DynamicForm, TabForms -) -from modoboa.lib.web_utils import render_to_json_response - - -class DomainFormGeneral(forms.ModelForm, DynamicForm): - 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." - ) - ) - name = DomainNameField( - widget=forms.TextInput() - ) - - class Meta: - model = Domain - fields = ("name", "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... - """ - super(DomainFormGeneral, self).clean() - cleaned_data = self.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) - newq = 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): - 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, user, domain): - if not self.fields: - return - if self.cleaned_data["create_dom_admin"] == "no": - return - 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 self.cleaned_data["create_aliases"] == "yes": - events.raiseEvent("CanCreate", user, "mailbox_aliases") - alias = Alias(address="postmaster", domain=domain, enabled=True) - alias.save(int_rcpts=[mb]) - 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 = [] - 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: - cbargs += [kwargs["instances"]["general"]] - 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): - domain = self.instances["general"] - domadmins = [u for u in 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": domain.name, - "action": reverse("admin:domain_change", args=[domain.pk]), - "formid": "domform", - "domain": domain, - "domadmins": domadmins - }) - - def is_valid(self): - """Custom validation. - - We just save the current name before it is potentially - modified. - - """ - 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. - """ - self.forms[0]['instance'].save( - self.request.user, domalias_post_create=True - ) - for f in self.forms[1:]: - f["instance"].save(self.request.user) - - def done(self): - 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( - DomainFormGeneral, _("General"), - formtpl="admin/domain_general_form.html" - ) - self.add_step( - DomainFormOptions, _("Options"), - formtpl="admin/domain_options_form.html", - new_args=[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) - try: - self.steps[1].form.save(self.request.user, domain) - except ModoboaException: - from django.db import transaction - transaction.rollback() - raise - return render_to_json_response(_("Domain created")) diff --git a/modoboa/extensions/admin/forms/export.py b/modoboa/extensions/admin/forms/export.py deleted file mode 100644 index f1f8a868b..000000000 --- a/modoboa/extensions/admin/forms/export.py +++ /dev/null @@ -1,45 +0,0 @@ -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" diff --git a/modoboa/extensions/admin/forms/forward.py b/modoboa/extensions/admin/forms/forward.py deleted file mode 100644 index c4d1e4a7b..000000000 --- a/modoboa/extensions/admin/forms/forward.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Forms related to forwards management.""" - -from django import forms -from django.utils.translation import ugettext as _, ugettext_lazy - -from modoboa.extensions.admin.models import ( - Domain -) -from modoboa.lib.email_utils import split_mailbox -from modoboa.lib.exceptions import BadRequest, PermDeniedException - - -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 parse_dest(self): - self.dests = [] - rawdata = self.cleaned_data["dest"].strip() - if rawdata == "": - return - for d in rawdata.split(","): - local_part, domname = split_mailbox(d) - if not local_part or not domname or not len(domname): - raise BadRequest("Invalid mailbox syntax for %s" % d) - try: - Domain.objects.get(name=domname) - except Domain.DoesNotExist: - self.dests += [d] - else: - raise PermDeniedException( - _("You can't define a forward to a local destination. " - "Please ask your administrator to create an alias " - "instead.") - ) diff --git a/modoboa/extensions/admin/forms/import_.py b/modoboa/extensions/admin/forms/import_.py deleted file mode 100644 index 0091e4ba8..000000000 --- a/modoboa/extensions/admin/forms/import_.py +++ /dev/null @@ -1,43 +0,0 @@ -"""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" - ) - ) diff --git a/modoboa/extensions/admin/lib.py b/modoboa/extensions/admin/lib.py deleted file mode 100644 index 9ba1e9585..000000000 --- a/modoboa/extensions/admin/lib.py +++ /dev/null @@ -1,114 +0,0 @@ -# coding: utf-8 -from functools import wraps -from itertools import chain -from django.db.models import Q -from django.contrib.contenttypes.models import ContentType -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 modoboa.lib.email_utils import split_mailbox -from modoboa.extensions.admin.models import ( - Domain, 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 request.user.mailbox_set.count(): - 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: - if '@' in searchquery: - local_part, domname = split_mailbox(searchquery) - if local_part: - q &= Q(address__icontains=local_part) - if domname: - q &= Q(domain__name__icontains=domname) - else: - 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 = [] - if domfilter is None or not domfilter or domfilter == 'domain': - domains = Domain.objects.get_for_admin(user) - if searchquery is not None: - q = Q(name__contains=searchquery) - q |= Q(domainalias__name__contains=searchquery) - domains = domains.filter(q).distinct() - extra_domain_entries = events.raiseQueryEvent( - 'ExtraDomainEntries', user, domfilter, searchquery, **extrafilters - ) - return chain(domains, extra_domain_entries) - - -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 diff --git a/modoboa/extensions/admin/management/__init__.py b/modoboa/extensions/admin/management/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/extensions/admin/management/commands/__init__.py b/modoboa/extensions/admin/management/commands/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/extensions/admin/management/commands/handle_mailbox_operations.py b/modoboa/extensions/admin/management/commands/handle_mailbox_operations.py deleted file mode 100644 index b0cea9d46..000000000 --- a/modoboa/extensions/admin/management/commands/handle_mailbox_operations.py +++ /dev/null @@ -1,97 +0,0 @@ - -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 modoboa.extensions.admin.models import MailboxOperation - - -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): - try: - if parameters.get_admin("HANDLE_MAILBOXES") == 'no': - return - except parameters.NotDefined: - 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']) diff --git a/modoboa/extensions/admin/migrations/0001_initial.py b/modoboa/extensions/admin/migrations/0001_initial.py deleted file mode 100644 index ed6048142..000000000 --- a/modoboa/extensions/admin/migrations/0001_initial.py +++ /dev/null @@ -1,161 +0,0 @@ -# -*- 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 (without the domain part). For a 'catch-all' address, just enter an * character.", max_length=254, verbose_name='address')), - ('extmboxes', models.TextField(blank=True)), - ('enabled', models.BooleanField(default=True, help_text='Check to activate this alias', verbose_name='enabled')), - ('aliases', models.ManyToManyField(help_text='The aliases this alias points to', to='admin.Alias')), - ], - options={ - 'ordering': ['domain__name', 'address'], - 'permissions': (('view_aliases', 'View aliases'),), - }, - 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')), - ], - 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.ForeignKey(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='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'), - preserve_default=True, - ), - migrations.AddField( - model_name='alias', - name='mboxes', - field=models.ManyToManyField(help_text='The mailboxes this alias points to', to='admin.Mailbox', verbose_name='mailboxes'), - preserve_default=True, - ), - migrations.AlterUniqueTogether( - name='alias', - unique_together=set([('address', 'domain')]), - ), - ] diff --git a/modoboa/extensions/admin/migrations/__init__.py b/modoboa/extensions/admin/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/extensions/admin/models/__init__.py b/modoboa/extensions/admin/models/__init__.py deleted file mode 100644 index 641eff9ad..000000000 --- a/modoboa/extensions/admin/models/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from .base import AdminObject -from .domain import Domain -from .domain_alias import DomainAlias -from .mailbox import Mailbox, Quota, MailboxOperation -from .alias import Alias - -__all__ = [ - 'Domain', 'DomainAlias', 'Mailbox', 'Quota', 'Alias', - 'MailboxOperation', 'AdminObject' -] diff --git a/modoboa/extensions/admin/models/alias.py b/modoboa/extensions/admin/models/alias.py deleted file mode 100644 index 646dab326..000000000 --- a/modoboa/extensions/admin/models/alias.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Models related to 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 .mailbox import Mailbox -from modoboa.lib.email_utils import split_mailbox -from modoboa.lib.exceptions import ( - PermDeniedException, BadRequest, Conflict -) - - -class Alias(AdminObject): - - """ - Mailbox alias. - """ - address = models.CharField( - ugettext_lazy('address'), max_length=254, - help_text=ugettext_lazy( - "The alias address (without the domain part). For a 'catch-all' " - "address, just enter an * character." - ) - ) - domain = models.ForeignKey(Domain) - mboxes = models.ManyToManyField( - Mailbox, verbose_name=ugettext_lazy('mailboxes'), - help_text=ugettext_lazy("The mailboxes this alias points to") - ) - aliases = models.ManyToManyField( - 'Alias', - help_text=ugettext_lazy("The aliases this alias points to") - ) - extmboxes = models.TextField(blank=True) - enabled = models.BooleanField( - ugettext_lazy('enabled'), - help_text=ugettext_lazy("Check to activate this alias"), - default=True - ) - _objectname = 'MailboxAlias' - - class Meta: - permissions = ( - ("view_aliases", "View aliases"), - ) - unique_together = (("address", "domain"),) - ordering = ["domain__name", "address"] - app_label = 'admin' - - def __unicode__(self): - return self.full_address - - @property - def full_address(self): - return "%s@%s" % (self.address, self.domain.name) - - @property - def identity(self): - return self.full_address - - @property - def name_or_rcpt(self): - rcpts_count = self.get_recipients_count() - if not rcpts_count: - return "---" - rcpts = self.get_recipients() - if rcpts_count > 1: - return "%s, ..." % rcpts[0] - return rcpts[0] - - @property - def type(self): - cpt = self.get_recipients_count() - if cpt > 1: - return "dlist" - if self.extmboxes != "": - 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 save(self, *args, **kwargs): - """Custom save method.""" - if 'ext_rcpts' in kwargs: - self.extmboxes = ",".join(kwargs['ext_rcpts']) \ - if kwargs['ext_rcpts'] else "" - del kwargs['ext_rcpts'] - if 'int_rcpts' in kwargs: - int_rcpts = kwargs['int_rcpts'] - del kwargs['int_rcpts'] - else: - int_rcpts = [] - super(Alias, self).save(*args, **kwargs) - curaliases = self.aliases.all() - curmboxes = self.mboxes.all() - for rcpt in int_rcpts: - if isinstance(rcpt, Alias): - if rcpt not in curaliases: - self.aliases.add(rcpt) - continue - if rcpt not in curmboxes: - self.mboxes.add(rcpt) - for rcpt in curaliases: - if rcpt not in int_rcpts: - self.aliases.remove(rcpt) - for rcpt in curmboxes: - if rcpt not in int_rcpts: - self.mboxes.remove(rcpt) - - def get_recipients(self, with_external=True): - """Return the recipients list. - - Internal and external addresses are mixed into a single list. - - :param bool with_external: include external addresses or not - :rtype: list - :return: a list of addresses - """ - result = [al.full_address for al in self.aliases.all()] - result += [mb.full_address for mb in self.mboxes.all()] - if with_external and self.extmboxes != "": - result += self.extmboxes.split(',') - return result - - def get_recipients_count(self): - """Return the number of recipients of this alias. - - :rtype: int - """ - total = 0 - if self.extmboxes != "": - total += len(self.extmboxes.split(',')) - return total + self.aliases.count() + self.mboxes.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)) - localpart, domname = split_mailbox(row[1].strip()) - 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 - try: - Alias.objects.get(address=localpart, domain__name=domain) - except Alias.DoesNotExist: - pass - else: - raise Conflict - self.address = localpart - self.domain = domain - self.enabled = (row[2].strip() in ["True", "1", "yes", "y"]) - int_rcpts = [] - ext_rcpts = [] - for rcpt in row[3:]: - rcpt = rcpt.strip() - if not rcpt: - continue - localpart, domname = split_mailbox(rcpt) - try: - Domain.objects.get(name=domname) - except Domain.DoesNotExist: - ext_rcpts += [rcpt] - continue - try: - target = Alias.objects.get( - domain__name=domname, address=localpart - ) - if target.full_address == self.full_address: - target = None - except Alias.DoesNotExist: - target = None - if target is None: - try: - target = Mailbox.objects.get(address=localpart, - domain__name=domname) - except Mailbox.DoesNotExist: - raise BadRequest(_("Local recipient %s not found" % rcpt)) - int_rcpts += [target] - self.save(int_rcpts=int_rcpts, ext_rcpts=ext_rcpts, creator=user) - - def to_csv(self, csvwriter): - row = [self.type, self.full_address.encode("utf-8"), self.enabled] - row += self.get_recipients() - csvwriter.writerow(row) - -reversion.register(Alias) diff --git a/modoboa/extensions/admin/models/base.py b/modoboa/extensions/admin/models/base.py deleted file mode 100644 index c576bdcad..000000000 --- a/modoboa/extensions/admin/models/base.py +++ /dev/null @@ -1,81 +0,0 @@ -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() diff --git a/modoboa/extensions/admin/models/domain.py b/modoboa/extensions/admin/models/domain.py deleted file mode 100644 index 8d996def4..000000000 --- a/modoboa/extensions/admin/models/domain.py +++ /dev/null @@ -1,185 +0,0 @@ -"""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) - - 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.count() - - @property - def tags(self): - return [{"name": "domain", "label": _("Domain"), "type": "dom"}] - - @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() - try: - Domain.objects.get(name=self.name) - except Domain.DoesNotExist: - pass - else: - 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) diff --git a/modoboa/extensions/admin/models/domain_alias.py b/modoboa/extensions/admin/models/domain_alias.py deleted file mode 100644 index ba784a3cb..000000000 --- a/modoboa/extensions/admin/models/domain_alias.py +++ /dev/null @@ -1,72 +0,0 @@ -"""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]: - try: - model.objects.get(name=self.name) - except model.DoesNotExist: - pass - else: - raise Conflict - domname = row[2].strip() - try: - self.target = Domain.objects.get(name=domname) - 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) diff --git a/modoboa/extensions/admin/models/mailbox.py b/modoboa/extensions/admin/models/mailbox.py deleted file mode 100644 index fcc46e52c..000000000 --- a/modoboa/extensions/admin/models/mailbox.py +++ /dev/null @@ -1,334 +0,0 @@ -"""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.db.models.signals import pre_delete -from django.dispatch import receiver -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, events -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.ForeignKey(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.alias_set.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 = [] - for alias in self.alias_set.all(): - aliases += [alias.full_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) - if creator.is_superuser and not self.user.has_perm("admin.add_domain"): - # 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 - - """ - try: - q = Quota.objects.get(username=self.full_address) - except Quota.DoesNotExist: - pass - else: - q.delete() - if not keepdir: - self.delete_dir() - super(Mailbox, self).delete() - -reversion.register(Mailbox) - - -@receiver(pre_delete, sender=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.permissions import ungrant_access_to_object - - mb = kwargs['instance'] - events.raiseEvent("MailboxDeleted", mb) - ungrant_access_to_object(mb) - for alias in mb.alias_set.all(): - alias.mboxes.remove(mb) - if alias.mboxes.count() == 0: - alias.delete() - - -class MailboxOperation(models.Model): - 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 diff --git a/modoboa/extensions/admin/modo_extension.py b/modoboa/extensions/admin/modo_extension.py deleted file mode 100644 index 15d64de5b..000000000 --- a/modoboa/extensions/admin/modo_extension.py +++ /dev/null @@ -1,285 +0,0 @@ -"""Registration with Modoboa takes place here.""" - -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext as _, ugettext_lazy - -from modoboa.core.extensions import ModoExtension, exts_pool -from modoboa.extensions.admin.models import ( - Domain, DomainAlias, Mailbox, Alias -) -from modoboa.lib import parameters, events -from modoboa.lib.email_utils import split_mailbox -from modoboa.lib.exceptions import PermDeniedException, BadRequest, Conflict - - -ADMIN_EVENTS = [ - "DomainCreated", - "DomainModified", - "DomainDeleted", - "DomainOwnershipRemoved", - "ExtraDomainEntries", - "ExtraDomainMenuEntries", - "ExtraDomainFilters", - "GetDomainActions", - "GetDomainModifyLink", - "CheckDomainName", - - "DomainAliasCreated", - "DomainAliasDeleted", - - "MailboxCreated", - "MailboxDeleted", - "MailboxModified", - - "MailboxAliasCreated", - "MailboxAliasDeleted", - - "ExtraDomainForm", - "FillDomainInstances", - - "ExtraAccountForm", - "CheckExtraAccountForm", - "FillAccountInstances", - - "ExtraDomainImportHelp", - "ImportObject" -] - -PERMISSIONS = { - "DomainAdmins": [ - ["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"] - ] -} - - -class AdminConsole(ModoExtension): - name = "admin" - label = ugettext_lazy("Administration console") - version = "1.0" - description = ugettext_lazy( - "Web based console to manage domains, accounts and aliases" - ) - always_active = True - - def load(self): - from modoboa.extensions.admin.app_settings import AdminParametersForm - parameters.register( - AdminParametersForm, ugettext_lazy("Administration")) - events.declare(ADMIN_EVENTS) - -exts_pool.register_extension(AdminConsole) - - -@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.extensions.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")} - ] - if user.has_perm("core.add_user") or user.has_perm("admin.add_alias"): - 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 user.mailbox_set.count(): - 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_set.all()[0].quota] \ - if admin.mailbox_set.count() 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.extensions.admin.lib import check_if_domain_exists - from modoboa.lib.permissions import grant_access_to_object - - 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 request.user.mailbox_set.count(): - 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 account.mailbox_set.count(): - return - mb = account.mailbox_set.all()[0] - if not byuser.can_access(mb): - raise PermDeniedException - keep_mb_dir = kwargs.get("keep_mb_dir", True) - mb.delete(keepdir=keep_mb_dir) diff --git a/modoboa/extensions/admin/static/admin/css/admin.css b/modoboa/extensions/admin/static/admin/css/admin.css deleted file mode 100644 index 2768bf843..000000000 --- a/modoboa/extensions/admin/static/admin/css/admin.css +++ /dev/null @@ -1,33 +0,0 @@ -#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; -} diff --git a/modoboa/extensions/admin/static/admin/css/settings.css b/modoboa/extensions/admin/static/admin/css/settings.css deleted file mode 100644 index 24fc269b9..000000000 --- a/modoboa/extensions/admin/static/admin/css/settings.css +++ /dev/null @@ -1,9 +0,0 @@ - -#settings_content .form-horizontal .control-label { - margin-left: 20px; - text-align: left; -} - -#settings_content > .form-horizontal .controls { - margin-left: 240px; -} \ No newline at end of file diff --git a/modoboa/extensions/admin/static/admin/js/admin.js b/modoboa/extensions/admin/static/admin/js/admin.js deleted file mode 100644 index 85eb24af9..000000000 --- a/modoboa/extensions/admin/static/admin/js/admin.js +++ /dev/null @@ -1,556 +0,0 @@ -/** - * 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) ? - {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('
' - + gettext("No domain administrator defined") + "
"); - } - } - }]); - }); - }, - - newdomain_cb: function() { - this.generalform_init(); - this.optionsform_init(); - $("#wizard").cwizard({ - formid: "domform", - transition_callbacks: { - 1: this.optionsform_prefill - }, - error_callbacks: { - 2: $.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() { - $("#id_email").removeClass("disabled") - .attr("readonly", null) - .autocompleter("listen"); - }, - - 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(); - } - }, this)); - 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: { - 1: 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_email").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); diff --git a/modoboa/extensions/admin/tables.py b/modoboa/extensions/admin/tables.py deleted file mode 100644 index d8f4f1c8d..000000000 --- a/modoboa/extensions/admin/tables.py +++ /dev/null @@ -1,15 +0,0 @@ -# coding: utf-8 -from django.utils.translation import ugettext_lazy -from modoboa.lib import tables - - -class ExtensionsTable(tables.Table): - idkey = "id" - selection = tables.SelectionColumn("selection", width="4%", header=False) - label = tables.Column("label", label=ugettext_lazy("Name"), width="15%") - version = tables.Column( - "version", label=ugettext_lazy("Version"), width="6%" - ) - descr = tables.Column("description", label=ugettext_lazy("Description")) - - cols_order = ["selection", "label", "version", "descr"] diff --git a/modoboa/extensions/admin/templates/admin/account_general_form.html b/modoboa/extensions/admin/templates/admin/account_general_form.html deleted file mode 100644 index 3df108317..000000000 --- a/modoboa/extensions/admin/templates/admin/account_general_form.html +++ /dev/null @@ -1,12 +0,0 @@ -{% 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.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 %} diff --git a/modoboa/extensions/admin/templates/admin/aliasform.html b/modoboa/extensions/admin/templates/admin/aliasform.html deleted file mode 100644 index 5eef9092c..000000000 --- a/modoboa/extensions/admin/templates/admin/aliasform.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "common/generic_modal_form.html" %}{% load form_tags %} -{% block formbody %} -{% render_field form.email %} -{% 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 %} diff --git a/modoboa/extensions/admin/templates/admin/domain_general_form.html b/modoboa/extensions/admin/templates/admin/domain_general_form.html deleted file mode 100644 index 7a79f0b51..000000000 --- a/modoboa/extensions/admin/templates/admin/domain_general_form.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load i18n form_tags %} -{% render_field form.name %} -{% render_field form.quota appended_text=_("MB") %} -{% render_fields_group form "aliases" %} -{% render_field form.enabled %} diff --git a/modoboa/extensions/admin/templates/admin/domain_options_form.html b/modoboa/extensions/admin/templates/admin/domain_options_form.html deleted file mode 100644 index 0993c11fa..000000000 --- a/modoboa/extensions/admin/templates/admin/domain_options_form.html +++ /dev/null @@ -1,8 +0,0 @@ -{% 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 %} -
{% trans "You can't create a new domain administrator" %}
-{% endif %} diff --git a/modoboa/extensions/admin/templates/admin/domain_statistics.html b/modoboa/extensions/admin/templates/admin/domain_statistics.html deleted file mode 100644 index 2be458dab..000000000 --- a/modoboa/extensions/admin/templates/admin/domain_statistics.html +++ /dev/null @@ -1,63 +0,0 @@ -{% extends "admin/header.html" %} - -{% load i18n %} -{% load admin_tags %} - -{% block leftcol %} - - -{% get_extra_admin_content user "leftcol" selection %} -{% endblock %} - -{% block apparea %} -

{% trans "Statistics" %} {% trans "Domains and identities" %}

-
- -
- {% if user.group == "SuperAdmins" %} -
-
-

{% trans "Global counters" %}

-
- - - - - - - - - - - - - - -
{% trans "Type" %}{% trans "Quantity" %}
{% trans "Domains" %}{{ domains_counter }}
{% trans "Domain aliases" %}{{ domain_aliases_counter }}
{% trans "Identities" %}{{ identities_counter }}
-
-
- {% endif %} - -
-
-

{% trans "Per-domain counters" %}

-
- - - - - - {% for domain in domains %} - - - - {% endfor %} -
{% trans "Domain" %}{% trans "Identities" %}
{{ domain.name }}{{ domain.identities_count }}
-
-
- -
- -{% endblock %} diff --git a/modoboa/extensions/admin/templates/admin/domains.html b/modoboa/extensions/admin/templates/admin/domains.html deleted file mode 100644 index ec4025f0c..000000000 --- a/modoboa/extensions/admin/templates/admin/domains.html +++ /dev/null @@ -1,29 +0,0 @@ -{% 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 }} - -{% extra_static_content "domains" "js" user %} -{% endblock %} - -{% block actions %}{% domains_menu "domains" user %}{% endblock %} - -{% block table_header %} -{% trans "Name" %} -{% trans "Alias(es)" %} -{% trans "Tags" %} -{% trans "Actions" %} -{% endblock %} diff --git a/modoboa/extensions/admin/templates/admin/domains_table.html b/modoboa/extensions/admin/templates/admin/domains_table.html deleted file mode 100644 index c79777a8b..000000000 --- a/modoboa/extensions/admin/templates/admin/domains_table.html +++ /dev/null @@ -1,9 +0,0 @@ -{% load i18n admin_tags lib_tags %} -{% for domain in domains %} - - {% domain_modify_link domain %}{% if not domain.enabled %} ({% trans "disabled" %}){% endif %} - {% domain_aliases domain %} - {% render_tags domain.tags %} - {% domain_actions user domain %} - -{% endfor %} diff --git a/modoboa/extensions/admin/templates/admin/editdomainform.html b/modoboa/extensions/admin/templates/admin/editdomainform.html deleted file mode 100644 index 09d19065f..000000000 --- a/modoboa/extensions/admin/templates/admin/editdomainform.html +++ /dev/null @@ -1,28 +0,0 @@ -{% extends "common/tabforms.html" %}{% load i18n lib_tags admin_tags %} -{% block extratabs %} -
  • - {% trans "Administrators" %} -
  • -
  • - {% trans "Miscellaneous" %} -
  • -{% endblock %} -{% block extratabs_content %} -
    {% if domadmins %} - - - {% for da in domadmins %} - - - - {% endfor %} -
    {% trans "Username" %}{% trans "Name" %}
    {{ da.username }}{{ da.fullname }}{% if perms.admin.change_domain %}{% domadmin_actions da.id domain.id %}{% endif %}
    {% else %}
    {% trans "No domain administrator defined" %}
    {% endif %} -
    -
    -
    {% trans "Creation date" %}{{ domain.creation }}
    -
    {% trans "Last modification date" %}{{ domain.last_modification }}
    -
    {% trans "Domain aliases" %}{{ domain.domainalias_count }}
    -
    {% trans "Mailboxes" %}{{ domain.mailbox_count }}
    -
    {% trans "Mailbox aliases" %}{{ domain.mbalias_count }}
    -
    -{% endblock %} diff --git a/modoboa/extensions/admin/templates/admin/forward.html b/modoboa/extensions/admin/templates/admin/forward.html deleted file mode 100644 index 3146beea6..000000000 --- a/modoboa/extensions/admin/templates/admin/forward.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load i18n form_tags %}{% load url from future %} -

    {% trans "Forward" %} {% trans "Automatically forward emails to another address" %}

    -
    -
    - {% csrf_token %} - {% render_form form %} -
    - -
    -
    diff --git a/modoboa/extensions/admin/templates/admin/header.html b/modoboa/extensions/admin/templates/admin/header.html deleted file mode 100644 index 1dacea7a8..000000000 --- a/modoboa/extensions/admin/templates/admin/header.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "twocols.html" %} -{% load static %} - -{% block extra_css %} - -{% endblock %} - -{% block extra_js %} - - - -{% endblock %} diff --git a/modoboa/extensions/admin/templates/admin/identities.html b/modoboa/extensions/admin/templates/admin/identities.html deleted file mode 100644 index dde5b0e5f..000000000 --- a/modoboa/extensions/admin/templates/admin/identities.html +++ /dev/null @@ -1,26 +0,0 @@ -{% 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 }} - - -{% extra_static_content "identities" "js" user %} -{% endblock %} - -{% block actions %}{% identities_menu user "identities" %}{% endblock %} diff --git a/modoboa/extensions/admin/templates/admin/identities_table.html b/modoboa/extensions/admin/templates/admin/identities_table.html deleted file mode 100644 index 15fad1e00..000000000 --- a/modoboa/extensions/admin/templates/admin/identities_table.html +++ /dev/null @@ -1,10 +0,0 @@ -{% load i18n admin_tags lib_tags %} -{% for ident in identities %} - {% check_identity_status ident as identity_is_enabled %} - - {% identity_modify_link ident %}{% if not identity_is_enabled %} (disabled){% endif %} - {{ ident.name_or_rcpt }} - {% render_tags ident.tags %} - {% identity_actions user ident %} - -{% endfor %} diff --git a/modoboa/extensions/admin/templates/admin/identity_headers.html b/modoboa/extensions/admin/templates/admin/identity_headers.html deleted file mode 100644 index 53c18e745..000000000 --- a/modoboa/extensions/admin/templates/admin/identity_headers.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load i18n %} -{% trans "Name" %} -{% trans "Fullname/Recipient" %} -{% trans "Tags" %} -{% trans "Actions" %} diff --git a/modoboa/extensions/admin/templates/admin/import_done.html b/modoboa/extensions/admin/templates/admin/import_done.html deleted file mode 100644 index 55a3191b0..000000000 --- a/modoboa/extensions/admin/templates/admin/import_done.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/modoboa/extensions/admin/templates/admin/importform.html b/modoboa/extensions/admin/templates/admin/importform.html deleted file mode 100644 index 8e15acf69..000000000 --- a/modoboa/extensions/admin/templates/admin/importform.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "common/generic_modal_form.html" %} -{% load form_tags i18n %} -{% load static %} -{% block formbody %} -
    -

    {% trans "Help" %}

    - {{ helptext|safe }} -
    -{% render_form form %} - - -
    -{% endblock %} diff --git a/modoboa/extensions/admin/templates/admin/listing.html b/modoboa/extensions/admin/templates/admin/listing.html deleted file mode 100644 index a5c5c7dbb..000000000 --- a/modoboa/extensions/admin/templates/admin/listing.html +++ /dev/null @@ -1,47 +0,0 @@ -{% extends "admin/header.html" %}{% load i18n admin_tags %} -{% load static %} - -{% block extra_css %} -{{ block.super }} - -{% endblock %} - -{% block extra_js %} -{{ block.super }} - - - - - -{% endblock %} - -{% block leftcol %} - - -{% get_extra_admin_content user "leftcol" selection %} -{% endblock %} - -{% block apparea %} -
    -
    - {% include "admin/searchform.html" %} -
    -
    -
    - -
    - - - - {% block table_header %}{% endblock %} - - - - -
    -
    -{% endblock %} diff --git a/modoboa/extensions/admin/templates/admin/mailboxes_raw.html b/modoboa/extensions/admin/templates/admin/mailboxes_raw.html deleted file mode 100644 index 9723f4a20..000000000 --- a/modoboa/extensions/admin/templates/admin/mailboxes_raw.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load i18n %} -{% if target == "permissions" %} - -{% endif %} -{% for mb in mailboxes %} -{% if target == "permissions" %} - -{% else %} - -{% endif %} -{% endfor %} diff --git a/modoboa/extensions/admin/templates/admin/mailform.html b/modoboa/extensions/admin/templates/admin/mailform.html deleted file mode 100644 index 4b7b6ec63..000000000 --- a/modoboa/extensions/admin/templates/admin/mailform.html +++ /dev/null @@ -1,5 +0,0 @@ -{% 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" %} diff --git a/modoboa/extensions/admin/templates/admin/newdomainform.html b/modoboa/extensions/admin/templates/admin/newdomainform.html deleted file mode 100644 index 8b4d2ba29..000000000 --- a/modoboa/extensions/admin/templates/admin/newdomainform.html +++ /dev/null @@ -1,6 +0,0 @@ -{% 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 %} diff --git a/modoboa/extensions/admin/templates/admin/permsform.html b/modoboa/extensions/admin/templates/admin/permsform.html deleted file mode 100644 index b1856a04e..000000000 --- a/modoboa/extensions/admin/templates/admin/permsform.html +++ /dev/null @@ -1,2 +0,0 @@ -{% load form_tags %} -{% render_fields_group form "domains" %} diff --git a/modoboa/extensions/admin/templates/admin/quota_headers.html b/modoboa/extensions/admin/templates/admin/quota_headers.html deleted file mode 100644 index e496973e0..000000000 --- a/modoboa/extensions/admin/templates/admin/quota_headers.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load i18n %} -{% trans "Address" %} -{% trans "Value" %} -{% trans "Limit" %} -{% trans "Usage (%)" %} diff --git a/modoboa/extensions/admin/templates/admin/quotas.html b/modoboa/extensions/admin/templates/admin/quotas.html deleted file mode 100644 index 266474b21..000000000 --- a/modoboa/extensions/admin/templates/admin/quotas.html +++ /dev/null @@ -1,15 +0,0 @@ -{% load lib_tags admin_tags %} - -{% for mbox in mboxes %} - - {% identity_modify_link mbox.user "mail" %} - {{ mbox.get_quota }}M - {{ mbox.quota }}M - -
    -
    -
    - - -{% endfor %} diff --git a/modoboa/extensions/admin/templates/admin/searchform.html b/modoboa/extensions/admin/templates/admin/searchform.html deleted file mode 100644 index bac694e17..000000000 --- a/modoboa/extensions/admin/templates/admin/searchform.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load i18n %} -
    -
    -
    - - -
    -
    -
    diff --git a/modoboa/extensions/admin/templatetags/__init__.py b/modoboa/extensions/admin/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/extensions/admin/templatetags/admin_tags.py b/modoboa/extensions/admin/templatetags/admin_tags.py deleted file mode 100644 index d05b8ad9c..000000000 --- a/modoboa/extensions/admin/templatetags/admin_tags.py +++ /dev/null @@ -1,280 +0,0 @@ -""" -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 - -register = template.Library() - -genders = { - "Enabled": (ugettext_lazy("enabled_m"), ugettext_lazy("enabled_f")) -} - - -@register.simple_tag -def domains_menu(selection, user): - """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 selection != "statistics" 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 = [] - if domain.__class__.__name__ == '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" - }) - else: - actions = events.raiseQueryEvent('GetDomainActions', user, domain) - - 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": reverse("admin:alias_delete") + "?selection=%s" % objid, - "img": "fa fa-trash", - "title": _("Delete %s?" % ident.full_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 identity.mailbox_set.count() \ - and not identity.mailbox_set.all()[0].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
    ' % 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) diff --git a/modoboa/extensions/admin/tests/__init__.py b/modoboa/extensions/admin/tests/__init__.py deleted file mode 100644 index 8fc7ac21f..000000000 --- a/modoboa/extensions/admin/tests/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from .domain import DomainTestCase -from .domain_alias import DomainAliasTestCase -from .account import AccountTestCase, PermissionsTestCase -from .alias import AliasTestCase -from .import_ import ImportTestCase -from .export import ExportTestCase -from .password_schemes import PasswordSchemesTestCase -from .user import ForwardTestCase - -__all__ = [ - 'DomainTestCase', 'DomainAliasTestCase', 'AccountTestCase', - 'PermissionsTestCase', 'AliasTestCase', 'ImportTestCase', - 'ExportTestCase', 'PasswordSchemesTestCase', 'ForwardTestCase' -] diff --git a/modoboa/extensions/admin/tests/account.py b/modoboa/extensions/admin/tests/account.py deleted file mode 100644 index 6881056a4..000000000 --- a/modoboa/extensions/admin/tests/account.py +++ /dev/null @@ -1,197 +0,0 @@ -from django.core.urlresolvers import reverse - -from modoboa.core.models import User -from modoboa.extensions.admin import factories -from modoboa.extensions.admin.models import ( - Domain, Mailbox -) -from modoboa.lib.tests import ModoTestCase - - -class AccountTestCase(ModoTestCase): - fixtures = ["initial_users.json"] - - 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="toto", password2="toto", 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_set.all()[0] - self.assertEqual(mb.full_address, "tester@test.com") - self.assertEqual(mb.quota, 10) - self.assertEqual(mb.enabled, True) - self.assertEqual(mb.quota_value.username, "tester@test.com") - self.assertEqual(account.username, mb.full_address) - self.assertTrue(account.check_password("toto")) - self.assertEqual(account.first_name, "Tester") - self.assertEqual(account.last_name, "Toto") - self.assertEqual(mb.domain.mailbox_count, 3) - - values["username"] = "pouet@test.com" - self.ajax_post( - reverse("admin:account_change", args=[account.id]), values - ) - mb = Mailbox.objects.get(pk=mb.id) - self.assertEqual(mb.full_address, "pouet@test.com") - self.assertEqual(mb.quota_value.username, "pouet@test.com") - self.ajax_post( - reverse("admin:account_delete", args=[account.id]), {} - ) - - 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) - - -class PermissionsTestCase(ModoTestCase): - fixtures = ["initial_users.json"] - - 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(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="toto", password2="toto", - 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( - email="all@test.com", - recipients="user@test.com", - recipients_1="user@test2.com", - enabled=True - ) - self.ajax_post(reverse("admin:dlist_add"), values) diff --git a/modoboa/extensions/admin/tests/alias.py b/modoboa/extensions/admin/tests/alias.py deleted file mode 100644 index dbf6c3b90..000000000 --- a/modoboa/extensions/admin/tests/alias.py +++ /dev/null @@ -1,130 +0,0 @@ -from django.core.urlresolvers import reverse - -from modoboa.core.models import User -from modoboa.extensions.admin import factories -from modoboa.extensions.admin.models import ( - Alias -) -from modoboa.lib.tests import ModoTestCase - - -class AliasTestCase(ModoTestCase): - fixtures = ["initial_users.json"] - - 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(user.mailbox_set.all()[0].alias_set.count(), 2) - - del values["aliases_1"] - self.ajax_post( - reverse("admin:account_change", args=[user.id]), - values - ) - self.assertEqual(user.mailbox_set.all()[0].alias_set.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 - ) - self.assertEqual(user.mailbox_set.first().alias_set.count(), 1) - self.assertEqual( - user.mailbox_set.first().alias_set.first(), "toto@test.com") - - values = { - "email": "Titi@test.com", "recipient": "user@test.com", - "enabled": True - } - self.ajax_post(reverse("admin:alias_add"), values) - self.assertEqual( - user.mailbox_set.first().alias_set.first(), "titi@test.com") - - def test_dlist(self): - values = dict(email="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") - self.assertEqual(user.mailbox_set.all()[0].alias_set.count(), 2) - admin = User.objects.get(username="admin@test.com") - self.assertEqual(admin.mailbox_set.all()[0].alias_set.count(), 1) - - dlist = Alias.objects.get(address="all", domain__name="test.com") - self.assertEqual(len(dlist.get_recipients()), 3) - del values["recipients_1"] - self.ajax_post( - reverse("admin:alias_change", args=[dlist.id]), - values - ) - self.assertEqual(dlist.get_recipients_count(), 2) - - self.ajax_post( - reverse("admin:alias_delete") + "?selection=%d" % dlist.id, {} - ) - self.assertRaises(Alias.DoesNotExist, Alias.objects.get, - address="all", domain__name="test.com") - - def test_forward(self): - values = dict(email="forward2@test.com", recipients="rcpt@dest.com") - self.ajax_post( - reverse("admin:forward_add"), values - ) - fwd = Alias.objects.get(address="forward2", domain__name="test.com") - self.assertEqual(fwd.get_recipients_count(), 1) - - values["recipients"] = "rcpt2@dest.com" - self.ajax_post( - reverse("admin:alias_change", - args=[fwd.id]), - values - ) - self.assertEqual(fwd.get_recipients_count(), 1) - - self.ajax_post( - reverse("admin:alias_delete") + "?selection=%d" - % fwd.id, {} - ) - self.assertRaises(Alias.DoesNotExist, Alias.objects.get, - address="forward2", domain__name="test.com") - - def test_forward_and_local_copies(self): - values = dict(email="user@test.com", recipients="rcpt@dest.com") - self.ajax_post( - reverse("admin:forward_add"), values - ) - fwd = Alias.objects.get(address="user", domain__name="test.com") - self.assertEqual(fwd.get_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.get_recipients_count(), 2) - self.assertEqual(fwd.aliases.count(), 0) diff --git a/modoboa/extensions/admin/tests/domain.py b/modoboa/extensions/admin/tests/domain.py deleted file mode 100644 index 3c6cd739a..000000000 --- a/modoboa/extensions/admin/tests/domain.py +++ /dev/null @@ -1,112 +0,0 @@ -from django.core.urlresolvers import reverse -from modoboa.core.models import User -from modoboa.lib import parameters -from modoboa.lib.tests import ModoTestCase -from modoboa.extensions.admin.models import ( - Domain, Alias -) -from modoboa.extensions.admin import factories - - -class DomainTestCase(ModoTestCase): - fixtures = ["initial_users.json"] - - 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", - "stepid": 'step2' - } - 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", - "stepid": 'step2' - } - 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", domain__name="pouet.com") - self.assertIn(da.mailbox_set.all()[0], al.mboxes.all()) - 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", - "stepid": 'step2' - } - 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", domain__name="pouet.com") - self.assertIn(da.mailbox_set.all()[0], al.mboxes.all()) - 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", - "stepid": 'step2' - } - 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_set.all()[0].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, "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) diff --git a/modoboa/extensions/admin/tests/domain_alias.py b/modoboa/extensions/admin/tests/domain_alias.py deleted file mode 100644 index a13abc915..000000000 --- a/modoboa/extensions/admin/tests/domain_alias.py +++ /dev/null @@ -1,47 +0,0 @@ -from django.core.urlresolvers import reverse -from modoboa.lib.tests import ModoTestCase -from modoboa.extensions.admin.models import ( - Domain, DomainAlias -) -from modoboa.extensions.admin import factories - - -class DomainAliasTestCase(ModoTestCase): - fixtures = ['initial_users.json'] - - 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) - - domal.name = "domalias.org" - domal.save() - - domal.delete() - - 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") - 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) diff --git a/modoboa/extensions/admin/tests/export.py b/modoboa/extensions/admin/tests/export.py deleted file mode 100644 index ca95c4b7d..000000000 --- a/modoboa/extensions/admin/tests/export.py +++ /dev/null @@ -1,84 +0,0 @@ -from django.core.urlresolvers import reverse - -from modoboa.extensions.admin import factories -from modoboa.lib.tests import ModoTestCase - - -class ExportTestCase(ModoTestCase): - fixtures = ["initial_users.json"] - - 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;{CRYPT}dTTsGDkA5ZHKg;;;True;SuperAdmins;;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\nalias;alias@test.com;True;user@test.com\r\nforward;forward@test.com;True;user@external.com\r\ndlist;postmaster@test.com;True;toto@titi.com;test@truc.fr\r\n", - response.content.strip() - ) - - def test_export_simpleusers(self): - response = self.__export_identities( - idtfilter="account", grpfilter="SimpleUsers" - ) - self.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): - response = self.__export_identities( - idtfilter="account", grpfilter="SuperAdmins" - ) - self.assertEqual( - response.content.strip(), - "account;admin;{CRYPT}dTTsGDkA5ZHKg;;;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" - ) diff --git a/modoboa/extensions/admin/tests/import_.py b/modoboa/extensions/admin/tests/import_.py deleted file mode 100644 index dee11d995..000000000 --- a/modoboa/extensions/admin/tests/import_.py +++ /dev/null @@ -1,191 +0,0 @@ -# coding: utf-8 -from django.core.files.base import ContentFile -from django.core.urlresolvers import reverse - -from modoboa.core.models import User -from modoboa.extensions.admin import factories -from modoboa.extensions.admin.models import ( - Domain, Alias, DomainAlias -) -from modoboa.lib.tests import ModoTestCase - - -class ImportTestCase(ModoTestCase): - fixtures = ["initial_users.json"] - - 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; 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_set.all()[0] - 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_set.all()[0] - 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", domain__name="test.com") - self.assertIn(u1.mailbox_set.all()[0], al.mboxes.all()) - self.assertTrue(admin.is_owner(al)) - - fwd = Alias.objects.get(address="fwd1", domain__name="test.com") - self.assertIn("user@extdomain.com", fwd.extmboxes) - self.assertTrue(admin.is_owner(fwd)) - - dlist = Alias.objects.get(address="dlist", domain__name="test.com") - self.assertIn(u1.mailbox_set.all()[0], dlist.mboxes.all()) - self.assertIn("user@extdomain.com", dlist.extmboxes) - self.assertTrue(admin.is_owner(dlist)) - - 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_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_set.all()[0].quota, - account.mailbox_set.all()[0].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( - domain__name="test.com", address="user.alias" - ) - self.assertEqual(alias.type, "alias") diff --git a/modoboa/extensions/admin/tests/password_schemes.py b/modoboa/extensions/admin/tests/password_schemes.py deleted file mode 100644 index 413d43651..000000000 --- a/modoboa/extensions/admin/tests/password_schemes.py +++ /dev/null @@ -1,55 +0,0 @@ -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 modoboa.extensions.admin 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="toto", password2="toto", 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('toto')) - - 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}') diff --git a/modoboa/extensions/admin/tests/user.py b/modoboa/extensions/admin/tests/user.py deleted file mode 100644 index 173d03026..000000000 --- a/modoboa/extensions/admin/tests/user.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.core.urlresolvers import reverse -from modoboa.core.models import User -from modoboa.lib.tests import ModoTestCase -from modoboa.extensions.admin.factories import populate_database -from modoboa.extensions.admin.models import Alias - - -class ForwardTestCase(ModoTestCase): - fixtures = ['initial_users.json'] - - 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', domain__name='test.com') - 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)) diff --git a/modoboa/extensions/admin/urls.py b/modoboa/extensions/admin/urls.py deleted file mode 100644 index e601c45d1..000000000 --- a/modoboa/extensions/admin/urls.py +++ /dev/null @@ -1,60 +0,0 @@ -from django.conf.urls import patterns, url - - -urlpatterns = patterns( - 'modoboa.extensions.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\d+)/edit/$', 'editdomain', name="domain_change"), - url(r'^domains/(?P\d+)/delete/$', 'deldomain', - name="domain_delete"), -) - -urlpatterns += patterns( - 'modoboa.extensions.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\d+)/$', 'editaccount', - name="account_change"), - url(r'^accounts/delete/(?P\d+)/$', 'delaccount', - name="account_delete"), -) - -urlpatterns += patterns( - 'modoboa.extensions.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\d+)/$', 'editalias', name="alias_change"), - url(r'^aliases/delete/$', 'delalias', name="alias_delete"), -) - -urlpatterns += patterns( - 'modoboa.extensions.admin.views.import', - - url(r'^domains/import/$', 'import_domains', name="domain_import"), - url(r'^identities/import/$', 'import_identities', name="identity_import"), -) - -urlpatterns += patterns( - 'modoboa.extensions.admin.views.export', - - url(r'^domains/export/$', 'export_domains', name="domain_export"), - url(r'^identities/export/$', 'export_identities', name="identity_export"), -) diff --git a/modoboa/extensions/admin/views/__init__.py b/modoboa/extensions/admin/views/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/modoboa/extensions/admin/views/alias.py b/modoboa/extensions/admin/views/alias.py deleted file mode 100644 index 0104b667a..000000000 --- a/modoboa/extensions/admin/views/alias.py +++ /dev/null @@ -1,135 +0,0 @@ -import reversion -from django.db import IntegrityError -from django.utils.translation import ugettext as _, ungettext -from django.shortcuts import render -from django.core.urlresolvers import reverse -from django.contrib.auth.decorators import ( - login_required, permission_required -) -from modoboa.lib import events -from modoboa.lib.web_utils import render_to_json_response -from modoboa.lib.exceptions import PermDeniedException, Conflict -from modoboa.extensions.admin.forms import AliasForm -from modoboa.extensions.admin.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(): - form.set_recipients() - 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": - if len(alias.get_recipients()) >= 2: - successmsg = _("Distribution list modified") - elif alias.extmboxes != "": - 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.full_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) diff --git a/modoboa/extensions/admin/views/domain.py b/modoboa/extensions/admin/views/domain.py deleted file mode 100644 index 35ef6a68d..000000000 --- a/modoboa/extensions/admin/views/domain.py +++ /dev/null @@ -1,154 +0,0 @@ -""" -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 modoboa.extensions.admin.models import ( - Domain, DomainAlias, Mailbox, Alias -) -from modoboa.extensions.admin.forms import ( - DomainForm -) -from modoboa.extensions.admin.lib import get_domains - - -@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): - from modoboa.extensions.admin.forms import DomainWizard - - 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.count(), - }) - context.update({"domains": Domain.objects.get_for_admin(request.user)}) - return render(request, "admin/domain_statistics.html", context) diff --git a/modoboa/extensions/admin/views/export.py b/modoboa/extensions/admin/views/export.py deleted file mode 100644 index 38fe6494f..000000000 --- a/modoboa/extensions/admin/views/export.py +++ /dev/null @@ -1,90 +0,0 @@ -"""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 modoboa.extensions.admin.forms import ( - ExportIdentitiesForm, ExportDomainsForm -) -from modoboa.extensions.admin.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) diff --git a/modoboa/extensions/admin/views/identity.py b/modoboa/extensions/admin/views/identity.py deleted file mode 100644 index cf7898114..000000000 --- a/modoboa/extensions/admin/views/identity.py +++ /dev/null @@ -1,211 +0,0 @@ -"""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.extensions.admin.forms import ( - AccountForm -) -from modoboa.extensions.admin.lib import get_identities -from modoboa.extensions.admin.models import Mailbox, Domain -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 -) - - -@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.""" - from modoboa.extensions.admin.forms import AccountWizard - 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 = None - if account.mailbox_set.count(): - mb = account.mailbox_set.all()[0] - - 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({}) diff --git a/modoboa/extensions/admin/views/import.py b/modoboa/extensions/admin/views/import.py deleted file mode 100644 index 015db07d2..000000000 --- a/modoboa/extensions/admin/views/import.py +++ /dev/null @@ -1,177 +0,0 @@ -"""Import related views.""" - -import csv - -from django.contrib.auth.decorators import ( - login_required, permission_required, user_passes_test -) -from django.core.urlresolvers import reverse -from django.shortcuts import render -from django.utils.translation import ugettext as _ - -import reversion - -from modoboa.core.models import User -from modoboa.extensions.admin.forms import ImportIdentitiesForm, ImportDataForm -from modoboa.extensions.admin.models import ( - Domain, DomainAlias, Alias -) -from modoboa.lib import events -from modoboa.lib.exceptions import ModoboaException, Conflict - - -def import_domain(user, row, formopts): - """Specific code for domains import""" - dom = Domain() - dom.from_csv(user, row) - - -def import_domainalias(user, row, formopts): - """Specific code for domain aliases import""" - domalias = DomainAlias() - domalias.from_csv(user, row) - - -@login_required -@permission_required("admin.add_domain") -def import_domains(request): - if request.method == "POST": - return importdata(request) - - helptext = _("""Provide a CSV file where lines respect one of the following formats: -
      -
    • domain; name; quota; enabled
    • -
    • domainalias; name; targeted domain; enabled
    • - %s -
    -

    The first element of each line is mandatory and must be equal to one of the previous values.

    -

    You can use a different character as separator.

    -""" % ''.join([unicode(hlp) for hlp in - events.raiseQueryEvent('ExtraDomainImportHelp')])) - - ctx = dict( - title=_("Import domains"), - action_label=_("Import"), - action_classes="submit", - action=reverse("admin:domain_import"), - formid="importform", - enctype="multipart/form-data", - target="import_target", - helptext=helptext, - form=ImportDataForm() - ) - return render(request, "admin/importform.html", ctx) - - -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) - - -@reversion.create_revision() -def importdata(request, formclass=ImportDataForm): - """Generic import function - - As the process of importing data from a CSV file is the same - whatever the type, we do a maximum of the work here. - - :param request: a ``Request`` instance - :param typ: a string indicating the object type being imported - :return: a ``Response`` instance - """ - error = None - form = formclass(request.POST, request.FILES) - if form.is_valid(): - try: - reader = csv.reader(request.FILES['sourcefile'], - delimiter=form.cleaned_data['sepchar']) - except csv.Error as inst: - error = str(inst) - - if error is None: - try: - cpt = 0 - for row in reader: - if not row: - continue - try: - fct = globals()["import_%s" % row[0].strip()] - except KeyError: - fct = events.raiseQueryEvent( - 'ImportObject', row[0].strip() - ) - if not fct: - continue - fct = fct[0] - try: - fct(request.user, row, form.cleaned_data) - except Conflict: - if form.cleaned_data["continue_if_exists"]: - continue - raise Conflict( - _("Object already exists: %s" - % form.cleaned_data['sepchar'].join(row[:2])) - ) - cpt += 1 - msg = _("%d objects imported successfully" % cpt) - return render(request, "admin/import_done.html", { - "status": "ok", "msg": msg - }) - except (ModoboaException) as e: - error = str(e) - - return render(request, "admin/import_done.html", { - "status": "ko", "msg": error - }) - - -@login_required -@user_passes_test( - lambda u: u.has_perm("core.add_user") or u.has_perm("admin.add_alias") -) -def import_identities(request): - if request.method == "POST": - return importdata(request, ImportIdentitiesForm) - - helptext = _("""Provide a CSV file where lines respect one of the following formats: -
      -
    • account; loginname; password; first name; last name; enabled; group; address; quota; [, domain, ...]
    • -
    • alias; address; enabled; internal recipient
    • -
    • forward; address; enabled; external recipient
    • -
    • dlist; address; enabled; recipient; recipient; ...
    • -
    -

    The first element of each line is mandatory and must be equal to one of the previous values.

    - -

    You can use a different character as separator.

    -""") - ctx = dict( - title=_("Import identities"), - action_label=_("Import"), - action_classes="submit", - action=reverse("admin:identity_import"), - formid="importform", - enctype="multipart/form-data", - target="import_target", - form=ImportIdentitiesForm(), - helptext=helptext - ) - return render(request, "admin/importform.html", ctx) diff --git a/modoboa/extensions/admin/views/user.py b/modoboa/extensions/admin/views/user.py deleted file mode 100644 index 55166166c..000000000 --- a/modoboa/extensions/admin/views/user.py +++ /dev/null @@ -1,57 +0,0 @@ -import reversion -from django.contrib.auth.decorators import login_required -from django.utils.translation import ugettext as _ -from modoboa.lib.web_utils import ( - render_to_json_response, _render_to_string -) -from modoboa.extensions.admin.lib import needs_mailbox -from modoboa.extensions.admin.models import Alias, Mailbox -from modoboa.extensions.admin.forms import ForwardForm - - -@login_required -@needs_mailbox() -@reversion.create_revision() -def forward(request, tplname='admin/forward.html'): - mb = request.user.mailbox_set.all()[0] - try: - al = Alias.objects.get(address=mb.address, - domain__name=mb.domain.name) - except Alias.DoesNotExist: - al = None - if request.method == "POST": - form = ForwardForm(request.POST) - error = None - if form.is_valid(): - if al is None: - al = Alias() - al.address = mb.address - al.domain = mb.domain - al.enabled = mb.user.is_active - intdests = [] - if form.cleaned_data["keepcopies"]: - intdests += [mb] - form.parse_dest() - al.save( - int_rcpts=intdests, ext_rcpts=form.dests, creator=request.user - ) - return render_to_json_response(_("Forward updated")) - - return render_to_json_response( - {'form_errors': form.errors}, status=400 - ) - - form = ForwardForm() - if al is not None: - form.fields["dest"].initial = al.extmboxes - try: - al.mboxes.get(pk=mb.id) - except Mailbox.DoesNotExist: - pass - else: - form.fields["keepcopies"].initial = True - return render_to_json_response({ - "content": _render_to_string(request, tplname, { - "form": form - }) - }) diff --git a/modoboa/lib/web_utils.py b/modoboa/lib/web_utils.py index 947c32503..f79484023 100644 --- a/modoboa/lib/web_utils.py +++ b/modoboa/lib/web_utils.py @@ -162,7 +162,8 @@ def topredirection(request): else: path = reverse("core:user_index") else: - path = reverse("admin:domain_list") + # FIXME + path = reverse("modoboa_admin:domain_list") return HttpResponseRedirect(path)