From eff3c7d815e764f3f2942c04d0a87df6f822daec Mon Sep 17 00:00:00 2001 From: tonio Date: Thu, 25 Nov 2010 23:19:04 +0100 Subject: [PATCH] see #92 Work in progress. I've moved permissions management to the new dedicated page. Adding permissions is working but deleting ones. I must also filter the list of users available when creating a super admin because the default admin appears inside :p --- admin/forms.py | 29 ++++++++ admin/models.py | 8 ++ admin/templatetags/admin_extras.py | 28 +++---- admin/urls.py | 6 +- admin/views.py | 101 ++++++++++++++++++-------- lib/events.py | 2 + lib/tables.py | 14 +++- static/js/common.js | 26 +++---- static/js/domains.js | 22 ++---- static/js/permissions.js | 28 +++++++ templates/admin/add_domain_admin.html | 20 +++++ templates/admin/add_permission.html | 18 +++++ templates/admin/addpermission.html | 36 --------- templates/admin/permissions.html | 73 ++++--------------- 14 files changed, 240 insertions(+), 171 deletions(-) create mode 100644 static/js/permissions.js create mode 100644 templates/admin/add_domain_admin.html create mode 100644 templates/admin/add_permission.html delete mode 100644 templates/admin/addpermission.html diff --git a/admin/forms.py b/admin/forms.py index 9ad3fc305..04d5ef26b 100644 --- a/admin/forms.py +++ b/admin/forms.py @@ -1,7 +1,9 @@ from django import forms from modoboa.admin.models import Domain, Mailbox, Alias from django.utils.translation import ugettext as _ +from django.contrib.auth.models import User from modoboa.admin.templatetags.admin_extras import gender +from modoboa.lib import tables class DomainForm(forms.ModelForm): class Meta: @@ -63,3 +65,30 @@ class PermissionForm(forms.Form): choices=(("SuperAdmins", _("Super administrator")), ("DomainAdmins", _("Domain administrator"))), help_text=_("Select the role you want this user to have")) + +class SuperAdminForm(forms.Form): + user = forms.ModelChoiceField(queryset=User.objects.all(), + label=_("User"), required=True, + help_text=_("Select a user in the list")) + +class DomainAdminForm(forms.Form): + domain = forms.ModelChoiceField(queryset=Domain.objects.all(), label=_("Domain"), + required=True, + help_text=_("Select a domain in the list")) + user = forms.ChoiceField(label=_("User"), required=True, + help_text=_("Select a user in the list")) + +class SuperAdminsTable(tables.Table): + idkey = "id" + selection = tables.SelectionColumn("selection", width="4%", first=True) + _1_user_name = tables.Column("user_name", label=_("User name")) + _2_full_name = tables.Column("full_name", label=_("Full name")) + _3_enabled = tables.Column("enabled", label=_("Enabled_m"), width="10%") + +class DomainAdminsTable(tables.Table): + idkey = "id" + selection = tables.SelectionColumn("selection", width="4%", first=True) + _1_domain = tables.Column("domain", label=_("Domain")) + _2_full_name = tables.Column("full_name", label=_("Full name")) + _3_enabled = tables.Column("enabled", label=_("Enabled_m"), width="10%") + diff --git a/admin/models.py b/admin/models.py index 0639b7596..d3a546b97 100644 --- a/admin/models.py +++ b/admin/models.py @@ -99,6 +99,14 @@ class Mailbox(models.Model): % (parameters.get_admin("STORAGE_PATH"), self.domain.name, self.address)) + def tohash(self): + return { + "id" : self.id, + "domain" : self.domain.name, + "full_name" : self.name, + "enabled" : self.user.is_active + } + class Alias(models.Model): address = models.CharField(_('address'), max_length=100, help_text=_("The alias address (without the domain part)")) diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 35b9665ca..434e46872 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -31,7 +31,7 @@ def admin_menu(user): if user.has_perm("auth.view_permissions"): entries[0]["menu"] += [ {"name" : "permissions", - "url" : "", + "url" : reverse(admin.views.permissions), "label" : _("Permissions"), "img" : static_url("pics/permissions.png")} ] @@ -40,7 +40,7 @@ def admin_menu(user): {"name" : "settings", "img" : static_url("pics/settings.png"), "label" : _("Settings"), - "url" : reverse(admin.views.settings)} + "url" : reverse(admin.views.viewparameters)} ] if not len(entries[0]["menu"]): @@ -48,7 +48,6 @@ def admin_menu(user): return render_to_string("common/menulist.html", {"entries" : entries, "user" : user}) - @register.simple_tag def domain_menu(domain_id, selection, user): entries = [ @@ -86,16 +85,6 @@ def domain_menu(domain_id, selection, user): @register.simple_tag def settings_menu(selection, user): entries = [ - {"name" : "permissions", - "url" : reverse(admin.views.settings), - "label" : _("Permissions"), - "img" : static_url("pics/permissions.png")}, - {"name" : "addperm", - "url" : reverse(admin.views.addpermission), - "img" : static_url("pics/add.png"), - "label" : _("Add permission"), - "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:320,y:210}}"}, {"name" : "parameters", "url" : reverse(admin.views.viewparameters), "img" : static_url("pics/domains.png"), @@ -129,6 +118,19 @@ def domains_menu(selection, user): {"entries" : entries, "selection" : selection, "user" : user}) +@register.simple_tag +def permissions_menu(user): + entries = [ + {"name" : "addperm", + "label" : _("Add permission"), + "img" : static_url("pics/add.png")}, + {"name" : "delperms", + "label" : _("Remove permissions"), + "img" : static_url("pics/remove.png")}, + ] + return render_to_string('common/menu.html', + {"entries" : entries, "user" : user}) + @register.simple_tag def loadadminextmenu(user): menu = events.raiseQueryEvent("AdminMenuDisplay", target="admin_menu_box", diff --git a/admin/urls.py b/admin/urls.py index 8ffe45754..bdd176238 100644 --- a/admin/urls.py +++ b/admin/urls.py @@ -16,11 +16,13 @@ urlpatterns = patterns('modoboa.admin.views', url(r'^domains/(?P\d+)/aliases/$', "aliases", name="full-aliases"), url(r'^domains/(?P\d+)/aliases/(?P\d+)/$', "aliases", name="mbox-aliases"), (r'^domains/(?P\d+)/delalias/(?P\d+)/$', 'delalias'), - (r'^settings/$', 'settings'), - (r'^settings/addperm/$', 'addpermission'), + (r'^settings/$', 'viewparameters'), (r'^settings/delperm/(?P\d+)/(?P\w+)/$', 'deletepermission'), (r'^settings/parameters/$', 'viewparameters'), (r'^settings/parameters/save/$', 'saveparameters'), (r'^settings/extensions/$', 'viewextensions'), (r'^settings/extensions/save/$', 'saveextensions'), + (r'^permissions/$', 'permissions'), + (r'^permissions/superadmin/add/$', 'add_superadmin'), + (r'^permissions/domainadmin/add/$', 'add_domain_admin'), ) diff --git a/admin/views.py b/admin/views.py index 81226fdf0..2b2e33699 100644 --- a/admin/views.py +++ b/admin/views.py @@ -14,11 +14,10 @@ from django.contrib.auth.models import User, Group from django.contrib import messages from modoboa import admin, userprefs -from modoboa.admin.models import * -from forms import MailboxForm, DomainForm, AliasForm, PermissionForm +from models import * +from forms import * from modoboa.lib.authbackends import crypt_password -from modoboa.lib import _render, _ctx_ok, _ctx_ko, getctx -from modoboa.lib import events, parameters +from modoboa.lib import _render, _ctx_ok, _ctx_ko, getctx, events, parameters from modoboa.lib.models import Parameter import string import copy @@ -88,7 +87,7 @@ def newdomain(request): return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") form = DomainForm() - return render_to_response('admin/newdomain.html', { + return _render(request, 'admin/newdomain.html', { "form" : form }) @@ -356,43 +355,87 @@ def delalias(request, dom_id, alias_id): args=[dom_id])) @login_required -@user_passes_test(lambda u: u.is_superuser) -def settings(request): - admins = User.objects.filter(is_superuser=True) +@permission_required("auth.view_permissions") +def permissions(request): + permtables = [] + if request.user.is_superuser: + admins = User.objects.filter(is_superuser=True) + admins_list = [] + for admin in admins: + admins_list += [{"id" : admin.id, "user_name" : admin.username, + "full_name" : "%s %s" % (admin.first_name, admin.last_name), + "enabled" : admin.is_active}] + permtables += [ + {"title" : _("Super administrators"), + "content" : SuperAdminsTable(admins_list).render(request), + "addurl" : reverse(add_superadmin)} + ] + + permtables += events.raiseQueryEvent("PermsGetTables", user=request.user) + domadmins = Mailbox.objects.filter(user__groups__name="DomainAdmins") + domadmins_list = [] + for admin in domadmins: + domadmins_list += [admin.tohash()] + permtables += [ + {"title" : _("Domain administrators"), + "content" : DomainAdminsTable(domadmins_list).render(request), + "addurl" : reverse(add_domain_admin)} + ] + return _render(request, 'admin/permissions.html', { - "admins" : admins, "domadmins" : domadmins + "permtables" : permtables }) @login_required @user_passes_test(lambda u: u.is_superuser) -def addpermission(request): +def add_superadmin(request): if request.method == "POST": - form = PermissionForm(request.POST) - if request.POST.has_key('user'): - mboxid = request.POST['user'] - form.fields["user"].choices = \ - [(mboxid, Mailbox.objects.get(pk=mboxid)),] + form = SuperAdminForm(request.POST) if form.is_valid(): - mb = Mailbox.objects.get(pk=request.POST["user"]) - if request.POST["role"] == "SuperAdmins": - mb.user.is_superuser = True - mb.user.groups.clear() - else: - mb.user.is_superuser = False - mb.user.groups.add(Group.objects.get(name=request.POST["role"])) - mb.user.save() - ctx = _ctx_ok(reverse(admin.views.settings)) + user = User.objects.get(pk=request.POST["user"]) + user.is_superuser = True + user.groups.clear() + user.save() + ctx = _ctx_ok(reverse(admin.views.permissions)) return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") - ctx = _ctx_ko("admin/addpermission.html", { + ctx = _ctx_ko("admin/add_superadmin.html", { "form" : form }) return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") - - form = PermissionForm() - return _render(request, 'admin/addpermission.html', { - "form" : form + + form = SuperAdminForm() + return _render(request, 'admin/add_permission.html', { + "form" : form, "title" : _("Add super administrator"), + "action" : reverse(add_superadmin) + }) + +@login_required +@permission_required("domains.create") +def add_domain_admin(request): + if request.method == "POST": + form = DomainAdminForm(request.POST) + mboxid = request.POST['user'] + form.fields["user"].choices = \ + [(mboxid, Mailbox.objects.get(pk=mboxid)),] + if form.is_valid(): + mb = Mailbox.objects.get(pk=request.POST["user"]) + mb.user.is_superuser = False + mb.user.groups.add(Group.objects.get(name="DomainAdmins")) + mb.user.save() + ctx = _ctx_ok(reverse(admin.views.permissions)) + return HttpResponse(simplejson.dumps(ctx), + mimetype="application/json") + ctx = _ctx_ko("admin/add_domain_admin.html", { + "form" : form + }) + return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + + form = DomainAdminForm() + return _render(request, 'admin/add_domain_admin.html', { + "form" : form, "title" : _("Add domain administrator"), + "action" : reverse(add_domain_admin) }) @login_required diff --git a/lib/events.py b/lib/events.py index 2d7bdc5a7..afb65a3ce 100644 --- a/lib/events.py +++ b/lib/events.py @@ -15,6 +15,8 @@ events = ["CreateDomain", "UserMenuDisplay", "AdminMenuDisplay", + "PermsGetTables", + "UserLogin", "UserLogout"] diff --git a/lib/tables.py b/lib/tables.py index 98b322c1f..58a44fcca 100644 --- a/lib/tables.py +++ b/lib/tables.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import inspect import lxml +from django.utils.translation import ugettext as _ from django.template import Template, Context from django.template.loader import render_to_string @@ -83,15 +84,20 @@ class Table(object): self.rows += [nrow] def render(self, request): - t = Template(""" + if len(self.rows): + t = Template(""" {% include "common/table_head.html" %} {% include "common/table_body.html" %}
""") - return t.render(Context({ - "table" : self, "tableid" : self.tableid - })) + return t.render(Context({ + "table" : self, "tableid" : self.tableid + })) + t = Template(""" +
%s
+""" % _("No entries to display")) + return t.render(Context()) def render_head(self, request): t = Template(""" diff --git a/static/js/common.js b/static/js/common.js index a6841c5c8..4c730afe4 100644 --- a/static/js/common.js +++ b/static/js/common.js @@ -1,7 +1,7 @@ window.addEvent('domready', function(){ parse_menubar('topmenubar'); parse_menubar('menubar'); - + infobox = new InfoBox({ parent : $("menubar"), message : "Loading" @@ -21,7 +21,7 @@ get_callback = function(name) { current_anchor = null; check_anchor = function() { - if (current_anchor.serialized == location.hash + if (current_anchor.serialized == location.hash && !$defined(current_anchor.force)) { return; } @@ -67,9 +67,9 @@ function HashWrapper(deflocation) { }; this.parse_string = function(value, reset) { - var splits = (value.charAt(0) == '#') + var splits = (value.charAt(0) == '#') ? value.substring(1).split('?') : value.split('?'); - + if (splits.length == 0) { return this; } @@ -143,7 +143,7 @@ function HashWrapper(deflocation) { this.params.set(name, value); return this; }; - + this.setparams = function(params) { this.params.extend(params); return this; @@ -191,7 +191,7 @@ parse_menubar = function(id) { var myFx = new Fx.Slide(list, { duration: 200 }); - + myFx.wrapper.setStyles({ /*"clear" : "both",*/ "position" : "absolute" @@ -227,12 +227,12 @@ get_iframe_body = function(id) { data = targetif.contentDocument.defaultView.document.body; } else { data = targetif.document.body; - } + } } else if (targetif.contentWindow && !saf) { // IE 5.5 & 6.x data = targetif.contentWindow.document.body; } - return (data); + return (data); } confirmation = function(question, action, callback) { @@ -244,12 +244,12 @@ confirmation = function(question, action, callback) { var elt = ibody.getElement("input[name=result]"); if (elt.get("value") == "ok") { - params = "" + params = ""; if (typeof callback != "undefined") { params = callback(ibody); } new Request({ - method: "get", + method: "get", url: action.get("href"), onSuccess: function(res) { window.location.reload(); @@ -262,7 +262,7 @@ confirmation = function(question, action, callback) { } setDivHeight = function(id, extrah, modulo) { - var contentsize = $(document.body).getSize().y + var contentsize = $(document.body).getSize().y - $("topmenubar").getSize().y - $("header").getSize().y - $("footer").getSize().y @@ -326,7 +326,7 @@ searchbox_init = function() { $("searchfield").addEvent("click", function(event) { $("searchfield").select(); }); - + $("searchfield").addEvent("blur", function(event) { if ($("searchfield").value == "") { $("searchfield").set("value", gettext("Search...")); @@ -352,7 +352,7 @@ gethref = function(obj) { var url = obj.get("href"); var re = new RegExp("^(https?):"); var scheme = re.exec(url); - + if (scheme != null) { var baseurl = scheme[0] + "://" + location.host + location.pathname; return url.replace(baseurl, ""); diff --git a/static/js/domains.js b/static/js/domains.js index 9840dbe67..fda427149 100644 --- a/static/js/domains.js +++ b/static/js/domains.js @@ -1,34 +1,24 @@ window.addEvent('domready', function() { var handlers = new Array(); - + if ($defined($("domcontent"))) { SqueezeBox.assign($("domcontent").getElements('a[class=boxed]'), { - parse: 'rel', + parse: 'rel', }); } if ($defined($("subcontent"))) { SqueezeBox.assign($("subcontent").getElements('a[class=boxed]'), { - parse: 'rel', + parse: 'rel', }); } -// handlers['loadDom'] = function(obj, target) { -/* $$('a[name=loadDom]').setStyles({'color' : '#999'});*/ -/* obj.setStyles({"color": "black"});*/ -// loadPage(target, "content", null); -// }; - -// handlers['loadContent'] = function(obj, target) { -// loadPage(target, "content", null); -// }; - if ($$("a[name=deleteDom]")) $$("a[name=deleteDom]").addEvent('click', function(event) { if (!confirm(gettext("Delete this domain?"))) event.stop(); }); - + if ($$("a[name=deleteMb]")) $$("a[name=deleteMb]").addEvent('click', function(event) { event.stop(); @@ -50,7 +40,7 @@ window.addEvent('domready', function() { event.stop(); }); - + $(document.body).addEvent('click', function(event) { target = event.target; if (target.get('tag') == 'a') { @@ -92,7 +82,7 @@ newdom = function() { loadPage = function(url, destination, callback) { var destination = (destination == null) ? "page" : destination; - + if (callback) new Request({url: url, method: "get", onSuccess: callback}).send(); else diff --git a/static/js/permissions.js b/static/js/permissions.js new file mode 100644 index 000000000..5520513b0 --- /dev/null +++ b/static/js/permissions.js @@ -0,0 +1,28 @@ +window.addEvent('domready', function() { + var tip = new Tips($$(".Tips")); + + var addperm_url = ""; + var accordion = new Accordion('h3.atStart', 'div.atStart', { + opacity: true, + onActive: function(toggler, element){ + toggler.setStyle('color', '#ff3300'); + addperm_url = element.get("name"); + }, + + onBackground: function(toggler, element) { + toggler.setStyle('color', '#222'); + } + }, $('accordion')); + + $$("a[name=addperm]").addEvent("click", function(evt) { + evt.stop(); + if (addperm_url == "") { + return; + } + SqueezeBox.initialize({ + size: {x: 300, y: 280}, + handler: 'iframe' + }); + SqueezeBox.open(addperm_url); + }); +}); \ No newline at end of file diff --git a/templates/admin/add_domain_admin.html b/templates/admin/add_domain_admin.html new file mode 100644 index 000000000..2ae58fd3f --- /dev/null +++ b/templates/admin/add_domain_admin.html @@ -0,0 +1,20 @@ +{% extends "admin/add_permission.html" %} +{% block html_head %} +{{ block.super }} + +{% endblock %} diff --git a/templates/admin/add_permission.html b/templates/admin/add_permission.html new file mode 100644 index 000000000..0c0e3f73d --- /dev/null +++ b/templates/admin/add_permission.html @@ -0,0 +1,18 @@ +{% extends "layout_simple.html" %} +{% load i18n %} +{% block html_head %} + +{% endblock %} +{% block content %} +

{{ title }}

+ +{% if error %}
{{ error }}
{% endif %} +
+ {% include "common/genericform.html" %} +
+ +
+ +
+
+{% endblock %} diff --git a/templates/admin/addpermission.html b/templates/admin/addpermission.html deleted file mode 100644 index 739727da9..000000000 --- a/templates/admin/addpermission.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "layout_simple.html" %} -{% load i18n %} -{% block html_head %} - - - -{% endblock %} -{% block content %} -

{% trans "Add permission" %}

- -{% if error %}
{{ error }}
{% endif %} -{% url admin.views.addpermission as addperm_url %} -
- {% include "common/genericform.html" %} -
- -
- -
-
-{% endblock %} diff --git a/templates/admin/permissions.html b/templates/admin/permissions.html index 62eacde3d..95ddf0f63 100644 --- a/templates/admin/permissions.html +++ b/templates/admin/permissions.html @@ -1,62 +1,19 @@ -{% extends "admin/settings_header.html" %} +{% extends "nlayout.html" %} +{% load i18n admin_extras %} +{% block html_head %} + +{% endblock %} +{% block middletitle %}{% trans "Permissions" %}{% endblock %} {% block menu %} -{% load admin_extras %} -{% settings_menu "permissions" user %} +{% permissions_menu user %} {% endblock %} -{% block setcontent %} -{% load i18n %} -{% if not admins %} -

{% trans "No superuser defined?!" %}

-{% else %} -

{% trans "Super administrators" %}

- - - - - - - - {% for admin in admins %} - - - - - - +{% block content %} +
+ {% for table in permtables %} +

{{ table.title }}

+
+ {{ table.content }} +
{% endfor %} -
{% trans "Actions" %}{% trans "Full name" %}{% trans "User name" %}{% trans "Enabled" %}
- {% url admin.views.deletepermission admin.id "SuperAdmins" as del_url %} - - - - {{ admin.first_name }} {{ admin.last_name }}{{ admin.username }}{{ admin.is_active|yesno }}
-

-{% endif %} -

{% trans "Domain administrators" %}

-{% if not domadmins %} -

{% trans "No domain admin defined." %}

-{% else %} - - - - - - - - {% for admin in domadmins %} - - - - - - - {% endfor %} -
{% trans "Actions" %}{% trans "Domain" %}{% trans "Name" %}{% trans "Enabled" %}
- {% url admin.views.deletepermission admin.id "DomainAdmins" as del_url %} - - - - {{ admin.domain.name }}{{ admin.name }}{{ admin.user.is_active|yesno }}
-{% endif %} + {% endblock %}