diff --git a/admin/lib.py b/admin/lib.py index 84d838dd6..c1cf1cc9d 100644 --- a/admin/lib.py +++ b/admin/lib.py @@ -2,8 +2,10 @@ from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth.models import User, Group from django.utils.translation import ugettext as _ +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from models import * -from modoboa.lib.webutils import _render_error +from modoboa.lib.webutils import _render_error, _render +from modoboa.admin.tables import * def is_not_localadmin(errortpl="error"): def dec(f): @@ -64,3 +66,40 @@ def good_domain(f): login_url = settings.LOGIN_URL return HttpResponseRedirect("%s?next=%s" % (login_url, path)) return dec + +def render_listing(request, objtype, tplname="admin/listing.html", + **kwargs): + """Common function to render a listing + + All listing pages available into the admin application use the + same layout, rendered by this function. + + :param request: a ``Request`` object + :param objtype: the object type's name (lowercase) + :param tplname: the template used to render the HTML + """ + tblclass = "%sTable" % objtype.capitalize() + if not globals().has_key(tblclass): + raise AdminError(_("Unknown object type")) + tblclass = globals()[tblclass] + + if request.GET.has_key("domid"): + kwargs["domid"] = request.GET["domid"] + else: + kwargs["domid"] = "" + kwargs["selection"] = objtype + paginator = Paginator(kwargs["objects"], + int(parameters.get_admin("ITEMS_PER_PAGE"))) + try: + page = request.GET.get("page", "1") + except ValueError: + page = 1 + try: + kwargs["objects"] = paginator.page(page) + except (EmptyPage, PageNotAnInteger): + kwargs["objects"] = paginator.page(paginator.num_pages) + kwargs["last_page"] = paginator.num_pages + kwargs["total"] = paginator.count + kwargs["table"] = tblclass(request, kwargs["objects"].object_list) + + return _render(request, tplname, kwargs) diff --git a/admin/views.py b/admin/views.py index 7c7b56472..f9e80b68d 100644 --- a/admin/views.py +++ b/admin/views.py @@ -9,7 +9,6 @@ from django.db.models import Q from django.contrib.auth.models import User, Group from django.contrib import messages from django.db import transaction, IntegrityError -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from lib import * from modoboa import admin, userprefs @@ -24,33 +23,6 @@ from modoboa.lib.emailutils import split_mailbox from modoboa.lib.models import Parameter import copy -def render_listing(request, objtype, tplname="admin/listing.html", - **kwargs): - tblclass = "%sTable" % objtype.capitalize() - if not globals().has_key(tblclass): - raise AdminError(_("Unknown object type")) - tblclass = globals()[tblclass] - if request.GET.has_key("domid"): - kwargs["domid"] = request.GET["domid"] - else: - kwargs["domid"] = "" - kwargs["selection"] = objtype - paginator = Paginator(kwargs["objects"], - int(parameters.get_admin("ITEMS_PER_PAGE"))) - try: - page = request.GET.get("page", "1") - except ValueError: - page = 1 - try: - kwargs["objects"] = paginator.page(page) - except (EmptyPage, PageNotAnInteger): - kwargs["objects"] = paginator.page(paginator.num_pages) - kwargs["last_page"] = paginator.num_pages - kwargs["total"] = paginator.count - kwargs["table"] = tblclass(request, kwargs["objects"].object_list) - - return _render(request, tplname, kwargs) - @login_required def domains(request): if not request.user.has_perm("admin.view_domains"): diff --git a/extensions/limits/__init__.py b/extensions/limits/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/extensions/limits/fixtures/initial_data.json b/extensions/limits/fixtures/initial_data.json new file mode 100644 index 000000000..6bd24036c --- /dev/null +++ b/extensions/limits/fixtures/initial_data.json @@ -0,0 +1 @@ +[{"pk": 2, "model": "auth.group", "fields": {"name": "Resellers", "permissions": [["add_domain", "admin", "domain"], ["change_domain", "admin", "domain"], ["delete_domain", "admin", "domain"], ["view_domains", "admin", "domain"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_domain", "admin", "domain"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}}] diff --git a/extensions/limits/forms.py b/extensions/limits/forms.py new file mode 100644 index 000000000..efabfc74e --- /dev/null +++ b/extensions/limits/forms.py @@ -0,0 +1,50 @@ +# coding: utf-8 + +from django import forms +from django.utils.translation import ugettext_noop as _ +from django.contrib.auth.models import User, Group +from models import * + +class ResellerForm(forms.ModelForm): + class Meta: + model = User + fields = ("username", "first_name", "last_name", "email") + + def save(self, commit=True): + user = super(ResellerForm, self).save(commit=False) + if self.cleaned_data.has_key("password1"): + user.set_password(self.cleaned_data["password1"]) + if commit: + user.save() + grp = Group.objects.get(name="Resellers") + if not grp in user.groups.all(): + user.groups.add(grp) + user.save() + try: + pool = LimitsPool.objects.get(user=user) + except LimitsPool.DoesNotExist: + pool = LimitsPool() + pool.user = user + pool.save() + return user + +class ResellerWithPasswordForm(ResellerForm): + password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput) + password2 = forms.CharField(label=_("Confirmation"), + widget=forms.PasswordInput, + help_text = _("Enter the same password as above, for verification.")) + + def clean_password2(self): + password1 = self.cleaned_data.get("password1", "") + password2 = self.cleaned_data["password2"] + if password1 != password2: + raise forms.ValidationError(_("The two password fields didn't match.")) + return password2 + +class LimitsPoolForm(forms.ModelForm): + class Meta: + model = LimitsPool + fields = ("maxdomains", "maxdomaliases", "maxmboxes", "maxmbaliases") + + + diff --git a/extensions/limits/main.py b/extensions/limits/main.py new file mode 100644 index 000000000..b55a77923 --- /dev/null +++ b/extensions/limits/main.py @@ -0,0 +1,47 @@ +# coding: utf-8 + +""" +The *limits* extension +---------------------- + + +""" +from django.utils.translation import ugettext_noop as _, ugettext +from django.core.urlresolvers import reverse +from django.conf.urls.defaults import include +from modoboa.lib import events, parameters +from modoboa.lib.webutils import static_url + +baseurl = "limits" + +def init(): + events.register("AdminMenuDisplay", menu) + +def destroy(): + events.unregister("AdminMenuDisplay", menu) + +def infos(): + return { + "name" : "Limits", + "version" : "1.0", + "description" : ugettext("Limits for objects creation"), + "url" : baseurl + } + +def menu(**kwargs): + import views + + if kwargs["target"] != "top_menu": + return [] + if not kwargs["user"].is_superuser: + return [] + return [ + {"name" : "resellers", + "label" : _("Resellers"), + "url" : reverse(views.resellers), + "img" : static_url("pics/resellers.png")} + ] + +def urls(prefix): + return (r'^%s%s/' % (prefix, baseurl), + include('modoboa.extensions.limits.urls')) diff --git a/extensions/limits/models.py b/extensions/limits/models.py new file mode 100644 index 000000000..d00ebcf0f --- /dev/null +++ b/extensions/limits/models.py @@ -0,0 +1,21 @@ +# coding: utf-8 + +from django.db import models +from django.contrib.auth.models import User +from django.utils.translation import ugettext as _, ugettext_noop + +class LimitsPool(models.Model): + user = models.OneToOneField(User) + + maxdomains = models.IntegerField(ugettext_noop("Max domains"), + default=0, + help_text=ugettext_noop("Maximum number of domains that can be created by this user")) + maxdomaliases = models.IntegerField(ugettext_noop("Max domain aliases"), + default=0, + help_text=ugettext_noop("Maximum number of domain aliases that can be created by this user")) + maxmboxes = models.IntegerField(ugettext_noop("Max mailboxes"), + default=0, + help_text=ugettext_noop("Maximum number of mailboxes that can be created by this user")) + maxmbaliases = models.IntegerField(ugettext_noop("Max mailbox aliases"), + default=0, + help_text=ugettext_noop("Maximum number of mailbox aliases that can be created by this user")) diff --git a/extensions/limits/static/js/resellers.js b/extensions/limits/static/js/resellers.js new file mode 100644 index 000000000..6f68e7f2a --- /dev/null +++ b/extensions/limits/static/js/resellers.js @@ -0,0 +1,42 @@ +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 new file mode 100644 index 000000000..0ed3fd8b1 --- /dev/null +++ b/extensions/limits/tables.py @@ -0,0 +1,23 @@ +# 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" + + 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 = ["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 new file mode 100644 index 000000000..f43491e1c --- /dev/null +++ b/extensions/limits/templates/limits/pool.html @@ -0,0 +1,16 @@ +{% extends "layout_simple.html" %} +{% load i18n %} +{% block html_head %} + +{% endblock %} +{% block content %} +
-
+
{% endif %}