Extension structure has been simplified. All extensions have been ported to the new version.

see #197
This commit is contained in:
Antoine Nguyen
2012-01-27 17:03:54 +01:00
parent b7d77254ce
commit 645ebfd70f
27 changed files with 527 additions and 558 deletions
+10 -12
View File
@@ -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()
+18 -21
View File
@@ -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
+67
View File
@@ -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 []
-69
View File
@@ -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 []
@@ -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) {
+11 -14
View File
@@ -1,16 +1,13 @@
from django.conf.urls.defaults import *
extpatterns = [
(r'^$', 'index'),
(r'^listing/$', '_listing'),
(r'^getmailcontent/(?P<mail_id>[\w\-\+]+)/$', 'getmailcontent'),
(r'^(?P<mail_id>[\w\-\+]+)/headers/$', 'viewheaders'),
(r'^process/$', 'process'),
(r'^delete/(?P<mail_id>[\w\-\+]+)/$', 'delete'),
(r'^release/(?P<mail_id>[\w\-\+]+)/$', 'release'),
(r'^(?P<mail_id>[\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<mail_id>[\w\-\+]+)/$', 'getmailcontent'),
(r'^(?P<mail_id>[\w\-\+]+)/headers/$', 'viewheaders'),
(r'^process/$', 'process'),
(r'^delete/(?P<mail_id>[\w\-\+]+)/$', 'delete'),
(r'^release/(?P<mail_id>[\w\-\+]+)/$', 'release'),
(r'^(?P<mail_id>[\w\-\+]+)/$', 'viewmail'),
)
+81
View File
@@ -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}}"},
]
-87
View File
@@ -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}}"},
]
+1 -1
View File
@@ -11,7 +11,7 @@ window.addEvent("domready", function() {
{% block content %}
<div class="title"><h1>{% trans "Limits pool for" %} {{ reseller.username }}</h1></div>
<div class="error">{% if error %}{{ error }}{% endif %}</div>
<form action="{% url extensions.limits.views.edit_limits_pool reseller.id %}"
<form action="{% url modoboa.extensions.limits.views.edit_limits_pool reseller.id %}"
method="POST" id="limits_pool_form" name="limits_pool_form">
{% include "common/genericform.html" %}
<div class="spacer"></div>
+93
View File
@@ -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()
-98
View File
@@ -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()
@@ -29,7 +29,7 @@ window.addEvent("domready", function() {
{% block content %}
<div class="title"><h1>{% trans "Auto-reply message" %}</h1></div>
<form action="{% url extensions.postfix_autoreply.views.autoreply %}"
<form action="{% url modoboa.extensions.postfix_autoreply.views.autoreply %}"
method="POST" id="armform" name="armform" class="simple">
{% include "common/genericform.html" %}
<div class="spacer"></div>
+71
View File
@@ -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()
-74
View File
@@ -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()
@@ -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();
@@ -15,7 +15,7 @@
<td>
<a name="togglestate"
class={% if filter.enabled %}"green"{% else %}"red"{% endif %}
href="{% url extensions.sievefilters.views.toggle_filter_state fs.name filter.name %}">
href="{% url modoboa.extensions.sievefilters.views.toggle_filter_state fs.name filter.name %}">
{{ filter.enabled|yesno }}
</a>
</td>
@@ -3,7 +3,7 @@
<h1>{% trans "Actions" %}</h1>
{% fset_menu "raw" name %}
</div>
<form action="{% url extensions.sievefilters.views.savefs name %}"
<form action="{% url modoboa.extensions.sievefilters.views.savefs name %}"
method="POST" id="feditor" name="feditor">
<textarea id="scriptcontent" name="scriptcontent">{{ scriptcontent }}</textarea>
</form>
+47
View File
@@ -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 []
-51
View File
@@ -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 []
+103
View File
@@ -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()
-108
View File
@@ -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()
@@ -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", "<img src='{{MEDIA_URL}}pics/remove.png' />")
.inject(ndiv);
var label = new Element("label").set("html", fname).inject(ndiv);
@@ -87,7 +87,7 @@ window.addEvent("domready", function() {
<div id="error" class="error">{{ error }}</div>
<form enctype="multipart/form-data" method="post" name="uploadfile" id="uploadfile"
target="upload_target"
action="{% url extensions.webmail.views.attachments %}">
action="{% url modoboa.extensions.webmail.views.attachments %}">
{% include "common/genericform.html" %}
<div class="row">
<input type="submit" id="submit" name="submit" value="{% trans 'Attach' %}" />
@@ -101,7 +101,7 @@ window.addEvent("domready", function() {
<div id="attachment_list">
{% for att in attachments %}
<div>
<a href="{% url extensions.webmail.views.delattachment %}?name={{ att.tmpname }}"
<a href="{% url modoboa.extensions.webmail.views.delattachment %}?name={{ att.tmpname }}"
name="delattachment">
<img src="{{ MEDIA_URL }}pics/remove.png" />
</a>
@@ -3,7 +3,7 @@
<div id="compose-container">
<div id="mailheader">
{% include "common/genericform.html" %}
<div id="attachments" name="{% url extensions.webmail.views.attachments %}">
<div id="attachments" name="{% url modoboa.extensions.webmail.views.attachments %}">
<div>
<img src="{{ MEDIA_URL }}pics/attachment.png" />
{% trans "Attachments" %}
@@ -2,7 +2,7 @@
{% load webextras %}
{% if titlebar %}
<div class="boxtitle">
<a href="{% url extensions.webmail.views.newfolder %}" class="boxed"
<a href="{% url modoboa.extensions.webmail.views.newfolder %}" class="boxed"
rel="{handler: 'iframe', size: {x: 350, y: 400}, closeBtn: true}">
<img src="{{MEDIA_URL}}pics/add.png"
alt="{% trans 'Add a new folder' %}"/>
@@ -6,7 +6,7 @@
<tr>
<th>{% trans "Attachments" %}</th>
<td>
{% 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 %}
<a href="{{ test }}?fname={{ fname }}&partnumber={{ key }}">{{ fname }}</a>
{% endfor %}
@@ -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});
};
+9 -7
View File
@@ -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