mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
wip: resellers and limits
I've finally started to rethink the database structure. The goal I'm trying to reach: only use User objects to manage permissions and accesses to the webui and associate other objects to them if needed (like mailboxes). see #105
This commit is contained in:
+2
-2
@@ -51,13 +51,13 @@ parameters.register_admin("DEFAULT_TOP_REDIRECTION", type="list", deflt="admin",
|
||||
values=enabled_applications(),
|
||||
help=_("The default redirection used when no application is specified"))
|
||||
|
||||
def unset_default_topredirection(**kwargs):
|
||||
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")
|
||||
if topredirection == kwargs["ext"].name:
|
||||
if topredirection == extension.name:
|
||||
parameters.save_admin("DEFAULT_TOP_REDIRECTION", "userprefs", app="general")
|
||||
|
||||
def update_available_applications(extension):
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_domain", "admin", "domain"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}}]
|
||||
[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_domain", "admin", "domain"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}},{"pk": 2, "model": "auth.group", "fields": {"name": "SimpleUsers", "permissions": []}}]
|
||||
|
||||
+112
-48
@@ -7,6 +7,7 @@ from django.contrib.auth.models import User
|
||||
from modoboa.admin.templatetags.admin_extras import gender
|
||||
from modoboa.lib import tables
|
||||
from modoboa.lib.emailutils import split_mailbox
|
||||
from lib import get_user_domains
|
||||
|
||||
class DomainForm(forms.ModelForm):
|
||||
class Meta:
|
||||
@@ -32,10 +33,10 @@ class ProxyForm(forms.ModelForm):
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(ProxyForm, self).__init__(*args, **kwargs)
|
||||
|
||||
if user.is_superuser:
|
||||
self._domain = None
|
||||
if not user.is_superuser:
|
||||
self._domains = Domain.objects.filter(owners__user=user)
|
||||
else:
|
||||
self._domain = user.mailbox_set.all()[0].domain
|
||||
self._domains = None
|
||||
|
||||
class DomainAliasForm(ProxyForm):
|
||||
class Meta:
|
||||
@@ -44,8 +45,9 @@ class DomainAliasForm(ProxyForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DomainAliasForm, self).__init__(*args, **kwargs)
|
||||
if self._domain is not None:
|
||||
self.fields["target"].queryset = Domain.objects.filter(pk=self._domain.id)
|
||||
if self._domains is not None:
|
||||
#self.fields["target"].queryset = Domain.objects.filter(pk=self._domain.id)
|
||||
self.fields["target"].queryset = self._domains
|
||||
|
||||
class MailboxForm(ProxyForm):
|
||||
quota = forms.IntegerField(label=ugettext_noop("Quota"), required=False,
|
||||
@@ -68,9 +70,10 @@ class MailboxForm(ProxyForm):
|
||||
super(MailboxForm, self).__init__(*args, **kwargs)
|
||||
for f in ['name', 'address', 'quota', 'password1', 'password2']:
|
||||
self.fields[f].widget.attrs['size'] = 14
|
||||
if self._domain is not None:
|
||||
self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id)
|
||||
self.fields["domain"].initial = self._domain
|
||||
if self._domains is not None:
|
||||
self.fields["domain"].queryset = self._domains
|
||||
#self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id)
|
||||
#self.fields["domain"].initial = self._domain
|
||||
|
||||
if kwargs.has_key("instance"):
|
||||
self.fields['quota'].initial = kwargs["instance"].quota
|
||||
@@ -111,22 +114,26 @@ class AliasForm(ProxyForm):
|
||||
# member of Alias, we need to fill it manually. if
|
||||
# self._domain exists, it means a DomainAdmin is going to
|
||||
# create a new alias, we know how to fill the fields.
|
||||
if self._domain is not None:
|
||||
self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id)
|
||||
# if self._domain is not None:
|
||||
# self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id)
|
||||
|
||||
if self._domains is not None:
|
||||
self.fields["domain"].queryset = self._domains
|
||||
|
||||
# if 'instance' exists, it means we are modifying an existing
|
||||
# record, we need to select the right value for 'domain' and
|
||||
# to fill the first "targets" input (because it is created by the
|
||||
# Form object, not by us).
|
||||
domain = None
|
||||
if self._domain:
|
||||
domain = self._domain
|
||||
#domain = None
|
||||
# if self._domain:
|
||||
# domain = self._domain
|
||||
if kwargs.has_key("instance"):
|
||||
if self._domain is None:
|
||||
domain = kwargs["instance"].domain
|
||||
# if self._domain is None:
|
||||
# domain = kwargs["instance"].domain
|
||||
self.fields["targets"].initial = kwargs["instance"].first_target()
|
||||
if domain is not None:
|
||||
self.fields["domain"].initial = domain
|
||||
|
||||
# if domain is not None:
|
||||
# self.fields["domain"].initial = domain
|
||||
|
||||
self.fields.keyOrder = ['address', 'domain', 'enabled', 'targets']
|
||||
|
||||
@@ -140,33 +147,37 @@ class AliasForm(ProxyForm):
|
||||
return self.cleaned_data["address"]
|
||||
|
||||
def set_targets(self, user, values):
|
||||
"""Targets dispatching
|
||||
|
||||
We make a difference between 'local' targets (the ones hosted
|
||||
by Modoboa) and 'external targets.
|
||||
"""
|
||||
from modoboa.admin.lib import is_object_owner
|
||||
|
||||
self.ext_targets = []
|
||||
self.int_targets = []
|
||||
if user.id != 1:
|
||||
umb = Mailbox.objects.get(user=user)
|
||||
|
||||
for addr in values:
|
||||
if addr == "":
|
||||
continue
|
||||
local_part, domain = split_mailbox(addr)
|
||||
if domain is None:
|
||||
local_part, domname = split_mailbox(addr)
|
||||
if domname is None:
|
||||
raise AdminError("%s %s" % (_("Invalid mailbox"), addr))
|
||||
if is_domain_admin(user) and umb.domain.name != domain:
|
||||
try:
|
||||
d = Domain.objects.get(name=domain)
|
||||
except Domain.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
raise AdminError("%s %s" % (_("Access denied for"), addr))
|
||||
try:
|
||||
mb = Mailbox.objects.get(address=local_part, domain__name=domain)
|
||||
except Mailbox.DoesNotExist:
|
||||
self.ext_targets += [addr]
|
||||
else:
|
||||
if not user.is_superuser:
|
||||
usermb = Mailbox.objects.get(user=user.id)
|
||||
if usermb.domain.id != mb.domain.id:
|
||||
raise AdminError("%s %s" % (_("Permission denied on"), addr))
|
||||
domain = Domain.objects.get(name=domname)
|
||||
except Domain.DoesNotExist:
|
||||
domain = None
|
||||
if domain:
|
||||
if not is_object_owner(user, domain):
|
||||
raise PermDeniedException(addr)
|
||||
try:
|
||||
mb = Mailbox.objects.get(domain=domain, address=local_part)
|
||||
except Mailbox.DoesNotExist:
|
||||
raise AdminError(_("Mailbox %s does not exist" % addr))
|
||||
self.int_targets += [mb]
|
||||
continue
|
||||
|
||||
self.ext_targets += [addr]
|
||||
|
||||
def save(self, force_insert=False, force_update=False, commit=True):
|
||||
a = super(AliasForm, self).save(commit=False)
|
||||
@@ -186,19 +197,72 @@ class SuperAdminForm(forms.Form):
|
||||
self.fields["user"].queryset = User.objects.exclude(pk__in=[1, user.id])
|
||||
|
||||
class DomainAdminForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DomainAdminForm, self).__init__(*args, **kwargs)
|
||||
createfrom = forms.ChoiceField(
|
||||
choices=[("new", ugettext_noop("Create a new account")),
|
||||
("select", ugettext_noop("Promote an existing account"))],
|
||||
widget=forms.RadioSelect)
|
||||
|
||||
self.fields["domain"] = \
|
||||
forms.ModelChoiceField(queryset=Domain.objects.all(),
|
||||
label=_("Domain"),
|
||||
required=True,
|
||||
empty_label=_("Select a domain"),
|
||||
help_text=_("Select a domain in the list"))
|
||||
self.fields["user"] = \
|
||||
forms.ChoiceField(label=_("User"), required=True,
|
||||
choices=[("", _("Empty"))],
|
||||
help_text=_("Select a user in the list"))
|
||||
domain = forms.ModelChoiceField(
|
||||
queryset=Domain.objects.all(),
|
||||
label=_("Domain"),
|
||||
required=True,
|
||||
empty_label=ugettext_noop("Select a domain"),
|
||||
help_text=ugettext_noop("Select a domain in the list")
|
||||
)
|
||||
|
||||
user = forms.ChoiceField(
|
||||
label=ugettext_noop("User"), required=True,
|
||||
choices=[("", ugettext_noop("Empty"))],
|
||||
help_text=ugettext_noop("Select a user in the list"))
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
createmb = forms.BooleanField(
|
||||
label=ugettext_noop("Create mailbox"), required=False,
|
||||
help_text=ugettext_noop("Create a mailbox for this user using the given address")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "first_name", "last_name", "email")
|
||||
|
||||
def save(self, commit=True, group=None):
|
||||
user = super(UserForm, self).save(commit=False)
|
||||
if self.cleaned_data.has_key("password1"):
|
||||
user.set_password(self.cleaned_data["password1"])
|
||||
if commit:
|
||||
user.save()
|
||||
if group:
|
||||
user.groups.add(Group.objects.get(name=group))
|
||||
user.save()
|
||||
return user
|
||||
|
||||
class UserWithPasswordForm(UserForm):
|
||||
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
|
||||
password2 = forms.CharField(
|
||||
label=ugettext_noop("Confirmation"),
|
||||
widget=forms.PasswordInput,
|
||||
help_text=ugettext_noop("Enter the same password as above, for verification.")
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
class DomainAdminPromotionForm(forms.Form):
|
||||
name = forms.CharField(
|
||||
label=ugettext_noop("Name"),
|
||||
help_text=ugettext_noop("The username/email address/name of the account to promote")
|
||||
)
|
||||
|
||||
def clean_name(self):
|
||||
try:
|
||||
u = User.objects.get(username=self.cleaned_data["name"])
|
||||
except User.DoesNotExist:
|
||||
raise forms.ValidationError(_("Unknown user"))
|
||||
return self.cleaned_data["name"]
|
||||
|
||||
class ImportDataForm(forms.Form):
|
||||
sourcefile = forms.FileField(label=_("Select a file"))
|
||||
|
||||
+112
@@ -1,12 +1,21 @@
|
||||
# coding: utf-8
|
||||
from functools import wraps
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from models import *
|
||||
from modoboa.lib.exceptions import ModoboaException
|
||||
from modoboa.lib.webutils import _render_error, _render
|
||||
from modoboa.admin.tables import *
|
||||
|
||||
class AdminError(ModoboaException):
|
||||
"""
|
||||
Just a Custom exception to identify errors coming from the admin
|
||||
application.
|
||||
"""
|
||||
pass
|
||||
|
||||
def is_not_localadmin(errortpl="error"):
|
||||
def dec(f):
|
||||
def wrapped_f(request, *args, **kwargs):
|
||||
@@ -46,6 +55,7 @@ def good_domain(f):
|
||||
def dec(request, **kwargs):
|
||||
if request.user.is_superuser:
|
||||
return f(request, **kwargs)
|
||||
|
||||
if is_domain_admin(request.user):
|
||||
mb = Mailbox.objects.get(user=request.user.id)
|
||||
access = True
|
||||
@@ -67,6 +77,18 @@ def good_domain(f):
|
||||
return HttpResponseRedirect("%s?next=%s" % (login_url, path))
|
||||
return dec
|
||||
|
||||
# def has_access_to_domain(user, domain):
|
||||
# """
|
||||
# """
|
||||
# access = events.raiseQueryEvent("CanViewDomain", user, domain)
|
||||
# if True in access:
|
||||
# return True
|
||||
# if is_domain_admin(user):
|
||||
# mb = Mailbox.objects.get(user=request.user.id)
|
||||
# if mb.domain == domain:
|
||||
# return True
|
||||
# return False
|
||||
|
||||
def render_listing(request, objtype, tplname="admin/listing.html",
|
||||
**kwargs):
|
||||
"""Common function to render a listing
|
||||
@@ -103,3 +125,93 @@ def render_listing(request, objtype, tplname="admin/listing.html",
|
||||
kwargs["table"] = tblclass(request, kwargs["objects"].object_list)
|
||||
|
||||
return _render(request, tplname, kwargs)
|
||||
|
||||
def set_object_ownership(user, obj):
|
||||
"""Simple shorcut to associate an object to a user
|
||||
|
||||
:param user: a ``User`` object
|
||||
:param obj: an admin. object (Domain, Mailbox, ...)
|
||||
"""
|
||||
obj_owner = ObjectOwner(user=user, content_object=obj)
|
||||
obj_owner.save()
|
||||
|
||||
def is_object_owner(user, obj):
|
||||
"""Check if a user is the object's owner
|
||||
|
||||
An object can have multiple owners but this function only
|
||||
concerns one given user.
|
||||
|
||||
:param user: a ``User`` object
|
||||
:param obj: a admin object
|
||||
:return: a boolean
|
||||
"""
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
qs = user.objectowner_set.filter(content_type__app_label="admin",
|
||||
content_type__model=obj._meta.module_name)
|
||||
for entry in qs.all():
|
||||
if entry.content_object == obj:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_domain_ownership(f):
|
||||
@wraps(f)
|
||||
def dec(request, **kwargs):
|
||||
if not request.user.is_superuser:
|
||||
domain = None
|
||||
domid = request.GET.get("domid", None)
|
||||
if domid:
|
||||
domain = Domain.objects.get(pk=domid)
|
||||
else:
|
||||
mbid = request.GET.get("mbid", None)
|
||||
if mbid:
|
||||
domain = Mailbox.objects.get(pk=mbid).domain
|
||||
if domain and not is_object_owner(request.user, domain):
|
||||
raise PermDeniedError(_("You can't access this domain"))
|
||||
|
||||
return f(request, **kwargs)
|
||||
return dec
|
||||
|
||||
def get_user_domains(user):
|
||||
if user.is_superuser:
|
||||
return Domain.objects.all()
|
||||
|
||||
qs = user.objectowner_set.filter(content_type__app_label="admin",
|
||||
content_type__model="domain")
|
||||
domains = []
|
||||
for entry in qs.all():
|
||||
domains += [entry.content_object]
|
||||
return domains
|
||||
|
||||
def get_user_domaliases(user):
|
||||
if user.is_superuser:
|
||||
return DomainAlias.objects.all()
|
||||
qs = user.objectowner_set.filter(content_type__app_label="admin",
|
||||
content_type__model="domain")
|
||||
domaliases = []
|
||||
for entry in qs.all():
|
||||
domaliases += entry.content_object.domainalias_set.all()
|
||||
return domaliases
|
||||
|
||||
def get_user_mailboxes(user):
|
||||
if user.is_superuser:
|
||||
return Mailbox.objects.all()
|
||||
|
||||
qs = user.objectowner_set.filter(content_type__app_label="admin",
|
||||
content_type__model="domain")
|
||||
mboxes = []
|
||||
for ro in qs.all():
|
||||
mboxes += Mailbox.objects.filter(domain=ro.content_object)
|
||||
return mboxes
|
||||
|
||||
def get_user_mbaliases(user):
|
||||
if user.is_superuser:
|
||||
return Alias.objects.all()
|
||||
|
||||
qs = user.objectowner_set.filter(content_type__app_label="admin",
|
||||
content_type__model="domain")
|
||||
mbaliases = []
|
||||
for ro in qs.all():
|
||||
mbaliases += Alias.objects.filter(domain=ro.content_object)
|
||||
return mbaliases
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# 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):
|
||||
|
||||
# Adding model 'ObjectOwner'
|
||||
db.create_table('admin_objectowner', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
|
||||
('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
|
||||
('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
|
||||
))
|
||||
db.send_create_signal('admin', ['ObjectOwner'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Deleting model 'ObjectOwner'
|
||||
db.delete_table('admin_objectowner')
|
||||
|
||||
|
||||
models = {
|
||||
'admin.alias': {
|
||||
'Meta': {'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': {'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']"}),
|
||||
'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']"})
|
||||
},
|
||||
'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'})
|
||||
},
|
||||
'admin.objectowner': {
|
||||
'Meta': {'object_name': 'ObjectOwner'},
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
|
||||
'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']
|
||||
+23
-18
@@ -1,31 +1,28 @@
|
||||
# coding: utf-8
|
||||
from django.db import models, IntegrityError
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.utils.translation import ugettext as _, ugettext_noop
|
||||
from django.conf import settings
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib.sysutils import exec_cmd, exec_as_vuser
|
||||
from modoboa.lib.emailutils import split_mailbox
|
||||
from modoboa.lib.ldaputils import *
|
||||
from modoboa.auth.lib import crypt_password
|
||||
import os
|
||||
import pwd
|
||||
|
||||
class AdminError(Exception):
|
||||
"""Custom exception
|
||||
try:
|
||||
from modoboa.lib.ldaputils import *
|
||||
ldap_available = True
|
||||
except ImportError:
|
||||
ldap_available = False
|
||||
|
||||
|
||||
"""
|
||||
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 ObjectOwner(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = generic.GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
class ObjectDates(models.Model):
|
||||
"""Dates recording for admin objects
|
||||
@@ -80,6 +77,7 @@ class Domain(DatesAware):
|
||||
quota = models.IntegerField(help_text=ugettext_noop("Default quota in MB applied to mailboxes"))
|
||||
enabled = models.BooleanField(ugettext_noop('enabled'),
|
||||
help_text=ugettext_noop("Check to activate this domain"))
|
||||
owners = generic.GenericRelation(ObjectOwner)
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
@@ -265,6 +263,10 @@ class Mailbox(DatesAware):
|
||||
raise AdminError(_("Mailbox with this address already exists"))
|
||||
self.user = user
|
||||
|
||||
if len(user.groups.all()) == 0:
|
||||
user.groups.add(Group.objects.get(name="SimpleUsers"))
|
||||
user.save()
|
||||
|
||||
if kwargs.has_key("password") and kwargs["password"] != u"é":
|
||||
self.password = crypt_password(kwargs["password"])
|
||||
# If we ever had numerical uid, don't resolve it
|
||||
@@ -305,6 +307,8 @@ class Mailbox(DatesAware):
|
||||
self.password = crypt_password(newvalue)
|
||||
self.save()
|
||||
return
|
||||
if not ldap_available:
|
||||
raise AdminError(_("Failed to update password: LDAP module not installed"))
|
||||
|
||||
ab = LDAPAuthBackend()
|
||||
try:
|
||||
@@ -456,11 +460,12 @@ class Alias(DatesAware):
|
||||
return "<br/>".join(self.extmboxes.split(","))
|
||||
|
||||
def ui_disabled(self, user):
|
||||
from modoboa.admin.lib import is_object_owner
|
||||
|
||||
if user.is_superuser:
|
||||
return False
|
||||
usermb = Mailbox.objects.get(user=user.id)
|
||||
for mb in self.mboxes.all():
|
||||
if mb.domain.id != usermb.domain.id:
|
||||
if not is_object_owner(user, mb.domain):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
+35
-38
@@ -7,11 +7,15 @@ from modoboa.lib.webutils import _render, getctx, _render_to_string
|
||||
from modoboa.lib import events
|
||||
from forms import *
|
||||
from tables import SuperAdminsTable, DomainAdminsTable
|
||||
from lib import set_object_ownership
|
||||
|
||||
class Permissions(object):
|
||||
submit_label = ugettext_noop("Add")
|
||||
formtpl = 'admin/add_permission.html'
|
||||
|
||||
@staticmethod
|
||||
def is_authorized(user):
|
||||
return True
|
||||
return user.is_superuser
|
||||
|
||||
def get_add_form(self, request):
|
||||
raise NotImplementedError
|
||||
@@ -25,21 +29,20 @@ class Permissions(object):
|
||||
def get(self, request):
|
||||
raise NotImplementedError
|
||||
|
||||
def _render_form(self, request, form, instring=False):
|
||||
ctx = dict(form=form, title=self.title, submit_label=self.submit_label,
|
||||
role=self.role)
|
||||
if instring:
|
||||
return _render_to_string(request, self.formtpl, ctx)
|
||||
return _render(request, self.formtpl, ctx)
|
||||
|
||||
class SuperAdminsPerms(Permissions):
|
||||
title = ugettext_noop("Add super administrator")
|
||||
role = "super_admins"
|
||||
|
||||
@staticmethod
|
||||
def is_authorized(user):
|
||||
return user.is_superuser
|
||||
|
||||
def get_add_form(self, request):
|
||||
form = SuperAdminForm(request.user)
|
||||
return _render(request, 'admin/add_permission.html', {
|
||||
"form" : form,
|
||||
"title" : _(self.title),
|
||||
"role": self.role
|
||||
})
|
||||
return self._render_form(request, form)
|
||||
|
||||
def add(self, request):
|
||||
form = SuperAdminForm(request.user, request.POST)
|
||||
@@ -51,11 +54,7 @@ class SuperAdminsPerms(Permissions):
|
||||
user.save()
|
||||
return True, None
|
||||
|
||||
content = _render_to_string(request, "admin/add_permission.html", {
|
||||
"form" : form,
|
||||
"title" : _(self.title),
|
||||
"role" : self.role
|
||||
})
|
||||
content = self._render_form(request, form, True)
|
||||
return False, getctx("ko", content=content)
|
||||
|
||||
def delete(self, selection):
|
||||
@@ -81,22 +80,19 @@ class SuperAdminsPerms(Permissions):
|
||||
class DomainAdminsPerms(Permissions):
|
||||
title = ugettext_noop("Add domain administrator")
|
||||
role = "domain_admins"
|
||||
|
||||
formtpl = 'admin/add_domain_admin.html'
|
||||
|
||||
@staticmethod
|
||||
def is_authorized(user):
|
||||
return user.has_perm("admin.view_domains")
|
||||
|
||||
def get_add_form(self, request):
|
||||
form = DomainAdminForm()
|
||||
return _render(request, 'admin/add_domain_admin.html', {
|
||||
"form" : form,
|
||||
"title" : _(self.title),
|
||||
"role" : self.role
|
||||
})
|
||||
return self._render_form(request, form)
|
||||
|
||||
def add(self, request):
|
||||
form = DomainAdminForm(request.POST)
|
||||
if request.POST.has_key("user") and request.POST["user"] != "":
|
||||
if request.POST.get("user", "") != "":
|
||||
mboxid = request.POST['user']
|
||||
mb = Mailbox.objects.get(pk=mboxid)
|
||||
form.fields["user"].choices = [(mboxid, mb),]
|
||||
@@ -105,27 +101,28 @@ class DomainAdminsPerms(Permissions):
|
||||
mb.user.date_joined = datetime.datetime.now()
|
||||
mb.user.groups.add(Group.objects.get(name="DomainAdmins"))
|
||||
mb.user.save()
|
||||
set_object_ownership(mb.user, mb.domain)
|
||||
return True, None
|
||||
content = _render_to_string(request, "admin/add_domain_admin.html", {
|
||||
"form" : form,
|
||||
"title" : _(self.title),
|
||||
"role" : self.role
|
||||
})
|
||||
content = self._render_form(request, form, True)
|
||||
return False, getctx("ko", content=content)
|
||||
|
||||
def delete(self, selection):
|
||||
for s in selection:
|
||||
mbox = Mailbox.objects.get(pk=int(s))
|
||||
grp = Group.objects.get(name="DomainAdmins")
|
||||
mbox.user.groups.remove(grp)
|
||||
mbox.user.save()
|
||||
User.objects.filter(id__in=selection).delete()
|
||||
# for s in selection:
|
||||
# user.delete()
|
||||
# try:
|
||||
# mbox = Mailbox.objects.get(pk=int(s))
|
||||
# grp = Group.objects.get(name="DomainAdmins")
|
||||
# mbox.user.groups.remove(grp)
|
||||
# mbox.user.save()
|
||||
|
||||
def get(self, request):
|
||||
domadmins = Mailbox.objects.filter(user__groups__name="DomainAdmins")
|
||||
domadmins_list = []
|
||||
for admin in domadmins:
|
||||
domadmins_list += [admin.tohash()]
|
||||
return DomainAdminsTable(request, domadmins_list).render()
|
||||
#domadmins = Mailbox.objects.filter(user__groups__name="DomainAdmins")
|
||||
domainadmins = User.objects.filter(groups__name="DomainAdmins")
|
||||
# domadmins_list = []
|
||||
# for admin in domadmins:
|
||||
# domadmins_list += [admin.tohash()]
|
||||
return DomainAdminsTable(request, domainadmins).render()
|
||||
|
||||
def get_perms_class(user, role):
|
||||
"""Retrieves the class associated to a given role
|
||||
@@ -150,7 +147,7 @@ def get_perms_class(user, role):
|
||||
if globals()[cname].is_authorized(user):
|
||||
return globals()[cname]
|
||||
return None
|
||||
res = events.raiseQueryEvent("PermsGetClass", role=role)
|
||||
res = events.raiseQueryEvent("PermsGetClass", role)
|
||||
if not len(res):
|
||||
return None
|
||||
return res[0]
|
||||
|
||||
+16
-5
@@ -142,11 +142,22 @@ class SuperAdminsTable(tables.Table):
|
||||
|
||||
class DomainAdminsTable(tables.Table):
|
||||
idkey = "id"
|
||||
selection = tables.SelectionColumn("selection", width="4%", first=True)
|
||||
domain = tables.Column("domain", label=ugettext_noop("Domain"))
|
||||
full_name = tables.Column("full_name", label=ugettext_noop("Full name"))
|
||||
selection = tables.SelectionColumn("selection", width="4%")
|
||||
username = tables.Column("username", label=ugettext_noop("User name"))
|
||||
first_name = tables.Column("first_name", label=ugettext_noop("First name"))
|
||||
last_name = tables.Column("last_name", label=ugettext_noop("Last name"))
|
||||
date_joined = tables.Column("date_joined", label=ugettext_noop("Defined"))
|
||||
enabled = tables.Column("enabled", label=gender("Enabled", "m"), width="10%")
|
||||
enabled = tables.Column("is_active", label=gender("Enabled", "m"), width="10%")
|
||||
actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"),
|
||||
width="50px", align="center",
|
||||
defvalue=domain_admin_actions)
|
||||
|
||||
cols_order = ["selection", "domain", "full_name", "date_joined", "enabled"]
|
||||
cols_order = ["selection", "username", "first_name",
|
||||
"last_name", "date_joined", "enabled", "actions"]
|
||||
|
||||
def __init__(self, request, users):
|
||||
super(DomainAdminsTable, self).__init__(request)
|
||||
self.populate(self._rows_from_model(users))
|
||||
|
||||
def parse_is_active(self, value):
|
||||
return _("yes") if value else _("no")
|
||||
|
||||
@@ -199,6 +199,18 @@ def mailbox_actions(user, mboxid):
|
||||
]
|
||||
return render_actions(actions)
|
||||
|
||||
@register.simple_tag
|
||||
def domain_admin_actions(user, daid):
|
||||
actions = [
|
||||
{"name" : "editdomainadmin",
|
||||
"url" : reverse(admin.views.edit_domain_admin, args=[daid]),
|
||||
"img" : static_url("pics/edit.png"),
|
||||
"title" : _("Edit domain admin"),
|
||||
"class" : "boxed",
|
||||
"rel" : "{handler:'iframe',size:{x:350,y:320}}"},
|
||||
]
|
||||
return render_actions(actions)
|
||||
|
||||
@register.simple_tag
|
||||
def mbalias_actions(user, aliasid):
|
||||
from modoboa.admin.models import Alias
|
||||
@@ -274,3 +286,8 @@ def gender(value, target):
|
||||
if trans.find("_") == -1:
|
||||
return trans
|
||||
return value
|
||||
|
||||
@register.simple_tag
|
||||
def get_footer_content(user, objtype):
|
||||
res = events.raiseQueryEvent("AdminFooterDisplay", user, objtype)
|
||||
return "".join(res)
|
||||
|
||||
+41
-36
@@ -1,38 +1,43 @@
|
||||
from django.conf.urls.defaults import *
|
||||
|
||||
urlpatterns = patterns('modoboa.admin.views',
|
||||
url(r'^$', 'domains', name="index"),
|
||||
url(r'^domains/$', 'domains', name="domains"),
|
||||
(r'^domains/new/', 'newdomain'),
|
||||
(r'^domains/(?P<dom_id>\d+)/edit/$', 'editdomain'),
|
||||
(r'^domains/delete/$', 'deldomain'),
|
||||
(r'^domains/import/$', 'importdata'),
|
||||
|
||||
(r'^domaliases/$', "domaliases"),
|
||||
(r'^domaliases/new/', 'newdomalias'),
|
||||
(r'^domaliases/edit/(?P<alias_id>\d+)/', 'editdomalias'),
|
||||
(r'^domaliases/delete/$', 'deldomalias'),
|
||||
|
||||
(r'^mailboxes/$', 'mailboxes'),
|
||||
(r'^mailboxes/new/$', 'newmailbox'),
|
||||
(r'^mailboxes/edit/(?P<mbox_id>\d+)/$', 'editmailbox'),
|
||||
(r'^mailboxes/delete/$', 'delmailbox'),
|
||||
(r'^mailboxes/search/$', 'mailboxes_search'),
|
||||
|
||||
(r'^mbaliases/$', 'mbaliases'),
|
||||
(r'^mbaliases/new/$', 'newmbalias'),
|
||||
(r'^mbaliases/edit/(?P<alias_id>\d+)/$', 'editmbalias'),
|
||||
(r'^mbaliases/delete/$', 'delmbalias'),
|
||||
|
||||
(r'^domains/(?P<dom_id>\d+)/rawmboxes/$', 'mailboxes_raw'),
|
||||
|
||||
(r'^settings/$', 'viewparameters'),
|
||||
(r'^settings/parameters/$', 'viewparameters'),
|
||||
(r'^settings/parameters/save/$', 'saveparameters'),
|
||||
(r'^settings/extensions/$', 'viewextensions'),
|
||||
(r'^settings/extensions/save/$', 'saveextensions'),
|
||||
|
||||
(r'^permissions/$', 'permissions'),
|
||||
(r'^permissions/add/$', 'add_permission'),
|
||||
(r'^permissions/delete/$', 'delete_permissions'),
|
||||
)
|
||||
urlpatterns = patterns(
|
||||
'modoboa.admin.views',
|
||||
url(r'^$', 'domains', name="index"),
|
||||
url(r'^domains/$', 'domains', name="domains"),
|
||||
(r'^domains/new/', 'newdomain'),
|
||||
(r'^domains/(?P<dom_id>\d+)/edit/$', 'editdomain'),
|
||||
(r'^domains/delete/$', 'deldomain'),
|
||||
(r'^domains/import/$', 'importdata'),
|
||||
|
||||
(r'^domaliases/$', "domaliases"),
|
||||
(r'^domaliases/new/', 'newdomalias'),
|
||||
(r'^domaliases/edit/(?P<alias_id>\d+)/', 'editdomalias'),
|
||||
(r'^domaliases/delete/$', 'deldomalias'),
|
||||
|
||||
(r'^mailboxes/$', 'mailboxes'),
|
||||
(r'^mailboxes/new/$', 'newmailbox'),
|
||||
(r'^mailboxes/edit/(?P<mbox_id>\d+)/$', 'editmailbox'),
|
||||
(r'^mailboxes/delete/$', 'delmailbox'),
|
||||
(r'^mailboxes/search/$', 'mailboxes_search'),
|
||||
|
||||
(r'^mbaliases/$', 'mbaliases'),
|
||||
(r'^mbaliases/new/$', 'newmbalias'),
|
||||
(r'^mbaliases/edit/(?P<alias_id>\d+)/$', 'editmbalias'),
|
||||
(r'^mbaliases/delete/$', 'delmbalias'),
|
||||
|
||||
(r'^domains/(?P<dom_id>\d+)/rawmboxes/$', 'mailboxes_raw'),
|
||||
|
||||
(r'^settings/$', 'viewparameters'),
|
||||
(r'^settings/parameters/$', 'viewparameters'),
|
||||
(r'^settings/parameters/save/$', 'saveparameters'),
|
||||
(r'^settings/extensions/$', 'viewextensions'),
|
||||
(r'^settings/extensions/save/$', 'saveextensions'),
|
||||
|
||||
(r'^permissions/$', 'permissions'),
|
||||
(r'^permissions/add/$', 'add_permission'),
|
||||
(r'^permissions/domainadmin/new/$', 'create_domain_admin'),
|
||||
(r'^permissions/domainadmin/edit/(?P<da_id>\d+)/$', 'edit_domain_admin'),
|
||||
(r'^permissions/domainadmin/promote/$', 'domain_admin_promotion'),
|
||||
(r'^permissions/searchaccount/$', 'search_account'),
|
||||
(r'^permissions/delete/$', 'delete_permissions'),
|
||||
)
|
||||
|
||||
+182
-72
@@ -16,6 +16,7 @@ from models import *
|
||||
from modoboa.admin.permissions import *
|
||||
from modoboa.admin.tables import *
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib.exceptions import *
|
||||
from modoboa.auth.lib import crypt_password
|
||||
from modoboa.lib.webutils \
|
||||
import _render, ajax_response, ajax_simple_response, getctx
|
||||
@@ -31,10 +32,7 @@ def domains(request):
|
||||
|
||||
return HttpResponseRedirect(reverse(userprefs.views.preferences))
|
||||
|
||||
if request.user.is_superuser:
|
||||
domains = Domain.objects.all()
|
||||
else:
|
||||
domains = events.raiseQueryEvent("GetUserDomains", request.user)
|
||||
domains = get_user_domains(request.user)
|
||||
for dom in domains:
|
||||
dom.mbalias_counter = len(Alias.objects.filter(domain=dom.id))
|
||||
deloptions = {"keepdir" : _("Do not delete domain directory")}
|
||||
@@ -53,7 +51,6 @@ def _validate_domain(request, form, successmsg, commonctx,
|
||||
"""
|
||||
error = None
|
||||
if form.is_valid():
|
||||
events.raiseEvent("CanCreateDomain", request.user)
|
||||
try:
|
||||
domain = form.save()
|
||||
except AdminError, e:
|
||||
@@ -74,7 +71,9 @@ def _validate_domain(request, form, successmsg, commonctx,
|
||||
@permission_required("admin.add_domain")
|
||||
@transaction.commit_on_success
|
||||
def newdomain(request, tplname="admin/adminform.html"):
|
||||
events.raiseEvent("CanCreateDomain", request.user)
|
||||
def newdomain_cb(user, domain):
|
||||
set_object_ownership(user, domain)
|
||||
events.raiseEvent("CreateDomain", user, domain)
|
||||
|
||||
commonctx = {"title" : _("New domain"),
|
||||
@@ -94,6 +93,9 @@ def newdomain(request, tplname="admin/adminform.html"):
|
||||
@permission_required("admin.change_domain")
|
||||
def editdomain(request, dom_id, tplname="admin/adminform.html"):
|
||||
domain = Domain.objects.get(pk=dom_id)
|
||||
if not is_object_owner(request.user, domain):
|
||||
raise PermDeniedException(_("You can't edit this domain"))
|
||||
|
||||
commonctx = {"title" : _("Domain editing"),
|
||||
"submit_label" : _("Update"),
|
||||
"action" : reverse(editdomain, args=[dom_id]),
|
||||
@@ -108,47 +110,47 @@ def editdomain(request, dom_id, tplname="admin/adminform.html"):
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.delete_domain")
|
||||
@transaction.commit_on_success
|
||||
def deldomain(request):
|
||||
selection = request.GET["selection"].split(",")
|
||||
error = None
|
||||
keepdir = True if request.GET.get("keepdir", "false") == "true" else False
|
||||
try:
|
||||
mb = Mailbox.objects.get(user__id=request.user.id)
|
||||
except Mailbox.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
if str(mb.domain.id) in selection:
|
||||
error = _("You can't delete your own domain")
|
||||
mb = None
|
||||
|
||||
if error is None:
|
||||
if request.GET.has_key("keepdir") and request.GET["keepdir"] == "true":
|
||||
keepdir = True
|
||||
else:
|
||||
keepdir = False
|
||||
for dom in Domain.objects.filter(id__in=selection):
|
||||
events.raiseEvent("DeleteDomain", dom)
|
||||
dom.delete(keepdir=keepdir)
|
||||
msg = ungettext("Domain deleted", "Domains deleted", len(selection))
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request)
|
||||
return ajax_response(request, status="ko", respmsg=error)
|
||||
for dom in Domain.objects.filter(id__in=selection):
|
||||
if not is_object_owner(request.user, dom):
|
||||
raise PermDeniedException(_("You can't delete this domain"))
|
||||
if mb and mb.domain == dom:
|
||||
raise AdminError(_("You can't delete your own domain"))
|
||||
|
||||
events.raiseEvent("DeleteDomain", request.user, dom)
|
||||
dom.delete(keepdir=keepdir)
|
||||
|
||||
msg = ungettext("Domain deleted", "Domains deleted", len(selection))
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request)
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.view_domaliases")
|
||||
@check_domain_ownership
|
||||
def domaliases(request):
|
||||
if request.GET.has_key("domid"):
|
||||
domain = Domain.objects.get(pk=request.GET["domid"])
|
||||
domaliases = DomainAlias.objects.filter(target=request.GET["domid"])
|
||||
else:
|
||||
domain = None
|
||||
domaliases = DomainAlias.objects.all()
|
||||
domaliases = get_user_domaliases(request.user)
|
||||
return render_listing(request, "domaliases",
|
||||
title=_("Domain aliases"),
|
||||
rel="310 190",
|
||||
objects=domaliases)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.add_domainalias")
|
||||
@transaction.commit_on_success
|
||||
def newdomalias(request, tplname="admin/adminform.html"):
|
||||
commonctx = {"title" : _("New domain alias"),
|
||||
"submit_label" : _("Create"),
|
||||
@@ -158,6 +160,7 @@ def newdomalias(request, tplname="admin/adminform.html"):
|
||||
form = DomainAliasForm(request.user, request.POST)
|
||||
error = None
|
||||
if form.is_valid():
|
||||
events.raiseEvent("CanCreateDomainAlias", request.user)
|
||||
domalias = form.save(commit=False)
|
||||
if Domain.objects.filter(name=domalias.name):
|
||||
error = _("A domain with this name already exists")
|
||||
@@ -167,6 +170,7 @@ def newdomalias(request, tplname="admin/adminform.html"):
|
||||
except IntegrityError:
|
||||
error = _("Alias with this name already exists")
|
||||
else:
|
||||
events.raiseEvent("DomainAliasCreated", request.user, domalias)
|
||||
messages.info(request, _("Domain alias created"), fail_silently=True)
|
||||
return ajax_response(request, url=reverse(admin.views.domaliases))
|
||||
|
||||
@@ -181,10 +185,12 @@ def newdomalias(request, tplname="admin/adminform.html"):
|
||||
return _render(request, tplname, commonctx)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.change_domainalias")
|
||||
def editdomalias(request, alias_id, tplname="admin/adminform.html"):
|
||||
domalias = DomainAlias.objects.get(pk=alias_id)
|
||||
if not is_object_owner(request.user, domalias.target):
|
||||
raise PermDeniedException(_("You do not have access to this domain"))
|
||||
|
||||
commonctx = {"title" : _("Domain alias editing"),
|
||||
"submit_label" : _("Update"),
|
||||
"action" : reverse(editdomalias, args=[alias_id]),
|
||||
@@ -213,18 +219,23 @@ def editdomalias(request, alias_id, tplname="admin/adminform.html"):
|
||||
return _render(request, tplname, commonctx)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.delete_domainalias")
|
||||
@transaction.commit_on_success
|
||||
def deldomalias(request):
|
||||
selection = request.GET["selection"].split(",")
|
||||
DomainAlias.objects.filter(id__in=selection).delete()
|
||||
for daid in selection:
|
||||
domalias = DomainAlias.objects.get(pk=daid)
|
||||
if not is_object_owner(request.user, domalias.target):
|
||||
raise PermDeniedException(_("You don't have acces to this domain"))
|
||||
events.raiseEvent("DomainAliasDeleted", request.user, domalias)
|
||||
domalias.delete()
|
||||
msg = ungettext("Domain alias deleted", "Domain aliases deleted", len(selection))
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.view_mailboxes")
|
||||
@check_domain_ownership
|
||||
def mailboxes(request):
|
||||
title = _("Available mailboxes")
|
||||
if request.GET.has_key("domid"):
|
||||
@@ -232,7 +243,7 @@ def mailboxes(request):
|
||||
mboxes = Mailbox.objects.filter(domain=domain.id)
|
||||
title += " (%s)" % domain.name
|
||||
else:
|
||||
mboxes = Mailbox.objects.all()
|
||||
mboxes = get_user_mailboxes(request.user)
|
||||
domain = None
|
||||
deloptions = {"keepdir" : _("Do not delete mailbox directory")}
|
||||
return render_listing(request, "mailboxes",
|
||||
@@ -255,8 +266,8 @@ def mailboxes_raw(request, dom_id=None):
|
||||
})
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.view_mailboxes")
|
||||
@check_domain_ownership
|
||||
def mailboxes_search(request):
|
||||
if request.method != "POST":
|
||||
return
|
||||
@@ -266,13 +277,15 @@ def mailboxes_search(request):
|
||||
query &= Q(domain__name__startswith=domain)
|
||||
if request.GET.has_key("domid"):
|
||||
query = Q(domain=request.GET["domid"]) & query
|
||||
elif not request.user.is_superuser:
|
||||
query = Q(domain__in=get_user_domains(request.user)) & query
|
||||
mboxes = Mailbox.objects.filter(query)
|
||||
result = map(lambda mb: mb.full_address, mboxes)
|
||||
return ajax_simple_response(result)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.add_mailbox")
|
||||
@transaction.commit_on_success
|
||||
def newmailbox(request, tplname="admin/adminform.html"):
|
||||
commonctx = {"title" : _("New mailbox"),
|
||||
"submit_label" : _("Create"),
|
||||
@@ -282,12 +295,17 @@ def newmailbox(request, tplname="admin/adminform.html"):
|
||||
form = MailboxForm(request.user, request.POST)
|
||||
error = None
|
||||
if form.is_valid():
|
||||
if not request.user.is_superuser:
|
||||
if not is_object_owner(request.user, form.cleaned_data["domain"]):
|
||||
raise PermDeniedException(_("You do not have access to this domain"))
|
||||
|
||||
events.raiseEvent("CanCreateMailbox", request.user)
|
||||
try:
|
||||
mb = form.save()
|
||||
except AdminError as e:
|
||||
error = str(e)
|
||||
else:
|
||||
events.raiseEvent("CreateMailbox", mb)
|
||||
events.raiseEvent("CreateMailbox", request.user, mb)
|
||||
messages.info(request, _("Mailbox created"),
|
||||
fail_silently=True)
|
||||
return ajax_response(request, url=reverse(admin.views.mailboxes) + "?domid=%d" % mb.domain.id)
|
||||
@@ -301,10 +319,12 @@ def newmailbox(request, tplname="admin/adminform.html"):
|
||||
return _render(request, tplname, commonctx)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.change_mailbox")
|
||||
def editmailbox(request, mbox_id=None, tplname="admin/adminform.html"):
|
||||
mb = Mailbox.objects.get(pk=mbox_id)
|
||||
if not is_object_owner(request.user, mb.domain):
|
||||
raise PermDeniedException(_("You do not have access to this domain"))
|
||||
|
||||
commonctx = {"title" : _("Mailbox editing"),
|
||||
"submit_label" : _("Create"),
|
||||
"action" : reverse(editmailbox, args=[mbox_id]),
|
||||
@@ -336,66 +356,63 @@ def editmailbox(request, mbox_id=None, tplname="admin/adminform.html"):
|
||||
return _render(request, tplname, commonctx)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.delete_mailbox")
|
||||
@transaction.commit_on_success
|
||||
def delmailbox(request):
|
||||
selection = request.GET["selection"].split(",")
|
||||
error = None
|
||||
if not request.user.is_superuser:
|
||||
mb = Mailbox.objects.get(user__pk=request.user.id)
|
||||
if str(mb.id) in selection:
|
||||
error = _("You can't delete your own mailbox")
|
||||
keepdir = True if request.GET.get("keepdir", "false") == "true" else False
|
||||
usermb = request.user.mailbox_set.all()[0] \
|
||||
if len(request.user.mailbox_set.all()) else None
|
||||
|
||||
if error is None:
|
||||
if request.GET.has_key("keepdir") and request.GET["keepdir"] == "true":
|
||||
keepdir = True
|
||||
else:
|
||||
keepdir = False
|
||||
for mb in Mailbox.objects.filter(id__in=selection):
|
||||
events.raiseEvent("DeleteMailbox", mb)
|
||||
mb.delete(keepdir=keepdir)
|
||||
msg = ungettext("Mailbox deleted", "Mailboxes deleted", len(selection))
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request)
|
||||
for mb in Mailbox.objects.filter(id__in=selection):
|
||||
if not is_object_owner(request.user, mb.domain):
|
||||
raise PermDeniedException(_("You don't have access to this domain"))
|
||||
if usermb and usermb == mb:
|
||||
raise AdminError(_("You can't delete your own mailbox"))
|
||||
events.raiseEvent("DeleteMailbox", request.user, mb)
|
||||
mb.delete(keepdir=keepdir)
|
||||
|
||||
return ajax_response(request, status="ko", error=error)
|
||||
msg = ungettext("Mailbox deleted", "Mailboxes deleted", len(selection))
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.view_aliases")
|
||||
@check_domain_ownership
|
||||
def mbaliases(request):
|
||||
if request.GET.has_key("domid"):
|
||||
aliases = Alias.objects.filter(domain=request.GET["domid"])
|
||||
elif request.GET.has_key("mbid"):
|
||||
aliases = Alias.objects.filter(mboxes__id=request.GET["mbid"]).distinct()
|
||||
else:
|
||||
aliases = Alias.objects.all()
|
||||
aliases = get_user_mbaliases(request.user)
|
||||
|
||||
return render_listing(request, "mbaliases",
|
||||
title=_("Mailbox aliases"),
|
||||
rel="320 300",
|
||||
objects=aliases)
|
||||
|
||||
def _validate_mbalias(request, form, successmsg, tplname, commonctx):
|
||||
def _validate_mbalias(request, form, successmsg, tplname, commonctx, callback=None):
|
||||
"""Mailbox alias validation
|
||||
|
||||
Common function shared between creation and modification actions.
|
||||
"""
|
||||
error = None
|
||||
if form.is_valid():
|
||||
if not request.POST.has_key("targets"):
|
||||
raise AdminError(_("No target defined"))
|
||||
form.set_targets(request.user, request.POST.getlist("targets"))
|
||||
try:
|
||||
if not request.POST.has_key("targets"):
|
||||
raise AdminError(_("No target defined"))
|
||||
form.set_targets(request.user, request.POST.getlist("targets"))
|
||||
try:
|
||||
alias = form.save()
|
||||
except IntegrityError:
|
||||
raise AdminError(_("Alias with this name already exists"))
|
||||
alias = form.save()
|
||||
except IntegrityError:
|
||||
raise AdminError(_("Alias with this name already exists"))
|
||||
|
||||
messages.info(request, successmsg, fail_silently=True)
|
||||
return ajax_response(request, url=reverse(admin.views.mbaliases))
|
||||
except AdminError, e:
|
||||
error = str(e)
|
||||
if callback:
|
||||
callback(request.user, alias)
|
||||
|
||||
messages.info(request, successmsg, fail_silently=True)
|
||||
return ajax_response(request, url=reverse(admin.views.mbaliases))
|
||||
|
||||
if request.POST.has_key("targets"):
|
||||
targets = request.POST.getlist("targets")
|
||||
@@ -406,17 +423,21 @@ def _validate_mbalias(request, form, successmsg, tplname, commonctx):
|
||||
return ajax_response(request, status="ko", template=tplname, **commonctx)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.add_alias")
|
||||
@transaction.commit_on_success
|
||||
def newmbalias(request, tplname="admin/mbaliasform.html"):
|
||||
events.raiseEvent("CanCreateMailboxAlias", request.user)
|
||||
commonctx = {"title" : _("New mailbox alias"),
|
||||
"submit_label" : _("Create"),
|
||||
"action" : reverse(newmbalias),
|
||||
"formid" : "mbaliasform"}
|
||||
if request.method == "POST":
|
||||
def callback(user, mbalias):
|
||||
events.raiseEvent("MailboxAliasCreated", user, mbalias)
|
||||
|
||||
form = AliasForm(request.user, request.POST)
|
||||
return _validate_mbalias(request, form, _("Mailbox alias created"),
|
||||
tplname, commonctx)
|
||||
tplname, commonctx, callback)
|
||||
|
||||
form = AliasForm(request.user)
|
||||
commonctx["form"] = form
|
||||
@@ -424,10 +445,11 @@ def newmbalias(request, tplname="admin/mbaliasform.html"):
|
||||
return _render(request, tplname, commonctx)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.change_alias")
|
||||
def editmbalias(request, alias_id, tplname="admin/mbaliasform.html"):
|
||||
alias = Alias.objects.get(pk=alias_id)
|
||||
if not is_object_owner(request.user, alias.domain):
|
||||
raise PermDeniedException(_("You do not have access to this domain"))
|
||||
commonctx = {"title" : _("Mailbox alias editing"),
|
||||
"submit_label" : _("Update"),
|
||||
"action" : reverse(editmbalias, args=[alias.id]),
|
||||
@@ -443,11 +465,17 @@ def editmbalias(request, alias_id, tplname="admin/mbaliasform.html"):
|
||||
return _render(request, tplname, commonctx)
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.delete_alias")
|
||||
@transaction.commit_on_success
|
||||
def delmbalias(request):
|
||||
selection = request.GET["selection"].split(",")
|
||||
Alias.objects.filter(id__in=selection).delete()
|
||||
for mbaid in selection:
|
||||
mbalias = Alias.objects.get(pk=mbaid)
|
||||
if not is_object_owner(request.user, mbalias.domain):
|
||||
raise PermDeniedException(_("You do not have access to this domain"))
|
||||
events.raiseEvent("MailboxAliasDeleted", request.user, mbalias)
|
||||
mbalias.delete()
|
||||
|
||||
msg = ungettext("Alias deleted", "Aliases deleted", len(selection))
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request)
|
||||
@@ -464,12 +492,12 @@ def permissions(request, tplname='admin/permissions.html'):
|
||||
"content" : SuperAdminsPerms().get(request)}
|
||||
]
|
||||
|
||||
permtables += events.raiseQueryEvent("PermsGetTables", user=request.user)
|
||||
permtables += events.raiseQueryEvent("PermsGetTables", request)
|
||||
|
||||
permtables += [
|
||||
{"id" : "domain_admins",
|
||||
"title" : _("Domain administrators"),
|
||||
"rel" : "310 180",
|
||||
"rel" : "350 250",
|
||||
"content" : DomainAdminsPerms().get(request)}
|
||||
]
|
||||
|
||||
@@ -492,10 +520,92 @@ def add_permission(request):
|
||||
status, data = pobj.add(request)
|
||||
if not status:
|
||||
return HttpResponse(simplejson.dumps(data), mimetype="application/json")
|
||||
messages.info(request, _("Permission added."),
|
||||
fail_silently=True)
|
||||
try:
|
||||
msg = pobj.add_success_msg
|
||||
except AttributeError:
|
||||
msg = _("Permission added")
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request, url=reverse(admin.views.permissions))
|
||||
|
||||
@login_required
|
||||
@permission_required("auth.view_permissions")
|
||||
def create_domain_admin(request, tplname="admin/create_domain_admin.html"):
|
||||
if request.method == "POST":
|
||||
form = UserWithPasswordForm(request.POST)
|
||||
if form.is_valid():
|
||||
user = form.save(group="DomainAdmins")
|
||||
events.raiseEvent("DomainAdminCreated", user)
|
||||
messages.info(request, _("Domain admin created"), fail_silently=True)
|
||||
return ajax_response(request, url=reverse(admin.views.permissions))
|
||||
|
||||
return ajax_response(request, status="ko", template=tplname, form=form)
|
||||
|
||||
form = UserWithPasswordForm()
|
||||
return ajax_response(request, tplname, form=form)
|
||||
|
||||
@login_required
|
||||
@permission_required("auth.view_permissions")
|
||||
def edit_domain_admin(request, da_id, tplname="admin/adminform.html"):
|
||||
ctx = dict(title=_("Edit domain admin"),
|
||||
action=reverse(edit_domain_admin, args=[da_id]),
|
||||
formid="eda_form",
|
||||
submit_label=_("Update"))
|
||||
u = User.objects.get(pk=da_id, groups__name="DomainAdmins")
|
||||
if request.method == "POST":
|
||||
if request.POST.get("password1", "") != "" and \
|
||||
request.POST.get("password2", "") != "":
|
||||
form = UserWithPasswordForm(request.POST, instance=u)
|
||||
else:
|
||||
form = UserForm(request.POST, instance=u)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
if form.cleaned_data["createmb"]:
|
||||
pass
|
||||
messages.info(request, _("Domain admin updated"), fail_silently=True)
|
||||
return ajax_response(request,
|
||||
url=reverse("modoboa.admin.views.permissions"))
|
||||
ctx.update(form=form)
|
||||
return ajax_response(request, status="ko", template=tplname, **ctx)
|
||||
|
||||
form = UserWithPasswordForm(instance=u)
|
||||
ctx.update(form=form)
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@permission_required("auth.view_permissions")
|
||||
def domain_admin_promotion(request):
|
||||
if request.method == "POST":
|
||||
form = DomainAdminPromotionForm(request.POST)
|
||||
if form.is_valid():
|
||||
u = User.objects.get(username=form.cleaned_data["name"])
|
||||
u.groups.add(Group.objects.get(name="DomainAdmins"))
|
||||
u.save()
|
||||
messages.info(request, _("Domain admin added"), fail_silently=True)
|
||||
return ajax_response(request, url=reverse(admin.views.permissions))
|
||||
|
||||
form = DomainAdminPromotionForm()
|
||||
return ajax_response(request, template="admin/domain_admin_promotion.html",
|
||||
form=form)
|
||||
|
||||
@login_required
|
||||
@permission_required("admin.view_permissions")
|
||||
@check_domain_ownership
|
||||
def search_account(request):
|
||||
search = request.POST.get("search", None)
|
||||
if search is None:
|
||||
return ajax_simple_response([])
|
||||
|
||||
query = Q(username__startswith=search)
|
||||
query &= Q(is_superuser=False)
|
||||
if not request.user.has_perm("admin.add_domain"):
|
||||
query &= Q(groups__name="SimpleUsers")
|
||||
else:
|
||||
query &= ~Q(groups__name__in=["DomainAdmins"])
|
||||
|
||||
users = User.objects.filter(query)
|
||||
result = map(lambda u: u.username, users.all())
|
||||
return ajax_simple_response(result)
|
||||
|
||||
@login_required
|
||||
def delete_permissions(request):
|
||||
pclass = get_perms_class(request.user, request.GET["role"])
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
:mod:`controls` --- provides event handlers to check if limits are reached
|
||||
--------------------------------------------------------------------------
|
||||
:mod:`controls` --- provides event handlers that check if limits are reached
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
|
||||
from modoboa.admin.models import Mailbox
|
||||
from modoboa.lib import events
|
||||
from lib import *
|
||||
from models import *
|
||||
|
||||
def check_domains_limit(user):
|
||||
"""Check if user has reached its limit for domains creation
|
||||
def check_limit(user, lname):
|
||||
"""Check if a user has reached a defined limit
|
||||
|
||||
We check if the user will not reach the given limit after this
|
||||
creation.
|
||||
|
||||
Raise a ``LimitReached`` exception if the limit is reached.
|
||||
|
||||
@@ -17,28 +21,64 @@ def check_domains_limit(user):
|
||||
"""
|
||||
if not is_reseller(user):
|
||||
return
|
||||
if not user.limitspool.inc_curvalue('domains_limit'):
|
||||
raise LimitReached(user.limitspool.get_limit('domains_limit'))
|
||||
try:
|
||||
if user.limitspool.will_be_reached(lname):
|
||||
raise LimitReached(user.limitspool.get_limit(lname))
|
||||
except LimitsPool.DoesNotExist:
|
||||
pass
|
||||
|
||||
def associate_domain_to_reseller(user, domain):
|
||||
if not is_reseller(user):
|
||||
return
|
||||
ro = ResellerObject(user=user, content_object=domain)
|
||||
ro.save()
|
||||
def inc_limit(user, lname):
|
||||
try:
|
||||
user.limitspool.inc_curvalue(lname)
|
||||
except LimitsPool.DoesNotExist:
|
||||
pass
|
||||
|
||||
def get_reseller_domains(user):
|
||||
qs = user.resellerobject_set.filter(content_type__app_label="admin",
|
||||
content_type__model="domain")
|
||||
domains = []
|
||||
for ro in qs.all():
|
||||
domains += [ro.content_object]
|
||||
return domains
|
||||
def dec_limit(user, lname):
|
||||
try:
|
||||
user.limitspool.dec_curvalue(lname)
|
||||
except LimitsPool.DoesNotExist:
|
||||
pass
|
||||
|
||||
def check_domaliases_limit(user, domalias):
|
||||
pass
|
||||
@events.observe('CanCreateDomain')
|
||||
def check_domains_limit(user):
|
||||
check_limit(user, 'domains_limit')
|
||||
|
||||
def check_mboxes_limit(user, mbox):
|
||||
pass
|
||||
@events.observe('CreateDomain')
|
||||
def inc_nb_domains(user, domain):
|
||||
inc_limit(user, 'domains_limit')
|
||||
|
||||
def check_mbaliases_limit(user, mbalias):
|
||||
pass
|
||||
@events.observe('DeleteDomain')
|
||||
def dec_nb_domains(user, domain):
|
||||
dec_limit(user, 'domains_limit')
|
||||
|
||||
@events.observe('CanCreateDomainAlias')
|
||||
def check_domaliases_limit(user):
|
||||
check_limit(user, 'domain_aliases_limit')
|
||||
|
||||
@events.observe('DomainAliasCreated')
|
||||
def inc_nb_domaliases(user, domalias):
|
||||
inc_limit(user, 'domain_aliases_limit')
|
||||
|
||||
@events.observe('DomainAliasDeleted')
|
||||
def dec_nb_domaliases(user, domainalias):
|
||||
dec_limit(user, 'domain_aliases_limit')
|
||||
|
||||
@events.observe('CanCreateMailbox')
|
||||
def check_mboxes_limit(user):
|
||||
check_limit(user, 'mailboxes_limit')
|
||||
|
||||
@events.observe('CreateMailbox')
|
||||
def inc_nb_mailboxes(user, mailbox):
|
||||
inc_limit(user, 'mailboxes_limit')
|
||||
|
||||
@events.observe('DeleteMailbox')
|
||||
def dec_nb_mailboxes(user, mailbox):
|
||||
dec_limit(user, 'mailboxes_limit')
|
||||
|
||||
@events.observe('CanCreateMailboxAlias')
|
||||
def check_mbaliases_limit(user):
|
||||
check_limit(user, 'mailbox_aliases_limit')
|
||||
|
||||
@events.observe('MailboxAliasCreated')
|
||||
def inc_nb_mbaliases(user, mailboxalias):
|
||||
inc_limit(user, 'mailbox_aliases_limit')
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"pk": 2, "model": "auth.group", "fields": {"name": "Resellers", "permissions": [["add_domain", "admin", "domain"], ["change_domain", "admin", "domain"], ["delete_domain", "admin", "domain"], ["view_domains", "admin", "domain"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_domain", "admin", "domain"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}}]
|
||||
[{"pk": 3, "model": "auth.group", "fields": {"name": "Resellers", "permissions": [["add_domain", "admin", "domain"], ["change_domain", "admin", "domain"], ["delete_domain", "admin", "domain"], ["view_domains", "admin", "domain"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_domain", "admin", "domain"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"], ["view_permissions", "auth", "permission"]]}}]
|
||||
|
||||
+27
-25
@@ -3,28 +3,23 @@
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
from django.contrib.auth.models import User, Group
|
||||
from modoboa.admin.forms import UserForm, UserWithPasswordForm
|
||||
from models import *
|
||||
|
||||
class ResellerForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "first_name", "last_name", "email")
|
||||
class ResellerForm(UserForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ResellerForm, self).__init__(*args, **kwargs)
|
||||
del self.fields["createmb"]
|
||||
|
||||
def save(self, commit=True):
|
||||
user = super(ResellerForm, self).save(commit=False)
|
||||
if self.cleaned_data.has_key("password1"):
|
||||
user.set_password(self.cleaned_data["password1"])
|
||||
def save(self, commit=True, group=None):
|
||||
user = super(ResellerForm, self).save(commit, group)
|
||||
if commit:
|
||||
user.save()
|
||||
grp = Group.objects.get(name="Resellers")
|
||||
if not grp in user.groups.all():
|
||||
user.groups.add(grp)
|
||||
user.save()
|
||||
try:
|
||||
pool = user.limitspool
|
||||
except LimitsPool.DoesNotExist:
|
||||
pool = LimitsPool()
|
||||
pool.user = user
|
||||
print pool.user
|
||||
pool.save()
|
||||
|
||||
for lname in reseller_limits_tpl:
|
||||
@@ -35,18 +30,8 @@ class ResellerForm(forms.ModelForm):
|
||||
|
||||
return user
|
||||
|
||||
class ResellerWithPasswordForm(ResellerForm):
|
||||
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
|
||||
password2 = forms.CharField(label=_("Confirmation"),
|
||||
widget=forms.PasswordInput,
|
||||
help_text = _("Enter the same password as above, for verification."))
|
||||
|
||||
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
|
||||
class ResellerWithPasswordForm(ResellerForm, UserWithPasswordForm):
|
||||
pass
|
||||
|
||||
class ResellerPoolForm(forms.Form):
|
||||
domains_limit = forms.IntegerField(ugettext_noop("Max domains"),
|
||||
@@ -58,6 +43,23 @@ class ResellerPoolForm(forms.Form):
|
||||
mailbox_aliases_limit = forms.IntegerField(ugettext_noop("Max mailbox aliases"),
|
||||
help_text=ugettext_noop("Maximum number of mailbox aliases that can be created by this user"))
|
||||
|
||||
def check_limit_value(self, lname):
|
||||
if self.cleaned_data[lname] < -1:
|
||||
raise forms.ValidationError(_("Invalid limit"))
|
||||
return self.cleaned_data[lname]
|
||||
|
||||
def clean_domains_limit(self):
|
||||
return self.check_limit_value("domains_limit")
|
||||
|
||||
def clean_domain_aliases_limit(self):
|
||||
return self.check_limit_value("domain_aliases_limit")
|
||||
|
||||
def clean_mailboxes_limit(self):
|
||||
return self.check_limit_value("mailboxes_limit")
|
||||
|
||||
def clean_mailbox_aliases_limit(self):
|
||||
return self.check_limit_value("mailbox_aliases_limit")
|
||||
|
||||
def load_from_user(self, user):
|
||||
for l in reseller_limits_tpl:
|
||||
self.fields[l].initial = user.limitspool.getmaxvalue(l)
|
||||
|
||||
@@ -4,16 +4,19 @@
|
||||
-------------------------------
|
||||
|
||||
"""
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from modoboa.lib.exceptions import ModoboaException
|
||||
|
||||
class LimitReached(Exception):
|
||||
class LimitReached(ModoboaException):
|
||||
def __init__(self, limit):
|
||||
self.limit = limit
|
||||
|
||||
def __str__(self):
|
||||
return "%s reached" % self.limit.name
|
||||
|
||||
class BadLimitValue(ModoboaException):
|
||||
pass
|
||||
|
||||
def is_reseller(user):
|
||||
"""Tell if a user is a reseller or not
|
||||
|
||||
|
||||
+24
-20
@@ -11,18 +11,17 @@ from django.core.urlresolvers import reverse
|
||||
from django.conf.urls.defaults import include
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib.webutils import static_url
|
||||
from controls import *
|
||||
from models import *
|
||||
import permissions
|
||||
import controls
|
||||
|
||||
baseurl = "limits"
|
||||
|
||||
def init():
|
||||
events.register("AdminMenuDisplay", menu)
|
||||
events.register("CanCreateDomain", check_domains_limit)
|
||||
events.register("CreateDomain", associate_domain_to_reseller)
|
||||
events.register("GetUserDomains", get_reseller_domains)
|
||||
pass
|
||||
|
||||
def destroy():
|
||||
events.unregister("AdminMenuDisplay", menu)
|
||||
pass
|
||||
|
||||
def infos():
|
||||
return {
|
||||
@@ -32,20 +31,25 @@ def infos():
|
||||
"url" : baseurl
|
||||
}
|
||||
|
||||
def menu(target, user):
|
||||
import views
|
||||
|
||||
if target != "top_menu":
|
||||
return []
|
||||
if not user.is_superuser:
|
||||
return []
|
||||
return [
|
||||
{"name" : "resellers",
|
||||
"label" : _("Resellers"),
|
||||
"url" : reverse(views.resellers),
|
||||
"img" : static_url("pics/resellers.png")}
|
||||
]
|
||||
|
||||
def urls(prefix):
|
||||
return (r'^%s%s/' % (prefix, baseurl),
|
||||
include('modoboa.extensions.limits.urls'))
|
||||
|
||||
@events.observe('AdminFooterDisplay')
|
||||
def display_pool_usage(user, objtype):
|
||||
if objtype == "domaliases":
|
||||
objtype = "domain_aliases"
|
||||
elif objtype == "mbaliases":
|
||||
objtype = "mailbox_aliases"
|
||||
try:
|
||||
l = user.limitspool.get_limit('%s_limit' % objtype)
|
||||
except LimitsPool.DoesNotExist:
|
||||
return []
|
||||
if l.maxvalue == -2:
|
||||
label = _("undefined")
|
||||
elif l.maxvalue == -1:
|
||||
label = _("unlimited")
|
||||
else:
|
||||
label = str(l.maxvalue)
|
||||
return [_("Pool usage: %s / %s") % (l.curvalue, label)]
|
||||
|
||||
|
||||
+23
-13
@@ -17,32 +17,42 @@ class LimitsPool(models.Model):
|
||||
user = models.OneToOneField(User)
|
||||
|
||||
def getcurvalue(self, lname):
|
||||
l = Limit.objects.get(name=lname)
|
||||
l = self.limit_set.get(name=lname)
|
||||
return l.curvalue
|
||||
|
||||
def getmaxvalue(self, lname):
|
||||
l = Limit.objects.get(name=lname)
|
||||
l = self.limit_set.get(name=lname)
|
||||
return l.maxvalue
|
||||
|
||||
def inc_curvalue(self, lname, nb=1):
|
||||
l = Limit.objects.get(name=lname)
|
||||
if l.curvalue + nb > l.maxvalue:
|
||||
return False
|
||||
l = self.limit_set.get(name=lname)
|
||||
l.curvalue += nb
|
||||
l.save()
|
||||
return True
|
||||
|
||||
def dec_curvalue(self, lname, nb=1):
|
||||
l = self.limit_set.get(name=lname)
|
||||
if not l.curvalue:
|
||||
return
|
||||
l.curvalue -= nb
|
||||
l.save()
|
||||
|
||||
def will_be_reached(self, lname, nb=1):
|
||||
l = self.limit_set.get(name=lname)
|
||||
if l.maxvalue <= -1:
|
||||
return False
|
||||
if l.curvalue + nb > l.maxvalue:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_limit(self, lname):
|
||||
return Limit.objects.get(name=lname)
|
||||
try:
|
||||
return self.limit_set.get(name=lname)
|
||||
except Limit.DoesNotExist:
|
||||
return None
|
||||
|
||||
class Limit(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
curvalue = models.IntegerField(default=0)
|
||||
maxvalue = models.IntegerField(default=0)
|
||||
maxvalue = models.IntegerField(default=-2)
|
||||
pool = models.ForeignKey(LimitsPool)
|
||||
|
||||
class ResellerObject(models.Model):
|
||||
user = models.ForeignKey(User)
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = generic.GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# coding: utf-8
|
||||
|
||||
from django.utils.translation import ugettext as _, ugettext_noop
|
||||
from django.contrib.auth.models import User
|
||||
from modoboa.admin.permissions import Permissions
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.webutils import _render, _render_to_string
|
||||
from tables import *
|
||||
from forms import *
|
||||
|
||||
class ResellersPerms(Permissions):
|
||||
title = ugettext_noop("New reseller")
|
||||
role = "resellers"
|
||||
add_success_msg = ugettext_noop("Reseller added")
|
||||
|
||||
def get_add_form(self, request):
|
||||
form = ResellerWithPasswordForm()
|
||||
return self._render_form(request, form)
|
||||
|
||||
def add(self, request):
|
||||
form = ResellerWithPasswordForm(request.POST)
|
||||
error = None
|
||||
if form.is_valid():
|
||||
form.save(group="Resellers")
|
||||
return True, None
|
||||
content = self._render_form(request, form, True)
|
||||
return False, dict(status="ko", content=content)
|
||||
|
||||
def delete(self, selection):
|
||||
User.objects.filter(id__in=selection).delete()
|
||||
|
||||
def get(self, request):
|
||||
resellers = User.objects.filter(groups__name="Resellers")
|
||||
return ResellersTable(request, resellers).render()
|
||||
|
||||
@events.observe('PermsGetTables')
|
||||
def get_resellers_table(request):
|
||||
if not request.user.is_superuser:
|
||||
return []
|
||||
return [{"id" : "resellers",
|
||||
"title" : _("Resellers"),
|
||||
"rel" : "350 300",
|
||||
"content" : ResellersPerms().get(request)}]
|
||||
|
||||
@events.observe("PermsGetClass")
|
||||
def get_perms_class(role):
|
||||
print role
|
||||
if role != "resellers":
|
||||
return []
|
||||
return [ResellersPerms]
|
||||
@@ -8,6 +8,7 @@ class ResellersTable(tables.Table):
|
||||
tableid = "resellers_table"
|
||||
idkey = "id"
|
||||
|
||||
selection = tables.SelectionColumn("selection", width="4%")
|
||||
fname = tables.Column("first_name", label=ugettext_noop("First name"))
|
||||
lname = tables.Column("last_name", label=ugettext_noop("Last name"))
|
||||
username = tables.Column("username", label=ugettext_noop("Username"))
|
||||
@@ -16,7 +17,7 @@ class ResellersTable(tables.Table):
|
||||
width="50px",
|
||||
align="center", defvalue=reseller_actions)
|
||||
|
||||
cols_order = ["username", "fname", "lname", "created", "actions"]
|
||||
cols_order = ["selection", "username", "fname", "lname", "created", "actions"]
|
||||
|
||||
def __init__(self, request, users):
|
||||
super(ResellersTable, self).__init__(request)
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
{% load i18n %}
|
||||
{% block html_head %}
|
||||
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
window.addEvent("domready", function() {
|
||||
var form = new Form();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="title"><h1>{% trans "Limits pool for" %} {{ reseller.username }}</h1></div>
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
<form action="" method="POST" id="limits_pool_form" name="limits_pool_form">
|
||||
<form action="{% url extensions.limits.views.edit_limits_pool reseller.id %}"
|
||||
method="POST" id="limits_pool_form" name="limits_pool_form">
|
||||
{% include "common/genericform.html" %}
|
||||
<div class="spacer"></div>
|
||||
<div class="lastrow" align="center">
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.utils.translation import ugettext as _
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.urlresolvers import reverse
|
||||
from modoboa.lib.webutils import static_url, render_actions
|
||||
from modoboa.extensions import limits
|
||||
from modoboa.extensions.limits import views
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -16,11 +16,11 @@ def resellers_menu(user):
|
||||
"img" : static_url("pics/add.png"),
|
||||
"class" : "boxed",
|
||||
"rel" : "{handler:'iframe',size:{x:350,y:300}}",
|
||||
"url" : reverse(limits.views.new_reseller)},
|
||||
"url" : reverse(views.new_reseller)},
|
||||
{"name" : "remove",
|
||||
"label" : _("Remove"),
|
||||
"img" : static_url("pics/remove.png"),
|
||||
"url" : reverse(limits.views.delete_resellers)
|
||||
"url" : reverse(views.delete_resellers)
|
||||
}]
|
||||
return render_to_string("common/menulist.html",
|
||||
{"entries" : entries, "user" : user})
|
||||
@@ -29,13 +29,13 @@ def resellers_menu(user):
|
||||
def reseller_actions(user, resid):
|
||||
actions = [
|
||||
{"name" : "editreseller",
|
||||
"url" : reverse(limits.views.edit_reseller, args=[resid]),
|
||||
"url" : reverse(views.edit_reseller, args=[resid]),
|
||||
"img" : static_url("pics/edit.png"),
|
||||
"title" : _("Edit reseller"),
|
||||
"class" : "boxed",
|
||||
"rel" : "{handler:'iframe',size:{x:350,y:300}}"},
|
||||
{"name" : "editlimits",
|
||||
"url" : reverse(limits.views.edit_limits_pool, args=[resid]),
|
||||
"url" : reverse(views.edit_limits_pool, args=[resid]),
|
||||
"img" : static_url("pics/settings.png"),
|
||||
"title" : _("Edit limits allocated to this reseller"),
|
||||
"class" : "boxed",
|
||||
|
||||
@@ -2,9 +2,6 @@ from django.conf.urls.defaults import *
|
||||
|
||||
urlpatterns = patterns(
|
||||
'modoboa.extensions.limits.views',
|
||||
(r'^$', 'resellers'),
|
||||
(r'reseller/new/$', 'new_reseller'),
|
||||
(r'reseller/edit/(?P<resid>\d+)/$', 'edit_reseller'),
|
||||
(r'reseller/delete/$', 'delete_resellers'),
|
||||
(r'pool/edit/(?P<resid>\d+)/$', 'edit_limits_pool'),
|
||||
)
|
||||
|
||||
+21
-66
@@ -1,103 +1,58 @@
|
||||
# coding: utf-8
|
||||
from django.utils.translation import ugettext as _, ungettext
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.db import IntegrityError
|
||||
from django.contrib.auth.models import *
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators \
|
||||
import login_required, user_passes_test
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from tables import *
|
||||
from forms import *
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.webutils import _render, ajax_response
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def resellers(request, tplname="limits/resellers.html"):
|
||||
users = User.objects.filter(groups__name="Resellers")
|
||||
paginator = Paginator(users,
|
||||
int(parameters.get_admin("ITEMS_PER_PAGE", app="admin")))
|
||||
page = int(request.GET.get("page", "1"))
|
||||
try:
|
||||
subset = paginator.page(page)
|
||||
except (EmptyPage, PageNotAnInteger):
|
||||
subset = paginator.page(paginator.num_pages)
|
||||
|
||||
ctx = dict(table=ResellersTable(request, subset.object_list),
|
||||
page=subset,
|
||||
paginator=paginator)
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
def validate_reseller(request, form, action, tplname="limits/reseller.html"):
|
||||
error = None
|
||||
if form.is_valid():
|
||||
try:
|
||||
form.save()
|
||||
except IntegrityError:
|
||||
error = _("User with this Username already exists")
|
||||
else:
|
||||
msg = _("Reseller created") if action == "create" \
|
||||
else _("Reseller updated")
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request, url=reverse(resellers))
|
||||
submit_label = _("Create") if action == "create" else _("Update")
|
||||
ctx = dict(form=form, error=error, submit_label=submit_label)
|
||||
return ajax_response(request, status="ko", template=tplname, **ctx)
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def new_reseller(request, tplname="limits/reseller.html"):
|
||||
if request.method == "POST":
|
||||
form = ResellerWithPasswordForm(request.POST)
|
||||
return validate_reseller(request, form, "create")
|
||||
|
||||
form = ResellerWithPasswordForm()
|
||||
ctx = dict(form=form, submit_label=_("Create"))
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def edit_reseller(request, resid, tplname="limits/reseller.html"):
|
||||
def edit_reseller(request, resid, tplname="admin/add_permission.html"):
|
||||
reseller = User.objects.get(pk=resid)
|
||||
ctx = dict(title=_("Edit reseller"), submit_label=_("Update"),
|
||||
addurl=reverse(edit_reseller, args=[resid]))
|
||||
if request.method == "POST":
|
||||
if request.POST.get("password1", "") != "" and \
|
||||
request.POST.get("password2", "") != "":
|
||||
form = ResellerWithPasswordForm(request.POST, instance=reseller)
|
||||
else:
|
||||
form = ResellerForm(request.POST, instance=reseller)
|
||||
return validate_reseller(request, form, action="update")
|
||||
if form.is_valid():
|
||||
try:
|
||||
form.save()
|
||||
except IntegrityError:
|
||||
error = _("User with this Username already exists")
|
||||
else:
|
||||
messages.info(request, _("Reseller updated"), fail_silently=True)
|
||||
return ajax_response(request,
|
||||
url=reverse("modoboa.admin.views.permissions"))
|
||||
|
||||
ctx.update(form=form, error=error)
|
||||
return ajax_response(request, status="ko", template=tplname, **ctx)
|
||||
|
||||
form = ResellerWithPasswordForm(instance=reseller)
|
||||
ctx = dict(form=form, submit_label=_("Update"))
|
||||
ctx.update(form=form)
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def delete_resellers(request):
|
||||
selection = map(lambda idx: int(idx), request.GET.get("selection", "").split(","))
|
||||
if not len(selection):
|
||||
raise Exception("")
|
||||
|
||||
User.objects.filter(id__in=selection).delete()
|
||||
msg = ungettext("Reselled deleted", "Resellers deleted", len(selection))
|
||||
messages.info(request, msg, fail_silently=True)
|
||||
return ajax_response(request)
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def edit_limits_pool(request, resid, tplname="limits/pool.html"):
|
||||
reseller = User.objects.get(pk=resid)
|
||||
ctx = dict(reseller=reseller)
|
||||
if request.method == "POST":
|
||||
form = ResellerPoolForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save_new_limits(reseller.limitspool)
|
||||
messages.info(request, _("Pool updated"), fail_silently=True)
|
||||
return ajax_response(request, url=reverse(resellers))
|
||||
ctx = dict(form=form)
|
||||
return ajax_response(request, url=reverse("modoboa.admin.views.permissions"))
|
||||
ctx.update(form=form)
|
||||
return ajax_response(request, status="ko", template=tplname, **ctx)
|
||||
|
||||
form = ResellerPoolForm()
|
||||
form.load_from_user(reseller)
|
||||
ctx = dict(form=form, reseller=reseller)
|
||||
ctx.update(form=form)
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
+61
-3
@@ -8,17 +8,30 @@ This module provides a simple way of managing events between Modoboa
|
||||
core application and additional components.
|
||||
|
||||
"""
|
||||
from functools import wraps
|
||||
import inspect
|
||||
import re
|
||||
|
||||
events = [
|
||||
"CanCreateDomain",
|
||||
"CreateDomain",
|
||||
"DeleteDomain",
|
||||
|
||||
"CanCreateDomainAlias",
|
||||
"DomainAliasCreated",
|
||||
"DomainAliasDeleted",
|
||||
|
||||
"CanCreateMailbox",
|
||||
"CreateMailbox",
|
||||
"DeleteMailbox",
|
||||
"ModifyMailbox",
|
||||
|
||||
"GetUserDomains",
|
||||
|
||||
"CanCreateMailboxAlias",
|
||||
"MailboxAliasCreated",
|
||||
"MailboxAliasDeleted",
|
||||
|
||||
"DomainAdminCreated",
|
||||
|
||||
"PasswordChange",
|
||||
|
||||
"UserMenuDisplay",
|
||||
@@ -33,7 +46,9 @@ events = [
|
||||
"UserLogin",
|
||||
"UserLogout",
|
||||
|
||||
"GetAnnouncement"
|
||||
"GetAnnouncement",
|
||||
|
||||
"AdminFooterDisplay",
|
||||
]
|
||||
|
||||
callbacks = {}
|
||||
@@ -54,6 +69,49 @@ def register(event, callback):
|
||||
callbacks[event].append(callback)
|
||||
return 1
|
||||
|
||||
class observe(object):
|
||||
"""Event observing decorator
|
||||
|
||||
Automatically register the decorated function to observe the given
|
||||
event. If the decorated function is located into an extension, we
|
||||
check before each call if the extension is enabled or not. If
|
||||
that's not the case, the callback is not called.
|
||||
|
||||
.. note::
|
||||
|
||||
That's not a really optimized behaviour but I haven't found
|
||||
another solution to achieve that feature.
|
||||
|
||||
:param evtname: the event's name
|
||||
"""
|
||||
def __init__(self, evtname):
|
||||
self.evtname = evtname
|
||||
|
||||
def __guess_extension_name(self, modname):
|
||||
if modname.startswith('modoboa.extensions'):
|
||||
m = re.match(r'modoboa\.extensions\.([^\.]+)', modname)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return None
|
||||
|
||||
def __call__(self, f):
|
||||
modname = inspect.getmodule(inspect.stack()[1][0]).__name__
|
||||
extname = self.__guess_extension_name(modname)
|
||||
@wraps(f)
|
||||
def wrapped_f(*args):
|
||||
if extname:
|
||||
from modoboa.admin.models import Extension
|
||||
try:
|
||||
ext = Extension.objects.get(name=extname)
|
||||
except Extension.DoesNotExist:
|
||||
return []
|
||||
if not ext.enabled:
|
||||
return []
|
||||
return f(*args)
|
||||
|
||||
register(self.evtname, wrapped_f)
|
||||
return wrapped_f
|
||||
|
||||
def unregister(event, callback):
|
||||
"""Unregister a callback for a specific event
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
:mod:`exceptions` --- Custom Modoboa exceptions
|
||||
-----------------------------------------------
|
||||
|
||||
"""
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
class ModoboaException(Exception):
|
||||
pass
|
||||
|
||||
class PermDeniedException(ModoboaException):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return _("Permission denied: %s" % self.msg)
|
||||
|
||||
@@ -33,6 +33,7 @@ Extracted from `this blog
|
||||
one instance to another.
|
||||
|
||||
"""
|
||||
import sys
|
||||
import ldap
|
||||
import re
|
||||
import base64
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import re
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from modoboa.admin.models import Extension
|
||||
from modoboa.lib.webutils import _render_error, ajax_response
|
||||
from modoboa.lib.exceptions import ModoboaException
|
||||
|
||||
class ExtControlMiddleware(object):
|
||||
def process_view(self, request, view, args, kwargs):
|
||||
@@ -22,3 +24,14 @@ class AjaxLoginRedirect(object):
|
||||
if type(response) == HttpResponseRedirect:
|
||||
response.status_code = 278
|
||||
return response
|
||||
|
||||
class CommonExceptionCatcher(object):
|
||||
def process_exception(self, request, exception):
|
||||
if not isinstance(exception, ModoboaException):
|
||||
return None
|
||||
|
||||
if not request.is_ajax():
|
||||
return _render_error(request, errortpl="error_simple",
|
||||
user_context=dict(error=str(exception)))
|
||||
|
||||
return ajax_response(request, status="ko", respmsg=str(exception))
|
||||
|
||||
+26
-1
@@ -26,7 +26,7 @@ input {
|
||||
}
|
||||
|
||||
input[type=text], input[type=password] {
|
||||
width: 140px
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
/*input:hover {
|
||||
@@ -102,6 +102,10 @@ td.even {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#id_username {
|
||||
width: 120px;
|
||||
}
|
||||
@@ -139,6 +143,15 @@ td.even {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/*
|
||||
* clearfix related
|
||||
*/
|
||||
@@ -164,3 +177,15 @@ html[xmlns] .clearfix {
|
||||
height: 1%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/*
|
||||
* Domain admin form
|
||||
*/
|
||||
#createfrom {
|
||||
margin: 10px auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#permform > #id_name {
|
||||
width: 180px;
|
||||
}
|
||||
@@ -39,9 +39,14 @@ div#footer {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
#adminfo {
|
||||
float: left;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#navbar {
|
||||
float: right;
|
||||
padding: 0;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ var Confirmation = new Class({
|
||||
}
|
||||
params += item.get("name") + "=true";
|
||||
});
|
||||
new Request.JSON({
|
||||
new Request.JSON.mdb({
|
||||
method: "get",
|
||||
url: Confirmation.url,
|
||||
onSuccess: function(res) {
|
||||
@@ -76,10 +76,7 @@ var Confirmation = new Class({
|
||||
infobox.error(res.respmsg);
|
||||
infobox.hide(2);
|
||||
}
|
||||
},
|
||||
onFailure: function(xhr) {
|
||||
$(parent.document.body).set("html", xhr.responseText);
|
||||
}
|
||||
}
|
||||
}).send(params);
|
||||
},
|
||||
|
||||
|
||||
+194
-56
@@ -1,3 +1,133 @@
|
||||
var Form = new Class({
|
||||
Implements: [Options],
|
||||
|
||||
options: {
|
||||
resptarget: null
|
||||
},
|
||||
|
||||
callbacks: {
|
||||
error: {},
|
||||
success: {},
|
||||
extra_data: {}
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.setOptions(options);
|
||||
|
||||
$(document.body)
|
||||
.removeEvents("submit")
|
||||
.addEvent("submit", this.submit.bind(this));
|
||||
|
||||
this.init_help_messages();
|
||||
},
|
||||
|
||||
init_help_messages: function() {
|
||||
$$("input[type!=submit]", "select").addEvents({
|
||||
focus: function(evt) {
|
||||
var helpdiv = $(evt.target.name + "_helptext");
|
||||
if (helpdiv) {
|
||||
var coords = evt.target.getCoordinates();
|
||||
helpdiv.setPosition({x: 10, y: coords['top'] + coords['height'] + 5});
|
||||
helpdiv.setStyle("display", "block");
|
||||
}
|
||||
},
|
||||
blur: function(evt) {
|
||||
var helpdiv = $(evt.target.name + "_helptext");
|
||||
if (helpdiv) {
|
||||
helpdiv.setStyle("display", "none");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
register_callback: function(category, name, callback) {
|
||||
if (this.callbacks[category][name] == undefined) {
|
||||
return false;
|
||||
}
|
||||
this.callbacks[category][name] = callback;
|
||||
return true;
|
||||
},
|
||||
|
||||
register_error_callback: function(name, callback) {
|
||||
return this.register_callback("error", name, callback);
|
||||
},
|
||||
|
||||
register_success_callback: function(name, callback) {
|
||||
return this.register_callback("success", name, callback);
|
||||
},
|
||||
|
||||
register_extra_data_callback: function(name, callback) {
|
||||
return this.register_callback("extra_data", name, callback);
|
||||
},
|
||||
|
||||
submit: function(event) {
|
||||
var form = event.target;
|
||||
var myid = form.get("id");
|
||||
var data = form.toQueryString();
|
||||
|
||||
console.log("submit");
|
||||
|
||||
event.stop();
|
||||
if (this.callbacks["extra_data"][myid] != undefined) {
|
||||
if (data != "") {
|
||||
data += "&";
|
||||
}
|
||||
data += this.callbacks["extra_data"][myid]();
|
||||
}
|
||||
|
||||
new Request.JSON({
|
||||
url: form.get("action"),
|
||||
method: form.get("method"),
|
||||
data: data,
|
||||
onSuccess: function(response) {
|
||||
if (response.status == "ko") {
|
||||
if (response.norefresh != undefined && response.norefresh == true) {
|
||||
$$("div[class=error]").set("html", response.respmsg);
|
||||
} else {
|
||||
var target = (this.options.target)
|
||||
? $(this.options.target) : $(document.body);
|
||||
|
||||
target.set("html", response.content);
|
||||
this.init_help_messages();
|
||||
/*$(document.body).addEvent("submit", ajax_submit);*/
|
||||
if (this.callbacks["error"][myid] != undefined) {
|
||||
this.callbacks["error"][myid]();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($defined(response.respmsg)) {
|
||||
if ($defined(parent.current_anchor)) {
|
||||
parent.current_anchor.loading_message = response.respmsg;
|
||||
parent.current_anchor.loading_color = "green";
|
||||
} else {
|
||||
parent.infobox.info(response.respmsg);
|
||||
parent.infobox.hide(1);
|
||||
}
|
||||
}
|
||||
if ($defined(response.ajaxnav)) {
|
||||
if ($defined(response.url)) {
|
||||
parent.current_anchor.baseurl(response.url, 1);
|
||||
}
|
||||
parent.current_anchor.update(1);
|
||||
} else if ($defined(response.url)) {
|
||||
parent.location.href = response.url;
|
||||
}
|
||||
if (this.callbacks["success"][myid] != undefined) {
|
||||
this.callbacks["success"][myid](response);
|
||||
}
|
||||
parent.SqueezeBox.close();
|
||||
}.bind(this),
|
||||
onFailure: function(xhr) {
|
||||
parent.document.getElement('iframe')
|
||||
.setStyles({width: 800, height: 600});
|
||||
$(document.body).set("html", xhr.responseText);
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var error_callbacks = {
|
||||
permform: function() {
|
||||
if ($defined($("id_domain"))) {
|
||||
@@ -36,70 +166,74 @@ function register_extra_data_callback(name, callback) {
|
||||
}
|
||||
|
||||
function ajax_submit(event) {
|
||||
var form = event.target;
|
||||
var myid = form.get("id");
|
||||
var data = form.toQueryString();
|
||||
var form = event.target;
|
||||
var myid = form.get("id");
|
||||
var data = form.toQueryString();
|
||||
|
||||
event.stop();
|
||||
if ($defined(extra_data_callbacks[myid])) {
|
||||
if (data != "") {
|
||||
data += "&";
|
||||
event.stop();
|
||||
if ($defined(extra_data_callbacks[myid])) {
|
||||
if (data != "") {
|
||||
data += "&";
|
||||
}
|
||||
data += extra_data_callbacks[myid]();
|
||||
}
|
||||
data += extra_data_callbacks[myid]();
|
||||
}
|
||||
|
||||
new Request({
|
||||
method: form.get("method"),
|
||||
data: data,
|
||||
onSuccess: function(responseText){
|
||||
var response = JSON.decode(responseText);
|
||||
new Request.JSON({
|
||||
url: form.get("action"),
|
||||
method: form.get("method"),
|
||||
data: data,
|
||||
onSuccess: function(response) {
|
||||
if (response.status == "ko") {
|
||||
if (response.norefresh != undefined && response.norefresh == true) {
|
||||
$$("div[class=error]").set("html", response.respmsg);
|
||||
} else {
|
||||
var target = (resptarget != undefined)
|
||||
? $(resptarget) : $(document.body);
|
||||
|
||||
if (response.status == "ko") {
|
||||
if ($defined(response.norefresh) && response.norefresh == true) {
|
||||
$("error").set("html", response.respmsg);
|
||||
} else {
|
||||
$(document.body).set("html", response.content);
|
||||
$(document.body).addEvent("submit", ajax_submit);
|
||||
if ($defined(error_callbacks[myid])) {
|
||||
error_callbacks[myid]();
|
||||
}
|
||||
target.set("html", response.content);
|
||||
/*$(document.body).addEvent("submit", ajax_submit);*/
|
||||
if ($defined(error_callbacks[myid])) {
|
||||
error_callbacks[myid]();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($defined(response.respmsg)) {
|
||||
if ($defined(parent.current_anchor)) {
|
||||
parent.current_anchor.loading_message = response.respmsg;
|
||||
parent.current_anchor.loading_color = "green";
|
||||
} else {
|
||||
parent.infobox.info(response.respmsg);
|
||||
parent.infobox.hide(1);
|
||||
}
|
||||
}
|
||||
if ($defined(response.ajaxnav)) {
|
||||
if ($defined(response.url)) {
|
||||
parent.current_anchor.baseurl(response.url, 1);
|
||||
}
|
||||
parent.current_anchor.update(1);
|
||||
} else if ($defined(response.url)) {
|
||||
parent.location.href = response.url;
|
||||
}
|
||||
if ($defined(success_callbacks[myid])) {
|
||||
success_callbacks[myid](response);
|
||||
}
|
||||
parent.SqueezeBox.close();
|
||||
},
|
||||
onFailure: function(xhr) {
|
||||
parent.document.getElement('iframe').setStyles({width: 800, height: 600});
|
||||
$(document.body).set("html", xhr.responseText);
|
||||
}
|
||||
} else {
|
||||
if ($defined(response.respmsg)) {
|
||||
if ($defined(parent.current_anchor)) {
|
||||
parent.current_anchor.loading_message = response.respmsg;
|
||||
parent.current_anchor.loading_color = "green";
|
||||
} else {
|
||||
parent.infobox.info(response.respmsg);
|
||||
parent.infobox.hide(1);
|
||||
}
|
||||
}
|
||||
if ($defined(response.ajaxnav)) {
|
||||
if ($defined(response.url)) {
|
||||
parent.current_anchor.baseurl(response.url, 1);
|
||||
}
|
||||
parent.current_anchor.update(1);
|
||||
} else if ($defined(response.url)) {
|
||||
parent.location.href = response.url;
|
||||
}
|
||||
if ($defined(success_callbacks[myid])) {
|
||||
success_callbacks[myid](response);
|
||||
}
|
||||
parent.SqueezeBox.close();
|
||||
}
|
||||
},
|
||||
onFailure: function(xhr) {
|
||||
parent.document.getElement('iframe').setStyles({width: 800, height: 600});
|
||||
$(document.body).set("html", xhr.responseText);
|
||||
}
|
||||
}).send();
|
||||
}).send();
|
||||
}
|
||||
|
||||
window.addEvent("domready", function() {
|
||||
$(document.body).addEvent("submit", ajax_submit);
|
||||
function initform() {
|
||||
$(document.body)
|
||||
.removeEvents("submit")
|
||||
.addEvent("submit", ajax_submit);
|
||||
|
||||
$$("input[type!=submit]", "select").addEvents({
|
||||
focus: function(evt) {
|
||||
focus: function(evt) {
|
||||
var helpdiv = $(evt.target.name + "_helptext");
|
||||
if (helpdiv) {
|
||||
var coords = evt.target.getCoordinates();
|
||||
@@ -114,4 +248,8 @@ window.addEvent("domready", function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*window.addEvent("domready", function() {
|
||||
initform();
|
||||
});*/
|
||||
|
||||
@@ -16,6 +16,10 @@ window.addEvent('domready', function() {
|
||||
}
|
||||
}, $('accordion'));
|
||||
|
||||
SqueezeBox.assign($("content").getElements('a[class=boxed]'), {
|
||||
parse: 'rel'
|
||||
});
|
||||
|
||||
$$("a[name=addperm]").addEvent("click", function(evt) {
|
||||
evt.stop();
|
||||
var sizes = current_sizes.split(" ");
|
||||
|
||||
@@ -25,6 +25,7 @@ Request.JSON.mdb = new Class({
|
||||
Extends: Request.JSON,
|
||||
|
||||
options: {
|
||||
format: "json",
|
||||
onComplete: ajax_login_redirect,
|
||||
onFailure: function(xhr) {
|
||||
$(document.body).setStyle("overflow", "auto");
|
||||
|
||||
@@ -1,28 +1,57 @@
|
||||
{% extends "admin/add_permission.html" %}
|
||||
{% extends "layout_simple.html" %}
|
||||
{% load i18n %}
|
||||
{% block html_head %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/Autocompleter.css" />
|
||||
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/Observer.js"></script>
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/Autocompleter.js"></script>
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/Autocompleter.Request.js"></script>
|
||||
<script type="text/javascript">
|
||||
window.addEvent("domready", function() {
|
||||
domadminform_init = function() {
|
||||
$("id_domain").addEvent("change", function(event) {
|
||||
var domid = this.value;
|
||||
if (domid == "") {
|
||||
$("id_user").set("html", "");
|
||||
return;
|
||||
}
|
||||
{% url domains as doms_url %}
|
||||
new Request({
|
||||
url: "{% url domains %}" + domid + "/rawmboxes/?target=permissions",
|
||||
method: "get",
|
||||
onComplete: ajax_login_redirect,
|
||||
onSuccess: function(data) {
|
||||
$("id_user").set("html", data);
|
||||
$("id_user").options[0].setProperty("selected", "selected");
|
||||
}
|
||||
}).send();
|
||||
});
|
||||
};
|
||||
domadminform_init();
|
||||
var form = new Form({target: "formcontainer"});
|
||||
|
||||
$("createfrom").addEvent("change", function(evt) {
|
||||
var url = "";
|
||||
var styles = null;
|
||||
var from = this.get("value");
|
||||
|
||||
evt.stop();
|
||||
if (from == "create") {
|
||||
url = "{% url admin.views.create_domain_admin %}";
|
||||
styles = {height: 380};
|
||||
} else {
|
||||
url = "{% url admin.views.domain_admin_promotion %}";
|
||||
styles = {height: 200};
|
||||
}
|
||||
new Request.JSON.mdb({
|
||||
url: url,
|
||||
onSuccess: function(response) {
|
||||
$("formcontainer").set("html", response.content);
|
||||
form.init_help_messages();
|
||||
if (from == "promote") {
|
||||
new Autocompleter.Request.JSON("id_name",
|
||||
"{% url admin.views.search_account %}", {
|
||||
postVar: "search",
|
||||
minLength: 3,
|
||||
onComplete: ajax_login_redirect
|
||||
});
|
||||
}
|
||||
$(parent.document).getElement('iframe').setStyles(styles);
|
||||
}
|
||||
}).get();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="title"><h1>{{ title }}</h1></div>
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
<div class="center">
|
||||
<select id="createfrom">
|
||||
<option value="none">{% trans "Choose" %}</option>
|
||||
<option value="create">{% trans "Create a new account" %}</option>
|
||||
<option value="promote">{% trans "Promote an existing account" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="formcontainer"></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,18 +2,24 @@
|
||||
{% load i18n %}
|
||||
{% block html_head %}
|
||||
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
window.addEvent("domready", function() {
|
||||
var form = new Form();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="title"><h1>{{ title }}</h1></div>
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
{% if not addurl %}
|
||||
{% url admin.views.add_permission as addurl %}
|
||||
{% endif %}
|
||||
<form action="{{ addurl }}" method="POST" id="permform" name="permform">
|
||||
<input type="hidden" name="role" id="role" value="{{ role }}" />
|
||||
{% include "common/genericform.html" %}
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="lastrow" align="center">
|
||||
<input type="submit" name="submit" value="{% trans 'Add' %}" />
|
||||
<input type="submit" name="submit" value="{{ submit_label }}" />
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
{% extends "layout_simple.html" %}
|
||||
{% block html_head %}
|
||||
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
window.addEvent("domready", function() {
|
||||
var form = new Form();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="title"><h1>{{ title }}</h1></div>
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
<div class="error">{% if error %}{{ error }}{% endif %}</div>
|
||||
<form action="{{ action }}" enctype="{{ enctype }}"
|
||||
method="POST" id="{{ formid }}" name="{{ formid }}">
|
||||
{% include "common/genericform.html" %}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{% load i18n %}
|
||||
<form action="{% url admin.views.create_domain_admin %}" method="POST"
|
||||
id="permform" name="permform">
|
||||
{% include "common/genericform.html" %}
|
||||
<div class="spacer"></div>
|
||||
<div class="lastrow" align="center">
|
||||
<input type="submit" name="submit" value="{% trans 'Create' %}" />
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,10 @@
|
||||
{% load i18n %}
|
||||
<form action="{% url admin.views.domain_admin_promotion %}" method="POST"
|
||||
id="permform" name="permform">
|
||||
<input type="hidden" name="role" id="role" value="{{ role }}" />
|
||||
{% include "common/genericform.html" %}
|
||||
<div class="spacer"></div>
|
||||
<div class="lastrow" align="center">
|
||||
<input type="submit" name="submit" value="{% trans 'Promote' %}" />
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "admin/domains_header.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n admin_extras %}
|
||||
{% block middletitle %}{{ title }}{% endblock %}
|
||||
{% block subcontent %}
|
||||
<div id="objects" name="{{ selection }}" rel="{{ rel }}">
|
||||
@@ -7,6 +7,7 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block footer %}
|
||||
<div id="adminfo">{% get_footer_content user selection %}</div>
|
||||
<div id="navbar">
|
||||
{% if total %}
|
||||
<label>{% trans "Items" %} {{ objects.start_index }} {% trans "to" %} {{ objects.end_index }} {% trans "of" %} {{ total }}</label>
|
||||
|
||||
@@ -49,8 +49,7 @@ def options_menu(user):
|
||||
@register.simple_tag
|
||||
def uprefs_menu(user):
|
||||
entries = []
|
||||
entries += events.raiseQueryEvent("UserMenuDisplay",
|
||||
target="uprefs_menu")
|
||||
entries += events.raiseQueryEvent("UserMenuDisplay", "uprefs_menu")
|
||||
|
||||
return render_to_string('common/menulist.html',
|
||||
{"entries" : entries, "user" : user})
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ from django.utils.translation import ugettext as _
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib.webutils import _render, _render_error, ajax_response
|
||||
from forms import *
|
||||
from modoboa.admin.models import Mailbox, Alias, AdminError
|
||||
from modoboa.admin.lib import is_domain_admin
|
||||
from modoboa.admin.models import Mailbox, Alias
|
||||
from modoboa.admin.lib import is_domain_admin, AdminError
|
||||
from modoboa.auth.lib import encrypt
|
||||
|
||||
@login_required
|
||||
|
||||
Reference in New Issue
Block a user