From 86f855220a39787f91355b52f54f9fd5992a1ee8 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Tue, 22 May 2012 11:40:50 +0200 Subject: [PATCH] Refactored extension API * Modoboa extensions must now declare themselves * Now using django 1.3 *staticfiles* module * Better distinction between *static* content and *media* content see #197 --- .hgignore | 2 + admin/models.py | 51 +++---- admin/tables.py | 4 +- admin/views.py | 4 +- extensions/__init__.py | 116 ++++++++------- extensions/amavis_quarantine/__init__.py | 134 ++++++++--------- .../css/quarantine.css | 0 .../css/selfservice.css | 0 .../{ => amavis_quarantine}/js/quarantine.js | 0 .../{ => amavis_quarantine}/js/selfservice.js | 0 .../templates/amavis_quarantine/index.html | 10 +- .../viewmail_selfservice.html | 20 +-- .../templatetags/amextras.py | 2 +- extensions/limits/__init__.py | 72 +++++----- extensions/postfix_autoreply/__init__.py | 74 +++++----- extensions/sievefilters/__init__.py | 65 +++++---- .../static/{ => sievefilters}/css/style.css | 0 .../{ => sievefilters}/js/jquery.dynfields.js | 0 .../static/{ => sievefilters}/js/lib.js | 0 .../templates/sievefilters/index.html | 10 +- extensions/stats/__init__.py | 46 +++--- .../stats/static/{ => stats}/css/stats.css | 0 .../stats/static/{ => stats}/js/stats.js | 0 extensions/stats/templates/stats/graphs.html | 2 +- extensions/stats/templates/stats/index.html | 6 +- extensions/webmail/__init__.py | 136 +++++++++--------- extensions/webmail/lib.py | 11 +- .../static/{ => webmail}/css/attachments.css | 0 .../static/{ => webmail}/css/folders.css | 0 .../static/{ => webmail}/css/webmail.css | 0 .../static/{ => webmail}/js/webmail.js | 2 +- .../templates/webmail/attachments.html | 2 +- .../webmail/templates/webmail/compose.html | 2 +- .../webmail/templates/webmail/index.html | 14 +- extensions/webmail/templatetags/webextras.py | 2 +- extensions/webmail/views.py | 2 +- lib/email_listing.py | 2 +- lib/webutils.py | 4 +- settings_sample.py | 8 +- static/js/global.js | 10 +- templates/admin/header.html | 4 +- templates/admin/identities.html | 4 +- templates/admin/importform.html | 2 +- templates/admin/listing.html | 2 +- templates/common/table_head.html | 2 +- templates/common/viewmail.html | 2 +- templates/nlayout.html | 28 ++-- templates/registration/login.html | 14 +- urls.py | 6 +- 49 files changed, 441 insertions(+), 436 deletions(-) rename extensions/amavis_quarantine/static/{ => amavis_quarantine}/css/quarantine.css (100%) rename extensions/amavis_quarantine/static/{ => amavis_quarantine}/css/selfservice.css (100%) rename extensions/amavis_quarantine/static/{ => amavis_quarantine}/js/quarantine.js (100%) rename extensions/amavis_quarantine/static/{ => amavis_quarantine}/js/selfservice.js (100%) rename extensions/sievefilters/static/{ => sievefilters}/css/style.css (100%) rename extensions/sievefilters/static/{ => sievefilters}/js/jquery.dynfields.js (100%) rename extensions/sievefilters/static/{ => sievefilters}/js/lib.js (100%) rename extensions/stats/static/{ => stats}/css/stats.css (100%) rename extensions/stats/static/{ => stats}/js/stats.js (100%) rename extensions/webmail/static/{ => webmail}/css/attachments.css (100%) rename extensions/webmail/static/{ => webmail}/css/folders.css (100%) rename extensions/webmail/static/{ => webmail}/css/webmail.css (100%) rename extensions/webmail/static/{ => webmail}/js/webmail.js (99%) diff --git a/.hgignore b/.hgignore index fdb24e263..edc3f22d9 100644 --- a/.hgignore +++ b/.hgignore @@ -1,6 +1,8 @@ .*~$ ^[^\.]+\.pyc ^[^\.]+\.mo +sitestatic +scripts static/tmp static/js/ckeditor settings.py diff --git a/admin/models.py b/admin/models.py index 3d2d1416b..a5b896535 100644 --- a/admin/models.py +++ b/admin/models.py @@ -10,7 +10,7 @@ from modoboa.lib import parameters, events from modoboa.lib.exceptions import PermDeniedException from modoboa.lib.sysutils import exec_cmd, exec_as_vuser from modoboa.lib.emailutils import split_mailbox -from modoboa.extensions import get_ext_module +from modoboa.extensions import exts_pool from exceptions import * import os, sys import pwd @@ -806,56 +806,49 @@ class Extension(models.Model): help_text=ugettext_lazy("Check to enable this extension") ) - def init(self): - module = get_ext_module(self.name) - if hasattr(module, "init"): - module.init() + def __init__(self, *args, **kwargs): + super(Extension, self).__init__(*args, **kwargs) + self.instance = exts_pool.get_extension(self.name) + self.__get_ext_dir() - def load(self): - module = get_ext_module(self.name) - if hasattr(module, "load"): - module.load() - - def unload(self): - module = get_ext_module(self.name) - module.destroy() + def __get_ext_dir(self): + modname = self.instance.__module__ + path = os.path.realpath(sys.modules[modname].__file__) + self.extdir = os.path.dirname(path) def on(self): self.enabled = True self.save() - self.load() - self.init() + self.instance.load() + self.instance.init() - extdir = "%s/extensions/%s" % (settings.MODOBOA_DIR, self.name) - scriptdir = "%s/%s" % (extdir, "scripts") + if self.instance.needs_media: + path = os.path.join(settings.MEDIA_ROOT, self.name) + exec_cmd("mkdir %s" % path) + + scriptdir = "%s/%s" % (self.extdir, "scripts") if os.path.exists(scriptdir): targetdir = os.path.join(settings.MODOBOA_DIR, "scripts") exec_cmd("ln -s %s %s/%s" % (scriptdir, targetdir, self.name)) - staticpath = "%s/%s" % (extdir, "static") - if os.path.exists(staticpath): - exec_cmd("ln -s %s/static %s/static/%s" % \ - (extdir, settings.MODOBOA_DIR, self.name)) - events.raiseEvent("ExtEnabled", self) def off(self): - self.unload() + self.instance.destroy() self.enabled = False self.save() - extdir = "%s/extensions/%s" % (settings.MODOBOA_DIR, self.name) - scriptdir = "%s/%s" % (extdir, "scripts") + if self.instance.needs_media: + path = os.path.join(settings.MEDIA_ROOT, self.name) + exec_cmd("rm -r %s" % path) + + scriptdir = "%s/%s" % (self.extdir, "scripts") if os.path.exists(scriptdir): target = "%s/scripts/%s" % (settings.MODOBOA_DIR, self.name) exec_cmd("rm %s" % target) - staticpath = "%s/%s" % (extdir, "static") - if os.path.exists(staticpath): - exec_cmd("rm -r %s/static/%s" % (settings.MODOBOA_DIR, self.name)) - events.raiseEvent("ExtDisabled", self) def populate_callback(sender, user=None, **kwargs): diff --git a/admin/tables.py b/admin/tables.py index 46be0df35..cf95c95a3 100644 --- a/admin/tables.py +++ b/admin/tables.py @@ -41,11 +41,11 @@ class DomainsTable(tables.Table): class ExtensionsTable(tables.Table): idkey = "id" selection = tables.SelectionColumn("selection", width="4%", header=False) - name = tables.Column("name", label=ugettext_lazy("Name"), width="15%") + label = tables.Column("label", label=ugettext_lazy("Name"), width="15%") version = tables.Column("version", label=ugettext_lazy("Version"), width="6%") descr = tables.Column("description", label=ugettext_lazy("Description")) - cols_order = ["selection", "name", "version", "descr"] + cols_order = ["selection", "label", "version", "descr"] class IdentitiesTable(tables.Table): idkey = "id" diff --git a/admin/views.py b/admin/views.py index 2877d6c72..a5c98e828 100644 --- a/admin/views.py +++ b/admin/views.py @@ -494,9 +494,9 @@ def saveparameters(request): @login_required @user_passes_test(lambda u: u.is_superuser) def viewextensions(request, tplname='admin/extensions.html'): - from modoboa.extensions import list_extensions + from modoboa.extensions import exts_pool - exts = list_extensions() + exts = exts_pool.list_all() for ext in exts: try: dbext = Extension.objects.get(name=ext["id"]) diff --git a/extensions/__init__.py b/extensions/__init__.py index fef30fd24..3b30cfd97 100644 --- a/extensions/__init__.py +++ b/extensions/__init__.py @@ -4,65 +4,71 @@ import re from django.conf import settings from django.conf.urls.defaults import include -def isinstalled(ext): - return "modoboa.extensions.%s" % ext in settings.INSTALLED_APPS +class ModoExtension(object): + """Base extension class -def get_ext_module(extname): - modname = "modoboa.extensions.%s" % extname - if not sys.modules.has_key(modname): - __import__(modname) - return sys.modules[modname] + Each Modoboa extension must inherit from this class to be + considered as valid. + """ + name = None + version = "0.1" + description = "" + url = None + needs_media = False -def get_extension_infos(name): - return get_ext_module(name).infos() + def infos(self): + return dict(name=self.name, label=self.label, version=self.version, + description=self.description, url=self.url) -def loadextensions(prefix): - # To avoid a circular import... - from modoboa.admin.models import Extension + def init(self): + pass - basedir = "%s/extensions" % settings.MODOBOA_DIR - result = [] - for f in os.listdir(basedir): - if not os.path.isdir("%s/%s" % (basedir, f)): - continue - if not isinstalled(f): - continue - try: - ext = Extension.objects.get(name=f) - if ext.enabled: - ext.load() - except Extension.DoesNotExist: - pass - module = get_ext_module(f) - baseurl = module.baseurl if hasattr(module, "baseurl") \ - else f - result += [(r'%s%s/' % (prefix, baseurl), - include("modoboa.extensions.%s.urls" % f))] - return result + def destroy(self): + pass + + def load(self): + pass -def list_extensions(): - basedir = "%s/extensions" % settings.MODOBOA_DIR - result = [] - for d in os.listdir(basedir): - if not os.path.isdir("%s/%s" % (basedir, d)): - continue - if not isinstalled(d): - continue - isvalid = True - for f in ['urls.py', '__init__.py', 'views.py']: - if not os.path.exists("%s/%s/%s" % (basedir, d, f)): - isvalid = False - break - if isvalid: - module = __import__(d, globals(), locals()) + +class ExtensionsPool(object): + """The extensions manager""" + + def __init__(self): + self.extensions = dict() + + def register_extension(self, ext): + self.extensions[ext.name] = dict(cls=ext) + + def get_extension(self, name): + if not self.extensions.has_key(name): + return None + if not self.extensions[name].has_key("instance"): + self.extensions[name]["instance"] = self.extensions[name]["cls"]() + return self.extensions[name]["instance"] + + def load_all(self, prefix): + from modoboa.admin.models import Extension + + result = [] + for extname, extdef in self.extensions.iteritems(): + extinstance = self.get_extension(extname) try: - infos = module.infos() - infos["id"] = d - if os.path.isdir("%s/%s/templates" % (basedir, d)): - infos["templates"] = True - else: - infos["templates"] = False - result += [infos] - except AttributeError: + ext = Extension.objects.get(name=extname) + if ext.enabled: + extinstance.load() + except Extension.DoesNotExist: pass - return result + baseurl = extinstance.url if extinstance.url is not None else extname + result += [(r'%s%s/' % (prefix, baseurl), + include("%s.urls" % extinstance.__module__))] + return result + + def list_all(self): + result = [] + for extname, extdef in self.extensions.iteritems(): + infos = extdef["instance"].infos() + infos["id"] = extname + result += [infos] + return sorted(result, key=lambda i: i["name"]) + +exts_pool = ExtensionsPool() diff --git a/extensions/amavis_quarantine/__init__.py b/extensions/amavis_quarantine/__init__.py index a33cfc2bd..922e09e13 100644 --- a/extensions/amavis_quarantine/__init__.py +++ b/extensions/amavis_quarantine/__init__.py @@ -7,78 +7,78 @@ from django.utils.translation import ugettext as _, ugettext_lazy from django.core.urlresolvers import reverse from django.template import Template, Context from modoboa.lib import events, parameters +from modoboa.extensions import ModoExtension, exts_pool -baseurl = "quarantine" +class AmavisQuarantine(ModoExtension): + name = "amavis_quarantine" + label = "Amavis quarantine" + version = "1.0" + description = ugettext_lazy("Simple amavis quarantine management tool") + url = "quarantine" -def infos(): - return { - "name" : "Amavis quarantine", - "version" : "1.0", - "description" : _("Simple amavis quarantine management tool"), - "url" : "quarantine" - } + def init(self): + """Init function + + Only run once, when the extension is enabled. We create records + for existing mailboxes in order Amavis considers them local. + """ + from modoboa.admin.models import Mailbox + from models import Users + + for mb in Mailbox.objects.all(): + try: + u = Users.objects.get(email=mb.full_address) + except Users.DoesNotExist: + u = Users() + u.email = mb.full_address + u.fullname = mb.user.fullname + u.local = "1" + u.priority = 7 + u.policy_id = 1 + u.save() -def init(): - """Init function + def load(self): + parameters.register_admin( + "MAX_MESSAGES_AGE", type="int", + deflt=14, + help=_("Quarantine messages maximum age (in days) before deletion") + ) + parameters.register_admin( + "RELEASED_MSGS_CLEANUP", type="list_yesno", deflt="no", + help=_("Remove messages marked as released while cleaning up the database") + ) + parameters.register_admin("AM_PDP_MODE", type="list", + deflt="unix", + values=[("inet", "inet"), ("unix", "unix")], + help=ugettext_lazy("Mode used to access the PDP server")) + parameters.register_admin("AM_PDP_HOST", type="string", + deflt="localhost", + help=ugettext_lazy("PDP server address (if inet mode)")) + parameters.register_admin("AM_PDP_PORT", type="int", + deflt=9998, + help=ugettext_lazy("PDP server port (if inet mode)")) + parameters.register_admin("AM_PDP_SOCKET", type="string", + deflt="/var/amavis/amavisd.sock", + help=ugettext_lazy("Path to the PDP server socket (if unix mode)")) + parameters.register_admin("CHECK_REQUESTS_INTERVAL", type="int", + deflt=30, + help=ugettext_lazy("Interval between two release requests checks")) + parameters.register_admin("USER_CAN_RELEASE", type="list_yesno", deflt="no", + help=ugettext_lazy("Allow users to directly release their messages")) + parameters.register_admin("SELF_SERVICE", type="list_yesno", deflt="no", + help=ugettext_lazy("Activate the 'self-service' mode")) + + parameters.register_user( + "MESSAGES_PER_PAGE", type="int", deflt=40, + label="Number of displayed emails per page", + help=ugettext_lazy("Sets the maximum number of messages displayed in a page") + ) - Only run once, when the extension is enabled. We create records - for existing mailboxes in order Amavis considers them local. - """ - from modoboa.admin.models import Mailbox - from models import Users + def destroy(self): + events.unregister("UserMenuDisplay", menu) + parameters.unregister_app("amavis_quarantine") - for mb in Mailbox.objects.all(): - try: - u = Users.objects.get(email=mb.full_address) - except Users.DoesNotExist: - u = Users() - u.email = mb.full_address - u.fullname = mb.user.fullname - u.local = "1" - u.priority = 7 - u.policy_id = 1 - u.save() - -def load(): - parameters.register_admin( - "MAX_MESSAGES_AGE", type="int", - deflt=14, - help=_("Quarantine messages maximum age (in days) before deletion") - ) - parameters.register_admin( - "RELEASED_MSGS_CLEANUP", type="list_yesno", deflt="no", - help=_("Remove messages marked as released while cleaning up the database") - ) - parameters.register_admin("AM_PDP_MODE", type="list", - deflt="unix", - values=[("inet", "inet"), ("unix", "unix")], - help=ugettext_lazy("Mode used to access the PDP server")) - parameters.register_admin("AM_PDP_HOST", type="string", - deflt="localhost", - help=ugettext_lazy("PDP server address (if inet mode)")) - parameters.register_admin("AM_PDP_PORT", type="int", - deflt=9998, - help=ugettext_lazy("PDP server port (if inet mode)")) - parameters.register_admin("AM_PDP_SOCKET", type="string", - deflt="/var/amavis/amavisd.sock", - help=ugettext_lazy("Path to the PDP server socket (if unix mode)")) - parameters.register_admin("CHECK_REQUESTS_INTERVAL", type="int", - deflt=30, - help=ugettext_lazy("Interval between two release requests checks")) - parameters.register_admin("USER_CAN_RELEASE", type="list_yesno", deflt="no", - help=ugettext_lazy("Allow users to directly release their messages")) - parameters.register_admin("SELF_SERVICE", type="list_yesno", deflt="no", - help=ugettext_lazy("Activate the 'self-service' mode")) - - parameters.register_user( - "MESSAGES_PER_PAGE", type="int", deflt=40, - label="Number of displayed emails per page", - help=ugettext_lazy("Sets the maximum number of messages displayed in a page") - ) - -def destroy(): - events.unregister("UserMenuDisplay", menu) - parameters.unregister_app("amavis_quarantine") +exts_pool.register_extension(AmavisQuarantine) @events.observe("UserMenuDisplay") def menu(target, user): diff --git a/extensions/amavis_quarantine/static/css/quarantine.css b/extensions/amavis_quarantine/static/amavis_quarantine/css/quarantine.css similarity index 100% rename from extensions/amavis_quarantine/static/css/quarantine.css rename to extensions/amavis_quarantine/static/amavis_quarantine/css/quarantine.css diff --git a/extensions/amavis_quarantine/static/css/selfservice.css b/extensions/amavis_quarantine/static/amavis_quarantine/css/selfservice.css similarity index 100% rename from extensions/amavis_quarantine/static/css/selfservice.css rename to extensions/amavis_quarantine/static/amavis_quarantine/css/selfservice.css diff --git a/extensions/amavis_quarantine/static/js/quarantine.js b/extensions/amavis_quarantine/static/amavis_quarantine/js/quarantine.js similarity index 100% rename from extensions/amavis_quarantine/static/js/quarantine.js rename to extensions/amavis_quarantine/static/amavis_quarantine/js/quarantine.js diff --git a/extensions/amavis_quarantine/static/js/selfservice.js b/extensions/amavis_quarantine/static/amavis_quarantine/js/selfservice.js similarity index 100% rename from extensions/amavis_quarantine/static/js/selfservice.js rename to extensions/amavis_quarantine/static/amavis_quarantine/js/selfservice.js diff --git a/extensions/amavis_quarantine/templates/amavis_quarantine/index.html b/extensions/amavis_quarantine/templates/amavis_quarantine/index.html index 8cb7f90eb..f331a4c2f 100644 --- a/extensions/amavis_quarantine/templates/amavis_quarantine/index.html +++ b/extensions/amavis_quarantine/templates/amavis_quarantine/index.html @@ -1,11 +1,11 @@ {% extends "fixed.html" %}{% load i18n %} {% block pagetitle %}{% trans "Quarantine management" %}{% endblock %} {% block html_head %} - - - - - + + + + + - - - - + + + + + + + + + {% url django.views.i18n.javascript_catalog as catalog_url %} diff --git a/extensions/amavis_quarantine/templatetags/amextras.py b/extensions/amavis_quarantine/templatetags/amextras.py index 3257a9df3..490811448 100644 --- a/extensions/amavis_quarantine/templatetags/amextras.py +++ b/extensions/amavis_quarantine/templatetags/amextras.py @@ -103,7 +103,7 @@ def quar_menu(user): else: extraopts = [] extracontent = render_to_string('common/email_searchbar.html', { - "MEDIA_URL" : settings.MEDIA_URL, + "STATIC_URL" : settings.STATIC_URL, "extraopts" : extraopts }) diff --git a/extensions/limits/__init__.py b/extensions/limits/__init__.py index 00fac2d99..47355376d 100644 --- a/extensions/limits/__init__.py +++ b/extensions/limits/__init__.py @@ -10,51 +10,49 @@ from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse from modoboa.lib import events -from modoboa.lib.webutils import static_url +from modoboa.extensions import ModoExtension, exts_pool from models import * from forms import ResourcePoolForm import controls import views -baseurl = "limits" +class Limits(ModoExtension): + name = "limits" + label = "Limits" + version = "1.0" + description = ugettext_lazy("Per administrator resources to limit the number of objects they can create") -def init(): - from modoboa.admin.models import User, Domain + def init(self): + from modoboa.admin.models import User, Domain + + ct = ContentType.objects.get(app_label="admin", model="domain") + dagrp = Group.objects.get(name="DomainAdmins") + + grp = Group(name="Resellers") + grp.save() + grp.permissions.add(*dagrp.permissions.all()) + + ct = ContentType.objects.get_for_model(Permission) + for pname in ["view_permissions"]: + perm = Permission.objects.get(content_type=ct, codename=pname) + grp.permissions.add(perm) + + ct = ContentType.objects.get_for_model(Domain) + for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]: + perm = Permission.objects.get(content_type=ct, codename=pname) + grp.permissions.add(perm) + grp.save() + + for user in User.objects.filter(groups__name='DomainAdmins'): + try: + controls.create_pool(user) + except IntegrityError: + pass - ct = ContentType.objects.get(app_label="admin", model="domain") - dagrp = Group.objects.get(name="DomainAdmins") + def destroy(self): + Group.objects.get(name="Resellers").delete() - grp = Group(name="Resellers") - grp.save() - grp.permissions.add(*dagrp.permissions.all()) - - ct = ContentType.objects.get_for_model(Permission) - for pname in ["view_permissions"]: - perm = Permission.objects.get(content_type=ct, codename=pname) - grp.permissions.add(perm) - - ct = ContentType.objects.get_for_model(Domain) - for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]: - perm = Permission.objects.get(content_type=ct, codename=pname) - grp.permissions.add(perm) - grp.save() - - for user in User.objects.filter(groups__name='DomainAdmins'): - try: - controls.create_pool(user) - except IntegrityError: - pass - -def destroy(): - Group.objects.get(name="Resellers").delete() - -def infos(): - return { - "name" : "Limits", - "version" : "1.0", - "description" : _("Per administrator resources to limit the number of objects they can create"), - "url" : baseurl - } +exts_pool.register_extension(Limits) @events.observe("GetExtraRoles") def get_extra_roles(user): diff --git a/extensions/postfix_autoreply/__init__.py b/extensions/postfix_autoreply/__init__.py index 3159bbf40..4b6a22510 100644 --- a/extensions/postfix_autoreply/__init__.py +++ b/extensions/postfix_autoreply/__init__.py @@ -10,49 +10,49 @@ from django.conf.urls.defaults import include from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _, ugettext_lazy, get_language from modoboa.lib import events, parameters -from modoboa.lib.webutils import static_url +from modoboa.extensions import ModoExtension, exts_pool from models import * -def infos(): - return { - "name" : "Postfix autoreply", - "version" : "1.0", - "description" : _("Auto-reply (vacation) functionality using Postfix"), - "url" : "postfix_autoreply" - } +class PostfixAutoreply(ModoExtension): + name = "postfix_autoreply" + label = "Postfix autoreply" + version = "1.0" + description = ugettext_lazy("Auto-reply (vacation) functionality using Postfix") -def init(): - from modoboa.admin.models import Domain - - for dom in Domain.objects.all(): - try: - trans = Transport.objects.get(domain="autoreply.%s" % dom.name) - except Transport.DoesNotExist: - onCreateDomain(None, dom) - else: - continue - - for mb in dom.mailbox_set.all(): + def init(self): + from modoboa.admin.models import Domain + + for dom in Domain.objects.all(): try: - alias = Alias.objects.get(full_address=mb.full_address) - except Alias.DoesNotExist: - onCreateMailbox(None, mb) + trans = Transport.objects.get(domain="autoreply.%s" % dom.name) + except Transport.DoesNotExist: + onCreateDomain(None, dom) + else: + continue -def load(): - parameters.register_admin( - "AUTOREPLIES_TIMEOUT", - type="int", deflt=86400, - help=ugettext_lazy("Timeout in seconds between two auto-replies to the same recipient") - ) + for mb in dom.mailbox_set.all(): + try: + alias = Alias.objects.get(full_address=mb.full_address) + except Alias.DoesNotExist: + onCreateMailbox(None, mb) -def destroy(): - events.unregister("CreateDomain", onCreateDomain) - events.unregister("DeleteDomain", onDeleteDomain) - events.unregister("CreateMailbox", onCreateMailbox) - events.unregister("DeleteMailbox", onDeleteMailbox) - events.unregister("ModifyMailbox", onModifyMailbox) - events.unregister("UserMenuDisplay", menu) - parameters.unregister_app("postfix_autoreply") + def load(self): + parameters.register_admin( + "AUTOREPLIES_TIMEOUT", + type="int", deflt=86400, + help=ugettext_lazy("Timeout in seconds between two auto-replies to the same recipient") + ) + + def destroy(self): + events.unregister("CreateDomain", onCreateDomain) + events.unregister("DeleteDomain", onDeleteDomain) + events.unregister("CreateMailbox", onCreateMailbox) + events.unregister("DeleteMailbox", onDeleteMailbox) + events.unregister("ModifyMailbox", onModifyMailbox) + events.unregister("UserMenuDisplay", menu) + parameters.unregister_app("postfix_autoreply") + +exts_pool.register_extension(PostfixAutoreply) @events.observe("UserMenuDisplay") def menu(target, user): diff --git a/extensions/sievefilters/__init__.py b/extensions/sievefilters/__init__.py index 984248996..061a4b473 100644 --- a/extensions/sievefilters/__init__.py +++ b/extensions/sievefilters/__init__.py @@ -2,46 +2,45 @@ from django.utils.translation import ugettext as _, ugettext_lazy from django.core.urlresolvers import reverse from modoboa.lib import events, parameters -from modoboa.lib.webutils import static_url +from modoboa.extensions import ModoExtension, exts_pool from sievelib.managesieve import SUPPORTED_AUTH_MECHS -baseurl = "sfilters" +class SieveFilters(ModoExtension): + name = "sievefilters" + label = "Sieve filters" + version = "1.0" + description = ugettext_lazy("Plugin to easily create server-side filters") + url = "sfilters" -def load(): - parameters.register_app(uparams_opts=dict(needs_mailbox=True)) + def load(self): + parameters.register_app(uparams_opts=dict(needs_mailbox=True)) + + parameters.register_admin("SERVER", type="string", + deflt="127.0.0.1", + help=ugettext_lazy("Address of your MANAGESIEVE server")) + parameters.register_admin("PORT", type="int", deflt="2000", + help=ugettext_lazy("Listening port of your MANAGESIEVE server")) + parameters.register_admin("STARTTLS", type="list_yesno", deflt="no", + help=ugettext_lazy("Use the STARTTLS extension")) - parameters.register_admin("SERVER", type="string", - deflt="127.0.0.1", - help=ugettext_lazy("Address of your MANAGESIEVE server")) - parameters.register_admin("PORT", type="int", deflt="2000", - help=ugettext_lazy("Listening port of your MANAGESIEVE server")) - parameters.register_admin("STARTTLS", type="list_yesno", deflt="no", - help=ugettext_lazy("Use the STARTTLS extension")) + values = [('AUTO', 'auto')] + for m in SUPPORTED_AUTH_MECHS: + values += [(m, m.lower())] + parameters.register_admin("AUTHENTICATION_MECH", type="list", deflt="auto", + values=values, + help=ugettext_lazy("Prefered authentication mechanism")) - values = [('AUTO', 'auto')] - for m in SUPPORTED_AUTH_MECHS: - values += [(m, m.lower())] - parameters.register_admin("AUTHENTICATION_MECH", type="list", deflt="auto", - values=values, - help=ugettext_lazy("Prefered authentication mechanism")) - - # User parameters - parameters.register_user("EDITOR_MODE", type="list", deflt="raw", - label=ugettext_lazy("Editor mode"), - values=[("raw", "raw"), ("gui", "simplified")], - help=ugettext_lazy("Select the mode you want the editor to work in")) + # User parameters + parameters.register_user("EDITOR_MODE", type="list", deflt="raw", + label=ugettext_lazy("Editor mode"), + values=[("raw", "raw"), ("gui", "simplified")], + help=ugettext_lazy("Select the mode you want the editor to work in")) -def destroy(): - events.unregister("UserMenuDisplay", menu) - parameters.unregister_app("sievefilters") + def destroy(self): + events.unregister("UserMenuDisplay", menu) + parameters.unregister_app("sievefilters") -def infos(): - return { - "name" : "Sieve filters", - "version" : "1.0", - "description" : _("Plugin to easily create server-side filters"), - "url" : "sfilters" - } +exts_pool.register_extension(SieveFilters) @events.observe("UserMenuDisplay") def menu(target, user): diff --git a/extensions/sievefilters/static/css/style.css b/extensions/sievefilters/static/sievefilters/css/style.css similarity index 100% rename from extensions/sievefilters/static/css/style.css rename to extensions/sievefilters/static/sievefilters/css/style.css diff --git a/extensions/sievefilters/static/js/jquery.dynfields.js b/extensions/sievefilters/static/sievefilters/js/jquery.dynfields.js similarity index 100% rename from extensions/sievefilters/static/js/jquery.dynfields.js rename to extensions/sievefilters/static/sievefilters/js/jquery.dynfields.js diff --git a/extensions/sievefilters/static/js/lib.js b/extensions/sievefilters/static/sievefilters/js/lib.js similarity index 100% rename from extensions/sievefilters/static/js/lib.js rename to extensions/sievefilters/static/sievefilters/js/lib.js diff --git a/extensions/sievefilters/templates/sievefilters/index.html b/extensions/sievefilters/templates/sievefilters/index.html index add85572b..2ed152bbf 100644 --- a/extensions/sievefilters/templates/sievefilters/index.html +++ b/extensions/sievefilters/templates/sievefilters/index.html @@ -1,11 +1,11 @@ {% extends "twocols.html" %}{% load i18n sfilters_tags %} {% block pagetitle %}{% trans "Message filters" %}{% endblock %} {% block html_head %} - - - - - + + + + + - + + + - - - - - + + + + + + + + + {% endblock %} diff --git a/templates/admin/identities.html b/templates/admin/identities.html index d5d7e427d..f00881344 100644 --- a/templates/admin/identities.html +++ b/templates/admin/identities.html @@ -2,8 +2,8 @@ {% block pagetitle %}{% trans "Identities" %}{% endblock %} {% block html_head %} {{ block.super }} - - + + + - - - {% if LANGUAGE_CODE != "en" %}{% endif %} - - - - + + + + + + + + + {% if LANGUAGE_CODE != "en" %}{% endif %} + + + + {% url django.views.i18n.javascript_catalog as catalog_url %} {% extra_head_content user %} {% block html_head %}{% endblock %} diff --git a/templates/registration/login.html b/templates/registration/login.html index a1a450886..85d16aa6a 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -3,12 +3,12 @@ {% trans "Welcome to Modoboa" %} - - - - - - + + + + + + {% url django.views.i18n.javascript_catalog as catalog_url %}