* Added a simple box to display the current resource usage (see #105)

* Cleanup (see #204)
* Fixed javascript issues
This commit is contained in:
Antoine Nguyen
2012-04-11 15:07:04 +02:00
parent 303c84eb64
commit 13c699e855
27 changed files with 106 additions and 323 deletions
+1 -1
View File
@@ -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()
+2 -2
View File
@@ -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)
+1
View File
@@ -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"
+42 -28
View File
@@ -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 """<style>
.resource {
margin: 5px 0;
}
.resource .progress {
margin-bottom: 0px;
}
.resource .progress .bar {
color: #000000;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$(".progress").tooltip();
});
</script>
"""
+3 -3
View File
@@ -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()
+7 -7
View File
@@ -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()
+1 -1
View File
@@ -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):
+23 -8
View File
@@ -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 ""
-10
View File
@@ -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 []
-42
View File
@@ -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);
}
});
-24
View File
@@ -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))
@@ -1,22 +0,0 @@
{% extends "layout_simple.html" %}
{% load i18n %}
{% block html_head %}
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
<script type="text/javascript">
window.addEvent("domready", function() {
var form = new Form();
});
</script>
{% endblock %}
{% block content %}
<div class="title"><h1>{% trans "Limits pool for" %} {{ reseller.username }}</h1></div>
<div class="error">{% if error %}{{ error }}{% endif %}</div>
<form action="{% url modoboa.extensions.limits.views.edit_limits_pool reseller.id %}"
method="POST" id="limits_pool_form" name="limits_pool_form">
{% include "common/genericform.html" %}
<div class="spacer"></div>
<div class="lastrow" align="center">
<input type="submit" name="submit" value="{% trans 'Update' %}" />
</div>
</form>
{% endblock %}
@@ -1,16 +0,0 @@
{% extends "layout_simple.html" %}
{% load i18n %}
{% block html_head %}
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
{% endblock %}
{% block content %}
<div class="title"><h1>{% trans "New reseller" %}</h1></div>
{% if error %}<div class="error">{{ error }}</div>{% endif %}
<form action="" method="POST" id="reseller_form" name="reseller_form">
{% include "common/genericform.html" %}
<div class="spacer"></div>
<div class="lastrow" align="center">
<input type="submit" name="submit" value="{{ submit_label }}" />
</div>
</form>
{% endblock %}
@@ -1,43 +0,0 @@
{% extends "nlayout.html" %}
{% load i18n limits_tags libextras %}
{% block html_head %}
<script src="{{ MEDIA_URL }}limits/js/resellers.js" type="text/javascript"></script>
<script type="text/javascript">
window.addEvent("domready", function() {
new Resellers();
});
</script>
{% endblock %}
{% block middletitle %}{% trans "Available resellers" %}{% endblock %}
{% block menu %}{% resellers_menu user %} {% endblock %}
{% block content %}
{% if messages %}
{% display_messages messages %}
{% endif %}
<div id="resellers" name="resellers">
{{ table|safe }}
</div>
{% endblock %}
{% block footer %}
<div id="navbar">
{% if paginator.count %}
<label>{% trans "Items" %} {{ page.start_index }} {% trans "to" %} {{ page.end_index }} {% trans "of" %} {{ paginator.count }}</label>
{% endif %}
{% if page.has_previous %}
<a href="?page=1">
<img src="{{ MEDIA_URL }}pics/first.png" border="0" title="{% trans "First" %}" />
</a>
<a href="?page={{ page.previous_page_number }}">
<img src="{{ MEDIA_URL }}pics/back.png" border="0" title="{% trans "Previous" %}" />
</a>
{% endif %}
{% if page.has_next %}
<a href="?page={{ page.next_page_number }}">
<img src="{{ MEDIA_URL }}pics/next.png" border="0" title="{% trans "Next" %}" />
</a>
<a href="?page={{ page.last_page }}">
<img src="{{ MEDIA_URL }}pics/last.png" border="0" title="{% trans "Last" %}" />
</a>
{% endif %}
</div>
{% endblock %}
@@ -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)
+1 -2
View File
@@ -2,6 +2,5 @@ from django.conf.urls.defaults import *
urlpatterns = patterns(
'modoboa.extensions.limits.views',
(r'reseller/edit/(?P<resid>\d+)/$', 'edit_reseller'),
(r'pool/edit/(?P<uid>\d+)/$', 'edit_limits_pool'),
)
-62
View File
@@ -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)
+1 -1
View File
@@ -54,7 +54,7 @@ events = [
"GetAnnouncement",
"TopNotifications",
"AdminFooterDisplay",
"ExtraAdminContent",
]
callbacks = {}
+9
View File
@@ -142,3 +142,12 @@ class="{{ mdclass }}{% if link.class %} {{ link.class }}{% endif %}"
{% if link.img %}<i class="{{ link.img }}"></i>{% endif %}
{{ link.label }}</a>""")
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"
+1 -1
View File
@@ -1,4 +1,4 @@
#searchform {
text-align: center;
margin-bottom: 10px;
}
}
+4 -1
View File
@@ -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
});
});
+6 -1
View File
@@ -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);
+1 -1
View File
@@ -20,4 +20,4 @@
</script>
{% endblock %}
{% block leftcol %}<div class="well sidebar-nav">{% include "admin/searchform.html" %}
{% domains_menu "domains" user %}</div>{% endblock %}
{% domains_menu "domains" user %}</div>{{ block.super }}{% endblock %}
+1 -1
View File
@@ -32,4 +32,4 @@ $(document).ready(function() {
</script>
{% endblock %}
{% block leftcol %}<div class="well sidebar-nav">{% include "admin/searchform.html" %}
{% identities_menu user %}</div>{% endblock %}
{% identities_menu user %}</div>{{ block.super }}{% endblock %}
+1 -1
View File
@@ -16,8 +16,8 @@ $(document).ready(function() {
});
</script>
{% endblock %}
{% block leftcol %}{% get_extra_admin_content user "leftcol" selection %}{% endblock %}
{% block apparea %}
{{ table|safe }}
{% endblock %}
{% block footerleft %}<div id="adminfo">{% get_footer_content user selection %}</div>{% endblock %}
{% block footerright %}{% pagination_bar page %}{% endblock %}
+1 -1
View File
@@ -1,7 +1,7 @@
{% extends "common/generic_modal.html" %}
{% load i18n libextras %}
{% block modalbody %}
<form class="form-horizontal" action="{{ action }}" method="POST">
<form id="{{ formid }}" class="form-horizontal" action="{{ action }}" method="POST">
<ul class="nav nav-pills">
{% for tab in tabs %}
<li class="{% if tabs.active_id == tab.id %}active{% endif %}">