wip: Resellers and limits

* We now make a difference between users that own a specific object and users that have access to a specific object
* Limits are not moved anymore (from one reseller to a domain admin) when we grant access to a domain

see #105
This commit is contained in:
Antoine Nguyen
2012-01-25 11:57:37 +01:00
parent 73cdaed057
commit 8dd717a334
15 changed files with 223 additions and 87 deletions
+14
View File
@@ -264,6 +264,20 @@ class DomainAdminPromotionForm(forms.Form):
raise forms.ValidationError(_("Unknown user"))
return self.cleaned_data["name"]
class AssignDomainsForm(forms.Form):
domains = forms.ModelMultipleChoiceField(
queryset=None,
label=ugettext_noop("Domains"),
required=True,
help_text=ugettext_noop("Select one or more domains in the list")
)
def __init__(self, user, domadmin, *args, **kwargs):
super(AssignDomainsForm, self).__init__(*args, **kwargs)
self.fields["domains"].queryset = get_user_domains_qs(user)
self.fields["domains"].initial = get_user_domains_qs(domadmin)
class ImportDataForm(forms.Form):
sourcefile = forms.FileField(label=_("Select a file"))
sepcar = forms.CharField(label=_("Separator"), max_length=1, required=False)
+73 -32
View File
@@ -115,35 +115,59 @@ def render_listing(request, objtype, tplname="admin/listing.html",
return _render(request, tplname, kwargs)
def set_object_ownership(user, obj):
"""Simple shorcut to associate an object to a user
def grant_access_to_object(user, obj, is_owner=False):
"""Grant access to an object for a given user
There are two different cases where we want to grant access to an
object for a specific user:
* He is the owner (he's just created the object)
* He is going to administrate the object (but he is not the owner)
:param user: a ``User`` object
:param obj: an admin. object (Domain, Mailbox, ...)
:param is_owner: the user is the unique object's owner
"""
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()
entry = user.objectaccess_set.get(content_type=ct, object_id=obj.id)
entry.is_owner = is_owner
entry.save()
except ObjectAccess.DoesNotExist:
pass
else:
return
def unset_object_ownership(obj):
"""Remove the ownership of the given object
try:
objaccess = ObjectAccess(user=user, content_object=obj, is_owner=is_owner)
objaccess.save()
except IntegrityError, e:
raise AdminError(_("Failed to grant access (%s)" % str(e)))
def ungrant_access_to_object(obj, user=None):
"""Ungrant access to an object for a specific user
If no user is provided, all entries referencing this object are
deleted from the database.
:param obj: an object inheriting from ``models.Model``
:param user: a ``User`` object
"""
ct = ContentType.objects.get_for_model(obj)
ObjectOwner.objects.get(content_type=ct, object_id=obj.id).delete()
if user:
try:
ObjectAccess.objects.get(user=user, content_type=ct, object_id=obj.id).delete()
except ObjectAccess.DoesNotExist:
pass
else:
ObjectAccess.objects.filter(content_type=ct, object_id=obj.id).delete()
def is_object_owner(user, obj):
"""Check if a user is the object's owner
def can_access_object(user, obj):
"""Check if a user can access a specific object
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.
This function is recursive : if the given user hasn't got direct
access to this object and if he has got access other ``User``
objects, we check if one of those users owns the object.
:param user: a ``User`` object
:param obj: a admin object
@@ -154,35 +178,52 @@ def is_object_owner(user, obj):
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = user.objectowner_set.get(content_type=ct, object_id=obj.id)
except ObjectOwner.DoesNotExist:
ooentry = user.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
pass
else:
return True
if ct.model == "user":
return False
if directowner:
return False
ct = ContentType.objects.get_for_model(User)
qs = user.objectowner_set.filter(content_type=ct)
qs = user.objectaccess_set.filter(content_type=ct)
for ooentry in qs.all():
if is_object_owner(ooentry.content_object, obj):
return True
return False
def is_object_owner(user, obj):
"""Tell is the user is the unique owner of this object
:param user: a ``User`` object
:param obj: an object inheriting from ``models.Model``
:return: a boolean
"""
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = user.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
return False
return ooentry.is_owner
def get_object_owner(obj):
"""Return this object's owner
"""Return the unique owner of this object
: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):
try:
entry = ObjectAccess.objects.get(content_type=ct, object_id=obj.id, is_owner=True)
except ObjectAccess.DoesNotExist:
return None
return res[0].user
return entry.user
def check_domain_ownership(f):
"""Decorator to check if the current user can access to a domain
def check_domain_access(f):
"""Decorator to check if the current user can access a domain
This decorator only applies to url that contain a 'domid' or
'mbid' parameter. (for a direct access)
@@ -201,7 +242,7 @@ def check_domain_ownership(f):
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):
if domain and not can_access_object(request.user, domain):
raise PermDeniedError(_("You can't access this domain"))
return f(request, **kwargs)
@@ -212,13 +253,13 @@ def get_user_domains(user):
return Domain.objects.all()
domct = ContentType.objects.get_for_model(Domain)
qs = user.objectowner_set.filter(content_type=domct)
qs = user.objectaccess_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)
qs = user.objectaccess_set.filter(content_type=userct)
for entry in qs:
if not is_domain_admin(entry.content_object):
continue
@@ -235,13 +276,13 @@ def get_user_domains_qs(user):
"""
if user.is_superuser:
return Domain.objects.all()
result = Domain.objects.filter(owners__user=user)
result = Domain.objects.filter(owners__user=user).distinct()
userct = ContentType.objects.get_for_model(User)
for entry in user.objectowner_set.filter(content_type=userct):
for entry in user.objectaccess_set.filter(content_type=userct):
if not is_domain_admin(entry.content_object):
continue
result |= get_user_domains_qs(entry.content_object)
result |= get_user_domains_qs(entry.content_object).distinct()
return result
def get_user_domaliases(user):
@@ -8,20 +8,27 @@ class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'ObjectOwner'
db.create_table('admin_objectowner', (
# Adding model 'ObjectAccess'
db.create_table('admin_objectaccess', (
('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')()),
('is_owner', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('admin', ['ObjectOwner'])
db.send_create_signal('admin', ['ObjectAccess'])
# Adding unique constraint on 'ObjectAccess', fields ['user', 'content_type', 'object_id']
db.create_unique('admin_objectaccess', ['user_id', 'content_type_id', 'object_id'])
def backwards(self, orm):
# Deleting model 'ObjectOwner'
db.delete_table('admin_objectowner')
# Removing unique constraint on 'ObjectAccess', fields ['user', 'content_type', 'object_id']
db.delete_unique('admin_objectaccess', ['user_id', 'content_type_id', 'object_id'])
# Deleting model 'ObjectAccess'
db.delete_table('admin_objectaccess')
models = {
@@ -71,19 +78,20 @@ class Migration(SchemaMigration):
'uid': ('django.db.models.fields.IntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'admin.objectaccess': {
'Meta': {'unique_together': "(('user', 'content_type', 'object_id'),)", 'object_name': 'ObjectAccess'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'admin.objectdates': {
'Meta': {'object_name': 'ObjectDates'},
'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'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'}),
+4 -3
View File
@@ -18,14 +18,15 @@ try:
except ImportError:
ldap_available = False
class ObjectOwner(models.Model):
class ObjectAccess(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
is_owner = models.BooleanField(default=False)
class Meta:
unique_together = (("content_type", "object_id"),)
unique_together = (("user", "content_type", "object_id"),)
class ObjectDates(models.Model):
"""Dates recording for admin objects
@@ -80,7 +81,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)
owners = generic.GenericRelation(ObjectAccess)
class Meta:
permissions = (
+8 -4
View File
@@ -7,7 +7,7 @@ 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
from lib import grant_access_to_object, ungrant_access_to_object
class Permissions(object):
submit_label = ugettext_noop("Add")
@@ -101,20 +101,24 @@ 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)
#set_object_ownership(mb.user, mb.domain)
return True, None
content = self._render_form(request, form, True)
return False, getctx("ko", content=content)
def delete(self, selection):
grp = Group.objects.get(name="DomainAdmins")
dagrp = Group.objects.get(name="DomainAdmins")
sugrp = Group.objects.get(name="SimpleUsers")
for uid in selection:
u = User.objects.get(pk=uid)
u.groups.remove(grp)
u.groups.remove(dagrp)
u.save()
events.raiseEvent("DomainAdminDeleted", u)
if not len(u.mailbox_set.all()):
ungrant_access_to_object(u)
u.delete()
else:
u.groups.add(sugrp)
def get(self, request):
#domadmins = Mailbox.objects.filter(user__groups__name="DomainAdmins")
+1 -1
View File
@@ -149,7 +149,7 @@ class DomainAdminsTable(tables.Table):
date_joined = tables.Column("date_joined", label=ugettext_noop("Defined"))
enabled = tables.Column("is_active", label=gender("Enabled", "m"), width="10%")
actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"),
width="50px", align="center",
width="70px", align="center",
defvalue=domain_admin_actions)
cols_order = ["selection", "username", "first_name",
+6
View File
@@ -209,6 +209,12 @@ def domain_admin_actions(user, daid):
"title" : _("Edit domain admin"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:350,y:320}}"},
{"name" : "assigndomains",
"url" : reverse(admin.views.assign_domains_to_admin, args=[daid]),
"img" : static_url("pics/domains.png"),
"title" : _("Assign domain(s) to this administrator"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:350,y:320}}"},
]
actions += events.raiseQueryEvent("DomainAdminActions", User.objects.get(pk=daid))
return render_actions(actions)
+1
View File
@@ -38,6 +38,7 @@ urlpatterns = patterns(
(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/domainadmin/assign/(?P<da_id>\d+)/$', 'assign_domains_to_admin'),
(r'^permissions/searchaccount/$', 'search_account'),
(r'^permissions/delete/$', 'delete_permissions'),
)
+55 -18
View File
@@ -32,7 +32,7 @@ def domains(request):
return HttpResponseRedirect(reverse(userprefs.views.preferences))
domains = get_user_domains(request.user)
domains = get_user_domains_qs(request.user)
for dom in domains:
dom.mbalias_counter = len(Alias.objects.filter(domain=dom.id))
deloptions = {"keepdir" : _("Do not delete domain directory")}
@@ -73,7 +73,7 @@ def _validate_domain(request, form, successmsg, commonctx,
def newdomain(request, tplname="admin/adminform.html"):
events.raiseEvent("CanCreateDomain", request.user)
def newdomain_cb(user, domain):
set_object_ownership(user, domain)
grant_access_to_object(user, domain, is_owner=True)
events.raiseEvent("CreateDomain", user, domain)
commonctx = {"title" : _("New domain"),
@@ -127,7 +127,7 @@ def deldomain(request):
raise AdminError(_("You can't delete your own domain"))
events.raiseEvent("DeleteDomain", dom)
unset_object_ownership(dom)
ungrant_access_to_object(dom)
dom.delete(keepdir=keepdir)
msg = ungettext("Domain deleted", "Domains deleted", len(selection))
@@ -136,7 +136,7 @@ def deldomain(request):
@login_required
@permission_required("admin.view_domaliases")
@check_domain_ownership
@check_domain_access
def domaliases(request):
if request.GET.has_key("domid"):
domain = Domain.objects.get(pk=request.GET["domid"])
@@ -171,7 +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)
grant_access_to_object(request.user, domalias, is_owner=True)
events.raiseEvent("DomainAliasCreated", request.user, domalias)
messages.info(request, _("Domain alias created"), fail_silently=True)
return ajax_response(request, url=reverse(admin.views.domaliases))
@@ -230,7 +230,7 @@ def deldomalias(request):
if not is_object_owner(request.user, domalias.target):
raise PermDeniedException(_("You don't have acces to this domain"))
events.raiseEvent("DomainAliasDeleted", domalias)
unset_object_ownership(domalias)
ungrant_access_to_object(domalias)
domalias.delete()
msg = ungettext("Domain alias deleted", "Domain aliases deleted", len(selection))
messages.info(request, msg, fail_silently=True)
@@ -238,7 +238,7 @@ def deldomalias(request):
@login_required
@permission_required("admin.view_mailboxes")
@check_domain_ownership
@check_domain_access
def mailboxes(request):
title = _("Available mailboxes")
if request.GET.has_key("domid"):
@@ -270,7 +270,7 @@ def mailboxes_raw(request, dom_id=None):
@login_required
@permission_required("admin.view_mailboxes")
@check_domain_ownership
@check_domain_access
def mailboxes_search(request):
if request.method != "POST":
return
@@ -300,7 +300,7 @@ def newmailbox(request, tplname="admin/adminform.html"):
error = None
if form.is_valid():
if not request.user.is_superuser:
if not is_object_owner(request.user, form.cleaned_data["domain"]):
if not can_access_object(request.user, form.cleaned_data["domain"]):
raise PermDeniedException(_("You do not have access to this domain"))
try:
@@ -308,7 +308,8 @@ def newmailbox(request, tplname="admin/adminform.html"):
except AdminError as e:
error = str(e)
else:
set_object_ownership(request.user, mb)
grant_access_to_object(request.user, mb, is_owner=True)
grant_access_to_object(request.user, mb.user, is_owner=True)
events.raiseEvent("CreateMailbox", request.user, mb)
messages.info(request, _("Mailbox created"),
fail_silently=True)
@@ -370,12 +371,13 @@ def delmailbox(request):
if len(request.user.mailbox_set.all()) else None
for mb in Mailbox.objects.filter(id__in=selection):
if not is_object_owner(request.user, mb.domain):
if not can_access_object(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", mb)
unset_object_ownership(mb)
ungrant_access_to_object(mb)
ungrant_access_to_object(mb.user)
mb.delete(keepdir=keepdir)
msg = ungettext("Mailbox deleted", "Mailboxes deleted", len(selection))
@@ -384,7 +386,7 @@ def delmailbox(request):
@login_required
@permission_required("admin.view_aliases")
@check_domain_ownership
@check_domain_access
def mbaliases(request):
if request.GET.has_key("domid"):
aliases = Alias.objects.filter(domain=request.GET["domid"])
@@ -438,7 +440,7 @@ def newmbalias(request, tplname="admin/mbaliasform.html"):
"formid" : "mbaliasform"}
if request.method == "POST":
def callback(user, mbalias):
set_object_ownership(user, mbalias)
grant_access_to_object(user, mbalias, is_owner=True)
events.raiseEvent("MailboxAliasCreated", user, mbalias)
form = AliasForm(request.user, request.POST)
@@ -480,7 +482,7 @@ def delmbalias(request):
if not is_object_owner(request.user, mbalias.domain):
raise PermDeniedException(_("You do not have access to this domain"))
events.raiseEvent("MailboxAliasDeleted", mbalias)
unset_object_ownership(mbalias)
ungrant_access_to_object(mbalias)
mbalias.delete()
msg = ungettext("Alias deleted", "Aliases deleted", len(selection))
@@ -541,7 +543,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)
grant_access_to_object(request.user, user, is_owner=True)
events.raiseEvent("DomainAdminCreated", user)
messages.info(request, _("Domain admin created"), fail_silently=True)
return ajax_response(request, url=reverse(admin.views.permissions))
@@ -588,7 +590,11 @@ 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)
if not can_access_object(request.user, u):
# The only case where we need to grant access to the
# object is when ``u`` has been create by a super
# admin
grant_access_to_object(request.user, u, is_owner=True)
events.raiseEvent("DomainAdminCreated", u)
messages.info(request, _("Domain admin added"), fail_silently=True)
return ajax_response(request, url=reverse(admin.views.permissions))
@@ -599,7 +605,38 @@ def domain_admin_promotion(request):
@login_required
@permission_required("auth.view_permissions")
@check_domain_ownership
@transaction.commit_on_success
def assign_domains_to_admin(request, da_id, tplname="admin/adminform.html"):
da = User.objects.get(pk=da_id)
ctx = dict(title=_("Assign domains to %s" % da.username),
action=reverse(assign_domains_to_admin, args=[da_id]),
formid="assignform",
submit_label=_("Assign"))
if request.method == "POST":
form = AssignDomainsForm(request.user, da, request.POST)
if form.is_valid():
current_domains = get_user_domains(da)
for domain in form.cleaned_data["domains"]:
if not domain in current_domains:
grant_access_to_object(da, domain)
else:
current_domains.remove(domain)
for domain in current_domains:
ungrant_access_to_object(domain, da)
msg = ungettext("Domain assigned", "Domains assigned",
len(form.cleaned_data["domains"]))
messages.info(request, msg)
return ajax_response(request, url=reverse(admin.views.permissions))
ctx.update(form=form)
return ajax_response(request, status="ko", template=tplname, **ctx)
form = AssignDomainsForm(request.user, da)
ctx.update(form=form)
return _render(request, tplname, ctx)
@login_required
@permission_required("auth.view_permissions")
@check_domain_access
def search_account(request):
search = request.POST.get("search", None)
if search is None:
+14 -13
View File
@@ -5,7 +5,9 @@
"""
from modoboa.admin.models import Mailbox
from modoboa.admin.lib import get_object_owner, set_object_ownership
from modoboa.admin.lib import \
get_object_owner, grant_access_to_object, is_object_owner, \
ungrant_access_to_object
from modoboa.lib import events
from lib import *
from models import *
@@ -20,8 +22,6 @@ def check_limit(user, lname):
:param user: a ``User`` object
"""
if not is_reseller(user):
return
try:
if user.limitspool.will_be_reached(lname):
raise LimitReached(user.limitspool.get_limit(lname))
@@ -107,16 +107,17 @@ def create_pool(user):
@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)
if not owner.is_superuser:
for ooentry in domadmin.objectaccess_set.all():
if ooentry.is_owner:
grant_access_to_object(owner, ooentry.content_object, True)
ooentry.delete()
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()
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()
+14
View File
@@ -43,6 +43,20 @@ class LimitsPool(models.Model):
l.curvalue -= nb
l.save()
def dec_limit(self, lname, nb=1):
l = self.limit_set.get(name=lname)
l.curvalue -= nb
if l.maxvalue > -1:
l.maxvalue -= nb
l.save()
def inc_limit(self, lname, nb=1):
l = self.limit_set.get(name=lname)
l.curvalue += nb
if l.maxvalue > -1:
l.maxvalue += nb
l.save()
def will_be_reached(self, lname, nb=1):
l = self.limit_set.get(name=lname)
if l.maxvalue <= -1:
+3 -3
View File
@@ -3,8 +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.admin.lib import grant_access_to_object
from modoboa.admin.models import ObjectAccess
from modoboa.lib import events
from modoboa.lib.webutils import _render, _render_to_string
from tables import *
@@ -24,7 +24,7 @@ class ResellersPerms(Permissions):
error = None
if form.is_valid():
r = form.save(group="Resellers")
set_object_ownership(request.user, r)
grant_access_to_object(request.user, r, True)
return True, None
content = self._render_form(request, form, True)
return False, dict(status="ko", content=content)
+1 -1
View File
@@ -3,7 +3,7 @@
import os
import time
import datetime
import rrdtool
#import rrdtool
from django.utils.translation import ugettext as _
from modoboa.lib import parameters
+1
View File
@@ -16,6 +16,7 @@ events = [
"CanCreateDomain",
"CreateDomain",
"DeleteDomain",
"DomainAssigned",
"CanCreateDomainAlias",
"DomainAliasCreated",
+8
View File
@@ -188,4 +188,12 @@ html[xmlns] .clearfix {
#permform > #id_name {
width: 180px;
}
/*
* Assign domains form
*/
#assignform #id_domains {
width: 310px;
height: 200px;
}