From 1c3fddb84993c6cef0ab5877863aa27ac879aa98 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 1 Feb 2012 10:56:15 +0100 Subject: [PATCH 01/13] Don't display preferences of extensions that require a mailbox. see #198 --- extensions/sievefilters/__init__.py | 2 + extensions/webmail/__init__.py | 3 +- lib/parameters.py | 77 ++++++++++++++++++++++++----- userprefs/views.py | 4 ++ 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/extensions/sievefilters/__init__.py b/extensions/sievefilters/__init__.py index 9905c097b..ed6043711 100644 --- a/extensions/sievefilters/__init__.py +++ b/extensions/sievefilters/__init__.py @@ -8,6 +8,8 @@ from sievelib.managesieve import SUPPORTED_AUTH_MECHS baseurl = "sfilters" def load(): + parameters.register_app(uparams_opts=dict(needs_mailbox=True)) + parameters.register_admin("SERVER", type="string", deflt="127.0.0.1", help=_("Address of your MANAGESIEVE server")) diff --git a/extensions/webmail/__init__.py b/extensions/webmail/__init__.py index a3f2e3bc1..65582503e 100644 --- a/extensions/webmail/__init__.py +++ b/extensions/webmail/__init__.py @@ -5,6 +5,8 @@ from modoboa.lib import events, parameters from modoboa.lib.webutils import static_url def load(): + parameters.register_app(uparams_opts=dict(needs_mailbox=True)) + parameters.register_admin("IMAP_SERVER", type="string", deflt="127.0.0.1", help=_("Address of your IMAP server")) @@ -30,7 +32,6 @@ def load(): parameters.register_admin("MAX_ATTACHMENT_SIZE", type="int", deflt=2048, help=_("Maximum attachment size in bytes (or KB, MB, GB if specified)")) - parameters.register_user("MESSAGES_PER_PAGE", type="int", deflt=40, label=_("Number of displayed emails per page"), help=_("Sets the maximum number of messages displayed in a page")) diff --git a/lib/parameters.py b/lib/parameters.py index 59ddc6e25..dfe27217a 100644 --- a/lib/parameters.py +++ b/lib/parameters.py @@ -27,6 +27,49 @@ class NotDefined(Exception): return "Application '%s' and/or parameter '%s' not defined" \ % (self.app, self.name) +def register_app(app=None, aparams_opts=None, uparams_opts=None): + """Manually register an application + + You don't need to call this function unless you want to provide + specific options for your application parameters. + + For example, you can indicate that user-level parameters are only + accessible for users that own a mailbox. + + :param app: the application's name + :param aparams_opts: a dict containing options applicable to admin-level params + :param uparams_opts: a dict containing options applicable to user-level params + """ + if app is None: + app = __guess_extension() + + if _params.has_key(app): + return + _params[app] = {} + _params[app]['options'] = {} + _params_order[app] = {} + for lvl in _levels.keys(): + _params[app][lvl] = {} + _params_order[app][lvl] = [] + + if aparams_opts: + _params[app]['options']['A'] = aparams_opts + if uparams_opts: + _params[app]['options']['U'] = uparams_opts + +def unregister_app(app): + """Unregister an application + + All parameters associated to this application will also be + removed. + + :param app: the application's name (string) + """ + if not _params.has_key(app): + return False + del _params[app] + return True + def __is_defined(app, level, name): if not level in _levels.keys() \ or not app in _params.keys() \ @@ -44,11 +87,8 @@ def __register(app, level, name, **kwargs): :param name: the parameter's name """ if not app in _params.keys(): - _params[app] = {} - _params_order[app] = {} - for lvl in _levels.keys(): - _params[app][lvl] = {} - _params_order[app][lvl] = [] + register_app(app) + if not level in _levels.keys(): return if _params[app][level].has_key(name): @@ -113,6 +153,10 @@ def update_admin(name, **kwargs): return __update(app, 'A', name, **kwargs) def register_user(name, **kwargs): + """Register a new user-level parameter + + :param name: the parameter's name + """ if kwargs.has_key("app"): app = kwargs["app"] del kwargs["app"] @@ -120,12 +164,6 @@ def register_user(name, **kwargs): app = __guess_extension() return __register(app, 'U', name, **kwargs) -def unregister_app(app): - if not _params.has_key(app): - return False - del _params[app] - return True - def save_admin(name, value, app=None): from models import Parameter @@ -157,7 +195,7 @@ def save_user(user, name, value, app=None): p.user = user p.name = fullname if p.value != value: - p.value = str(value).encode("string_escape").strip() + p.value = value.encode("unicode_escape").strip() p.save() return True @@ -183,4 +221,17 @@ def get_user(user, name, app=None): p = UserParameter.objects.get(user=user, name="%s.%s" % (app, name)) except UserParameter.DoesNotExist: return _params[app]["U"][name]["deflt"] - return p.value.decode("string_escape") + try: + dvalue = p.value.decode("string_escape") + except ValueError: + dvalue = p.value + return dvalue + +def get_app_option(lvl, name, dflt, app=None): + if app is None: + app = __guess_extension() + if not lvl in _params[app]['options']: + return dflt + if not _params[app]['options'][lvl].has_key(name): + return dflt + return _params[app]['options'][lvl][name] diff --git a/userprefs/views.py b/userprefs/views.py index 0f996a8d6..287995afc 100644 --- a/userprefs/views.py +++ b/userprefs/views.py @@ -95,6 +95,10 @@ def preferences(request): for app in apps: if not len(parameters._params[app]['U']): continue + if parameters.get_app_option('U', 'needs_mailbox', False, app=app) \ + and not request.user.has_mailbox: + continue + tmp = {"name" : app, "params" : []} for p in parameters._params_order[app]['U']: param_def = parameters._params[app]['U'][p] From 0c9e40f12c767559e80b9eee939cf189cef64a39 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 1 Feb 2012 12:58:55 +0100 Subject: [PATCH 02/13] Size elements must be integers. see #105 --- static/js/permissions.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/static/js/permissions.js b/static/js/permissions.js index 6b7c45080..158589733 100644 --- a/static/js/permissions.js +++ b/static/js/permissions.js @@ -23,12 +23,12 @@ window.addEvent('domready', function() { $$("a[name=addperm]").addEvent("click", function(evt) { evt.stop(); var sizes = current_sizes.split(" "); - SqueezeBox.initialize({ - size: {x: sizes[0], y: sizes[1]}, - handler: 'iframe' - }); var addperm_url = evt.target.get("href") + "?role=" + current_role; - SqueezeBox.open(addperm_url); + + SqueezeBox.open(addperm_url, { + size: {x: parseInt(sizes[0]), y: parseInt(sizes[1])}, + handler: 'iframe' + }); }); $$("a[name=delperms]").addEvent("click", function(evt) { From 8c29178f81ef5af215eb7ac358a5d3e5524f408e Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 1 Feb 2012 16:46:15 +0100 Subject: [PATCH 03/13] Fixed a permission issue for domain admins. see #196 --- admin/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/views.py b/admin/views.py index 0580e7fb9..c6b2be947 100644 --- a/admin/views.py +++ b/admin/views.py @@ -312,7 +312,7 @@ def newmailbox(request, tplname="admin/adminform.html"): @permission_required("admin.change_mailbox") def editmailbox(request, mbox_id=None, tplname="admin/adminform.html"): mb = Mailbox.objects.get(pk=mbox_id) - if not request.user.is_owner(mb.domain): + if not request.user.can_access(mb.domain): raise PermDeniedException(_("You do not have access to this domain")) commonctx = {"title" : _("Mailbox editing"), From 6d5f14ce6252d2bdaf90501920a128b9c15c34ec Mon Sep 17 00:00:00 2001 From: colangelo gerald Date: Wed, 1 Feb 2012 16:46:43 +0100 Subject: [PATCH 04/13] Fix #199 --- admin/models.py | 6 +++--- admin/tables.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/admin/models.py b/admin/models.py index e75ae0898..c405704b0 100644 --- a/admin/models.py +++ b/admin/models.py @@ -408,10 +408,10 @@ class Mailbox(DatesAware): def __str__(self): return self.__full_address() - - def __full_address(self): + + @property + def full_address(self): return "%s@%s" % (self.address, self.domain.name) - full_address = property(__full_address) def __enabled(self): return self.user.is_active diff --git a/admin/tables.py b/admin/tables.py index bcd15cd2a..6c8a27120 100644 --- a/admin/tables.py +++ b/admin/tables.py @@ -67,7 +67,7 @@ class MailboxesTable(tables.Table): tableid = "objects_table" idkey = "id" - address = tables.Column("address", label=ugettext_noop("Address")) + address = tables.Column("full_address", label=ugettext_noop("Address")) name = tables.Column("user", label=ugettext_noop("Name")) creation = tables.Column("creation", label=ugettext_noop("Created"), width="160px") From eb37c48109bbed061a8eb7788e0c59a72a15d0f7 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 1 Feb 2012 16:55:22 +0100 Subject: [PATCH 05/13] Fixed wrong method call. see #199 --- admin/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/models.py b/admin/models.py index c405704b0..797df04a9 100644 --- a/admin/models.py +++ b/admin/models.py @@ -407,7 +407,7 @@ class Mailbox(DatesAware): self.mdirroot += '/' def __str__(self): - return self.__full_address() + return self.full_address() @property def full_address(self): From 027d77f9497f7890c07b2121a258477bb203f335 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 1 Feb 2012 17:45:38 +0100 Subject: [PATCH 06/13] wip: Resellers and limits * A new limit has been added : the maximum number of domain admins a user can create * Code simplification see #105 --- admin/models.py | 41 +++++++++---------- admin/views.py | 11 ++--- extensions/limits/controls.py | 26 +++++------- extensions/limits/forms.py | 4 ++ extensions/limits/models.py | 1 + extensions/limits/templatetags/limits_tags.py | 2 +- lib/events.py | 6 +-- templates/admin/add_domain_admin.html | 2 + 8 files changed, 47 insertions(+), 46 deletions(-) diff --git a/admin/models.py b/admin/models.py index 797df04a9..9055bd63a 100644 --- a/admin/models.py +++ b/admin/models.py @@ -295,15 +295,14 @@ class DatesAware(models.Model): ObjectDates.set_for_object(self) super(DatesAware, self).save(*args, **kwargs) - def __creation(self): + @property + def creation(self): return self.dates.creation - def __last_modification(self): + @property + def last_modification(self): return self.dates.last_modification - creation = property(__creation) - last_modification = property(__last_modification) - class Domain(DatesAware): name = models.CharField(ugettext_noop('name'), max_length=100, unique=True, help_text=ugettext_noop("The domain name")) @@ -317,14 +316,14 @@ class Domain(DatesAware): ("view_domain", "View domain"), ("view_domains", "View domains"), ) - - def __domainalias_count(self): + + @property + def domainalias_count(self): return len(self.domainalias_set.all()) - domainalias_count = property(__domainalias_count) - def __mailbox_count(self): + @property + def mailbox_count(self): return len(self.mailbox_set.all()) - mailbox_count = property(__mailbox_count) def create_dir(self): if parameters.get_admin("CREATE_DIRECTORIES") == "yes": @@ -407,25 +406,25 @@ class Mailbox(DatesAware): self.mdirroot += '/' def __str__(self): - return self.full_address() + return self.full_address @property def full_address(self): return "%s@%s" % (self.address, self.domain.name) - def __enabled(self): + @property + def enabled(self): return self.user.is_active - enabled = property(__enabled) - def __full_path(self): + @property + def full_path(self): path = os.path.join(parameters.get_admin("STORAGE_PATH"), self.domain.name, self.address) return os.path.abspath(path) - full_path = property(__full_path) - def __alias_count(self): + @property + def alias_count(self): return len(self.alias_set.all()) - alias_count = property(__alias_count) def create_dir(self): if parameters.get_admin("CREATE_DIRECTORIES") == "yes": @@ -607,15 +606,15 @@ class Alias(DatesAware): ("view_aliases", "View aliases"), ) - def __full_address(self): + @property + def full_address(self): return "%s@%s" % (self.address, self.domain.name) - full_address = property(__full_address) - def __targets(self): + @property + def targets(self): ret = "
".join(map(lambda m: str(m), self.mboxes.all())) ret += "
" + self.repr_extmboxes() return ret - targets = property(__targets) def save(self, int_targets, ext_targets, *args, **kwargs): if len(ext_targets): diff --git a/admin/views.py b/admin/views.py index c6b2be947..9d4720718 100644 --- a/admin/views.py +++ b/admin/views.py @@ -70,7 +70,7 @@ def _validate_domain(request, form, successmsg, commonctx, @permission_required("admin.add_domain") @transaction.commit_on_success def newdomain(request, tplname="admin/adminform.html"): - events.raiseEvent("CanCreateDomain", request.user) + events.raiseEvent("CanCreate", request.user, "domains") def newdomain_cb(user, domain): grant_access_to_object(user, domain, is_owner=True) events.raiseEvent("CreateDomain", user, domain) @@ -152,7 +152,7 @@ def domaliases(request): @permission_required("admin.add_domainalias") @transaction.commit_on_success def newdomalias(request, tplname="admin/adminform.html"): - events.raiseEvent("CanCreateDomainAlias", request.user) + events.raiseEvent("CanCreate", request.user, "domain_aliases") commonctx = {"title" : _("New domain alias"), "submit_label" : _("Create"), "action" : reverse(newdomalias), @@ -275,7 +275,7 @@ def mailboxes_search(request): @permission_required("admin.add_mailbox") @transaction.commit_on_success def newmailbox(request, tplname="admin/adminform.html"): - events.raiseEvent("CanCreateMailbox", request.user) + events.raiseEvent("CanCreate", request.user, "mailboxes") commonctx = {"title" : _("New mailbox"), "submit_label" : _("Create"), "action" : reverse(newmailbox), @@ -423,7 +423,7 @@ def _validate_mbalias(request, form, successmsg, tplname, commonctx, callback=No @permission_required("admin.add_alias") @transaction.commit_on_success def newmbalias(request, tplname="admin/mbaliasform.html"): - events.raiseEvent("CanCreateMailboxAlias", request.user) + events.raiseEvent("CanCreate", request.user, "mailbox_aliases") commonctx = {"title" : _("New mailbox alias"), "submit_label" : _("Create"), "action" : reverse(newmbalias), @@ -496,7 +496,7 @@ def permissions(request, tplname='admin/permissions.html'): permtables += [ {"id" : "domain_admins", "title" : _("Domain administrators"), - "rel" : "350 250", + "rel" : "350 100", "content" : DomainAdminsPerms().get(request)} ] @@ -515,6 +515,7 @@ def add_permission(request): else: pobj = pclass() if request.method == "GET": + events.raiseEvent("CanCreate", request.user, role) return pobj.get_add_form(request) status, data = pobj.add(request) if not status: diff --git a/extensions/limits/controls.py b/extensions/limits/controls.py index 130050f58..bf947de48 100644 --- a/extensions/limits/controls.py +++ b/extensions/limits/controls.py @@ -40,9 +40,6 @@ def dec_limit(user, lname): except LimitsPool.DoesNotExist: pass -@events.observe('CanCreateDomain') -def check_domains_limit(user): - check_limit(user, 'domains_limit') @events.observe('CreateDomain') def inc_nb_domains(user, domain): @@ -59,10 +56,6 @@ def dec_nb_domains(domain): for mbalias in domain.alias_set.all(): dec_nb_mbaliases(mbalias) -@events.observe('CanCreateDomainAlias') -def check_domaliases_limit(user): - check_limit(user, 'domain_aliases_limit') - @events.observe('DomainAliasCreated') def inc_nb_domaliases(user, domalias): inc_limit(user, 'domain_aliases_limit') @@ -72,10 +65,6 @@ def dec_nb_domaliases(domainalias): owner = get_object_owner(domainalias) dec_limit(owner, 'domain_aliases_limit') -@events.observe('CanCreateMailbox') -def check_mboxes_limit(user): - check_limit(user, 'mailboxes_limit') - @events.observe('CreateMailbox') def inc_nb_mailboxes(user, mailbox): inc_limit(user, 'mailboxes_limit') @@ -85,10 +74,6 @@ def dec_nb_mailboxes(mailbox): owner = get_object_owner(mailbox) dec_limit(owner, 'mailboxes_limit') -@events.observe('CanCreateMailboxAlias') -def check_mbaliases_limit(user): - check_limit(user, 'mailbox_aliases_limit') - @events.observe('MailboxAliasCreated') def inc_nb_mbaliases(user, mailboxalias): inc_limit(user, 'mailbox_aliases_limit') @@ -98,12 +83,23 @@ def dec_nb_mbaliases(mailboxalias): owner = get_object_owner(mailboxalias) dec_limit(owner, 'mailbox_aliases_limit') +@events.observe('CanCreate') +def can_create_new_object(user, objtype): + check_limit(user, '%s_limit' % objtype) + @events.observe("DomainAdminCreated") def create_pool(user): + owner = get_object_owner(user) + inc_limit(owner, 'domain_admins_limit') p = LimitsPool(user=user) p.save() p.create_limits() +@events.observe("DomainAdminDeleted") +def create_pool(da): + owner = get_object_owner(da) + dec_limit(owner, 'domain_admins_limit') + @events.observe("DomainAdminDeleted", "SuperAdminPromotion") def move_pool_resource(user): owner = get_object_owner(user) diff --git a/extensions/limits/forms.py b/extensions/limits/forms.py index 77d0e742e..50554d91c 100644 --- a/extensions/limits/forms.py +++ b/extensions/limits/forms.py @@ -30,6 +30,10 @@ class ResellerWithPasswordForm(ResellerForm, UserWithPasswordForm): pass class ResellerPoolForm(forms.Form): + domain_admins_limit = forms.IntegerField( + ugettext_noop("Max domain admins"), + help_text=ugettext_noop("Maximum number of domain administrators that can be created by this user") + ) domains_limit = forms.IntegerField(ugettext_noop("Max domains"), help_text=ugettext_noop("Maximum number of domains that can be created by this user")) domain_aliases_limit = forms.IntegerField(ugettext_noop("Max domain aliases"), diff --git a/extensions/limits/models.py b/extensions/limits/models.py index 9c26ac570..90694e592 100644 --- a/extensions/limits/models.py +++ b/extensions/limits/models.py @@ -6,6 +6,7 @@ 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", diff --git a/extensions/limits/templatetags/limits_tags.py b/extensions/limits/templatetags/limits_tags.py index e61597397..37310b0d2 100644 --- a/extensions/limits/templatetags/limits_tags.py +++ b/extensions/limits/templatetags/limits_tags.py @@ -39,6 +39,6 @@ def reseller_actions(user, resid): "img" : static_url("pics/settings.png"), "title" : _("Edit limits allocated to this reseller"), "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:330,y:280}}"}, + "rel" : "{handler:'iframe',size:{x:330,y:310}}"}, ] return render_actions(actions) diff --git a/lib/events.py b/lib/events.py index 260bca306..82adfb485 100644 --- a/lib/events.py +++ b/lib/events.py @@ -15,20 +15,18 @@ import re events = [ "SuperAdminPromotion", - "CanCreateDomain", + "CanCreate", + "CreateDomain", "DeleteDomain", - "CanCreateDomainAlias", "DomainAliasCreated", "DomainAliasDeleted", - "CanCreateMailbox", "CreateMailbox", "DeleteMailbox", "ModifyMailbox", - "CanCreateMailboxAlias", "MailboxAliasCreated", "MailboxAliasDeleted", diff --git a/templates/admin/add_domain_admin.html b/templates/admin/add_domain_admin.html index df5304d0c..1c4dd537c 100644 --- a/templates/admin/add_domain_admin.html +++ b/templates/admin/add_domain_admin.html @@ -23,6 +23,7 @@ $("createfrom").addEvent("change", function(evt) { url = "{% url admin.views.domain_admin_promotion %}"; styles = {height: 200}; } + var size = {y: styles.height}; new Request.JSON.mdb({ url: url, onSuccess: function(response) { @@ -37,6 +38,7 @@ $("createfrom").addEvent("change", function(evt) { }); } $(parent.document).getElement('iframe').setStyles(styles); + parent.SqueezeBox.resize(size); } }).get(); }); From f7b89431cfa2de38c289347cc07408f58a949e1c Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 1 Feb 2012 17:46:58 +0100 Subject: [PATCH 07/13] Avoid callbacks duplication. see #105 --- extensions/limits/controls.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/extensions/limits/controls.py b/extensions/limits/controls.py index bf947de48..12ef82b78 100644 --- a/extensions/limits/controls.py +++ b/extensions/limits/controls.py @@ -95,15 +95,12 @@ def create_pool(user): p.save() p.create_limits() -@events.observe("DomainAdminDeleted") -def create_pool(da): - owner = get_object_owner(da) - dec_limit(owner, 'domain_admins_limit') - @events.observe("DomainAdminDeleted", "SuperAdminPromotion") def move_pool_resource(user): owner = get_object_owner(user) if not owner.is_superuser: + dec_limit(owner, 'domain_admins_limit') + for ooentry in user.objectaccess_set.all(): if ooentry.is_owner: grant_access_to_object(owner, ooentry.content_object, True) From fc56a5df31859ba54e01223a226fa1b01f9361bd Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 2 Feb 2012 09:55:37 +0100 Subject: [PATCH 08/13] Don't convert parameter values to str objects, leave them as unicode ones and use 'unicode_escape' before storing them to database. see #200 --- lib/parameters.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/parameters.py b/lib/parameters.py index dfe27217a..93af32cbf 100644 --- a/lib/parameters.py +++ b/lib/parameters.py @@ -177,7 +177,7 @@ def save_admin(name, value, app=None): p = Parameter() p.name = fullname if p.value != value: - p.value = str(value).encode("string_escape").strip() + p.value = value.encode("unicode_escape").strip() p.save() return True @@ -209,7 +209,7 @@ def get_admin(name, app=None): p = Parameter.objects.get(name="%s.%s" % (app, name)) except Parameter.DoesNotExist: return _params[app]["A"][name]["deflt"] - return p.value.decode("string_escape") + return p.value.decode("unicode_escape") def get_user(user, name, app=None): from models import UserParameter @@ -221,11 +221,7 @@ def get_user(user, name, app=None): p = UserParameter.objects.get(user=user, name="%s.%s" % (app, name)) except UserParameter.DoesNotExist: return _params[app]["U"][name]["deflt"] - try: - dvalue = p.value.decode("string_escape") - except ValueError: - dvalue = p.value - return dvalue + return p.value.decode("unicode_escape") def get_app_option(lvl, name, dflt, app=None): if app is None: From 51e6b2dab365d700798f7921924ebdab4c622966 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 2 Feb 2012 11:07:54 +0100 Subject: [PATCH 09/13] A new button is available into the domain actions list and let the current user see the defined administrators of each domain. see #201 --- admin/tables.py | 2 +- admin/templatetags/admin_extras.py | 9 +++++++++ admin/urls.py | 1 + admin/views.py | 8 ++++++++ static/css/box.css | 8 ++++++++ static/pics/{resellers.png => administrators.png} | Bin 6 files changed, 27 insertions(+), 1 deletion(-) rename static/pics/{resellers.png => administrators.png} (100%) diff --git a/admin/tables.py b/admin/tables.py index 6c8a27120..5fd152771 100644 --- a/admin/tables.py +++ b/admin/tables.py @@ -21,7 +21,7 @@ class DomainsTable(tables.Table): width="50px", align="center") enabled = tables.Column("enabled", label=gender("Enabled", "m"), width="50px", align="center") - actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"), width="50px", + actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"), width="70px", align="center", defvalue=domain_actions) cols_order = ["name", "creation", "modified", diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 90f819ea4..76f0ba1c8 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -169,6 +169,15 @@ def domain_actions(user, domid): "img" : static_url("pics/alias.png"), "title" : _("View aliases of this domain")} ] + if user.has_perm('admin.view_domains'): + actions += [ + {"name" : "viewadmins", + "url" : reverse(admin.views.view_domain_admins, args=[domid]), + "img" : static_url("pics/administrators.png"), + "title" : _("View administrators of this domain"), + "class" : "boxed", + "rel" : "{handler:'iframe',size:{x:310,y:190}}"}, + ] return render_actions(actions) @register.simple_tag diff --git a/admin/urls.py b/admin/urls.py index fe48c7bd7..a47f8a591 100644 --- a/admin/urls.py +++ b/admin/urls.py @@ -37,6 +37,7 @@ urlpatterns = patterns( (r'^permissions/domainadmin/edit/(?P\d+)/$', 'edit_domain_admin'), (r'^permissions/domainadmin/promote/$', 'domain_admin_promotion'), (r'^permissions/domainadmin/assign/(?P\d+)/$', 'assign_domains_to_admin'), + (r'^permissions/domainadmins/(?P\d+)/$', 'view_domain_admins'), (r'^permissions/searchaccount/$', 'search_account'), (r'^permissions/delete/$', 'delete_permissions'), ) diff --git a/admin/views.py b/admin/views.py index 9d4720718..88427ae15 100644 --- a/admin/views.py +++ b/admin/views.py @@ -625,6 +625,14 @@ def assign_domains_to_admin(request, da_id, tplname="admin/adminform.html"): ctx.update(form=form) return _render(request, tplname, ctx) +@login_required +@permission_required("auth.view_permissions") +def view_domain_admins(request, dom_id, tplname="admin/view_domain_admins.html"): + domain = Domain.objects.get(pk=dom_id) + return _render(request, tplname, { + "domain" : domain + }) + @login_required @permission_required("auth.view_permissions") @check_domain_access diff --git a/static/css/box.css b/static/css/box.css index 79327c7fd..26d0a0a75 100644 --- a/static/css/box.css +++ b/static/css/box.css @@ -196,4 +196,12 @@ html[xmlns] .clearfix { #assignform #id_domains { width: 310px; height: 200px; +} + +/* + * Domain admins list + */ +#domainadmins { + width: 100%; + height: 140px; } \ No newline at end of file diff --git a/static/pics/resellers.png b/static/pics/administrators.png similarity index 100% rename from static/pics/resellers.png rename to static/pics/administrators.png From e4997dda439bceeb1976eb2ebe9f3a5e1e3da7e4 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 2 Feb 2012 11:11:41 +0100 Subject: [PATCH 10/13] Django instructions moved after the DOCTYPE tag. see #202 --- templates/nlayout.html | 3 +-- templates/registration/login.html | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/nlayout.html b/templates/nlayout.html index fe931702a..976fefb9b 100644 --- a/templates/nlayout.html +++ b/templates/nlayout.html @@ -1,7 +1,6 @@ -{% load i18n %} -{% load admin_extras uprefs_extras libextras %} +{% load i18n admin_extras uprefs_extras libextras %} diff --git a/templates/registration/login.html b/templates/registration/login.html index 08af52fbc..4475c1cd4 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -1,7 +1,6 @@ -{% load i18n %} -{% load libextras %} +{% load i18n libextras %} From 6a18f8bd17ece8c51eb24ac414e69112b7e71e9c Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 2 Feb 2012 11:12:46 +0100 Subject: [PATCH 11/13] Works better with appriopriate template :p see #201 --- extensions/stats/grapher.py | 3 ++- templates/admin/view_domain_admins.html | 8 +++++++ tools/pfxadmin_migrate/migrate.py | 30 +++++++++++-------------- 3 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 templates/admin/view_domain_admins.html diff --git a/extensions/stats/grapher.py b/extensions/stats/grapher.py index ad52da760..4237ed7d9 100644 --- a/extensions/stats/grapher.py +++ b/extensions/stats/grapher.py @@ -3,7 +3,6 @@ import os import time import datetime -import rrdtool from django.utils.translation import ugettext as _ from modoboa.lib import parameters @@ -77,6 +76,8 @@ class Grapher(object): self.img_rootdir = parameters.get_admin("IMG_ROOTDIR") def process(self, target, suffix, start, end, tpl=traffic_avg_template): + import rrdtool + rrdfile = "%s/%s.rrd" % (self.rrd_rootdir, target) if not os.path.exists(rrdfile): return diff --git a/templates/admin/view_domain_admins.html b/templates/admin/view_domain_admins.html new file mode 100644 index 000000000..403011153 --- /dev/null +++ b/templates/admin/view_domain_admins.html @@ -0,0 +1,8 @@ +{% extends "layout_simple.html" %} +{% load i18n %} +{% block content %} +

{% trans "Administrators of" %} {{ domain.name }}

+ +{% endblock %} diff --git a/tools/pfxadmin_migrate/migrate.py b/tools/pfxadmin_migrate/migrate.py index 0bce33811..499818a52 100755 --- a/tools/pfxadmin_migrate/migrate.py +++ b/tools/pfxadmin_migrate/migrate.py @@ -115,9 +115,7 @@ def migrate_mailboxes(domain, options): old_mboxes = pf_models.Mailbox.objects.using(options._from).filter(domain=domain.name) for old_mb in old_mboxes: new_mb = md_models.Mailbox() - new_mb.name = old_mb.name new_mb.address = old_mb.local_part - new_mb.password = old_mb.password new_mb.domain = domain new_mb.dates = migrate_dates(old_mb) if old_mb.quota: @@ -132,7 +130,7 @@ def migrate_mailboxes(domain, options): print "Error: cannot rename mailbox directory\n%s" % output sys.exit(1) - new_mb.save(using=options.to) + new_mb.save(name=old_mb.name, password=old_mb.password, using=options.to) def migrate_domain(old_dom, options): @@ -161,29 +159,27 @@ def migrate_admins(options): print "Warning: skipping useless admin %s" % (old_admin.username) continue try: - mb = md_models.Mailbox.objects.using(options.to).get(user__username=old_admin.username) - except md_models.Mailbox.DoesNotExist: + user = md_models.User.objects.using(options.to).get(username=old_admin.username) + except md_models.User.DoesNotExist: try: domain = md_models.Domain.objects.using(options.to).get(name=domname) except md_models.Domain.DoesNotExist: print "Warning: skipping domain admin %s, domain not found" \ % old_admin.username continue - mb = md_models.Mailbox() - mb.address = local_part - mb.domain = domain - mb.name = local_part - mb.enabled = old_admin.active - mb.password = old_admin.password - mb.dates = migrate_dates(old_admin) - mb.save(using=options.to) + user = md_models.User() + user.username = old_admin.username + user.email = old_admin.username + user.password = old_admin.password + user.is_active = old_admin.active + user.save(using=options.to) - mb.user.date_joined = old_admin.modified + user.date_joined = old_admin.modified if creds.domain == "ALL": - mb.user.is_superuser = True + user.is_superuser = True else: - mb.user.groups.add(dagroup) - mb.save(using=options.to) + user.groups.add(dagroup) + user.save(using=options.to) def do_migration(options): pf_domains = pf_models.Domain.objects.using(options._from).all() From 907e329ebf350736dbef41bccaa4ea47afce7854 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 2 Feb 2012 11:15:28 +0100 Subject: [PATCH 12/13] Django instruction moved after the DOCTYPE tag. fix #202 --- templates/common/viewmail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/common/viewmail.html b/templates/common/viewmail.html index 8221b2fcf..19ff6449d 100644 --- a/templates/common/viewmail.html +++ b/templates/common/viewmail.html @@ -1,6 +1,6 @@ -{% load i18n %} +{% load i18n %} From 8dfbb6224b302413f8290791291703353bc391af Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 2 Feb 2012 13:51:42 +0100 Subject: [PATCH 13/13] Javascript code refactoring. see #196 --- admin/views.py | 7 +- .../postfix_autoreply/autoreply.html | 3 +- .../templates/sievefilters/filter.html | 6 +- .../templates/sievefilters/newfiltersset.html | 35 ++--- extensions/sievefilters/views.py | 4 +- .../webmail/templates/webmail/folder.html | 7 +- static/js/form.js | 133 +----------------- templates/admin/add_domain_admin.html | 2 +- templates/userprefs/setforward.html | 5 + 9 files changed, 45 insertions(+), 157 deletions(-) diff --git a/admin/views.py b/admin/views.py index 88427ae15..e70b52516 100644 --- a/admin/views.py +++ b/admin/views.py @@ -574,7 +574,7 @@ def edit_domain_admin(request, da_id, tplname="admin/adminform.html"): @login_required @permission_required("auth.view_permissions") -def domain_admin_promotion(request): +def domain_admin_promotion(request, tplname="admin/domain_admin_promotion.html"): if request.method == "POST": form = DomainAdminPromotionForm(request.POST) if form.is_valid(): @@ -590,9 +590,10 @@ def domain_admin_promotion(request): messages.info(request, _("Domain admin added"), fail_silently=True) return ajax_response(request, url=reverse(admin.views.permissions)) + return ajax_response(request, status="ko", template=tplname, form=form) + form = DomainAdminPromotionForm() - return ajax_response(request, template="admin/domain_admin_promotion.html", - form=form) + return ajax_response(request, template=tplname, form=form) @login_required @permission_required("auth.view_permissions") diff --git a/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html b/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html index 7f8790c35..c7a9a6fe2 100644 --- a/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html +++ b/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html @@ -16,8 +16,9 @@ @@ -29,7 +31,8 @@ $(document).addEvent("domready", function() { {% block content %}

{{ title }}

{% if error %}
{{ error }}
{% endif %} -
+ {% include "common/genericform.html" %}
diff --git a/extensions/sievefilters/views.py b/extensions/sievefilters/views.py index 59774e488..70c2dba2a 100644 --- a/extensions/sievefilters/views.py +++ b/extensions/sievefilters/views.py @@ -28,9 +28,9 @@ def index(request, tplname="sievefilters/index.html"): return _render_error(request, user_context={"error" : e}) if active_script is None: active_script = "" - default_script = scripts[0] if len(scripts) else "" + default_script = "%s/" % scripts[0] if len(scripts) else "" else: - default_script = active_script + default_script = "%s/" % active_script return _render(request, tplname, { "active_script" : active_script, "default_script" : default_script, diff --git a/extensions/webmail/templates/webmail/folder.html b/extensions/webmail/templates/webmail/folder.html index 1abc6a5b2..62af3b982 100644 --- a/extensions/webmail/templates/webmail/folder.html +++ b/extensions/webmail/templates/webmail/folder.html @@ -6,6 +6,8 @@ + {% endblock %} {% block content %} {% load i18n %}