diff --git a/admin/models.py b/admin/models.py index ce7970aea..b8feddede 100644 --- a/admin/models.py +++ b/admin/models.py @@ -10,8 +10,9 @@ 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 exceptions import * -import os +import os, sys import pwd import re import crypt, hashlib, string, base64 @@ -653,22 +654,19 @@ class Extension(models.Model): help_text=ugettext_noop("Check to enable this extension") ) - def __getmodule(self): - extname = "modoboa.extensions.%s" % self.name - return __import__(extname, globals(), locals(), ['main']) - def init(self): - module = self.__getmodule() - if hasattr(module.main, "init"): - module.main.init() + module = get_ext_module(self.name) + if hasattr(module, "init"): + module.init() def load(self): - module = self.__getmodule() - if hasattr(module.main, "load"): - module.main.load() + module = get_ext_module(self.name) + if hasattr(module, "load"): + module.load() def unload(self): - self.__getmodule().main.destroy() + module = get_ext_module(self.name) + module.destroy() def on(self): self.load() diff --git a/extensions/__init__.py b/extensions/__init__.py index b4aee3ef4..fef30fd24 100644 --- a/extensions/__init__.py +++ b/extensions/__init__.py @@ -1,14 +1,20 @@ -# -*- coding: utf-8 -*- -import os +# coding: utf-8 +import os, sys 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 +def get_ext_module(extname): + modname = "modoboa.extensions.%s" % extname + if not sys.modules.has_key(modname): + __import__(modname) + return sys.modules[modname] + def get_extension_infos(name): - ext = __import__(name, globals(), locals(), ["main"]) - return ext.main.infos() + return get_ext_module(name).infos() def loadextensions(prefix): # To avoid a circular import... @@ -21,17 +27,17 @@ def loadextensions(prefix): continue if not isinstalled(f): continue - module = __import__(f, globals(), locals(), ['main']) try: ext = Extension.objects.get(name=f) if ext.enabled: ext.load() except Extension.DoesNotExist: pass - u = module.main.urls(prefix) - if u == (): - continue - result += [u] + 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 list_extensions(): @@ -43,14 +49,14 @@ def list_extensions(): if not isinstalled(d): continue isvalid = True - for f in ['urls.py', 'main.py', '__init__.py', 'views.py']: + 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(), ['main']) + module = __import__(d, globals(), locals()) try: - infos = module.main.infos() + infos = module.infos() infos["id"] = d if os.path.isdir("%s/%s/templates" % (basedir, d)): infos["templates"] = True @@ -60,12 +66,3 @@ def list_extensions(): except AttributeError: pass return result - -def loadmenus(): - result = [] - for name, mod in extensions.iteritems(): - u = mod.main.urls() - if u == (): - continue - result += (u,) - return result diff --git a/extensions/amavis_quarantine/__init__.py b/extensions/amavis_quarantine/__init__.py index e69de29bb..fdfeb2769 100644 --- a/extensions/amavis_quarantine/__init__.py +++ b/extensions/amavis_quarantine/__init__.py @@ -0,0 +1,67 @@ +# coding: utf-8 +""" +Amavis quarantine manager (SQL based) + + +""" +from django.utils.translation import ugettext_noop as _, ugettext +from django.core.urlresolvers import reverse +from modoboa.lib import events, parameters + +baseurl = "quarantine" + +def infos(): + return { + "name" : "Amavis quarantine", + "version" : "1.0", + "description" : ugettext("Simple amavis quarantine management tool"), + "url" : "quarantine" + } + +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=_("Mode used to access the PDP server")) + parameters.register_admin("AM_PDP_HOST", type="string", + deflt="localhost", + help=_("PDP server address (if inet mode)")) + parameters.register_admin("AM_PDP_PORT", type="int", + deflt=9998, + help=_("PDP server port (if inet mode)")) + parameters.register_admin("AM_PDP_SOCKET", type="string", + deflt=_("/var/amavis/amavisd.sock"), + help=_("Path to the PDP server socket (if unix mode)")) + + 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") + ) + +def destroy(): + events.unregister("UserMenuDisplay", menu) + parameters.unregister_app("amavis_quarantine") + +@events.observe("UserMenuDisplay") +def menu(target, user): + from modoboa.lib.webutils import static_url + + if target == "top_menu": + return [ + {"name" : "quarantine", + "label" : _("Quarantine"), + "url" : reverse('modoboa.extensions.amavis_quarantine.views.index'), + "img" : static_url("pics/quarantine.png")} + ] + return [] + diff --git a/extensions/amavis_quarantine/main.py b/extensions/amavis_quarantine/main.py deleted file mode 100644 index 11dac1047..000000000 --- a/extensions/amavis_quarantine/main.py +++ /dev/null @@ -1,69 +0,0 @@ -# coding: utf-8 - -""" -""" -from django.conf.urls.defaults import * -from django.utils.translation import ugettext_noop as _, ugettext -from django.core.urlresolvers import reverse -from modoboa.lib import events, parameters -from modoboa.lib.webutils import static_url - -def infos(): - return { - "name" : "Amavis quarantine", - "version" : "1.0", - "description" : ugettext("Simple amavis quarantine management tool"), - "url" : "quarantine" - } - -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=_("Mode used to access the PDP server")) - parameters.register_admin("AM_PDP_HOST", type="string", - deflt="localhost", - help=_("PDP server address (if inet mode)")) - parameters.register_admin("AM_PDP_PORT", type="int", - deflt=9998, - help=_("PDP server port (if inet mode)")) - parameters.register_admin("AM_PDP_SOCKET", type="string", - deflt=_("/var/amavis/amavisd.sock"), - help=_("Path to the PDP server socket (if unix mode)")) - - 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") - ) - -def destroy(): - events.unregister("UserMenuDisplay", menu) - parameters.unregister_app("amavis_quarantine") - -def urls(prefix): - return (r'^%squarantine/' % prefix, - include('modoboa.extensions.amavis_quarantine.urls')) - -@events.observe("UserMenuDisplay") -def menu(target, menu): - import views - - if target == "top_menu": - return [ - {"name" : "quarantine", - "label" : _("Quarantine"), - "url" : reverse(views.index), - "img" : static_url("pics/quarantine.png")} - ] - return [] - diff --git a/extensions/amavis_quarantine/templates/amavis_quarantine/index.html b/extensions/amavis_quarantine/templates/amavis_quarantine/index.html index 22368f1f8..fb8ff89c9 100644 --- a/extensions/amavis_quarantine/templates/amavis_quarantine/index.html +++ b/extensions/amavis_quarantine/templates/amavis_quarantine/index.html @@ -23,7 +23,7 @@ new Element('input', { 'type' : 'hidden', 'name' : "action", style: "display:none", 'value' : name }).inject($("listingform"), "top"); - {% url extensions.amavis_quarantine.views.process as actionurl %} + {% url modoboa.extensions.amavis_quarantine.views.process as actionurl %} $("listingform").set("action", "{{ actionurl }}"); $("listingform").set("send", { onSuccess : function(res) { diff --git a/extensions/amavis_quarantine/urls.py b/extensions/amavis_quarantine/urls.py index a8c72de88..9633389cc 100644 --- a/extensions/amavis_quarantine/urls.py +++ b/extensions/amavis_quarantine/urls.py @@ -1,16 +1,13 @@ from django.conf.urls.defaults import * -extpatterns = [ - (r'^$', 'index'), - (r'^listing/$', '_listing'), - (r'^getmailcontent/(?P[\w\-\+]+)/$', 'getmailcontent'), - (r'^(?P[\w\-\+]+)/headers/$', 'viewheaders'), - (r'^process/$', 'process'), - (r'^delete/(?P[\w\-\+]+)/$', 'delete'), - (r'^release/(?P[\w\-\+]+)/$', 'release'), - (r'^(?P[\w\-\+]+)/$', 'viewmail') - ] - -urlpatterns = patterns('modoboa.extensions.amavis_quarantine.views', - *extpatterns - ) +urlpatterns = patterns( + 'modoboa.extensions.amavis_quarantine.views', + (r'^$', 'index'), + (r'^listing/$', '_listing'), + (r'^getmailcontent/(?P[\w\-\+]+)/$', 'getmailcontent'), + (r'^(?P[\w\-\+]+)/headers/$', 'viewheaders'), + (r'^process/$', 'process'), + (r'^delete/(?P[\w\-\+]+)/$', 'delete'), + (r'^release/(?P[\w\-\+]+)/$', 'release'), + (r'^(?P[\w\-\+]+)/$', 'viewmail'), + ) diff --git a/extensions/limits/__init__.py b/extensions/limits/__init__.py index e69de29bb..b7f89e1c5 100644 --- a/extensions/limits/__init__.py +++ b/extensions/limits/__init__.py @@ -0,0 +1,81 @@ +# coding: utf-8 + +""" +The *limits* extension +---------------------- +""" +from django.contrib.auth.models import Permission, Group +from django.db import IntegrityError +from django.contrib.contenttypes.models import ContentType +from django.utils.translation import ugettext_noop as _, ugettext +from django.core.urlresolvers import reverse +from modoboa.lib import events, parameters +from modoboa.lib.webutils import static_url +from modoboa.admin.models import User +from models import * +import permissions +import controls +import views + +baseurl = "limits" + +def init(): + ct = ContentType.objects.get(app_label="admin", model="domain") + grp = Group.objects.get(name="DomainAdmins") + 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(): + ct = ContentType.objects.get(app_label="admin", model="domain") + grp = Group.objects.get(name="DomainAdmins") + for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]: + perm = Permission.objects.get(content_type=ct, codename=pname) + grp.permissions.remove(perm) + grp.save() + +def infos(): + return { + "name" : "Limits", + "version" : "1.0", + "description" : ugettext("Limits for objects creation"), + "url" : baseurl + } + +@events.observe('AdminFooterDisplay') +def display_pool_usage(user, objtype): + if user.id == 1: + 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)] + +@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}}"}, + ] diff --git a/extensions/limits/main.py b/extensions/limits/main.py deleted file mode 100644 index d313d8e17..000000000 --- a/extensions/limits/main.py +++ /dev/null @@ -1,87 +0,0 @@ -# coding: utf-8 - -""" -The *limits* extension ----------------------- - - -""" -from django.contrib.auth.models import Permission, User, Group -from django.db import IntegrityError -from django.contrib.contenttypes.models import ContentType -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 -from models import * -import permissions -import controls -import views - -baseurl = "limits" - -def init(): - ct = ContentType.objects.get(app_label="admin", model="domain") - grp = Group.objects.get(name="DomainAdmins") - 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(): - ct = ContentType.objects.get(app_label="admin", model="domain") - grp = Group.objects.get(name="DomainAdmins") - for pname in ["view_domains", "add_domain", "change_domain", "delete_domain"]: - perm = Permission.objects.get(content_type=ct, codename=pname) - grp.permissions.remove(perm) - grp.save() - -def infos(): - return { - "name" : "Limits", - "version" : "1.0", - "description" : ugettext("Limits for objects creation"), - "url" : baseurl - } - -def urls(prefix): - return (r'^%s%s/' % (prefix, baseurl), - include('modoboa.extensions.limits.urls')) - -@events.observe('AdminFooterDisplay') -def display_pool_usage(user, objtype): - if user.id == 1: - 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)] - -@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}}"}, - ] diff --git a/extensions/limits/templates/limits/pool.html b/extensions/limits/templates/limits/pool.html index fd77fd0be..5f4da7005 100644 --- a/extensions/limits/templates/limits/pool.html +++ b/extensions/limits/templates/limits/pool.html @@ -11,7 +11,7 @@ window.addEvent("domready", function() { {% block content %}

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

{% if error %}{{ error }}{% endif %}
-
{% include "common/genericform.html" %}
diff --git a/extensions/postfix_autoreply/__init__.py b/extensions/postfix_autoreply/__init__.py index e69de29bb..6d99c025b 100644 --- a/extensions/postfix_autoreply/__init__.py +++ b/extensions/postfix_autoreply/__init__.py @@ -0,0 +1,93 @@ +# coding: utf-8 +""" +Postfix auto-replies plugin. + +This module provides a way to integrate Modoboa auto-reply +functionality into Postfix. + +""" +from django.conf.urls.defaults import include +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_noop as _, ugettext +from modoboa.lib import events, parameters +from modoboa.lib.webutils import static_url +from models import * + +def infos(): + return { + "name" : "Postfix autoreply", + "version" : "1.0", + "description" : ugettext("Auto-reply (vacation) functionality using Postfix"), + "url" : "postfix_autoreply" + } + +def load(): + parameters.register_admin( + "AUTOREPLIES_TIMEOUT", + type="int", deflt=86400, + help=_("Timeout in seconds between two auto-replies to the same recipient") + ) + +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") + +@events.observe("UserMenuDisplay") +def menu(target, user): + import views + + if target != "uprefs_menu": + return [] + if not user.has_mailbox: + return [] + return [ + {"name" : "autoreply", + "url" : reverse(views.autoreply), + "img" : static_url("pics/auto-reply.png"), + "class" : "boxed", + "rel" : "{handler:'iframe',size:{x:440,y:400}}", + "label" : ugettext("Auto-reply message")} + ] + +@events.observe("CreateDomain") +def onCreateDomain(user, domain): + transport = Transport() + transport.domain = "autoreply.%s" % domain.name + transport.method = "autoreply:" + transport.save() + +@events.observe("DeleteDomain") +def onDeleteDomain(domain): + trans = Transport.objects.get(domain="autoreply.%s" % domain.name) + trans.delete() + +@events.observe("CreateMailbox") +def onCreateMailbox(user, mailbox): + alias = Alias() + alias.full_address = mailbox.full_address + alias.autoreply_address = \ + "%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name) + alias.save() + +@events.observe("DeleteMailbox") +def onDeleteMailbox(mailbox): + try: + alias = Alias.objects.get(full_address=mailbox.full_address) + alias.delete() + except Alias.DoesNotExist: + pass + +@events.observe("ModifyMailbox") +def onModifyMailbox(mailbox, oldmailbox): + if oldmailbox.full_address == mailbox.full_address: + return + alias = Alias.objects.get(full_address=oldmailbox.full_address) + alias.full_address = mailbox.full_address + alias.autoreply_address = \ + "%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name) + alias.save() diff --git a/extensions/postfix_autoreply/main.py b/extensions/postfix_autoreply/main.py deleted file mode 100644 index 2fff53989..000000000 --- a/extensions/postfix_autoreply/main.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Postfix auto-replies plugin. - -This module provides a way to integrate Modoboa auto-reply -functionality into Postfix. - -""" -from django.conf.urls.defaults import include -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_noop as _, ugettext -from modoboa.lib import events, parameters -from modoboa.lib.webutils import static_url -from models import * - -def infos(): - return { - "name" : "Postfix autoreply", - "version" : "1.0", - "description" : ugettext("Auto-reply (vacation) functionality using Postfix"), - "url" : "postfix_autoreply" - } - -def load(): - parameters.register_admin( - "AUTOREPLIES_TIMEOUT", - type="int", deflt=86400, - help=_("Timeout in seconds between two auto-replies to the same recipient") - ) - -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 urls(prefix): - return (r'^%spostfix_autoreply/' % prefix, - include('modoboa.extensions.postfix_autoreply.urls')) - -@events.observe("UserMenuDisplay") -def menu(target, user): - import views - - if target != "uprefs_menu": - return [] - if not user.has_mailbox: - return [] - return [ - {"name" : "autoreply", - "url" : reverse(views.autoreply), - "img" : static_url("pics/auto-reply.png"), - "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:440,y:400}}", - "label" : ugettext("Auto-reply message")} - ] - -@events.observe("CreateDomain") -def onCreateDomain(user, domain): - transport = Transport() - transport.domain = "autoreply.%s" % domain.name - transport.method = "autoreply:" - transport.save() - -@events.observe("DeleteDomain") -def onDeleteDomain(domain): - trans = Transport.objects.get(domain="autoreply.%s" % domain.name) - trans.delete() - -@events.observe("CreateMailbox") -def onCreateMailbox(user, mailbox): - alias = Alias() - alias.full_address = mailbox.full_address - alias.autoreply_address = \ - "%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name) - alias.save() - -@events.observe("DeleteMailbox") -def onDeleteMailbox(mailbox): - try: - alias = Alias.objects.get(full_address=mailbox.full_address) - alias.delete() - except Alias.DoesNotExist: - pass - -@events.observe("ModifyMailbox") -def onModifyMailbox(mailbox, oldmailbox): - if oldmailbox.full_address == mailbox.full_address: - return - alias = Alias.objects.get(full_address=oldmailbox.full_address) - alias.full_address = mailbox.full_address - alias.autoreply_address = \ - "%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name) - alias.save() diff --git a/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html b/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html index b402bfc34..7f8790c35 100644 --- a/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html +++ b/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html @@ -29,7 +29,7 @@ window.addEvent("domready", function() { {% block content %}

{% trans "Auto-reply message" %}

- {% include "common/genericform.html" %}
diff --git a/extensions/sievefilters/__init__.py b/extensions/sievefilters/__init__.py index e69de29bb..9905c097b 100644 --- a/extensions/sievefilters/__init__.py +++ b/extensions/sievefilters/__init__.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +from django.utils.translation import ugettext_noop as _, ugettext +from django.core.urlresolvers import reverse +from modoboa.lib import events, parameters +from modoboa.lib.webutils import static_url +from sievelib.managesieve import SUPPORTED_AUTH_MECHS + +baseurl = "sfilters" + +def load(): + parameters.register_admin("SERVER", type="string", + deflt="127.0.0.1", + help=_("Address of your MANAGESIEVE server")) + parameters.register_admin("PORT", type="int", deflt="2000", + help=_("Listening port of your MANAGESIEVE server")) + parameters.register_admin("STARTTLS", type="list_yesno", deflt="no", + help=_("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=_("Prefered authentication mechanism")) + + # User parameters + parameters.register_user("EDITOR_MODE", type="list", deflt="raw", + label=_("Editor mode"), + values=[("raw", "raw"), ("gui", "simplified")], + help=_("Select the mode you want the editor to work in")) + +def destroy(): + events.unregister("UserMenuDisplay", menu) + parameters.unregister_app("sievefilters") + +def infos(): + return { + "name" : "Sieve filters", + "version" : "1.0", + "description" : ugettext("Plugin to easily create server-side filters"), + "url" : "sfilters" + } + +@events.observe("UserMenuDisplay") +def menu(target, user): + import views + + if target != "options_menu": + return [] + if not user.has_mailbox: + return [] + return [ + {"name" : "sievefilters", + "label" : ugettext("Message filters"), + "url" : reverse(views.index), + "img" : static_url("pics/filters.png")} + ] + +@events.observe("Userlogout") +def userlogout(request): + from lib import SieveClient + + if not request.user.has_mailbox: + return + try: + sc = SieveClient(user=request.user.username, + password=request.session["password"]) + except Exception, e: + pass + else: + sc.logout() diff --git a/extensions/sievefilters/main.py b/extensions/sievefilters/main.py deleted file mode 100644 index 7609a008f..000000000 --- a/extensions/sievefilters/main.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -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 -from sievelib.managesieve import SUPPORTED_AUTH_MECHS - -def load(): - parameters.register_admin("SERVER", type="string", - deflt="127.0.0.1", - help=_("Address of your MANAGESIEVE server")) - parameters.register_admin("PORT", type="int", deflt="2000", - help=_("Listening port of your MANAGESIEVE server")) - parameters.register_admin("STARTTLS", type="list_yesno", deflt="no", - help=_("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=_("Prefered authentication mechanism")) - - # User parameters - parameters.register_user("EDITOR_MODE", type="list", deflt="raw", - label=_("Editor mode"), - values=[("raw", "raw"), ("gui", "simplified")], - help=_("Select the mode you want the editor to work in")) - -def destroy(): - events.unregister("UserMenuDisplay", menu) - parameters.unregister_app("sievefilters") - -def infos(): - return { - "name" : "Sieve filters", - "version" : "1.0", - "description" : ugettext("Plugin to easily create server-side filters"), - "url" : "sfilters" - } - -def urls(prefix): - return (r'^%ssfilters/' % prefix, - include('modoboa.extensions.sievefilters.urls')) - -@events.observe("UserMenuDisplay") -def menu(target, user): - import views - - if target != "options_menu": - return [] - if not user.has_mailbox: - return [] - return [ - {"name" : "sievefilters", - "label" : ugettext("Message filters"), - "url" : reverse(views.index), - "img" : static_url("pics/filters.png")} - ] - -@events.observe("Userlogout") -def userlogout(request): - from lib import SieveClient - - if not request.user.has_mailbox: - return - try: - sc = SieveClient(user=request.user.username, - password=request.session["password"]) - except Exception, e: - pass - else: - sc.logout() diff --git a/extensions/sievefilters/templates/sievefilters/filter.html b/extensions/sievefilters/templates/sievefilters/filter.html index f56d853cb..2e2217be1 100644 --- a/extensions/sievefilters/templates/sievefilters/filter.html +++ b/extensions/sievefilters/templates/sievefilters/filter.html @@ -32,7 +32,7 @@ function initform() { }); da = new DynamicAction(alist, { templates: action_templates, - ufolders_url: "{% url extensions.sievefilters.views.get_user_folders %}" + ufolders_url: "{% url modoboa.extensions.sievefilters.views.get_user_folders %}" }); clist.addEvent("change", dc.onTargetChange.bind(dc)); @@ -43,13 +43,13 @@ function initform() { $(document).addEvent("domready", function() { new Request.JSON({ - url: "{% url extensions.sievefilters.views.get_templates 'condition' %}", + url: "{% url modoboa.extensions.sievefilters.views.get_templates 'condition' %}", onSuccess: function(response) { cond_templates = response; } }).get(); new Request.JSON({ - url: "{% url extensions.sievefilters.views.get_templates 'action' %}", + url: "{% url modoboa.extensions.sievefilters.views.get_templates 'action' %}", onSuccess: function(response) { action_templates = response; initform(); diff --git a/extensions/sievefilters/templates/sievefilters/filters.html b/extensions/sievefilters/templates/sievefilters/filters.html index 94404b21e..42f561aaa 100644 --- a/extensions/sievefilters/templates/sievefilters/filters.html +++ b/extensions/sievefilters/templates/sievefilters/filters.html @@ -15,7 +15,7 @@ + href="{% url modoboa.extensions.sievefilters.views.toggle_filter_state fs.name filter.name %}"> {{ filter.enabled|yesno }} diff --git a/extensions/sievefilters/templates/sievefilters/rawfilter.html b/extensions/sievefilters/templates/sievefilters/rawfilter.html index 1149afac2..895609303 100644 --- a/extensions/sievefilters/templates/sievefilters/rawfilter.html +++ b/extensions/sievefilters/templates/sievefilters/rawfilter.html @@ -3,7 +3,7 @@

{% trans "Actions" %}

{% fset_menu "raw" name %} -
diff --git a/extensions/stats/__init__.py b/extensions/stats/__init__.py index e69de29bb..a19a796f1 100644 --- a/extensions/stats/__init__.py +++ b/extensions/stats/__init__.py @@ -0,0 +1,47 @@ +# coding: utf-8 + +""" +Graphical statistics about emails traffic using RRDtool + +This module provides support to retrieve statistics from postfix log : +sent, received, bounced, rejected + +""" +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_noop as _, ugettext +from django.conf.urls.defaults import * +from modoboa.lib import events, parameters +from modoboa.lib.webutils import static_url + +def infos(): + return { + "name" : "Statistics", + "version" : "1.0", + "description" : ugettext("Graphical statistics about emails traffic using RRDtool"), + "url" : "stats" + } + +def load(): + parameters.register_admin("LOGFILE", type="string", + deflt="/var/log/mail.log", + help=_("Path to log file used to collect statistics")) + parameters.register_admin("RRD_ROOTDIR", type="string", + deflt="/tmp/modoboa", + help=_("Path to directory where RRD files are stored")) + parameters.register_admin("IMG_ROOTDIR", type="string", + deflt="/tmp/modoboa", + help=_("Path to directory where PNG files are stored")) + +def destroy(): + events.unregister("AdminMenuDisplay", menu) + +@events.observe("AdminMenuDisplay") +def menu(target, user): + if target == "admin_menu_box": + return [ + {"name" : "stats", + "label" : ugettext("Statistics"), + "url" : reverse('fullindex'), + "img" : static_url("pics/graph.png")} + ] + return [] diff --git a/extensions/stats/main.py b/extensions/stats/main.py deleted file mode 100644 index ec87417a6..000000000 --- a/extensions/stats/main.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Graphical statistics about emails traffic using RRDtool - -This module provides support to retrieve statistics from postfix log : -sent, received, bounced, rejected - -""" -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_noop as _, ugettext -from django.conf.urls.defaults import * -from modoboa.lib import events, parameters -from modoboa.lib.webutils import static_url - -def infos(): - return { - "name" : "Statistics", - "version" : "1.0", - "description" : ugettext("Graphical statistics about emails traffic using RRDtool"), - "url" : "stats" - } - -def load(): - parameters.register_admin("LOGFILE", type="string", - deflt="/var/log/mail.log", - help=_("Path to log file used to collect statistics")) - parameters.register_admin("RRD_ROOTDIR", type="string", - deflt="/tmp/modoboa", - help=_("Path to directory where RRD files are stored")) - parameters.register_admin("IMG_ROOTDIR", type="string", - deflt="/tmp/modoboa", - help=_("Path to directory where PNG files are stored")) - -def destroy(): - events.unregister("AdminMenuDisplay", menu) - -def urls(prefix): - return (r'^%sstats/' % prefix, - include('modoboa.extensions.stats.urls')) - -@events.observe("AdminMenuDisplay") -def menu(target, user): - if target == "admin_menu_box": - return [ - {"name" : "stats", - "label" : ugettext("Statistics"), - "url" : reverse('fullindex'), - "img" : static_url("pics/graph.png")} - ] - return [] diff --git a/extensions/webmail/__init__.py b/extensions/webmail/__init__.py index e69de29bb..a3f2e3bc1 100644 --- a/extensions/webmail/__init__.py +++ b/extensions/webmail/__init__.py @@ -0,0 +1,103 @@ +# coding: utf-8 +from django.utils.translation import ugettext_noop as _, ugettext +from django.core.urlresolvers import reverse +from modoboa.lib import events, parameters +from modoboa.lib.webutils import static_url + +def load(): + parameters.register_admin("IMAP_SERVER", type="string", + deflt="127.0.0.1", + help=_("Address of your IMAP server")) + parameters.register_admin("IMAP_SECURED", type="list_yesno", + deflt="no", + help=_("Use a secured connection to access IMAP server")) + parameters.register_admin("IMAP_PORT", type="int", deflt="143", + help=_("Listening port of your IMAP server")) + parameters.register_admin("SMTP_SERVER", type="string", + deflt="127.0.0.1", + help=_("Address of your SMTP server")) + parameters.register_admin("SMTP_SECURED_MODE", type="list", + values=[("none", _("None")), + ("starttls", "STARTTLS"), + ("ssl", "SSL/TLS")], + deflt="none", + help=_("Use a secured connection to access SMTP server")) + parameters.register_admin("SMTP_PORT", type="int", deflt="25", + help=_("Listening port of your SMTP server")) + parameters.register_admin("SMTP_AUTHENTICATION", type="list_yesno", + deflt="no", + help=_("Server needs authentication")) + 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")) + parameters.register_user("REFRESH_INTERVAL", type="int", deflt=300, + label=_("Listing refresh rate"), + help=_("Automatic folder refresh rate (in seconds)")) + parameters.register_user("TRASH_FOLDER", type="string", deflt="Trash", + label=_("Trash folder"), + help=_("Folder where deleted messages go")) + parameters.register_user("SENT_FOLDER", type="string", deflt="Sent", + label=_("Sent folder"), + help=_("Folder where copies of sent messages go")) + parameters.register_user("DRAFTS_FOLDER", type="string", deflt="Drafts", + label=_("Drafts folder"), + help=_("Folder where drafts go")) + parameters.register_user("SIGNATURE", type="text", deflt="", + label=_("Signature text"), + help=_("User defined email signature")) + parameters.register_user("EDITOR", type="list", deflt="plain", + label=_("Default editor"), + values=[("html", "html"), ("plain", "text")], + help=_("The default editor to use when composing a message")) + parameters.register_user("DISPLAYMODE", type="list", deflt="plain", + label=_("Default message display mode"), + values=[("html", "html"), ("plain", "text")], + help=_("The default mode used when displaying a message")) + parameters.register_user("ENABLE_LINKS", type="list_yesno", deflt="no", + label=_("Enable HTML links display"), + help=_("Enable/Disable HTML links display")) + +def destroy(): + events.unregister("UserMenuDisplay", menu) + parameters.unregister_app("webmail") + +def infos(): + return { + "name" : "Webmail", + "version" : "1.0", + "description" : ugettext("Simple IMAP webmail"), + "url" : "webmail" + } + +@events.observe("UserMenuDisplay") +def menu(target, user): + import views + + if target != "top_menu": + return [] + if not user.has_mailbox: + return [] + return [ + {"name" : "webmail", + "label" : _("Webmail"), + "url" : reverse(views.index), + "img" : static_url("pics/webmail.png")} + ] + +@events.observe("Userlogout") +def userlogout(request): + from lib import IMAPconnector + + if not request.user.has_mailbox: + return + try: + m = IMAPconnector(user=request.user.username, + password=request.session["password"]) + except Exception, e: + pass + else: + m.logout() diff --git a/extensions/webmail/main.py b/extensions/webmail/main.py deleted file mode 100644 index 4a476cb4c..000000000 --- a/extensions/webmail/main.py +++ /dev/null @@ -1,108 +0,0 @@ -# -*- coding: utf-8 -*- -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 - -def load(): - parameters.register_admin("IMAP_SERVER", type="string", - deflt="127.0.0.1", - help=_("Address of your IMAP server")) - parameters.register_admin("IMAP_SECURED", type="list_yesno", - deflt="no", - help=_("Use a secured connection to access IMAP server")) - parameters.register_admin("IMAP_PORT", type="int", deflt="143", - help=_("Listening port of your IMAP server")) - parameters.register_admin("SMTP_SERVER", type="string", - deflt="127.0.0.1", - help=_("Address of your SMTP server")) - parameters.register_admin("SMTP_SECURED_MODE", type="list", - values=[("none", _("None")), - ("starttls", "STARTTLS"), - ("ssl", "SSL/TLS")], - deflt="none", - help=_("Use a secured connection to access SMTP server")) - parameters.register_admin("SMTP_PORT", type="int", deflt="25", - help=_("Listening port of your SMTP server")) - parameters.register_admin("SMTP_AUTHENTICATION", type="list_yesno", - deflt="no", - help=_("Server needs authentication")) - 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")) - parameters.register_user("REFRESH_INTERVAL", type="int", deflt=300, - label=_("Listing refresh rate"), - help=_("Automatic folder refresh rate (in seconds)")) - parameters.register_user("TRASH_FOLDER", type="string", deflt="Trash", - label=_("Trash folder"), - help=_("Folder where deleted messages go")) - parameters.register_user("SENT_FOLDER", type="string", deflt="Sent", - label=_("Sent folder"), - help=_("Folder where copies of sent messages go")) - parameters.register_user("DRAFTS_FOLDER", type="string", deflt="Drafts", - label=_("Drafts folder"), - help=_("Folder where drafts go")) - parameters.register_user("SIGNATURE", type="text", deflt="", - label=_("Signature text"), - help=_("User defined email signature")) - parameters.register_user("EDITOR", type="list", deflt="plain", - label=_("Default editor"), - values=[("html", "html"), ("plain", "text")], - help=_("The default editor to use when composing a message")) - parameters.register_user("DISPLAYMODE", type="list", deflt="plain", - label=_("Default message display mode"), - values=[("html", "html"), ("plain", "text")], - help=_("The default mode used when displaying a message")) - parameters.register_user("ENABLE_LINKS", type="list_yesno", deflt="no", - label=_("Enable HTML links display"), - help=_("Enable/Disable HTML links display")) - -def destroy(): - events.unregister("UserMenuDisplay", menu) - parameters.unregister_app("webmail") - -def infos(): - return { - "name" : "Webmail", - "version" : "1.0", - "description" : ugettext("Simple IMAP webmail"), - "url" : "webmail" - } - -def urls(prefix): - return (r'^%swebmail/' % prefix, - include('modoboa.extensions.webmail.urls')) - -@events.observe("UserMenuDisplay") -def menu(target, user): - import views - - if target != "top_menu": - return [] - if not user.has_mailbox: - return [] - return [ - {"name" : "webmail", - "label" : _("Webmail"), - "url" : reverse(views.index), - "img" : static_url("pics/webmail.png")} - ] - -@events.observe("Userlogout") -def userlogout(request): - from lib import IMAPconnector - - if not request.user.has_mailbox: - return - try: - m = IMAPconnector(user=request.user.username, - password=request.session["password"]) - except Exception, e: - pass - else: - m.logout() diff --git a/extensions/webmail/templates/webmail/attachments.html b/extensions/webmail/templates/webmail/attachments.html index bf4f2e591..34b2a3d9f 100644 --- a/extensions/webmail/templates/webmail/attachments.html +++ b/extensions/webmail/templates/webmail/attachments.html @@ -22,7 +22,7 @@ function upload_cb(fname, tmpname) { var ndiv = new Element("div"); var delbtn = new Element("a") .set("name", "delattachment") - .set("href", "{% url extensions.webmail.views.delattachment %}?name=" + tmpname) + .set("href", "{% url modoboa.extensions.webmail.views.delattachment %}?name=" + tmpname) .set("html", "") .inject(ndiv); var label = new Element("label").set("html", fname).inject(ndiv); @@ -87,7 +87,7 @@ window.addEvent("domready", function() {
{{ error }}
+ action="{% url modoboa.extensions.webmail.views.attachments %}"> {% include "common/genericform.html" %}
@@ -101,7 +101,7 @@ window.addEvent("domready", function() {
{% for att in attachments %}
- diff --git a/extensions/webmail/templates/webmail/compose.html b/extensions/webmail/templates/webmail/compose.html index 43bfd0753..204d8b821 100644 --- a/extensions/webmail/templates/webmail/compose.html +++ b/extensions/webmail/templates/webmail/compose.html @@ -3,7 +3,7 @@
{% include "common/genericform.html" %} -
+
{% trans "Attachments" %} diff --git a/extensions/webmail/templates/webmail/folders.html b/extensions/webmail/templates/webmail/folders.html index cf0b5610b..2cc64549a 100644 --- a/extensions/webmail/templates/webmail/folders.html +++ b/extensions/webmail/templates/webmail/folders.html @@ -2,7 +2,7 @@ {% load webextras %} {% if titlebar %}
- {% trans 'Add a new folder' %} diff --git a/extensions/webmail/templates/webmail/headers.html b/extensions/webmail/templates/webmail/headers.html index 2afddd1da..900065fa6 100644 --- a/extensions/webmail/templates/webmail/headers.html +++ b/extensions/webmail/templates/webmail/headers.html @@ -6,7 +6,7 @@ {% trans "Attachments" %} - {% url extensions.webmail.views.getattachment folder mail_id as test %} + {% url modoboa.extensions.webmail.views.getattachment folder mail_id as test %} {% for key, fname in attachments.items %} {{ fname }} {% endfor %} diff --git a/extensions/webmail/templates/webmail/index.html b/extensions/webmail/templates/webmail/index.html index aadd73756..a7d3f8f97 100644 --- a/extensions/webmail/templates/webmail/index.html +++ b/extensions/webmail/templates/webmail/index.html @@ -20,8 +20,8 @@ function wm_updatelisting(resp) { var fdbuttons = $$("img[class*=footer]"); var fdmenu = new FdMenu({ - modify_url: "{% url extensions.webmail.views.editfolder %}", - delete_url: "{% url extensions.webmail.views.delfolder %}" + modify_url: "{% url modoboa.extensions.webmail.views.editfolder %}", + delete_url: "{% url modoboa.extensions.webmail.views.delfolder %}" }); fdbuttons.addEvents({ @@ -154,7 +154,7 @@ function wm_updatelisting(resp) { emails_table._selectedRows.each(function(item, index) { msgset.include(item.get("id")); }); - {% url extensions.webmail.views.move as moveurl %} + {% url modoboa.extensions.webmail.views.move as moveurl %} simple_request("{{ moveurl }}", {msgset : msgset.join(), to : dest}); }; diff --git a/lib/events.py b/lib/events.py index 71e780fdd..8264375c4 100644 --- a/lib/events.py +++ b/lib/events.py @@ -69,9 +69,10 @@ def register(event, callback): if not event in events: return 0 if not event in callbacks.keys(): - callbacks[event] = [] - if not callback in callbacks[event]: - callbacks[event].append(callback) + callbacks[event] = {} + fullname = "%s.%s" % (callback.__module__, callback.__name__) + if not callbacks[event].has_key(fullname): + callbacks[event][fullname] = callback return 1 class observe(object): @@ -127,9 +128,10 @@ def unregister(event, callback): return False if not callbacks.has_key(event): return False + fullname = "%s.%s" % (callback.__module__, callback.__name__) try: - callbacks[event].remove(callback) - except ValueError: + del callbacks[event][fullname] + except KeyError: pass def raiseEvent(event, *args): @@ -142,7 +144,7 @@ def raiseEvent(event, *args): """ if not event in events or not event in callbacks.keys(): return 0 - for callback in callbacks[event]: + for name, callback in callbacks[event].iteritems(): callback(*args) return 1 @@ -157,6 +159,6 @@ def raiseQueryEvent(event, *args): result = [] if not event in events or not event in callbacks.keys(): return result - for callback in callbacks[event]: + for name, callback in callbacks[event].iteritems(): result += callback(*args) return result