From 13c699e85522b112d6722b3ae276a3a807fdd77c Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 11 Apr 2012 15:07:04 +0200 Subject: [PATCH] * Added a simple box to display the current resource usage (see #105) * Cleanup (see #204) * Fixed javascript issues --- admin/forms.py | 2 +- admin/templatetags/admin_extras.py | 4 +- admin/views.py | 1 + extensions/limits/__init__.py | 70 +++++++++++-------- extensions/limits/controls.py | 6 +- extensions/limits/forms.py | 14 ++-- extensions/limits/lib.py | 2 +- extensions/limits/models.py | 31 +++++--- extensions/limits/permissions.py | 10 --- extensions/limits/static/js/resellers.js | 42 ----------- extensions/limits/tables.py | 24 ------- extensions/limits/templates/limits/pool.html | 22 ------ .../limits/templates/limits/reseller.html | 16 ----- .../limits/templates/limits/resellers.html | 43 ------------ extensions/limits/templatetags/__init__.py | 0 extensions/limits/templatetags/limits_tags.py | 44 ------------ extensions/limits/urls.py | 3 +- extensions/limits/views.py | 62 ---------------- lib/events.py | 2 +- lib/templatetags/libextras.py | 9 +++ static/css/admin.css | 2 +- static/js/admin.js | 5 +- static/js/creation_wizard.js | 7 +- templates/admin/domains.html | 2 +- templates/admin/identities.html | 2 +- templates/admin/listing.html | 2 +- templates/common/tabforms.html | 2 +- 27 files changed, 106 insertions(+), 323 deletions(-) delete mode 100644 extensions/limits/permissions.py delete mode 100644 extensions/limits/static/js/resellers.js delete mode 100644 extensions/limits/tables.py delete mode 100644 extensions/limits/templates/limits/pool.html delete mode 100644 extensions/limits/templates/limits/reseller.html delete mode 100644 extensions/limits/templates/limits/resellers.html delete mode 100644 extensions/limits/templatetags/__init__.py delete mode 100644 extensions/limits/templatetags/limits_tags.py diff --git a/admin/forms.py b/admin/forms.py index 3248d5563..f6d9a4216 100644 --- a/admin/forms.py +++ b/admin/forms.py @@ -246,7 +246,7 @@ class AccountFormGeneral(forms.ModelForm): role = None if self.cleaned_data.has_key("role"): role = self.cleaned_data["role"] - elif self.user.group == "DomainAdmins": + elif self.user.group == "DomainAdmins" and self.user != account: role = "SimpleUsers" if role is not None: account.groups.clear() diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 8b707452c..f473ca811 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -203,6 +203,6 @@ def gender(value, target): return value @register.simple_tag -def get_footer_content(user, objtype): - res = events.raiseQueryEvent("AdminFooterDisplay", user, objtype) +def get_extra_admin_content(user, target, currentpage): + res = events.raiseQueryEvent("ExtraAdminContent", user, target, currentpage) return "".join(res) diff --git a/admin/views.py b/admin/views.py index 5c54b1868..f6ae79024 100644 --- a/admin/views.py +++ b/admin/views.py @@ -355,6 +355,7 @@ def editaccount(request, accountid, tplname="common/tabforms.html"): ctx = dict( title=account.username, + formid="accountform", action=reverse(editaccount, args=[accountid]), action_label=_("Update"), action_classes="submit" diff --git a/extensions/limits/__init__.py b/extensions/limits/__init__.py index 96e016929..03911d940 100644 --- a/extensions/limits/__init__.py +++ b/extensions/limits/__init__.py @@ -57,36 +57,26 @@ def infos(): "url" : baseurl } -#@events.observe('AdminFooterDisplay') -def display_pool_usage(user, objtype): +@events.observe("GetExtraRoles") +def get_extra_roles(user): if user.is_superuser: - return [] - if objtype == "domaliases": - objtype = "domain_aliases" - elif objtype == "mbaliases": - objtype = "mailbox_aliases" - try: - l = user.limitspool.get_limit('%s_limit' % objtype) - except LimitsPool.DoesNotExist: - return [] - if l.maxvalue == -2: - label = _("undefined") - elif l.maxvalue == -1: - label = _("unlimited") - else: - label = str(l.maxvalue) - return [_("Pool usage: %s / %s") % (l.curvalue, label)] + return [("Resellers", _("Reseller")),] + return [] -@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}}"}, - ] +@events.observe("ExtraAdminContent") +def display_pool_usage(user, target, currentpage): + from django.template.loader import render_to_string + + if target != "leftcol" or user.is_superuser: + return [] + if currentpage == "identities": + names = ["mailboxes_limit", "mailbox_aliases_limit"] + if user.has_perm("admin.add_domain"): + names += ["domain_admins_limit"] + else: + names = ["domains_limit", "domain_aliases_limit"] + limits = user.limitspool.limit_set.filter(name__in=names) + return [render_to_string("limits/poolusage.html", dict(limits=limits))] @events.observe("ExtraAccountForm") def extra_account_form(user, account=None): @@ -120,3 +110,27 @@ def fill_account_instances(user, account, instances): not account.belongs_to_group("DomainAdmins"): return instances["resources"] = account + +@events.observe("GetStaticContent") +def get_static_content(user): + if user.group == "SimpleUsers": + return [] + return """ + +""" diff --git a/extensions/limits/controls.py b/extensions/limits/controls.py index 17d0346c4..fc956bfd6 100644 --- a/extensions/limits/controls.py +++ b/extensions/limits/controls.py @@ -43,11 +43,11 @@ def move_pool_resource(owner, user): except LimitsPool.DoesNotExist: return if not owner.is_superuser: - for lname in reseller_limits_tpl: - l = user.limitspool.get_limit(lname) + for ltpl in limits_tpl: + l = user.limitspool.get_limit(ltpl[0]) if l.maxvalue < 0: continue - ol = owner.limitspool.get_limit(lname) + ol = owner.limitspool.get_limit(ltpl[0]) ol.curvalue += l.curvalue ol.maxvalue += l.maxvalue ol.save() diff --git a/extensions/limits/forms.py b/extensions/limits/forms.py index 9c3d85ab6..ceec8e590 100644 --- a/extensions/limits/forms.py +++ b/extensions/limits/forms.py @@ -49,10 +49,10 @@ class ResourcePoolForm(forms.Form): return self.check_limit_value("mailbox_aliases_limit") def load_from_user(self, user): - for l in reseller_limits_tpl: - if not self.fields.has_key(l): + for l in limits_tpl: + if not self.fields.has_key(l[0]): continue - self.fields[l].initial = user.limitspool.getmaxvalue(l) + self.fields[l[0]].initial = user.limitspool.getmaxvalue(l[0]) def allocate_from_pool(self, limit, pool): ol = pool.get_limit(limit.name) @@ -76,13 +76,13 @@ class ResourcePoolForm(forms.Form): from modoboa.lib.permissions import get_object_owner owner = get_object_owner(self.account) - for lname in reseller_limits_tpl: - if not self.cleaned_data.has_key(lname): + for ltpl in limits_tpl: + if not self.cleaned_data.has_key(ltpl[0]): continue - l = self.account.limitspool.limit_set.get(name=lname) + l = self.account.limitspool.limit_set.get(name=ltpl[0]) if not owner.is_superuser: self.allocate_from_pool(l, owner.limitspool) - l.maxvalue = self.cleaned_data[lname] + l.maxvalue = self.cleaned_data[ltpl[0]] l.save() diff --git a/extensions/limits/lib.py b/extensions/limits/lib.py index 2bfbe0db5..b05902168 100644 --- a/extensions/limits/lib.py +++ b/extensions/limits/lib.py @@ -13,7 +13,7 @@ class LimitReached(ModoboaException): self.limit = limit def __str__(self): - return "%s reached" % self.limit.name + return _("%s: limit reached") % self.limit.label class UnsifficientResource(ModoboaException): def __init__(self, limit): diff --git a/extensions/limits/models.py b/extensions/limits/models.py index 90694e592..47c814ac9 100644 --- a/extensions/limits/models.py +++ b/extensions/limits/models.py @@ -5,21 +5,21 @@ from django.contrib.contenttypes import generic from django.utils.translation import ugettext as _, ugettext_noop from modoboa.admin.models import User -reseller_limits_tpl = [ - "domain_admins_limit", - "domains_limit", - "domain_aliases_limit", - "mailboxes_limit", - "mailbox_aliases_limit", +limits_tpl = [ + ("domain_admins_limit", ugettext_noop("Domain admins")), + ("domains_limit", ugettext_noop("Domains")), + ("domain_aliases_limit", ugettext_noop("Domain aliases")), + ("mailboxes_limit", ugettext_noop("Mailboxes")), + ("mailbox_aliases_limit", ugettext_noop("Mailbox aliases")) ] class LimitsPool(models.Model): user = models.OneToOneField(User) def create_limits(self): - for lname in reseller_limits_tpl: + for ltpl in limits_tpl: l = Limit() - l.name = lname + l.name = ltpl[0] l.pool = self l.save() @@ -79,3 +79,18 @@ class Limit(models.Model): class Meta: unique_together = (("name", "pool"),) + + @property + def usage(self): + if self.maxvalue < 0: + return -1 + if self.maxvalue == 0: + return 0 + return int(float(self.curvalue) / self.maxvalue * 100) + + @property + def label(self): + for l in limits_tpl: + if l[0] == self.name: + return l[1] + return "" diff --git a/extensions/limits/permissions.py b/extensions/limits/permissions.py deleted file mode 100644 index 2f5b316b8..000000000 --- a/extensions/limits/permissions.py +++ /dev/null @@ -1,10 +0,0 @@ -# coding: utf-8 - -from django.utils.translation import ugettext as _, ugettext_noop -from modoboa.lib import events - -@events.observe("GetExtraRoles") -def get_extra_roles(user): - if user.is_superuser: - return [("Resellers", _("Reseller")),] - return [] diff --git a/extensions/limits/static/js/resellers.js b/extensions/limits/static/js/resellers.js deleted file mode 100644 index 6f68e7f2a..000000000 --- a/extensions/limits/static/js/resellers.js +++ /dev/null @@ -1,42 +0,0 @@ -var Resellers = new Class({ - initialize: function() { - this.resellers_table = new HtmlTable($("resellers_table"), { - selectable: true - }); - SqueezeBox.assign($("content").getElements('a[class=boxed]'), { - parse: 'rel' - }); - - $$("a[name=remove]").addEvent("click", this.remove_clickcb.bind(this)); - }, - - remove_clickcb: function(evt) { - evt.stop(); - if (this.resellers_table._selectedRows.length == 0) { - return; - } - - if (!confirm(gettext("Remove this selection?"))) { - return; - } - - var selection = ""; - - this.resellers_table._selectedRows.each(function(item) { - if (selection != "") { - selection += ","; - } - selection += item.get("id"); - }); - - new Request.JSON.mdb({ - url: evt.target.get("href"), - method: "GET", - onSuccess: function(res) { - if (res.status == "ok") { - window.location.reload(); - } - } - }).send("selection=" + selection); - } -}); \ No newline at end of file diff --git a/extensions/limits/tables.py b/extensions/limits/tables.py deleted file mode 100644 index b4c7a2fce..000000000 --- a/extensions/limits/tables.py +++ /dev/null @@ -1,24 +0,0 @@ -# coding: utf-8 - -from django.utils.translation import ugettext as _, ugettext_noop -from modoboa.lib import tables -from templatetags.limits_tags import reseller_actions - -class ResellersTable(tables.Table): - tableid = "resellers_table" - idkey = "id" - - selection = tables.SelectionColumn("selection", width="4%") - fname = tables.Column("first_name", label=ugettext_noop("First name")) - lname = tables.Column("last_name", label=ugettext_noop("Last name")) - username = tables.Column("username", label=ugettext_noop("Username")) - created = tables.Column("date_joined", label=ugettext_noop("Created on")) - actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"), - width="50px", - align="center", defvalue=reseller_actions) - - cols_order = ["selection", "username", "fname", "lname", "created", "actions"] - - def __init__(self, request, users): - super(ResellersTable, self).__init__(request) - self.populate(self._rows_from_model(users)) diff --git a/extensions/limits/templates/limits/pool.html b/extensions/limits/templates/limits/pool.html deleted file mode 100644 index 5f4da7005..000000000 --- a/extensions/limits/templates/limits/pool.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "layout_simple.html" %} -{% load i18n %} -{% block html_head %} - - -{% endblock %} -{% block content %} -

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

-
{% if error %}{{ error }}{% endif %}
-
- {% include "common/genericform.html" %} -
-
- -
-
-{% endblock %} diff --git a/extensions/limits/templates/limits/reseller.html b/extensions/limits/templates/limits/reseller.html deleted file mode 100644 index 2655cfb32..000000000 --- a/extensions/limits/templates/limits/reseller.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "layout_simple.html" %} -{% load i18n %} -{% block html_head %} - -{% endblock %} -{% block content %} -

{% trans "New reseller" %}

-{% if error %}
{{ error }}
{% endif %} -
- {% include "common/genericform.html" %} -
-
- -
-
-{% endblock %} diff --git a/extensions/limits/templates/limits/resellers.html b/extensions/limits/templates/limits/resellers.html deleted file mode 100644 index 0bd6594b0..000000000 --- a/extensions/limits/templates/limits/resellers.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "nlayout.html" %} -{% load i18n limits_tags libextras %} -{% block html_head %} - - -{% endblock %} -{% block middletitle %}{% trans "Available resellers" %}{% endblock %} -{% block menu %}{% resellers_menu user %} {% endblock %} -{% block content %} -{% if messages %} -{% display_messages messages %} -{% endif %} -
- {{ table|safe }} -
-{% endblock %} -{% block footer %} - -{% endblock %} diff --git a/extensions/limits/templatetags/__init__.py b/extensions/limits/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/extensions/limits/templatetags/limits_tags.py b/extensions/limits/templatetags/limits_tags.py deleted file mode 100644 index 37310b0d2..000000000 --- a/extensions/limits/templatetags/limits_tags.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding: utf-8 - -from django import template -from django.utils.translation import ugettext as _ -from django.template.loader import render_to_string -from django.core.urlresolvers import reverse -from modoboa.lib.webutils import static_url, render_actions -from modoboa.extensions.limits import views - -register = template.Library() - -@register.simple_tag -def resellers_menu(user): - entries = [{"name" : "new", - "label" : _("New"), - "img" : static_url("pics/add.png"), - "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:350,y:300}}", - "url" : reverse(views.new_reseller)}, - {"name" : "remove", - "label" : _("Remove"), - "img" : static_url("pics/remove.png"), - "url" : reverse(views.delete_resellers) - }] - return render_to_string("common/menulist.html", - {"entries" : entries, "user" : user}) - -@register.simple_tag -def reseller_actions(user, resid): - actions = [ - {"name" : "editreseller", - "url" : reverse(views.edit_reseller, args=[resid]), - "img" : static_url("pics/edit.png"), - "title" : _("Edit reseller"), - "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:350,y:300}}"}, - {"name" : "editlimits", - "url" : reverse(views.edit_limits_pool, args=[resid]), - "img" : static_url("pics/settings.png"), - "title" : _("Edit limits allocated to this reseller"), - "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:330,y:310}}"}, - ] - return render_actions(actions) diff --git a/extensions/limits/urls.py b/extensions/limits/urls.py index 8f611080c..a0af85f49 100644 --- a/extensions/limits/urls.py +++ b/extensions/limits/urls.py @@ -2,6 +2,5 @@ from django.conf.urls.defaults import * urlpatterns = patterns( 'modoboa.extensions.limits.views', - (r'reseller/edit/(?P\d+)/$', 'edit_reseller'), - (r'pool/edit/(?P\d+)/$', 'edit_limits_pool'), + ) diff --git a/extensions/limits/views.py b/extensions/limits/views.py index 638100781..57d631c3f 100644 --- a/extensions/limits/views.py +++ b/extensions/limits/views.py @@ -1,63 +1 @@ # coding: utf-8 -from django.utils.translation import ugettext as _ -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, permission_required -from django.core.urlresolvers import reverse -from forms import * -from modoboa.lib.webutils import _render, ajax_response - -@login_required -@user_passes_test(lambda u: u.is_superuser) -def edit_reseller(request, resid, tplname="admin/add_permission.html"): - reseller = User.objects.get(pk=resid) - ctx = dict(title=_("Edit reseller"), submit_label=_("Update"), - addurl=reverse(edit_reseller, args=[resid])) - if request.method == "POST": - if request.POST.get("password1", "") != "" and \ - request.POST.get("password2", "") != "": - form = ResellerWithPasswordForm(request.POST, instance=reseller) - else: - form = ResellerForm(request.POST, instance=reseller) - if form.is_valid(): - try: - form.save() - except IntegrityError: - error = _("User with this Username already exists") - else: - messages.info(request, _("Reseller updated"), fail_silently=True) - return ajax_response(request, - url=reverse("modoboa.admin.views.permissions")) - - ctx.update(form=form, error=error) - return ajax_response(request, status="ko", template=tplname, **ctx) - - form = ResellerWithPasswordForm(instance=reseller) - ctx.update(form=form) - return _render(request, tplname, ctx) - -@login_required -@permission_required("auth.view_permissions") -@transaction.commit_on_success -def edit_limits_pool(request, uid, tplname="limits/pool.html"): - user = User.objects.get(pk=uid) - ctx = dict(reseller=user) - if request.method == "POST": - form = ResellerPoolForm(request.POST) - if not user.belongs_to_group('Resellers'): - del form.fields["domain_admins_limit"] - if form.is_valid(): - form.save_new_limits(user.limitspool) - messages.info(request, _("Pool updated"), fail_silently=True) - return ajax_response(request, url=reverse("modoboa.admin.views.permissions")) - ctx.update(form=form) - return ajax_response(request, status="ko", template=tplname, **ctx) - - form = ResellerPoolForm() - if not user.belongs_to_group('Resellers'): - del form.fields["domain_admins_limit"] - form.load_from_user(user) - ctx.update(form=form) - return _render(request, tplname, ctx) diff --git a/lib/events.py b/lib/events.py index 37c7eca83..627dd9003 100644 --- a/lib/events.py +++ b/lib/events.py @@ -54,7 +54,7 @@ events = [ "GetAnnouncement", "TopNotifications", - "AdminFooterDisplay", + "ExtraAdminContent", ] callbacks = {} diff --git a/lib/templatetags/libextras.py b/lib/templatetags/libextras.py index ab00740e5..f71ecbc12 100644 --- a/lib/templatetags/libextras.py +++ b/lib/templatetags/libextras.py @@ -142,3 +142,12 @@ class="{{ mdclass }}{% if link.class %} {{ link.class }}{% endif %}" {% if link.img %}{% endif %} {{ link.label }}""") return t.render(Context(dict(link=linkdef, mdclass=mdclass))) + +@register.simple_tag +def progress_color(value): + value = int(value) + if value < 50: + return "progress-success" + if value < 80: + return "progress-warning" + return "progress-danger" diff --git a/static/css/admin.css b/static/css/admin.css index 97b6fff58..9f3729324 100644 --- a/static/css/admin.css +++ b/static/css/admin.css @@ -1,4 +1,4 @@ #searchform { text-align: center; margin-bottom: 10px; -} \ No newline at end of file +} diff --git a/static/js/admin.js b/static/js/admin.js index 1fa65fb48..464ae1be9 100644 --- a/static/js/admin.js +++ b/static/js/admin.js @@ -3,6 +3,7 @@ function domainform_cb() { $("#id_aliases").dynamic_input(); $(".submit").one('click', function(e) { simple_ajax_form_post(e, { + formid: "domform", error_cb: domainform_cb }); }); @@ -24,13 +25,14 @@ function accountform_init() { function newaccount_cb() { $('input:text:visible:first').focus(); accountform_init(); - $("#wizard").cwizard(); + $("#wizard").cwizard({formid: "newaccount_form"}); } function editaccount_cb() { accountform_init(); $('.submit').one('click', function(e) { simple_ajax_form_post(e, { + formid: "accountform", error_cb: editaccount_cb }); }); @@ -44,6 +46,7 @@ function dlistform_cb() { $("#id_recipients").dynamic_input(); $(".submit").one('click', function(e) { simple_ajax_form_post(e, { + formid: "dlistform", error_cb: dlistform_cb }); }); diff --git a/static/js/creation_wizard.js b/static/js/creation_wizard.js index 68e5db07f..9777c29be 100644 --- a/static/js/creation_wizard.js +++ b/static/js/creation_wizard.js @@ -1,6 +1,7 @@ (function($) { var Cwizard = function(element, options) { this.$element = $(element); + this.options = $.extend({}, $.fn.cwizard.defaults, options); this.$element.carousel({pause: true}); this.$element.carousel("pause"); this.setcallbacks(); @@ -16,7 +17,7 @@ }, post: function(last) { - var $form = $('form'); + var $form = (this.options.formid) ? $('#' + this.options.formid) : $('form'); var data = $form.serialize() + "&stepid=" + $(".item.active").attr("id"); $.post($form.attr("action"), data, $.proxy(function(resp) { @@ -79,4 +80,8 @@ }); }; + $.fn.cwizard.defaults = { + + }; + })(jQuery); \ No newline at end of file diff --git a/templates/admin/domains.html b/templates/admin/domains.html index 9e49e9f21..f9cd9616c 100644 --- a/templates/admin/domains.html +++ b/templates/admin/domains.html @@ -20,4 +20,4 @@ {% endblock %} {% block leftcol %}{% endblock %} +{% domains_menu "domains" user %}{{ block.super }}{% endblock %} diff --git a/templates/admin/identities.html b/templates/admin/identities.html index f42b67528..3f52867fe 100644 --- a/templates/admin/identities.html +++ b/templates/admin/identities.html @@ -32,4 +32,4 @@ $(document).ready(function() { {% endblock %} {% block leftcol %}{% endblock %} +{% identities_menu user %}{{ block.super }}{% endblock %} diff --git a/templates/admin/listing.html b/templates/admin/listing.html index 52ff52c0c..abeb560fb 100644 --- a/templates/admin/listing.html +++ b/templates/admin/listing.html @@ -16,8 +16,8 @@ $(document).ready(function() { }); {% endblock %} +{% block leftcol %}{% get_extra_admin_content user "leftcol" selection %}{% endblock %} {% block apparea %} {{ table|safe }} {% endblock %} -{% block footerleft %}
{% get_footer_content user selection %}
{% endblock %} {% block footerright %}{% pagination_bar page %}{% endblock %} diff --git a/templates/common/tabforms.html b/templates/common/tabforms.html index 5eb25f104..5d7838b45 100644 --- a/templates/common/tabforms.html +++ b/templates/common/tabforms.html @@ -1,7 +1,7 @@ {% extends "common/generic_modal.html" %} {% load i18n libextras %} {% block modalbody %} -
+