mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-23 06:38:40 +02:00
* Improved parameters management (see #392)
* Supporting mailboxes rename and removal only with Dovecot 2.x (see #389)
This commit is contained in:
+93
-97
@@ -1,85 +1,10 @@
|
||||
# coding: utf-8
|
||||
|
||||
from modoboa.lib import parameters, events
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.db.utils import DatabaseError
|
||||
from models import Extension, Mailbox
|
||||
|
||||
|
||||
def update_uid(value):
|
||||
"""VIRTUAL_UID modification callback
|
||||
|
||||
Update all mailboxes with the new UID value
|
||||
|
||||
:param value: the new UID
|
||||
"""
|
||||
Mailbox.objects.all().update(uid=Mailbox.resolve_uid(value))
|
||||
|
||||
|
||||
def update_gid(value):
|
||||
"""VIRTUAL_GID modification callback
|
||||
|
||||
Update all mailboxes with the new GID value
|
||||
|
||||
:param value: the new GID
|
||||
"""
|
||||
Mailbox.objects.all().update(gid=Mailbox.resolve_gid(value))
|
||||
|
||||
|
||||
parameters.register_admin(
|
||||
"AUTHENTICATION_TYPE", type="list", deflt="local",
|
||||
values=[('local', ugettext_lazy("Local")),
|
||||
('ldap', "LDAP")],
|
||||
help=ugettext_lazy("The backend used for authentication")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"LDA", type="list", deflt="dovecot",
|
||||
values=[('dovecot', "Dovecot"), ('postfix', 'Postfix')],
|
||||
help=ugettext_lazy("Local Delivery Agent")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"STORAGE_PATH", type="string", deflt="/var/vmail",
|
||||
visible_if="LDA=postfix",
|
||||
help=ugettext_lazy("Path to the root directory where messages are stored")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"VIRTUAL_UID", type="string", deflt="vmail", modify_cb=update_uid,
|
||||
visible_if="LDA=postfix",
|
||||
help=ugettext_lazy("UID of the virtual user which owns domains/mailboxes/messages on the filesystem")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"VIRTUAL_GID", type="string", deflt="vmail", modify_cb=update_gid,
|
||||
visible_if="LDA=postfix",
|
||||
help=ugettext_lazy("GID of the virtual user which owns domains/mailboxes/messages on the filesystem")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"MAILBOX_TYPE", type="list", deflt="maildir",
|
||||
values=[("maildir", "maildir"), ("mbox", "mbox")],
|
||||
visible_if="LDA=postfix",
|
||||
help=ugettext_lazy("Mailboxes storage format")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"MAILDIR_ROOT", type="string", deflt=".maildir",
|
||||
visible_if="MAILBOX_TYPE=maildir",
|
||||
help=ugettext_lazy("Sub-directory (inside the mailbox) where messages are stored when using the maildir format")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"AUTO_ACCOUNT_REMOVAL", type="list_yesno", deflt="no",
|
||||
help=ugettext_lazy("When a mailbox is removed, also remove the associated account")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"PASSWORD_SCHEME", type="list", deflt="md5crypt",
|
||||
values=[("crypt", "crypt"),
|
||||
("md5", "md5"),
|
||||
("md5crypt", "md5crypt"),
|
||||
("sha256", "sha256"),
|
||||
("plain", "plain")],
|
||||
help=ugettext_lazy("Scheme used to crypt mailbox passwords")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"ITEMS_PER_PAGE", type="int", deflt=30,
|
||||
help=ugettext_lazy("Number of displayed items per page")
|
||||
)
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib.formutils import YesNoField, SeparatorField
|
||||
from modoboa.lib.sysutils import exec_cmd
|
||||
|
||||
|
||||
def enabled_applications():
|
||||
@@ -91,6 +16,7 @@ def enabled_applications():
|
||||
|
||||
:return: a list
|
||||
"""
|
||||
from modoboa.admin.models import Extension
|
||||
from modoboa.lib.dbutils import db_table_exists
|
||||
|
||||
result = [("admin", "admin"), ("userprefs", "userprefs")]
|
||||
@@ -100,12 +26,92 @@ def enabled_applications():
|
||||
return sorted(result, key=lambda e: e[0])
|
||||
|
||||
|
||||
parameters.register_admin(
|
||||
"DEFAULT_TOP_REDIRECTION", type="list", deflt="admin",
|
||||
app="general",
|
||||
values=enabled_applications(),
|
||||
help=ugettext_lazy("The default redirection used when no application is specified")
|
||||
)
|
||||
class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
app = "admin"
|
||||
|
||||
sep1 = SeparatorField(label=ugettext_lazy("Authentication"))
|
||||
|
||||
authentication_type = forms.ChoiceField(
|
||||
label=ugettext_lazy("Authentication type"),
|
||||
choices=[('local', ugettext_lazy("Local")),
|
||||
('ldap', "LDAP")],
|
||||
initial="local",
|
||||
help_text=ugettext_lazy("The backend used for authentication")
|
||||
)
|
||||
|
||||
password_scheme = forms.ChoiceField(
|
||||
label=ugettext_lazy("Default password scheme"),
|
||||
choices=[("crypt", "crypt"),
|
||||
("md5", "md5"),
|
||||
("md5crypt", "md5crypt"),
|
||||
("sha256", "sha256"),
|
||||
("plain", "plain")],
|
||||
initial="md5crypt",
|
||||
help_text=ugettext_lazy("Scheme used to crypt mailbox passwords")
|
||||
)
|
||||
|
||||
secret_key = forms.CharField(
|
||||
label=ugettext_lazy("Secret key"),
|
||||
initial="abcdefghijklmnop",
|
||||
help_text=ugettext_lazy("Key used to encrypt/decrypt passwords")
|
||||
)
|
||||
|
||||
sep2 = 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 ower"),
|
||||
initial="vmail",
|
||||
help_text=ugettext_lazy("The UNIX account who owns mailboxes on the filesystem")
|
||||
)
|
||||
|
||||
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"),
|
||||
)
|
||||
|
||||
sep3 = SeparatorField(label=ugettext_lazy("Miscellaneous"))
|
||||
|
||||
items_per_page = forms.IntegerField(
|
||||
label=ugettext_lazy("Items per page"),
|
||||
initial=30,
|
||||
help_text=ugettext_lazy("Number of displayed items per page")
|
||||
)
|
||||
|
||||
default_top_redirection = forms.ChoiceField(
|
||||
label=ugettext_lazy("Default top redirection"),
|
||||
choices=[],
|
||||
initial="admin",
|
||||
help_text=ugettext_lazy("The default redirection used when no application is specified")
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GeneralParametersForm, self).__init__(*args, **kwargs)
|
||||
self.fields["default_top_redirection"].choices = enabled_applications()
|
||||
hide_fields = False
|
||||
try:
|
||||
code, version = exec_cmd("dovecot --version")
|
||||
except OSError, e:
|
||||
hide_fields = True
|
||||
else:
|
||||
if code or not version.strip().startswith("2"):
|
||||
hide_fields = True
|
||||
if hide_fields:
|
||||
del self.fields["handle_mailboxes"]
|
||||
del self.fields["mailboxes_owner"]
|
||||
|
||||
# Visibility rules
|
||||
def visibility_mailboxes_owner(self):
|
||||
return "handle_mailboxes=yes"
|
||||
|
||||
|
||||
parameters.register(GeneralParametersForm, ugettext_lazy("General"))
|
||||
|
||||
|
||||
@events.observe("ExtDisabled")
|
||||
@@ -114,16 +120,6 @@ def unset_default_topredirection(extension):
|
||||
Simple callback to change the default redirection if the
|
||||
corresponding extension is being disabled.
|
||||
"""
|
||||
topredirection = parameters.get_admin("DEFAULT_TOP_REDIRECTION", app="general")
|
||||
topredirection = parameters.get_admin("DEFAULT_TOP_REDIRECTION")
|
||||
if topredirection == extension.name:
|
||||
parameters.save_admin("DEFAULT_TOP_REDIRECTION", "userprefs", app="general")
|
||||
|
||||
|
||||
@events.observe("ExtDisabled", "ExtEnabled")
|
||||
def update_available_applications(extension):
|
||||
"""Simple callback to update the list of available applications
|
||||
|
||||
Must be called each time an extension is disabled/enabled.
|
||||
"""
|
||||
parameters.update_admin("DEFAULT_TOP_REDIRECTION", app="general",
|
||||
values=enabled_applications())
|
||||
parameters.save_admin("DEFAULT_TOP_REDIRECTION", "userprefs")
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"pk": 2, "model": "admin.user", "fields": {"username": "user@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [2]}},{"pk": 3, "model": "admin.user", "fields": {"username": "admin@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [1]}},{"pk": 1, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 2, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 1, "model": "admin.Domain", "fields": {"name" : "test.com", "quota" : "100", "dates": "1"}}, {"pk": 1, "model": "admin.Mailbox", "fields": {"address": "user", "domain": "1", "dates": "1", "uid": "1001", "gid": "1001", "path": "user", "quota": "10", "user": "2"}}, {"pk": 2, "model": "admin.Mailbox", "fields": {"address": "admin", "domain": "1", "dates": "2", "uid": "1001", "gid": "1001", "path": "admin", "quota": "10", "user": "3"}}]
|
||||
[{"pk": 2, "model": "admin.user", "fields": {"username": "user@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [2]}},{"pk": 3, "model": "admin.user", "fields": {"username": "admin@test.com", "is_active": "True", "is_superuser": "False", "password": "{PLAIN}toto", "groups": [1]}},{"pk": 1, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 2, "model": "admin.ObjectDates", "fields": {"creation": "2011-09-07 09:27:07.579603", "last_modification": "2011-09-07 09:27:07.579603"}}, {"pk": 1, "model": "admin.Domain", "fields": {"name" : "test.com", "quota" : "100", "dates": "1"}}, {"pk": 1, "model": "admin.Mailbox", "fields": {"address": "user", "domain": "1", "dates": "1", "quota": "10", "user": "2"}}, {"pk": 2, "model": "admin.Mailbox", "fields": {"address": "admin", "domain": "1", "dates": "2", "quota": "10", "user": "3"}}]
|
||||
|
||||
+21
-8
@@ -75,10 +75,15 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
def save(self, user, commit=True):
|
||||
d = super(DomainFormGeneral, self).save(commit=False)
|
||||
if commit:
|
||||
if self.oldname is not None and d.name != self.oldname:
|
||||
if not d.rename_dir(self.oldname):
|
||||
raise AdminError(_("Failed to rename domain, check permissions"))
|
||||
old_mail_homes = None
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm == "yes":
|
||||
if self.oldname is not None and d.name != self.oldname:
|
||||
old_mail_homes = {mb.id: mb.mail_home for mb in d.mailbox_set.all()}
|
||||
d.save()
|
||||
if old_mail_homes is not None:
|
||||
for mb in d.mailbox_set.all():
|
||||
mb.rename_dir(old_mail_homes[mb.id])
|
||||
for k, v in self.cleaned_data.iteritems():
|
||||
if not k.startswith("aliases"):
|
||||
continue
|
||||
@@ -561,14 +566,22 @@ class AccountFormMail(forms.Form, DynamicForm):
|
||||
grant_access_to_object(admin, self.mb.user)
|
||||
|
||||
else:
|
||||
old_mail_home = None
|
||||
if self.cleaned_data["email"] != self.mb.full_address:
|
||||
if self.mb.rename_dir(domname, locpart):
|
||||
self.mb.domain = domain
|
||||
self.mb.address = locpart
|
||||
else:
|
||||
raise AdminError(_("Failed to rename mailbox, check permissions"))
|
||||
old_mail_home = self.mb.mail_home
|
||||
self.mb.domain = domain
|
||||
self.mb.address = locpart
|
||||
self.mb.set_quota(self.cleaned_data["quota"], user.has_perm("admin.add_domain"))
|
||||
self.mb.save()
|
||||
if old_mail_home is not None:
|
||||
# Problem here: the mailbox has already been saved so,
|
||||
# if the rename fails, we can't rollback!
|
||||
# Maybe django-reversion could be the solution?
|
||||
try:
|
||||
self.mb.rename_dir(old_mail_home)
|
||||
except AdminError:
|
||||
# do rollback
|
||||
pass
|
||||
|
||||
account.email = self.cleaned_data["email"]
|
||||
account.save()
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Deleting field 'Mailbox.uid'
|
||||
db.delete_column('admin_mailbox', 'uid')
|
||||
|
||||
# Deleting field 'Mailbox.gid'
|
||||
db.delete_column('admin_mailbox', 'gid')
|
||||
|
||||
# Deleting field 'Mailbox.path'
|
||||
db.delete_column('admin_mailbox', 'path')
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# User chose to not deal with backwards NULL issues for 'Mailbox.uid'
|
||||
raise RuntimeError("Cannot reverse this migration. 'Mailbox.uid' and its values cannot be restored.")
|
||||
|
||||
# User chose to not deal with backwards NULL issues for 'Mailbox.gid'
|
||||
raise RuntimeError("Cannot reverse this migration. 'Mailbox.gid' and its values cannot be restored.")
|
||||
|
||||
# User chose to not deal with backwards NULL issues for 'Mailbox.path'
|
||||
raise RuntimeError("Cannot reverse this migration. 'Mailbox.path' and its values cannot be restored.")
|
||||
|
||||
models = {
|
||||
'admin.alias': {
|
||||
'Meta': {'ordering': "['domain__name', 'address']", 'unique_together': "(('address', 'domain'),)", 'object_name': 'Alias'},
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
|
||||
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
|
||||
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'extmboxes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'mboxes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['admin.Mailbox']", 'symmetrical': 'False'})
|
||||
},
|
||||
'admin.domain': {
|
||||
'Meta': {'ordering': "['name']", 'object_name': 'Domain'},
|
||||
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
|
||||
'quota': ('django.db.models.fields.IntegerField', [], {})
|
||||
},
|
||||
'admin.domainalias': {
|
||||
'Meta': {'object_name': 'DomainAlias'},
|
||||
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
|
||||
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"})
|
||||
},
|
||||
'admin.extension': {
|
||||
'Meta': {'object_name': 'Extension'},
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
|
||||
},
|
||||
'admin.mailbox': {
|
||||
'Meta': {'object_name': 'Mailbox'},
|
||||
'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
|
||||
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'quota': ('django.db.models.fields.IntegerField', [], {}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'admin.objectaccess': {
|
||||
'Meta': {'unique_together': "(('user', 'content_type', 'object_id'),)", 'object_name': 'ObjectAccess'},
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
|
||||
},
|
||||
'admin.objectdates': {
|
||||
'Meta': {'object_name': 'ObjectDates'},
|
||||
'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
|
||||
},
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['admin']
|
||||
+44
-118
@@ -547,27 +547,6 @@ class Domain(DatesAware):
|
||||
@property
|
||||
def admins(self):
|
||||
return [oa.user for oa in self.owners.filter(user__is_superuser=False)]
|
||||
|
||||
def create_dir(self):
|
||||
if parameters.get_admin("LDA") == "postfix":
|
||||
path = "%s/%s" % (parameters.get_admin("STORAGE_PATH"), self.name)
|
||||
return exec_as_vuser("mkdir -p %s" % path)
|
||||
|
||||
return True
|
||||
|
||||
def rename_dir(self, oldname):
|
||||
if parameters.get_admin("LDA") == "postfix":
|
||||
stpath = parameters.get_admin("STORAGE_PATH")
|
||||
return exec_as_vuser("mv %s/%s %s/%s" \
|
||||
% (stpath, oldname, stpath, self.name))
|
||||
return True
|
||||
|
||||
def delete_dir(self):
|
||||
if parameters.get_admin("LDA") == "postfix":
|
||||
return exec_as_vuser("rm -r %s/%s" \
|
||||
% (parameters.get_admin("STORAGE_PATH"),
|
||||
self.name))
|
||||
return True
|
||||
|
||||
def delete(self, fromuser, keepdir=False):
|
||||
from modoboa.lib.permissions import ungrant_access_to_object, ungrant_access_to_objects
|
||||
@@ -578,6 +557,10 @@ class Domain(DatesAware):
|
||||
if self.mailbox_set.count():
|
||||
events.raiseEvent("DeleteMailbox", self.mailbox_set.all())
|
||||
ungrant_access_to_objects(self.mailbox_set.all())
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm == "yes" and not keepdir:
|
||||
for mb in self.mailbox_set.all():
|
||||
mb.delete_dir()
|
||||
if self.alias_set.count():
|
||||
events.raiseEvent("MailboxAliasDelete", self.alias_set.all())
|
||||
ungrant_access_to_objects(self.alias_set.all())
|
||||
@@ -587,13 +570,6 @@ class Domain(DatesAware):
|
||||
events.raiseEvent("DeleteDomain", self)
|
||||
ungrant_access_to_object(self)
|
||||
super(Domain, self).delete()
|
||||
if not keepdir:
|
||||
self.delete_dir()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.create_dir():
|
||||
raise AdminError(_("Failed to initialise domain, check permissions"))
|
||||
super(Domain, self).save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -665,30 +641,27 @@ class Mailbox(DatesAware):
|
||||
help_text=ugettext_lazy("Mailbox address (without the @domain.tld part)")
|
||||
)
|
||||
quota = models.IntegerField()
|
||||
uid = models.IntegerField()
|
||||
gid = models.IntegerField()
|
||||
path = models.CharField(max_length=255)
|
||||
domain = models.ForeignKey(Domain)
|
||||
user = models.ForeignKey(User)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("view_mailboxes", "View mailboxes"),
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Mailbox, self).__init__(*args, **kwargs)
|
||||
self.mbtype = parameters.get_admin("MAILBOX_TYPE")
|
||||
self.mdirroot = parameters.get_admin("MAILDIR_ROOT")
|
||||
if not self.mdirroot.endswith('/'):
|
||||
self.mdirroot += '/'
|
||||
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 "%s@%s" % (self.address, self.domain.name)
|
||||
return self.__full_address(self.address)
|
||||
|
||||
@property
|
||||
def name_and_address(self):
|
||||
@@ -701,92 +674,48 @@ class Mailbox(DatesAware):
|
||||
def enabled(self):
|
||||
return self.user.is_active
|
||||
|
||||
@property
|
||||
def full_path(self):
|
||||
path = os.path.join(parameters.get_admin("STORAGE_PATH"),
|
||||
self.domain.name, self.address)
|
||||
return os.path.abspath(path)
|
||||
|
||||
@property
|
||||
def alias_count(self):
|
||||
return self.alias_set.count()
|
||||
|
||||
def create_dir(self):
|
||||
if self.mbtype == "mbox":
|
||||
self.path = "%s/Inbox" % self.address
|
||||
else:
|
||||
self.path = "%s/" % self.address
|
||||
if parameters.get_admin("LDA") == "postfix":
|
||||
relpath = "%s/%s" % (self.domain.name, self.address)
|
||||
abspath = os.path.join(parameters.get_admin("STORAGE_PATH"), relpath)
|
||||
if os.path.exists(abspath):
|
||||
return True
|
||||
if not exec_as_vuser("mkdir -p %s" % abspath):
|
||||
return False
|
||||
if self.mbtype == "mbox":
|
||||
template = ["Inbox", "Drafts", "Sent", "Trash", "Junk"]
|
||||
for f in template:
|
||||
exec_as_vuser("touch %s/%s" % (abspath, f))
|
||||
else:
|
||||
template = [".Drafts/", ".Sent/", ".Trash/", ".Junk/"]
|
||||
for dir in template:
|
||||
for sdir in ["cur", "new", "tmp"]:
|
||||
exec_as_vuser("mkdir -p %s/%s/%s/%s" \
|
||||
% (abspath, self.mdirroot, dir, sdir))
|
||||
return True
|
||||
@property
|
||||
def mail_home(self):
|
||||
"""
|
||||
|
||||
def rename_dir(self, newdomain, newaddress):
|
||||
if parameters.get_admin("LDA") == "postfix":
|
||||
if self.domain.name == newdomain and newaddress == self.address:
|
||||
return True
|
||||
oldpath = "%s/%s" % (parameters.get_admin("STORAGE_PATH"), self.domain.name)
|
||||
newpath = "%s/%s" % (parameters.get_admin("STORAGE_PATH"), newdomain)
|
||||
oldpath = os.path.join(oldpath, self.address)
|
||||
newpath = os.path.join(newpath, newaddress)
|
||||
code = exec_as_vuser("mv %s %s" % (oldpath, newpath))
|
||||
"""
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm is None or hm == "no":
|
||||
return None
|
||||
if self.__mail_home is None:
|
||||
code, output = exec_cmd("doveadm user %s -f home" % self.full_address,
|
||||
sudo_user=parameters.get_admin("MAILBOXES_OWNER"))
|
||||
if code:
|
||||
self.path = "%s/" % newaddress
|
||||
return code
|
||||
else:
|
||||
self.path ="%s/" % newaddress
|
||||
raise AdminError(_("Failed to retrieve mailbox location"))
|
||||
self.__mail_home = output.strip()
|
||||
return self.__mail_home
|
||||
|
||||
return True
|
||||
def rename_dir(self, old_mail_home):
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm is None or hm == "no":
|
||||
return
|
||||
self.__mail_home = None
|
||||
if not os.path.exists(old_mail_home):
|
||||
return
|
||||
code, output = exec_cmd("mv %s %s" % (old_mail_home, self.mail_home),
|
||||
sudo_user=parameters.get_admin("MAILBOXES_OWNER"))
|
||||
if code:
|
||||
raise AdminError(_("Failed to rename mailbox: %s" % output))
|
||||
|
||||
def delete_dir(self):
|
||||
if parameters.get_admin("LDA") == "postfix":
|
||||
return exec_as_vuser("rm -r %s/%s/%s" \
|
||||
% (parameters.get_admin("STORAGE_PATH"),
|
||||
self.domain.name, self.address))
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def resolve_id(value, idname, errmsg):
|
||||
if value.isdigit():
|
||||
return value
|
||||
try:
|
||||
rid = getattr(pwd.getpwnam(value), idname)
|
||||
except KeyError:
|
||||
raise AdminError(errmsg)
|
||||
return rid
|
||||
|
||||
@staticmethod
|
||||
def resolve_uid(value):
|
||||
err = _("%s is not a valid uid/username" % value)
|
||||
return Mailbox.resolve_id(value, "pw_uid", err)
|
||||
|
||||
@staticmethod
|
||||
def resolve_gid(value):
|
||||
err = _("%s is not a valid gid/groupname" % value)
|
||||
return Mailbox.resolve_id(value, "pw_gid", err)
|
||||
|
||||
def set_ownership(self):
|
||||
"""Set uid and gid for this mailbox
|
||||
|
||||
Values come from global parameters. They are resolved
|
||||
(converted to integers) if needed.
|
||||
"""
|
||||
self.uid = self.resolve_uid(parameters.get_admin("VIRTUAL_UID"))
|
||||
self.gid = self.resolve_gid(parameters.get_admin("VIRTUAL_GID"))
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm is None or hm == "no":
|
||||
return
|
||||
if not os.path.exists(self.mail_home):
|
||||
return
|
||||
code, output = exec_cmd("rm -r %s" % self.mail_home,
|
||||
sudo_user=parameters.get_admin("MAILBOXES_OWNER"))
|
||||
if code:
|
||||
raise AdminError(_("Failed to remove mailbox: %s" % output))
|
||||
|
||||
def set_quota(self, value, override_domain=False):
|
||||
if value is None or (int(value) > self.domain.quota and not override_domain):
|
||||
@@ -804,9 +733,6 @@ class Mailbox(DatesAware):
|
||||
self.address = localpart
|
||||
self.domain = domain
|
||||
self.user = user
|
||||
if not self.create_dir():
|
||||
raise AdminError(_("Failed to initialise mailbox, check permissions"))
|
||||
self.set_ownership()
|
||||
self.set_quota(quota, True if (owner and owner.has_perm("admin.add_domain")) else False)
|
||||
super(Mailbox, self).save()
|
||||
|
||||
|
||||
@@ -22,4 +22,4 @@ table td[name="actions"] {
|
||||
width: 85%;
|
||||
-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;
|
||||
padding-left: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
#settings_content .form-horizontal .control-label {
|
||||
margin-left: 20px;
|
||||
text-align: left;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#settings_content > .form-horizontal .controls {
|
||||
margin-left: 240px;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{% load i18n %}{% load url from future %}
|
||||
{% load i18n libextras %}{% load url from future %}
|
||||
{% url "modoboa.admin.views.saveparameters" as action %}
|
||||
<h2>{% trans "Parameters" %} <small>{% trans "Configure Modoboa" %}</small></h2><hr />
|
||||
<form id="settings_form" method="POST" action="{% url 'modoboa.admin.views.saveparameters' %}" class="form-horizontal">
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{% load i18n libextras admin_extras %}
|
||||
{% block pagetitle %}{% trans "Modoboa settings" %}{% endblock %}
|
||||
{% block html_head %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/settings.css" />
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
|
||||
<script type="text/javascript" src="{{ STATIC_URL }}js/twocols_nav.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -224,6 +224,9 @@ def param(app, definition):
|
||||
value = definition.has_key("value") \
|
||||
and definition["value"] or definition["deflt"]
|
||||
|
||||
if definition["type"] == "separator":
|
||||
return "<h5>%s</h5>" % (unicode(definition["label"]))
|
||||
|
||||
extratags = ""
|
||||
if "visible_if" in definition:
|
||||
vfield, vvalue = definition["visible_if"].split("=")
|
||||
@@ -270,9 +273,12 @@ def param(app, definition):
|
||||
"""
|
||||
|
||||
if definition.has_key("help"):
|
||||
result += """
|
||||
<span class="help-block">%s</span>
|
||||
""" % _(definition["help"])
|
||||
result += """<a class="help" rel="popover" data-content="%s" data-original-title="%s">
|
||||
<i class="icon-info-sign"></i>
|
||||
</a>""" % (unicode(definition["help"]), _("Help"))
|
||||
# result += """
|
||||
# <span class="help-block">%s</span>
|
||||
# """ % _(definition["help"])
|
||||
|
||||
result += """
|
||||
</div>
|
||||
|
||||
+13
-13
@@ -3,8 +3,13 @@
|
||||
Basic tests for the 'admin' application
|
||||
=======================================
|
||||
|
||||
TODO:
|
||||
|
||||
* Add new tests to check domain/mailbox manipulations when
|
||||
create_directories == yes
|
||||
|
||||
"""
|
||||
import os
|
||||
from django.test import TestCase
|
||||
from django.test.client import Client
|
||||
from django.utils import simplejson
|
||||
@@ -14,7 +19,6 @@ from modoboa.admin.lib import *
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
|
||||
import os
|
||||
|
||||
class DomainTestCase(TestCase):
|
||||
fixtures = ["initial_users.json"]
|
||||
@@ -26,18 +30,12 @@ class DomainTestCase(TestCase):
|
||||
self.assertEqual(dom.quota, 100)
|
||||
self.assertEqual(dom.enabled, False)
|
||||
|
||||
self.assertEqual(dom.create_dir(), True)
|
||||
path = os.path.join(parameters.get_admin("STORAGE_PATH"), dom.name)
|
||||
self.assertEqual(os.path.exists(path), True)
|
||||
|
||||
dom.name = "toto.com"
|
||||
dom.save()
|
||||
self.assertEqual(dom.name, "toto.com")
|
||||
path = os.path.join(parameters.get_admin("STORAGE_PATH"), dom.name)
|
||||
self.assertEqual(os.path.exists(path), True)
|
||||
|
||||
dom.delete(User.objects.get(pk=1))
|
||||
self.assertEqual(os.path.exists(path), False)
|
||||
|
||||
|
||||
class DomainAliasTestCase(ModoTestCase):
|
||||
fixtures = ["initial_users.json", "test_content.json"]
|
||||
@@ -68,9 +66,14 @@ class DomainAliasTestCase(ModoTestCase):
|
||||
self.check_ajax_post(reverse("modoboa.admin.views.editdomain", args=[1]), values)
|
||||
self.assertEqual(dom.domainalias_set.count(), 1)
|
||||
|
||||
|
||||
class AccountTestCase(ModoTestCase):
|
||||
fixtures = ["initial_users.json", "test_content.json",]
|
||||
|
||||
def setUp(self):
|
||||
super(AccountTestCase, self).setUp()
|
||||
from modoboa import admin
|
||||
|
||||
def test(self):
|
||||
values = dict(username="tester@test.com", first_name="Tester", last_name="Toto",
|
||||
password1="toto", password2="toto", role="SimpleUsers",
|
||||
@@ -80,7 +83,6 @@ class AccountTestCase(ModoTestCase):
|
||||
account = User.objects.get(username="tester@test.com")
|
||||
mb = account.mailbox_set.all()[0]
|
||||
self.assertEqual(mb.full_address, "tester@test.com")
|
||||
self.assertEqual(os.path.exists(mb.full_path), True)
|
||||
self.assertEqual(mb.quota, 100)
|
||||
self.assertEqual(mb.enabled, True)
|
||||
self.assertEqual(account.username, mb.full_address)
|
||||
@@ -93,14 +95,10 @@ class AccountTestCase(ModoTestCase):
|
||||
self.check_ajax_post(reverse("modoboa.admin.views.editaccount", args=[account.id]), values)
|
||||
|
||||
mb = Mailbox.objects.get(pk=mb.id)
|
||||
self.assertEqual(mb.path, "%s/" % mb.address)
|
||||
path = mb.full_path
|
||||
self.assertEqual(mb.full_address, "pouet@test.com")
|
||||
self.assertEqual(os.path.exists(path), True)
|
||||
|
||||
self.check_ajax_get(reverse("modoboa.admin.views.delaccount") + "?selection=%d" \
|
||||
% account.id, {})
|
||||
self.assertEqual(os.path.exists(path), False)
|
||||
|
||||
def test_update_password(self):
|
||||
"""Password update
|
||||
@@ -124,6 +122,7 @@ class AccountTestCase(ModoTestCase):
|
||||
self.clt.logout()
|
||||
self.assertEqual(self.clt.login(username="user@test.com", password="tutu"), True)
|
||||
|
||||
|
||||
class AliasTestCase(ModoTestCase):
|
||||
fixtures = ["initial_users.json", "test_content.json"]
|
||||
|
||||
@@ -181,6 +180,7 @@ class AliasTestCase(ModoTestCase):
|
||||
self.assertRaises(Alias.DoesNotExist, Alias.objects.get,
|
||||
address="forward", domain__name="test.com")
|
||||
|
||||
|
||||
class PermissionsTestCase(ModoTestCase):
|
||||
fixtures = ["initial_users.json", "test_content.json"]
|
||||
|
||||
|
||||
@@ -481,17 +481,18 @@ def viewparameters(request, tplname='admin/parameters.html'):
|
||||
return ajax_simple_response({
|
||||
"status" : "ok",
|
||||
"left_selection" : "parameters",
|
||||
"content" : render_to_string(tplname, {"gparams" : parameters.get_all_admin_parameters()})
|
||||
"content" : render_to_string(tplname, {"forms" : parameters.get_admin_forms})
|
||||
})
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def saveparameters(request):
|
||||
for pname, v in request.POST.iteritems():
|
||||
if pname == "update":
|
||||
for formdef in parameters.get_admin_forms(request.POST):
|
||||
form = formdef["form"]
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
continue
|
||||
app, name = pname.split('.')
|
||||
parameters.save_admin(name, v, app=app)
|
||||
return ajax_simple_response({"status": "ko", "prefix": form.app, "errors": form.errors})
|
||||
return ajax_simple_response(dict(status="ok", respmsg=_("Parameters saved")))
|
||||
|
||||
@login_required
|
||||
@@ -510,7 +511,7 @@ def viewextensions(request, tplname='admin/extensions.html'):
|
||||
dbext.enabled = False
|
||||
dbext.save()
|
||||
ext["selection"] = False
|
||||
|
||||
|
||||
tbl = ExtensionsTable(request, exts)
|
||||
return ajax_simple_response({
|
||||
"status" : "ok",
|
||||
|
||||
Reference in New Issue
Block a user