diff --git a/admin/forms.py b/admin/forms.py index 16c66c777..dca068fb9 100644 --- a/admin/forms.py +++ b/admin/forms.py @@ -7,7 +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 +from lib import get_user_domains, get_user_domains_qs class DomainForm(forms.ModelForm): class Meta: @@ -34,7 +34,7 @@ class ProxyForm(forms.ModelForm): super(ProxyForm, self).__init__(*args, **kwargs) if not user.is_superuser: - self._domains = Domain.objects.filter(owners__user=user) + self._domains = get_user_domains_qs(user) else: self._domains = None diff --git a/admin/lib.py b/admin/lib.py index 1e631cb52..8d4296526 100644 --- a/admin/lib.py +++ b/admin/lib.py @@ -2,6 +2,7 @@ from functools import wraps from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth.models import User, Group +from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext as _ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from models import * @@ -77,18 +78,6 @@ 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 @@ -132,14 +121,29 @@ def set_object_ownership(user, obj): :param user: a ``User`` object :param obj: an admin. object (Domain, Mailbox, ...) """ - obj_owner = ObjectOwner(user=user, content_object=obj) - obj_owner.save() + try: + ct = ContentType.objects.get_for_model(obj) + obj_owner = user.objectowner_set.get(content_type=ct, object_id=obj.id) + except ObjectOwner.DoesNotExist: + obj_owner = ObjectOwner(user=user, content_object=obj) + obj_owner.save() + +def unset_object_ownership(obj): + """Remove the ownership of the given object + + :param obj: an object inheriting from ``models.Model`` + """ + ct = ContentType.objects.get_for_model(obj) + ObjectOwner.objects.get(content_type=ct, object_id=obj.id).delete() 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. + An object can only belongs to one user. + + This function is recursive : if the given user is not the owner + and if he owns other ``User`` objects, we also check if one of + those users owns the object. :param user: a ``User`` object :param obj: a admin object @@ -148,14 +152,44 @@ def is_object_owner(user, obj): 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: + ct = ContentType.objects.get_for_model(obj) + try: + ooentry = user.objectowner_set.get(content_type=ct, object_id=obj.id) + except ObjectOwner.DoesNotExist: + pass + else: + return True + if ct.model == "user": + return False + + ct = ContentType.objects.get_for_model(User) + qs = user.objectowner_set.filter(content_type=ct) + for ooentry in qs.all(): + if is_object_owner(ooentry.content_object, obj): return True return False +def get_object_owner(obj): + """Return this object's owner + + :param obj: an object inheriting from ``model.Model`` + :return: a ``User`` object + """ + ct = ContentType.objects.get_for_model(obj) + res = ObjectOwner.objects.filter(content_type=ct, object_id=obj.id) + if not len(res): + return None + return res[0].user + def check_domain_ownership(f): + """Decorator to check if the current user can access to a domain + + This decorator only applies to url that contain a 'domid' or + 'mbid' parameter. (for a direct access) + + Raise a ``PermDeniedError`` if the current user can't view the + requested object. + """ @wraps(f) def dec(request, **kwargs): if not request.user.is_superuser: @@ -177,41 +211,86 @@ 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") + domct = ContentType.objects.get_for_model(Domain) + qs = user.objectowner_set.filter(content_type=domct) domains = [] for entry in qs.all(): domains += [entry.content_object] + + userct = ContentType.objects.get_for_model(User) + qs = user.objectowner_set.filter(content_type=userct) + for entry in qs: + if not is_domain_admin(entry.content_object): + continue + domains += get_user_domains(entry.content_object) return domains +def get_user_domains_qs(user): + """Return the domains belonging to this user + + The result is a ``QuerySet`` object, so this function can be used + to fill ``ModelChoiceField`` objects. + + :param user: a ``User`` object + """ + if user.is_superuser: + return Domain.objects.all() + result = Domain.objects.filter(owners__user=user) + + userct = ContentType.objects.get_for_model(User) + for entry in user.objectowner_set.filter(content_type=userct): + if not is_domain_admin(entry.content_object): + continue + result |= get_user_domains_qs(entry.content_object) + return result + def get_user_domaliases(user): + """Return the domain aliases that belong to this user + + The result will contain the domain aliases defined for each domain + that user can see. + + :param user: a ``User`` object + :return: a list of ``DomainAlias`` objects + """ if user.is_superuser: return DomainAlias.objects.all() - qs = user.objectowner_set.filter(content_type__app_label="admin", - content_type__model="domain") + domains = get_user_domains_qs(user) domaliases = [] - for entry in qs.all(): - domaliases += entry.content_object.domainalias_set.all() + for dom in domains: + domaliases += dom.domainalias_set.all() return domaliases def get_user_mailboxes(user): + """Return the mailboxes that belong to this user + + The result will contain the mailboxes defined for each domain that + user can see. + + :param user: a ``User`` object + :return: a list of ``Mailbox`` objects + """ 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) + domains = get_user_domains_qs(user) + for dom in domains: + mboxes += dom.mailbox_set.all() return mboxes def get_user_mbaliases(user): + """Return the mailbox aliases that belong to this user + + The result will contain the mailbox aliases defined for each + domain that user can see. + + :param user: a ``User`` object + :return: a list of ``Alias`` objects + """ if user.is_superuser: return Alias.objects.all() - - qs = user.objectowner_set.filter(content_type__app_label="admin", - content_type__model="domain") + domains = get_user_domains_qs(user) mbaliases = [] - for ro in qs.all(): - mbaliases += Alias.objects.filter(domain=ro.content_object) + for dom in domains: + mbaliases += dom.alias_set.all() return mbaliases diff --git a/admin/models.py b/admin/models.py index 6dc9fbd42..82834409a 100644 --- a/admin/models.py +++ b/admin/models.py @@ -24,6 +24,9 @@ class ObjectOwner(models.Model): object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') + class Meta: + unique_together = (("content_type", "object_id"),) + class ObjectDates(models.Model): """Dates recording for admin objects @@ -471,21 +474,33 @@ class Alias(DatesAware): class Extension(models.Model): name = models.CharField(max_length=150) - enabled = models.BooleanField(ugettext_noop('enabled'), - help_text=ugettext_noop("Check to enable this extension")) + enabled = models.BooleanField( + ugettext_noop('enabled'), + help_text=ugettext_noop("Check to enable this extension") + ) def __getmodule(self): extname = "modoboa.extensions.%s" % self.name return __import__(extname, globals(), locals(), ['main']) + def init(self): + module = self.__getmodule() + if hasattr(module.main, "init"): + module.main.init() + def load(self): - self.__getmodule().main.init() + module = self.__getmodule() + if hasattr(module.main, "load"): + module.main.load() def unload(self): self.__getmodule().main.destroy() def on(self): self.load() + self.init() + self.enabled = True + self.save() extdir = "%s/extensions/%s" % (settings.MODOBOA_DIR, self.name) scriptdir = "%s/%s" % (extdir, "scripts") @@ -503,6 +518,9 @@ class Extension(models.Model): def off(self): self.unload() + self.enabled = False + self.save() + extdir = "%s/extensions/%s" % (settings.MODOBOA_DIR, self.name) scriptdir = "%s/%s" % (extdir, "scripts") if os.path.exists(scriptdir): diff --git a/admin/permissions.py b/admin/permissions.py index fc6894edb..8c1b65e4e 100644 --- a/admin/permissions.py +++ b/admin/permissions.py @@ -107,14 +107,14 @@ class DomainAdminsPerms(Permissions): return False, getctx("ko", content=content) def delete(self, selection): - 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() + grp = Group.objects.get(name="DomainAdmins") + for uid in selection: + u = User.objects.get(pk=uid) + u.groups.remove(grp) + u.save() + events.raiseEvent("DomainAdminDeleted", u) + if not len(u.mailbox_set.all()): + u.delete() def get(self, request): #domadmins = Mailbox.objects.filter(user__groups__name="DomainAdmins") diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 9d2c5ca66..9b0976d8e 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -2,6 +2,7 @@ from django import template from django.template.loader import render_to_string from django.utils.translation import ugettext as _, ugettext_noop from django.core.urlresolvers import reverse +from django.contrib.auth.models import User from modoboa import admin from modoboa.lib import events from modoboa.lib.webutils import static_url, render_actions @@ -179,7 +180,7 @@ def domalias_actions(user, aid): "img" : static_url("pics/edit.png"), "title" : _("Edit alias"), "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:300,y:190}}"} + "rel" : "{handler:'iframe',size:{x:310,y:190}}"} ] return render_actions(actions) @@ -191,7 +192,7 @@ def mailbox_actions(user, mboxid): "img" : static_url("pics/edit.png"), "title" : _("Edit mailbox"), "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:300,y:320}}"}, + "rel" : "{handler:'iframe',size:{x:310,y:320}}"}, {"name" : "aliases", "url" : reverse(admin.views.mbaliases) + "?mbid=%d" % mboxid, "img" : static_url("pics/alias.png"), @@ -209,6 +210,7 @@ def domain_admin_actions(user, daid): "class" : "boxed", "rel" : "{handler:'iframe',size:{x:350,y:320}}"}, ] + actions += events.raiseQueryEvent("DomainAdminActions", User.objects.get(pk=daid)) return render_actions(actions) @register.simple_tag diff --git a/admin/views.py b/admin/views.py index a7b554bd1..49d545027 100644 --- a/admin/views.py +++ b/admin/views.py @@ -126,7 +126,8 @@ def deldomain(request): if mb and mb.domain == dom: raise AdminError(_("You can't delete your own domain")) - events.raiseEvent("DeleteDomain", request.user, dom) + events.raiseEvent("DeleteDomain", dom) + unset_object_ownership(dom) dom.delete(keepdir=keepdir) msg = ungettext("Domain deleted", "Domains deleted", len(selection)) @@ -152,6 +153,7 @@ def domaliases(request): @permission_required("admin.add_domainalias") @transaction.commit_on_success def newdomalias(request, tplname="admin/adminform.html"): + events.raiseEvent("CanCreateDomainAlias", request.user) commonctx = {"title" : _("New domain alias"), "submit_label" : _("Create"), "action" : reverse(newdomalias), @@ -160,7 +162,6 @@ 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") @@ -170,6 +171,7 @@ def newdomalias(request, tplname="admin/adminform.html"): except IntegrityError: error = _("Alias with this name already exists") else: + set_object_ownership(request.user, domalias) events.raiseEvent("DomainAliasCreated", request.user, domalias) messages.info(request, _("Domain alias created"), fail_silently=True) return ajax_response(request, url=reverse(admin.views.domaliases)) @@ -227,7 +229,8 @@ def deldomalias(request): 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) + events.raiseEvent("DomainAliasDeleted", domalias) + unset_object_ownership(domalias) domalias.delete() msg = ungettext("Domain alias deleted", "Domain aliases deleted", len(selection)) messages.info(request, msg, fail_silently=True) @@ -287,6 +290,7 @@ def mailboxes_search(request): @permission_required("admin.add_mailbox") @transaction.commit_on_success def newmailbox(request, tplname="admin/adminform.html"): + events.raiseEvent("CanCreateMailbox", request.user) commonctx = {"title" : _("New mailbox"), "submit_label" : _("Create"), "action" : reverse(newmailbox), @@ -299,12 +303,12 @@ def newmailbox(request, tplname="admin/adminform.html"): 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: + set_object_ownership(request.user, mb) events.raiseEvent("CreateMailbox", request.user, mb) messages.info(request, _("Mailbox created"), fail_silently=True) @@ -370,7 +374,8 @@ def delmailbox(request): 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) + events.raiseEvent("DeleteMailbox", mb) + unset_object_ownership(mb) mb.delete(keepdir=keepdir) msg = ungettext("Mailbox deleted", "Mailboxes deleted", len(selection)) @@ -433,6 +438,7 @@ def newmbalias(request, tplname="admin/mbaliasform.html"): "formid" : "mbaliasform"} if request.method == "POST": def callback(user, mbalias): + set_object_ownership(user, mbalias) events.raiseEvent("MailboxAliasCreated", user, mbalias) form = AliasForm(request.user, request.POST) @@ -473,7 +479,8 @@ def delmbalias(request): 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) + events.raiseEvent("MailboxAliasDeleted", mbalias) + unset_object_ownership(mbalias) mbalias.delete() msg = ungettext("Alias deleted", "Aliases deleted", len(selection)) @@ -534,6 +541,7 @@ def create_domain_admin(request, tplname="admin/create_domain_admin.html"): form = UserWithPasswordForm(request.POST) if form.is_valid(): user = form.save(group="DomainAdmins") + set_object_ownership(request.user, user) events.raiseEvent("DomainAdminCreated", user) messages.info(request, _("Domain admin created"), fail_silently=True) return ajax_response(request, url=reverse(admin.views.permissions)) @@ -541,7 +549,7 @@ def create_domain_admin(request, tplname="admin/create_domain_admin.html"): return ajax_response(request, status="ko", template=tplname, form=form) form = UserWithPasswordForm() - return ajax_response(request, tplname, form=form) + return ajax_response(request, template=tplname, form=form) @login_required @permission_required("auth.view_permissions") @@ -580,6 +588,8 @@ def domain_admin_promotion(request): u = User.objects.get(username=form.cleaned_data["name"]) u.groups.add(Group.objects.get(name="DomainAdmins")) u.save() + set_object_ownership(request.user, u) + events.raiseEvent("DomainAdminCreated", u) messages.info(request, _("Domain admin added"), fail_silently=True) return ajax_response(request, url=reverse(admin.views.permissions)) @@ -588,7 +598,7 @@ def domain_admin_promotion(request): form=form) @login_required -@permission_required("admin.view_permissions") +@permission_required("auth.view_permissions") @check_domain_ownership def search_account(request): search = request.POST.get("search", None) @@ -687,15 +697,11 @@ def saveextensions(request): parts = k.split("_", 1) dbext = Extension.objects.get(name=parts[1]) if not dbext in actived_exts: - dbext.enabled = True - dbext.save() dbext.on() else: found += [dbext] for ext in actived_exts: if not ext in found: - ext.enabled = False - ext.save() ext.off() messages.info(request, _("Modifications applied."), fail_silently=True) return HttpResponseRedirect(reverse(admin.views.viewextensions)) diff --git a/extensions/__init__.py b/extensions/__init__.py index 0aa036685..b4aee3ef4 100644 --- a/extensions/__init__.py +++ b/extensions/__init__.py @@ -25,7 +25,7 @@ def loadextensions(prefix): try: ext = Extension.objects.get(name=f) if ext.enabled: - module.main.init() + ext.load() except Extension.DoesNotExist: pass u = module.main.urls(prefix) diff --git a/extensions/limits/controls.py b/extensions/limits/controls.py index 4d9ac882f..610c9d3d5 100644 --- a/extensions/limits/controls.py +++ b/extensions/limits/controls.py @@ -5,6 +5,7 @@ """ from modoboa.admin.models import Mailbox +from modoboa.admin.lib import get_object_owner, set_object_ownership from modoboa.lib import events from lib import * from models import * @@ -48,8 +49,15 @@ def inc_nb_domains(user, domain): inc_limit(user, 'domains_limit') @events.observe('DeleteDomain') -def dec_nb_domains(user, domain): - dec_limit(user, 'domains_limit') +def dec_nb_domains(domain): + owner = get_object_owner(domain) + dec_limit(owner, 'domains_limit') + for domalias in domain.domainalias_set.all(): + dec_nb_domaliases(domalias) + for mailbox in domain.mailbox_set.all(): + dec_nb_mailboxes(mailbox) + for mbalias in domain.alias_set.all(): + dec_nb_mbaliases(mbalias) @events.observe('CanCreateDomainAlias') def check_domaliases_limit(user): @@ -60,8 +68,9 @@ 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') +def dec_nb_domaliases(domainalias): + owner = get_object_owner(domainalias) + dec_limit(owner, 'domain_aliases_limit') @events.observe('CanCreateMailbox') def check_mboxes_limit(user): @@ -72,8 +81,9 @@ 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') +def dec_nb_mailboxes(mailbox): + owner = get_object_owner(mailbox) + dec_limit(owner, 'mailboxes_limit') @events.observe('CanCreateMailboxAlias') def check_mbaliases_limit(user): @@ -82,3 +92,31 @@ def check_mbaliases_limit(user): @events.observe('MailboxAliasCreated') def inc_nb_mbaliases(user, mailboxalias): inc_limit(user, 'mailbox_aliases_limit') + +@events.observe('MailboxAliasDeleted') +def dec_nb_mbaliases(mailboxalias): + owner = get_object_owner(mailboxalias) + dec_limit(owner, 'mailbox_aliases_limit') + +@events.observe("DomainAdminCreated") +def create_pool(user): + p = LimitsPool(user=user) + p.save() + p.create_limits() + +@events.observe("DomainAdminDeleted") +def move_pool_resource(domadmin): + owner = get_object_owner(domadmin) + for ooentry in domadmin.objectowner_set.all(): + obj = ooentry.content_object + ooentry.delete() + set_object_ownership(owner, obj) + + for lname in reseller_limits_tpl: + l = domadmin.limitspool.get_limit(lname) + ol = owner.limitspool.get_limit(lname) + ol.curvalue += l.curvalue + ol.maxvalue += l.maxvalue + ol.save() + + domadmin.limitspool.delete() diff --git a/extensions/limits/forms.py b/extensions/limits/forms.py index 617fc7e54..77d0e742e 100644 --- a/extensions/limits/forms.py +++ b/extensions/limits/forms.py @@ -4,7 +4,9 @@ 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 modoboa.admin.lib import get_object_owner from models import * +from lib import * class ResellerForm(UserForm): def __init__(self, *args, **kwargs): @@ -19,14 +21,8 @@ class ResellerForm(UserForm): except LimitsPool.DoesNotExist: pool = LimitsPool() pool.user = user - print pool.user pool.save() - - for lname in reseller_limits_tpl: - l = Limit() - l.name = lname - l.pool = pool - l.save() + pool.create_limits() return user @@ -64,9 +60,30 @@ class ResellerPoolForm(forms.Form): for l in reseller_limits_tpl: self.fields[l].initial = user.limitspool.getmaxvalue(l) + def allocate_from_pool(self, limit, pool): + ol = pool.get_limit(limit.name) + if ol.maxvalue == -2: + raise BadLimitValue(_("Your pool is not initialized yet")) + newvalue = self.cleaned_data[limit.name] + if newvalue == -1 and ol.maxvalue != -1: + raise BadLimitValue(_("You're not allowed to define unlimited values")) + + if limit.maxvalue > -1: + newvalue -= limit.maxvalue + if newvalue == 0: + return + remain = ol.maxvalue - ol.curvalue + if newvalue > remain: + raise UnsifficientResource(ol) + ol.maxvalue -= newvalue + ol.save() + def save_new_limits(self, pool): + owner = get_object_owner(pool.user) for lname in reseller_limits_tpl: l = pool.limit_set.get(name=lname) + if not owner.is_superuser: + self.allocate_from_pool(l, owner.limitspool) l.maxvalue = self.cleaned_data[lname] l.save() diff --git a/extensions/limits/lib.py b/extensions/limits/lib.py index 927979138..2bfbe0db5 100644 --- a/extensions/limits/lib.py +++ b/extensions/limits/lib.py @@ -5,6 +5,7 @@ """ from django.contrib.auth.models import Group +from django.utils.translation import ugettext as _ from modoboa.lib.exceptions import ModoboaException class LimitReached(ModoboaException): @@ -14,6 +15,13 @@ class LimitReached(ModoboaException): def __str__(self): return "%s reached" % self.limit.name +class UnsifficientResource(ModoboaException): + def __init__(self, limit): + self.limit = limit + + def __str__(self): + return _("Not enough resource into your pool") + class BadLimitValue(ModoboaException): pass diff --git a/extensions/limits/main.py b/extensions/limits/main.py index aaafb6090..74582e465 100644 --- a/extensions/limits/main.py +++ b/extensions/limits/main.py @@ -6,6 +6,8 @@ The *limits* extension """ +from django.contrib.auth.models import Permission, Group +from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext_noop as _, ugettext from django.core.urlresolvers import reverse from django.conf.urls.defaults import include @@ -14,14 +16,25 @@ from modoboa.lib.webutils import static_url from models import * import permissions import controls +import views baseurl = "limits" def init(): - pass + ct = ContentType.objects.get(app_label="admin", model="domain") + grp = Group.objects.get(name="DomainAdmins") + for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]: + perm = Permission.objects.get(content_type=ct, codename=pname) + grp.permissions.add(perm) + grp.save() def destroy(): - pass + ct = ContentType.objects.get(app_label="admin", model="domain") + grp = Group.objects.get(name="DomainAdmins") + for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]: + perm = Permission.objects.get(content_type=ct, codename=pname) + grp.permissions.remove(perm) + grp.save() def infos(): return { @@ -53,3 +66,13 @@ def display_pool_usage(user, objtype): label = str(l.maxvalue) return [_("Pool usage: %s / %s") % (l.curvalue, label)] +@events.observe("DomainAdminActions") +def get_da_actions(user): + return [ + {"name" : "editpool", + "url" : reverse(views.edit_limits_pool, args=[user.id]), + "img" : static_url("pics/settings.png"), + "title" : _("Edit limits allocated to this domain admin"), + "class" : "boxed", + "rel" : "{handler:'iframe',size:{x:330,y:280}}"}, + ] diff --git a/extensions/limits/models.py b/extensions/limits/models.py index cbe76ada4..bdf8f3f30 100644 --- a/extensions/limits/models.py +++ b/extensions/limits/models.py @@ -16,6 +16,13 @@ reseller_limits_tpl = [ class LimitsPool(models.Model): user = models.OneToOneField(User) + def create_limits(self): + for lname in reseller_limits_tpl: + l = Limit() + l.name = lname + l.pool = self + l.save() + def getcurvalue(self, lname): l = self.limit_set.get(name=lname) return l.curvalue @@ -56,3 +63,5 @@ class Limit(models.Model): maxvalue = models.IntegerField(default=-2) pool = models.ForeignKey(LimitsPool) + class Meta: + unique_together = (("name", "pool"),) diff --git a/extensions/limits/permissions.py b/extensions/limits/permissions.py index ebdd4548a..9c647947d 100644 --- a/extensions/limits/permissions.py +++ b/extensions/limits/permissions.py @@ -3,6 +3,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.admin.lib import set_object_ownership +from modoboa.admin.models import ObjectOwner from modoboa.lib import events from modoboa.lib.webutils import _render, _render_to_string from tables import * @@ -21,13 +23,21 @@ class ResellersPerms(Permissions): form = ResellerWithPasswordForm(request.POST) error = None if form.is_valid(): - form.save(group="Resellers") + r = form.save(group="Resellers") + set_object_ownership(request.user, r) 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() + ct = ContentType.objects.get_for_model(User) + for u in User.objects.filter(id__in=selection): + try: + ObjectOwner.objects.get(content_type=ct, object_id=u.id).delete() + except ObjectOwner.DoesNotExist: + pass + u.delete() + def get(self, request): resellers = User.objects.filter(groups__name="Resellers") diff --git a/extensions/limits/templates/limits/pool.html b/extensions/limits/templates/limits/pool.html index 4f4156bf4..fd77fd0be 100644 --- a/extensions/limits/templates/limits/pool.html +++ b/extensions/limits/templates/limits/pool.html @@ -10,7 +10,7 @@ window.addEvent("domready", function() { {% endblock %} {% block content %}

{% trans "Limits pool for" %} {{ reseller.username }}

-{% if error %}
{{ error }}
{% endif %} +
{% if error %}{{ error }}{% endif %}
{% include "common/genericform.html" %} diff --git a/extensions/limits/views.py b/extensions/limits/views.py index 714c457e4..375e819f2 100644 --- a/extensions/limits/views.py +++ b/extensions/limits/views.py @@ -1,10 +1,10 @@ # coding: utf-8 from django.utils.translation import ugettext as _ -from django.db import IntegrityError +from django.db import IntegrityError, transaction from django.contrib.auth.models import * from django.contrib import messages from django.contrib.auth.decorators \ - import login_required, user_passes_test + import login_required, user_passes_test, permission_required from django.core.urlresolvers import reverse from forms import * from modoboa.lib.webutils import _render, ajax_response @@ -39,7 +39,8 @@ def edit_reseller(request, resid, tplname="admin/add_permission.html"): return _render(request, tplname, ctx) @login_required -@user_passes_test(lambda u: u.is_superuser) +@permission_required("auth.view_permissions") +@transaction.commit_on_success def edit_limits_pool(request, resid, tplname="limits/pool.html"): reseller = User.objects.get(pk=resid) ctx = dict(reseller=reseller) diff --git a/lib/events.py b/lib/events.py index 76e475d99..ce8105f78 100644 --- a/lib/events.py +++ b/lib/events.py @@ -31,6 +31,8 @@ events = [ "MailboxAliasDeleted", "DomainAdminCreated", + "DomainAdminActions", + "DomainAdminDeleted", "PasswordChange", diff --git a/lib/middleware.py b/lib/middleware.py index f4dab641f..260b76526 100644 --- a/lib/middleware.py +++ b/lib/middleware.py @@ -34,4 +34,5 @@ class CommonExceptionCatcher(object): return _render_error(request, errortpl="error_simple", user_context=dict(error=str(exception))) - return ajax_response(request, status="ko", respmsg=str(exception)) + return ajax_response(request, status="ko", respmsg=str(exception), + norefresh=True) diff --git a/static/js/form.js b/static/js/form.js index f37a80356..1aa95d98d 100644 --- a/static/js/form.js +++ b/static/js/form.js @@ -65,8 +65,6 @@ var Form = new Class({ var myid = form.get("id"); var data = form.toQueryString(); - console.log("submit"); - event.stop(); if (this.callbacks["extra_data"][myid] != undefined) { if (data != "") {