Work in progress:
 * New domains admin page, all menus are now at the same level,
 * Views, models and forms partial refactoring (better code organization).
This commit is contained in:
tonio
2010-12-07 23:44:28 +01:00
parent 66f08caea9
commit 308702ec10
25 changed files with 462 additions and 256 deletions
+16 -1
View File
@@ -32,7 +32,7 @@ class MailboxForm(forms.ModelForm):
class Meta:
model = Mailbox
fields = ('name', 'address')
fields = ('domain', 'name', 'address')
def __init__(self, *args, **kwargs):
super(MailboxForm, self).__init__(*args, **kwargs)
@@ -49,6 +49,13 @@ class MailboxForm(forms.ModelForm):
raise forms.ValidationError(_("Passwords mismatch"))
return self.cleaned_data["password2"]
def save(self, force_insert=False, force_update=False, commit=True):
m = super(MailboxForm, self).save(commit=False)
if commit:
m.save(enabled=self.cleaned_data["enabled"],
password=self.cleaned_data["password1"])
return m
class AliasForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
domain = None
@@ -70,6 +77,14 @@ class AliasForm(forms.ModelForm):
return self.cleaned_data["address"].rsplit("@", 1)[0]
return self.cleaned_data["address"]
def save(self, force_insert=False, force_update=False, commit=True):
a = super(AliasForm, self).save(commit=False)
if commit:
domain = self.cleaned_data["mboxes"][0].domain
a.full_address = "%s@%s" % (a.address, domain.name)
a.save()
return a
class SuperAdminForm(forms.Form):
user = forms.ModelChoiceField([], label=_("User"), required=True,
empty_label=_("Select a user"),
@@ -7,7 +7,7 @@ from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
db.alter_column("auth_user", "username", models.CharField(max_length=254))
db.alter_column("auth_user", "username", models.CharField(unique=True, max_length=254))
db.alter_column("auth_user", "email", models.CharField(max_length=254))
def backwards(self, orm):
@@ -0,0 +1,98 @@
# encoding: 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):
db.alter_column("admin_alias", "full_address",
models.CharField(unique=True, max_length=254))
def backwards(self, orm):
db.alter_column("admin_alias", "full_address",
models.CharField(max_length=150))
models = {
'admin.alias': {
'Meta': {'object_name': 'Alias'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'full_address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '150'}),
'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': {'object_name': 'Domain'},
'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': '100'}),
'quota': ('django.db.models.fields.IntegerField', [], {})
},
'admin.domainalias': {
'Meta': {'object_name': 'DomainAlias'},
'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'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
'full_address': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
'gid': ('django.db.models.fields.IntegerField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'quota': ('django.db.models.fields.IntegerField', [], {}),
'uid': ('django.db.models.fields.IntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'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']
+71 -16
View File
@@ -1,10 +1,28 @@
from django.db import models
# coding: utf8
from django.db import models, IntegrityError
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext as _
from django.conf import settings
from modoboa.lib import parameters
from modoboa.lib import exec_cmd, exec_as_vuser
from modoboa.lib import exec_cmd, exec_as_vuser, crypt_password
import os
import pwd
class AdminError(Exception):
"""Custom exception
"""
def __init__(self, value):
"""Constructor
:param value: the information contained in this exception.
"""
self.value = str(value)
def __str__(self):
"""String representation"""
return self.value
class Domain(models.Model):
name = models.CharField(_('name'), max_length=100,
@@ -70,28 +88,29 @@ class Mailbox(models.Model):
self.mdirroot = parameters.get_admin("MAILDIR_ROOT")
def __str__(self):
return "%s" % (self.address)
return "%s" % (self.full_address)
def create_dir(self, domain):
path = "%s/%s/%s" % (parameters.get_admin("STORAGE_PATH"),
domain.name, self.address)
if os.path.exists(path):
def create_dir(self):
relpath = "%s/%s" % (self.domain.name, self.address)
abspath = os.path.join(parameters.get_admin("STORAGE_PATH"), relpath)
if self.mbtype == "mbox":
self.path = "%s/Inbox" % relpath
else:
self.path = "%s/%s" % (relpath, self.mdirroot)
if os.path.exists(abspath):
return True
if not exec_as_vuser("mkdir -p %s" % path):
if not exec_as_vuser("mkdir -p %s" % abspath):
return False
if self.mbtype == "mbox":
template = ["Inbox", "Drafts", "Sent", "Trash", "Junk"]
for dir in template:
exec_as_vuser("touch %s/%s" % (path, dir))
self.path = "%s/%s/Inbox" % (domain.name, self.address)
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" \
% (path, self.mdirroot, dir, sdir))
self.path = "%s/%s/%s/" % (domain.name, self.address, self.mdirroot)
% (abspath, self.mdirroot, dir, sdir))
return True
def rename_dir(self, domain, newaddress):
@@ -109,6 +128,42 @@ class Mailbox(models.Model):
% (parameters.get_admin("STORAGE_PATH"),
self.domain.name, self.address))
def save(self, *args, **kwargs):
if not self.create_dir():
raise AdminError(_("Failed to initialise mailbox, check permissions"))
try:
user = getattr(self, "user")
except User.DoesNotExist:
user = User()
user.username = user.email = "%s@%s" % (self.address, self.domain.name)
user.set_unusable_password()
user.is_active = kwargs["enabled"]
try:
fname, lname = self.name.split()
except ValueError:
fname = self.name
lname = ""
user.first_name = fname
user.last_name = lname
try:
user.save()
except IntegrityError:
raise AdminError(_("Mailbox with this address already exists"))
self.user = user
if kwargs["password"] != u"é":
self.password = crypt_password(kwargs["password"])
self.uid = pwd.getpwnam(parameters.get_admin("VIRTUAL_UID")).pw_uid
self.gid = pwd.getpwnam(parameters.get_admin("VIRTUAL_GID")).pw_gid
if not self.quota:
self.quota = self.domain.quota
self.full_address = self.user.email
try:
del kwargs["enabled"]
del kwargs["password"]
except KeyError:
pass
super(Mailbox, self).save(*args, **kwargs)
def tohash(self):
return {
"id" : self.id,
@@ -120,7 +175,7 @@ class Mailbox(models.Model):
class Alias(models.Model):
address = models.CharField(_('address'), max_length=100,
help_text=_("The alias address (without the domain part)"))
full_address = models.CharField(max_length=150)
full_address = models.CharField(max_length=254, unique=True)
mboxes = models.ManyToManyField(Mailbox, verbose_name=_('mailboxes'),
help_text=_("The mailboxes this alias points to"))
enabled = models.BooleanField(_('enabled'),
+64 -32
View File
@@ -62,17 +62,17 @@ def admin_menu(user):
def domain_menu(domain_id, selection, user):
entries = [
{"name" : "",
"url" : reverse(admin.views.newmailbox, args=[domain_id]),
"url" : reverse(admin.views.newmailbox),
"label" : _("New mailbox"),
"img" : static_url("pics/add.png"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:300,y:280}}"},
{"name" : "mailboxes",
"url" : reverse(admin.views.mailboxes, args=[domain_id]),
"url" : reverse(admin.views.mailboxes),
"img" : static_url("pics/mailbox.png"),
"label" : _("Mailboxes")},
{"name" : "aliases",
"url" : reverse("full-aliases", args=[domain_id]),
"url" : reverse("full-aliases"),
"img" : static_url("/pics/alias.png"),
"label" : _("Aliases")},
]
@@ -108,34 +108,47 @@ def settings_menu(selection, user):
{"selection" : selection, "entries" : entries,
"user" : user})
def dommenu_entry(name, label, img, listurl, newurl):
result = {"name" : name,
"label" : label,
"img" : img,
"url" : listurl,
"class" : "menubardropdown",
"menu" : [{"name" : "%s_new" % name,
"url" : newurl,
"label" : _("New"),
"img" : static_url("pics/add.png"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:300,y:200}}"}
]
}
return result
@register.simple_tag
def domains_menu(selection, user):
entries = [
{"name" : "newdomain",
"url" : reverse(admin.views.newdomain),
"label" : _("New domain"),
"img" : static_url("pics/add.png"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:300,y:200}}"},
{"name" : "domains",
"url" : reverse(admin.views.domains),
"label" : _("Domains"),
"img" : static_url("pics/domains.png")},
{"name" : "newdomalias",
"url" : reverse(admin.views.newdomalias),
"label" : _("New domain alias"),
"img" : static_url("pics/add.png"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:300,y:200}}"},
{"name" : "domaliases",
"url" : reverse(admin.views.domaliases),
"label" : _("Domain aliases"),
"img" : static_url("pics/alias.png")}
def domains_menu(selection, user, domid):
args = domid != "" and ("?domid=%s" % domid) or ""
entries = []
if user.has_perm("admin.view_domains"):
entries += dommenu_entry("domains", _("Domains"),
static_url("pics/domains.png"),
reverse(admin.views.domains),
reverse(admin.views.newdomain)),
entries += [
dommenu_entry("domaliases", _("Domain aliases"), static_url("pics/alias.png"),
reverse(admin.views.domaliases) + args,
reverse(admin.views.newdomalias) + args),
dommenu_entry("mailboxes", _("Mailboxes"), static_url("pics/mailbox.png"),
reverse(admin.views.mailboxes) + args,
reverse(admin.views.newmailbox) + args),
dommenu_entry("mbaliases", _("Mailbox aliases"), static_url("pics/alias.png"),
reverse(admin.views.mbaliases) + args,
reverse(admin.views.newmbalias) + args),
]
entries += events.raiseQueryEvent("AdminMenuDisplay", target="admin_menu_box",
user=user)
return render_to_string('common/menu.html',
{"entries" : entries, "selection" : selection,
{"entries" : entries,
"selection" : selection,
"user" : user})
@register.simple_tag
@@ -156,7 +169,8 @@ def permissions_menu(user):
def render_actions(actions):
t = template.Template("""
{% for a in actions %}
<a href="{{ a.url }}" name="{{ a.name }}" class="{{ a.class }}" rel="{{ a.rel }}">
<a href="{{ a.url }}" name="{{ a.name }}" class="{{ a.class }}" rel="{{ a.rel }}"
{% if a.confirm %}onclick="return confirm('{{ a.confirm }}')"{% endif %}>
<img src="{{ a.img }}" border="0" title="{{ a.title }}" />
</a>
{% endfor %}
@@ -177,7 +191,8 @@ def domain_actions(user, domid):
{"name" : "deldomain",
"url" : reverse(admin.views.deldomain, args=[domid]),
"img" : static_url("pics/remove.png"),
"title" : _("Delete domain")},
"title" : _("Delete domain"),
"confirm" : _("Delete this domain?")},
{"name" : "domaliases",
"url" : reverse(admin.views.domaliases) + "?domid=%s" % domid,
"img" : static_url("pics/alias.png"),
@@ -198,25 +213,42 @@ def domalias_actions(user, aid):
return render_actions(actions)
@register.simple_tag
def mailbox_actions(user, domid, mid):
def mailbox_actions(user, mbox):
actions = [
{"name" : "editmailbox",
"url" : reverse(admin.views.editmailbox, args=[domid, mid]),
"url" : reverse(admin.views.editmailbox, args=[mbox.id]),
"img" : static_url("pics/edit.png"),
"title" : _("Edit mailbox"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:300,y:280}}"},
{"name" : "deleteMb",
"url" : reverse(admin.views.delmailbox, args=[domid, mid]),
"url" : reverse(admin.views.delmailbox, args=[mbox.id]),
"img" : static_url("pics/remove.png"),
"title" : _("Delete mailbox")},
{"name" : "aliases",
"url" : reverse("mbox-aliases", args=[domid, mid]),
"url" : reverse(admin.views.mbaliases) + "?mbid=%d" % mbox.id,
"img" : static_url("pics/alias.png"),
"title" : _("View this mailbox aliases")},
]
return render_actions(actions)
@register.simple_tag
def mbalias_actions(user, alias):
actions = [
{"name" : "editmbalias",
"url" : reverse(admin.views.editmbalias, args=[alias.id]),
"img" : static_url("pics/edit.png"),
"title" : _("Edit mailbox alias"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:300,y:250}}"},
{"name" : "dembalias",
"url" : reverse(admin.views.delmbalias, args=[alias.id]),
"img" : static_url("pics/remove.png"),
"title" : _("Delete mailbox alias"),
"confirm" : _("Delete this alias?")},
]
return render_actions(actions)
@register.simple_tag
def loadadminextmenu(user):
menu = events.raiseQueryEvent("AdminMenuDisplay", target="admin_menu_box",
+12 -9
View File
@@ -11,16 +11,19 @@ urlpatterns = patterns('modoboa.admin.views',
(r'^domaliases/new/', 'newdomalias'),
(r'^domaliases/edit/(?P<alias_id>\d+)/', 'editdomalias'),
(r'^domains/(?P<dom_id>\d+)/newmailbox/$', 'newmailbox'),
(r'^domains/(?P<dom_id>\d+)/editmailbox/(?P<mbox_id>\d+)/$', 'editmailbox'),
(r'^domains/(?P<dom_id>\d+)/deletemailbox/(?P<mbox_id>\d+)/$', 'delmailbox'),
(r'^domains/(?P<dom_id>\d+)/newalias/$', 'newalias'),
(r'^domains/(?P<dom_id>\d+)/editalias/(?P<alias_id>\d+)/$', 'editalias'),
(r'^domains/(?P<dom_id>\d+)/$', 'mailboxes'),
(r'^mailboxes/$', 'mailboxes'),
(r'^mailboxes/new/$', 'newmailbox'),
(r'^mailboxes/edit/(?P<mbox_id>\d+)/$', 'editmailbox'),
(r'^mailboxes/delete/(?P<mbox_id>\d+)/$', 'delmailbox'),
(r'^mbaliases/$', 'mbaliases'),
(r'^mbaliases/new/$', 'newmbalias'),
(r'^mbaliases/edit/(?P<alias_id>\d+)/$', 'editmbalias'),
(r'^mbaliases/delete/(?P<alias_id>\d+)/$', 'delmbalias'),
(r'^domains/(?P<dom_id>\d+)/raw/$', 'mailboxes_raw'),
url(r'^domains/(?P<dom_id>\d+)/aliases/$', "aliases", name="full-aliases"),
url(r'^domains/(?P<dom_id>\d+)/aliases/(?P<mbox_id>\d+)/$', "aliases", name="mbox-aliases"),
(r'^domains/(?P<dom_id>\d+)/delalias/(?P<alias_id>\d+)/$', 'delalias'),
#url(r'^domains/(?P<dom_id>\d+)/aliases/$', "aliases", name="full-aliases"),
#url(r'^domains/(?P<dom_id>\d+)/aliases/(?P<mbox_id>\d+)/$', "aliases", name="mbox-aliases"),
(r'^settings/$', 'viewparameters'),
(r'^settings/parameters/$', 'viewparameters'),
+98 -130
View File
@@ -13,22 +13,28 @@ from django.db import transaction, IntegrityError
from modoboa import admin, userprefs
from models import *
from admin.permissions import *
from modoboa.lib.authbackends import crypt_password
from modoboa.lib import crypt_password
from modoboa.lib import _render, _render_to_string, _ctx_ok, \
getctx, events, parameters
from modoboa.lib.models import Parameter
import copy
import pwd
def good_domain(f):
def dec(request, dom_id, **kwargs):
def dec(request, **kwargs):
if request.user.is_superuser:
return f(request, dom_id, **kwargs)
return f(request, **kwargs)
mb = Mailbox.objects.get(user=request.user.id)
if isinstance(dom_id, str) or isinstance(dom_id, unicode):
dom_id = int(dom_id)
if dom_id == mb.domain.id:
return f(request, dom_id, **kwargs)
access = True
if request.GET.has_key("domid"):
dom_id = int(request.GET["domid"])
if dom_id != mb.domain.id:
access = False
else:
q = request.GET.copy()
q["domid"] = mb.domain.id
request.GET = q
if access:
return f(request, **kwargs)
from django.conf import settings
path = urlquote(request.get_full_path())
@@ -36,12 +42,20 @@ def good_domain(f):
return HttpResponseRedirect("%s?next=%s" % (login_url, path))
return dec
def render_domains_page(request, page, **kwargs):
template = "admin/%s.html" % page
if request.GET.has_key("domid"):
kwargs["domid"] = request.GET["domid"]
else:
kwargs["domid"] = ""
kwargs["selection"] = page
return _render(request, template, kwargs)
@login_required
def domains(request):
if not request.user.has_perm("admin.view_domains"):
if request.user.has_perm("admin.view_mailboxes"):
mb = Mailbox.objects.get(user=request.user.id)
return mailboxes(request, dom_id=mb.domain.id)
return HttpResponseRedirect(reverse(mailboxes))
return HttpResponseRedirect(reverse(userprefs.views.preferences))
@@ -49,9 +63,8 @@ def domains(request):
counters = {}
for dom in domains:
dom.mboxcounter = len(dom.mailbox_set.all())
return _render(request, 'admin/domains.html', {
"domains" : domains, "counters" : counters
})
return render_domains_page(request, "domains",
domains=domains, counter=counters)
@login_required
def domaliases(request):
@@ -61,9 +74,8 @@ def domaliases(request):
else:
domain = None
domaliases = DomainAlias.objects.all()
return _render(request, 'admin/domaliases.html', {
"domaliases" : domaliases, "domain" : domain
})
return render_domains_page(request, "domaliases",
domaliases=domaliases, domain=domain)
@login_required
def newdomalias(request):
@@ -199,12 +211,15 @@ def deldomain(request, dom_id):
@login_required
@good_domain
@permission_required("admin.view_mailboxes")
def mailboxes(request, dom_id=None):
domain = Domain.objects.get(pk=dom_id)
mailboxes = Mailbox.objects.filter(domain=dom_id)
return _render(request, 'admin/mailboxes.html', {
"mailboxes" : mailboxes, "domain" : domain
})
def mailboxes(request):
if request.GET.has_key("domid"):
domain = Domain.objects.get(pk=request.GET["domid"])
mailboxes = Mailbox.objects.filter(domain=domain.id)
else:
mailboxes = Mailbox.objects.all()
domain = None
return render_domains_page(request, "mailboxes",
mailboxes=mailboxes, domain=domain)
@login_required
@good_domain
@@ -218,102 +233,60 @@ def mailboxes_raw(request, dom_id=None):
@login_required
@good_domain
@permission_required("admin.add_mailbox")
def newmailbox(request, dom_id=None):
domain = Domain.objects.get(pk=dom_id)
def newmailbox(request):
if request.method == "POST":
form = MailboxForm(request.POST)
error = None
if form.is_valid():
mb = form.save(commit=False)
if mb.create_dir(domain):
from django.conf import settings
user = User()
user.username = user.email = "%s@%s" % (mb.address, domain.name)
user.set_unusable_password()
user.is_active = request.POST.has_key("enabled") \
and True or False
try:
fname, lname = mb.name.split()
except ValueError:
fname = mb.name
lname = ""
user.first_name = fname
user.last_name = lname
try:
user.save()
except IntegrityError:
error = _("Mailbox with this address already exists")
else:
mb.user = user
mb.password = crypt_password(request.POST["password1"])
mb.uid = pwd.getpwnam(parameters.get_admin("VIRTUAL_UID")).pw_uid
mb.gid = pwd.getpwnam(parameters.get_admin("VIRTUAL_GID")).pw_gid
mb.domain = domain
mb.quota = request.POST["quota"]
if not mb.quota:
mb.quota = domain.quota
mb.full_address = user.email
mb.save()
events.raiseEvent("CreateMailbox", mbox=mb)
messages.info(request, _("Mailbox created."),
fail_silently=True)
ctx = _ctx_ok(reverse(admin.views.mailboxes, args=[domain.id]))
return HttpResponse(simplejson.dumps(ctx),
mimetype="application/json")
try:
mb = form.save()
except AdminError as e:
error = str(e)
else:
error = _("Failed to initialise mailbox, check permissions")
content = _render_to_string("ko", "admin/newmailbox.html", {
"form" : form, "domain" : domain, "error" : error
events.raiseEvent("CreateMailbox", mbox=mb)
messages.info(request, _("Mailbox created"),
fail_silently=True)
ctx = _ctx_ok(reverse(admin.views.mailboxes) + "?domid=%d" % mb.domain.id)
return HttpResponse(simplejson.dumps(ctx),
mimetype="application/json")
content = _render_to_string(request, "admin/newmailbox.html", {
"form" : form, "error" : error
})
ctx = getctx("ko", content=content)
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
form = MailboxForm()
if request.GET.has_key("domid"):
form.fields["domain"].initial = request.GET["domid"]
return _render(request, "admin/newmailbox.html", {
"domain" : domain, "form" : form
"form" : form
})
@login_required
@good_domain
@permission_required("admin.change_mailbox")
@transaction.commit_manually
def editmailbox(request, dom_id, mbox_id=None):
def editmailbox(request, mbox_id=None):
mb = Mailbox.objects.get(pk=mbox_id)
if request.method == "POST":
oldmb = copy.deepcopy(mb)
form = MailboxForm(request.POST, instance=mb)
error = None
if form.is_valid():
mb = form.save(commit=False)
mb.user.is_active = request.POST.has_key("enabled") \
and request.POST["enabled"] or False
mb.user.username = mb.user.email = \
"%s@%s" % (mb.address, mb.domain.name)
try:
mb.user.save()
except IntegrityError:
error = _("Mailbox with this address already exists")
mb = form.save()
except AdminError, inst:
error = str(inst)
else:
if not oldmb.rename_dir(mb.domain.name, mb.address):
error = _("Failed to rename mailbox, check permissions")
else:
if request.POST["password1"] != u"é":
mb.password = crypt_password(request.POST["password1"])
mb.quota = request.POST["quota"]
if not mb.quota:
mb.quota = mb.domain.quota
mb.full_address = mb.user.email
mb.save()
if oldmb.rename_dir(mb.domain.name, mb.address):
transaction.commit()
events.raiseEvent("ModifyMailbox", mbox=mb, oldmbox=oldmb)
messages.info(request, _("Mailbox modified"),
fail_silently=True)
ctx = _ctx_ok(reverse(admin.views.mailboxes, args=[dom_id]))
ctx = _ctx_ok(reverse(admin.views.mailboxes) + "?domid=%d" % mb.domain.id)
return HttpResponse(simplejson.dumps(ctx),
mimetype="application/json")
error = _("Failed to rename mailbox, check permissions")
if error is not None:
transaction.rollback()
ctx = getctx("ko", content=_render_to_string(request, "admin/editmailbox.html", {
@@ -333,7 +306,7 @@ def editmailbox(request, dom_id, mbox_id=None):
@login_required
@good_domain
@permission_required("admin.delete_mailbox")
def delmailbox(request, dom_id, mbox_id=None):
def delmailbox(request, mbox_id=None):
mb = Mailbox.objects.get(pk=mbox_id)
if mb.user.id != request.user.id:
events.raiseEvent("DeleteMailbox", mbox=mb)
@@ -351,90 +324,85 @@ def delmailbox(request, dom_id, mbox_id=None):
@login_required
@good_domain
@permission_required("admin.view_aliases")
def aliases(request, dom_id=None, mbox_id=None):
domain = Domain.objects.get(pk=dom_id)
if not mbox_id:
aliases = Alias.objects.filter(mboxes__domain__id=dom_id).distinct()
def mbaliases(request):
if request.GET.has_key("domid"):
aliases = Alias.objects.filter(mboxes__domain__id=request.GET["domid"]).distinct()
if request.GET.has_key("mbid"):
aliases = Alias.objects.filter(mboxes__id=request.GET["mbid"]).distinct()
else:
aliases = Alias.objects.filter(mboxes__id=mbox_id)
return _render(request, 'admin/aliases.html', {
"aliases" : aliases, "domain" : domain
})
aliases = Alias.objects.all()
return render_domains_page(request, "mbaliases", aliases=aliases)
@login_required
@good_domain
@permission_required("admin.add_alias")
def newalias(request, dom_id):
domain = Domain.objects.get(pk=dom_id)
def newmbalias(request):
if request.method == "POST":
form = AliasForm(request.POST, domain=domain)
form = AliasForm(request.POST)
error = None
if form.is_valid():
if Alias.objects.filter(address=request.POST["address"]):
try:
alias = form.save()
except IntegrityError:
error = _("Alias with this name already exists")
else:
alias = form.save(commit=False)
alias.full_address = "%s@%s" % (alias.address, domain.name)
alias.save()
form.save_m2m()
ctx = _ctx_ok(reverse(admin.views.aliases, args=[dom_id]))
messages.info(request, _("Alias created"), fail_silently=True)
ctx = _ctx_ok(reverse(admin.views.mbaliases) )
messages.info(request, _("Mailbox alias created"), fail_silently=True)
return HttpResponse(simplejson.dumps(ctx),
mimetype="application/json")
content = _render_to_string(request, "admin/newalias.html", {
"domain" : dom_id, "form" : form, "error" : error
content = _render_to_string(request, "admin/newmbalias.html", {
"form" : form, "error" : error
})
ctx = getctx("ko", content=content)
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
form = AliasForm(domain=domain)
return _render(request, 'admin/newalias.html', {
"domain" : dom_id, "form" : form, "noerrors" : True
if request.GET.has_key("domid"):
form = AliasForm(domain=Domain.objects.get(pk=request.GET["domid"]))
else:
form = AliasForm()
return _render(request, 'admin/newmbalias.html', {
"form" : form, "noerrors" : True
})
@login_required
@good_domain
@permission_required("admin.change_alias")
def editalias(request, dom_id, alias_id):
def editmbalias(request, alias_id):
alias = Alias.objects.get(pk=alias_id)
if request.method == "POST":
form = AliasForm(request.POST, instance=alias)
error = None
if form.is_valid():
if alias.address != request.POST["address"] \
and Alias.objects.filter(address=request.POST["address"]):
try:
alias = form.save()
except IntegrityError:
error = _("Alias with this name already exists")
else:
domain = Domain.objects.get(pk=dom_id)
alias = form.save(commit=False)
alias.full_address = "%s@%s" % (alias.address,
domain.name)
alias.save()
form.save_m2m()
ctx = _ctx_ok(reverse(admin.views.aliases, args=[dom_id]))
messages.info(request, _("Alias modified"),
ctx = _ctx_ok(reverse(admin.views.mbaliases))
messages.info(request, _("Mailbox alias modified"),
fail_silently=True)
return HttpResponse(simplejson.dumps(ctx),
mimetype="application/json")
content = _render_to_string(request, "admin/editalias.html", {
"domain" : dom_id, "alias" : alias, "form" : form, "error" : error
content = _render_to_string(request, "admin/editmbalias.html", {
"alias" : alias, "form" : form, "error" : error
})
ctx = getctx("ko", content=content)
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
form = AliasForm(instance=alias)
return _render(request, 'admin/editalias.html', {
"form" : form, "alias" : alias, "domain" : dom_id
return _render(request, 'admin/editmbalias.html', {
"form" : form, "alias" : alias
})
@login_required
@good_domain
@permission_required("admin.delete_alias")
def delalias(request, dom_id, alias_id):
def delmbalias(request, alias_id):
alias = Alias.objects.get(pk=alias_id)
alias.delete()
messages.info(request, _("Alias deleted"), fail_silently=True)
return HttpResponseRedirect(reverse(admin.views.aliases,
args=[dom_id]))
return HttpResponseRedirect(reverse(admin.views.mbaliases))
@login_required
@permission_required("auth.view_permissions")
@@ -530,8 +498,8 @@ def saveparameters(request):
@login_required
@user_passes_test(lambda u: u.is_superuser)
def viewextensions(request):
from extensions import list_extensions
from lib import tables
from modoboa.extensions import list_extensions
from modoboa.lib import tables
class ExtensionsTable(tables.Table):
idkey = "id"
@@ -560,7 +528,7 @@ def viewextensions(request):
@login_required
@user_passes_test(lambda u: u.is_superuser)
def saveextensions(request):
import extensions
from modoboa import extensions
actived_exts = Extension.objects.filter(enabled=True)
found = []
+1 -1
View File
@@ -9,7 +9,7 @@ from modoboa.admin.views import good_domain
from modoboa.admin.models import Domain, Mailbox
from django.contrib.auth.decorators \
import login_required, user_passes_test, permission_required
from extensions.stats.grapher import *
from modoboa.extensions.stats.grapher import *
graph_types = ['AVERAGE', 'MAX']
graph_list = [{"name" : "traffic", "label" : _("Average normal traffic")},
@@ -156,6 +156,7 @@ textarea {
background-color: #eee;
width: 80%;
height: 30px;
word-wrap: break-word;
}
textarea:hover {
+19
View File
@@ -2,6 +2,8 @@
import os
import sys
import time
import hashlib, crypt, string
from random import Random
from django.conf import settings
from django.template import RequestContext
from django.shortcuts import render_to_response
@@ -118,3 +120,20 @@ class Singleton(type):
cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls.instance
def _check_password(password, crypted):
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
if scheme == "crypt":
return crypt.crypt(password, crypted) == crypted
if scheme == "md5":
return hashlib.md5(password).hexdigest() == crypted
return password
def crypt_password(password):
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
if scheme == "crypt":
salt = ''.join(Random().sample(string.letters + string.digits, 2))
return crypt.crypt(password, salt)
if scheme == "md5":
return hashlib.md5(password).hexdigest()
return password
+1 -21
View File
@@ -1,10 +1,6 @@
from django.contrib.auth.models import User
from modoboa.admin.models import Mailbox
import hashlib
import crypt
import string
from random import Random
from modoboa.lib import parameters
from modoboa.lib import _check_password
class SimpleBackend:
def authenticate(self, username=None, password=None):
@@ -26,19 +22,3 @@ class SimpleBackend:
except User.DoesNotExist:
return None
def _check_password(password, crypted):
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
if scheme == "crypt":
return crypt.crypt(password, crypted) == crypted
if scheme == "md5":
return hashlib.md5(password).hexdigest() == crypted
return password
def crypt_password(password):
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
if scheme == "crypt":
salt = ''.join(Random().sample(string.letters + string.digits, 2))
return crypt.crypt(password, salt)
if scheme == "md5":
return hashlib.md5(password).hexdigest()
return password
+5
View File
@@ -177,6 +177,11 @@ div#footer {
border-left: 5px solid #ccc;
position: relative;
z-index: 2;
}
.menubardropdown label {
cursor: pointer;
}
div.left {
+4 -1
View File
@@ -288,7 +288,10 @@ setDivHeight = function(id, extrah, modulo) {
bindRows = function(rooturl, page) {
$$("tr").addEvent("click", function(event) {
if (event.target.parentNode.id) {
var url = rooturl + event.target.parentNode.id + "?page=" + page;
var url = rooturl + "?domid=" + event.target.parentNode.id;
if (page != "") {
url += "?page=" + page;
}
event.target.parentNode.setStyle("background", "#ffffcc");
location.href = url;
}
+4 -10
View File
@@ -1,21 +1,16 @@
{% extends "nlayout.html" %}
{% extends "admin/domains_header.html" %}
{% load i18n admin_extras libextras %}
{% block html_head %}
<script src="{{ MEDIA_URL }}js/domains.js" type="text/javascript"></script>
{{ block.super }}
<script type="text/javascript">
window.addEvent('domready', function() {
{% url admin.views.domains as view_url %}
{% url admin.views.mailboxes as view_url %}
bindRows("{{ view_url }}", "{{ current_page }}");
});
</script>
{% endblock %}
{% block middletitle %}{% trans "Available domains" %}{% endblock %}
{% block menu %}{% domains_menu "domains" user %}{% endblock %}
{% block content %}
{% if messages %}
{% display_messages messages %}
{% endif %}
<div id="domcontent">
{% block subcontent %}
{% if not domains %}<div class="info">{% trans "No domain defined." %}</div>
{% else %}
<table>
@@ -37,5 +32,4 @@
{% endfor %}
</table>
{% endif %}
</div>
{% endblock %}
+17
View File
@@ -0,0 +1,17 @@
{% extends "nlayout.html" %}
{% load i18n admin_extras libextras %}
{% block html_head %}
<script src="{{ MEDIA_URL }}js/domains.js" type="text/javascript"></script>
{% endblock %}
{% block middletitle %}
{% block pagetitle %}{% endblock %}{% if domain %} ({% trans "for" %} {{ domain.name }}){% endif %}
{% endblock %}
{% block menu %}{% domains_menu selection user domid %}{% endblock %}
{% block content %}
{% if messages %}
{% display_messages messages %}
{% endif %}
<div id="subcontent">
{% block subcontent %}{% endblock %}
</div>
{% endblock %}
+3 -14
View File
@@ -1,17 +1,7 @@
{% extends "nlayout.html" %}
{% extends "admin/domains_header.html" %}
{% load i18n admin_extras libextras %}
{% block html_head %}
<script src="{{ MEDIA_URL }}js/domains.js" type="text/javascript"></script>
<script type="text/javascript">
</script>
{% endblock %}
{% block middletitle %}
{% trans "Domain aliases" %}{% if domain %} ({% trans "for" %} {{ domain.name }}){% endif %}
{% endblock %}
{% block menu %}{% domains_menu "domaliases" user %}{% endblock %}
{% block content %}
{% if messages %}{% display_messages messages %}{% endif %}
<div id="domcontent">
{% block pagetitle %}{% trans "Domain aliases" %}{% endblock %}
{% block subcontent %}
{% if not domaliases %}
<div class="info">{% trans "No domain alias defined." %}</div>
{% else %}
@@ -32,5 +22,4 @@
{% endfor %}
</table>
{% endif %}
</div>
{% endblock %}
+1 -1
View File
@@ -6,7 +6,7 @@
{% block content %}
<div class="title"><h1>{% trans "Mailbox" %} {{ mbox.name }}</h1></div>
{% if error %}<div class="error">{{ error }}</div>{% endif %}
{% url admin.views.editmailbox mbox.domain_id mbox.id as box_url %}
{% url admin.views.editmailbox mbox.id as box_url %}
<form action="{{ box_url }}" method="POST" id="mboxform" name="mboxform">
{% include "common/genericform.html" %}
<div class="spacer"></div>
@@ -4,9 +4,9 @@
{% endblock %}
{% block content %}
{% load i18n %}
<div class="title"><h1>Alias {{ alias.address }}</h1></div>
<div class="title"><h1>Mailbox alias {{ alias.address }}</h1></div>
{% if error %}<div class="error">{{ error }}</div>{% endif %}
{% url admin.views.editalias domain alias.id as edit_url %}
{% url admin.views.editmbalias alias.id as edit_url %}
<form action="{{ edit_url }}" method="POST" id="aliasform" name="aliasform">
{% include "common/genericform.html" %}
<div class="spacer"></div>
+6 -10
View File
@@ -1,13 +1,9 @@
{% extends "admin/domain_header.html" %}
{% load i18n %}
{% block menu %}
{% load admin_extras %}
{% domain_menu domain.id "mailboxes" user %}
{% endblock %}
{% block domcontent %}
{% load i18n %}
{% extends "admin/domains_header.html" %}
{% load i18n admin_extras %}
{% block pagetitle %}{% trans "Available mailboxes" %}{% endblock %}
{% block subcontent %}
{% if not mailboxes %}
<p>{% trans "No mailbox defined." %}</p>
<div class="info">{% trans "No mailbox defined." %}</div>
{% else %}
<table>
<tr>
@@ -19,7 +15,7 @@
</tr>
{% for mb in mailboxes %}
<tr class="{% cycle 'odd' 'even' %}">
<td>{% mailbox_actions user domain.id mb.id %}</td>
<td>{% mailbox_actions user mb %}</td>
<td>{{ mb.name }}</td>
<td>{{ mb.address }}@{{ mb.domain.name }}</td>
<td>{{ mb.quota }} {% trans "MB" %}</td>
+29
View File
@@ -0,0 +1,29 @@
{% extends "admin/domains_header.html" %}
{% load i18n admin_extras %}
{% block pagetitle %}{% trans "Mailbox aliases" %}{% endblock %}
{% block subcontent %}
{% if not aliases %}
<div class="info">{% trans "No mailbox alias defined." %}</div>
{% else %}
<table>
<tr>
<th width="50px">{% trans "Actions" %}</th>
<th width="150px">{% trans "Address" %}</th>
<th width="200px">{% trans "Mailbox" %}</th>
<th width="75px">{% trans "Enabled" %}</th>
</tr>
{% for alias in aliases %}
<tr>
<td>{% mbalias_actions user alias %}
<td>{{ alias.full_address }}</td>
<td>
{% for mb in alias.mboxes.all %}
{{ mb.full_address }}<br/>
{% endfor %}
</td>
<td>{{ alias.enabled|yesno }}</td
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
+1 -1
View File
@@ -7,7 +7,7 @@
<div class="title"><h1>{% trans "New mailbox" %}</h1></div>
{% if error %}<div class="error">{{ error }}</div>{% endif %}
{% url admin.views.newmailbox domain.id as box_url %}
{% url admin.views.newmailbox as box_url %}
<form action="{{ box_url }}" method="POST" id="mboxform" name="mboxform">
{% include "common/genericform.html" %}
<div class="spacer"></div>
@@ -4,9 +4,9 @@
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
{% endblock %}
{% block content %}
<div class="title"><h1>{% trans "New alias" %}</h1></div>
<div class="title"><h1>{% trans "New mailbox alias" %}</h1></div>
{% if error %}<div class="error">{{ error }}</div>{% endif %}
{% url admin.views.newalias domain as box_url %}
{% url admin.views.newmbalias as box_url %}
<form action="{{ box_url }}" method="POST" id="aliasform" name="aliasform">
{% include "common/genericform.html" %}
<div class="spacer"></div>
+4 -2
View File
@@ -1,9 +1,11 @@
{% load i18n %}
{% for entry in entries %}
{% if entry.menu %}
<li class="dropdown {{ entry.class }}">
<li class="dropdown {{ entry.class }}{% if selection == entry.name %} selected{% endif %}">
<img src="{{ entry.img }}" border="0" />
{% if entry.url %}<a href="{{ entry.url }}">{% endif %}
<label>{{ entry.label }}</label>
{% if entry.url %}</a>{% endif %}
<ul class="links"
{% if entry.width %}style="width: {{ entry.width }}px"{% endif %}>
{% for sentry in entry.menu %}
@@ -20,7 +22,7 @@
</ul>
</li>
{% else %}
<li {% ifequal selection entry.name %}class="selected"{% endifequal %}>
<li {% if selection == entry.name %}class="selected"{% endif %}>
<a href="{{ entry.url }}" name="{{ entry.name }}"
{% if entry.class %}class={{ entry.class }}{% endif %}
{% if entry.rel %}rel={{ entry.rel }}{% endif %}
+1 -1
View File
@@ -1,6 +1,6 @@
from django import forms
from modoboa.admin.models import Mailbox
from modoboa.lib.authbackends import _check_password
from modoboa.lib import _check_password
from django.contrib.auth.models import check_password
from django.utils.translation import ugettext as _
+1 -1
View File
@@ -10,7 +10,7 @@ from django.contrib.auth.decorators \
from django.utils import simplejson
from django.utils.translation import ugettext as _
from modoboa.lib import _render, _ctx_ok, _ctx_ko, getctx, parameters
from modoboa.lib.authbackends import crypt_password
from modoboa.lib import crypt_password
from forms import ChangePasswordForm
from modoboa.admin.models import Mailbox