mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
wip: Limits and resellers
* refactoring, limits are working pretty well * domain admins can now create domains (under control) when the limits extension is enabled see #105
This commit is contained in:
+2
-2
@@ -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
|
||||
|
||||
|
||||
+115
-36
@@ -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
|
||||
|
||||
+21
-3
@@ -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):
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
+18
-12
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}}"},
|
||||
]
|
||||
|
||||
@@ -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"),)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -10,7 +10,7 @@ window.addEvent("domready", function() {
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="title"><h1>{% trans "Limits pool for" %} {{ reseller.username }}</h1></div>
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
<div class="error">{% if error %}{{ error }}{% endif %}</div>
|
||||
<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" %}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -31,6 +31,8 @@ events = [
|
||||
"MailboxAliasDeleted",
|
||||
|
||||
"DomainAdminCreated",
|
||||
"DomainAdminActions",
|
||||
"DomainAdminDeleted",
|
||||
|
||||
"PasswordChange",
|
||||
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
@@ -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 != "") {
|
||||
|
||||
Reference in New Issue
Block a user