From 7480e479e78f73633e7fd7a6f073c52e7fd3a895 Mon Sep 17 00:00:00 2001 From: tonio Date: Tue, 4 May 2010 17:25:23 +0200 Subject: [PATCH] Ticket #48. This is not finished yet. More work than what I expected but the main part is done. It will need a lot of testing... There is still a bug when activating a new extension in development mode (ie. with python web server), I think it does not occur behind a real web server. --- admin/models.py | 55 +++++++++++++++++++++++++++- admin/templatetags/admin_extras.py | 1 - admin/urls.py | 1 + admin/views.py | 48 +++++++++++++++++++++++- extensions/__init__.py | 28 ++++++++------ extensions/amavis_quarantine/main.py | 4 ++ extensions/stats/main.py | 3 ++ extensions/webmail/imap_listing.py | 1 - extensions/webmail/main.py | 7 +++- lib/events.py | 7 ++++ lib/parameters.py | 6 +++ lib/tables.py | 44 ++++++++++------------ templates/admin/extensions.html | 11 +++--- templates/common/table_body.html | 2 +- urls.py | 5 ++- 15 files changed, 171 insertions(+), 52 deletions(-) diff --git a/admin/models.py b/admin/models.py index 1e1c1e746..13bd53160 100644 --- a/admin/models.py +++ b/admin/models.py @@ -2,7 +2,7 @@ from django.db import models from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ from mailng.lib import parameters -from mailng.lib import exec_as_vuser +from mailng.lib import exec_cmd, exec_as_vuser import os class Domain(models.Model): @@ -38,7 +38,7 @@ class Domain(models.Model): class Mailbox(models.Model): name = models.CharField(_('name'), max_length=100, help_text=_("First name and last name of mailbox owner")) - address = models.CharField(_('address'), max_length=100, + address = models.CharField(_('address'), max_length=10, help_text=_("Mailbox address (without the @domain.tld part)")) full_address = models.CharField(max_length=150) password = models.CharField(_('password'), max_length=100) @@ -111,3 +111,54 @@ class Alias(models.Model): permissions = ( ("view_aliases", "View aliases"), ) + +class Extension(models.Model): + name = models.CharField(max_length=150) + enabled = models.BooleanField(_('enabled'), + help_text=_("Check to enable this extension")) + has_templates = models.BooleanField() + + def on(self): + from django.core.management import sql, color + from django.db import connection, models + + style = color.no_style() + cursor = connection.cursor() + module = __import__("mailng.extensions.%s" % self.name, + globals(), locals(), ['main', 'models']) + try: + extmodels = module.models + except AttributeError: + extmodels = None + if extmodels is not None: + mods = [] + for k, v in extmodels.__dict__.iteritems(): + try: + if not issubclass(v, models.Model): + continue + statements, pending = \ + connection.creation.sql_create_model(v, style) + try: + for sql in statements: + cursor.execute(sql) + except Exception, msg: + pass + except TypeError: + pass + if self.has_templates: + code, output = exec_cmd("ln -s ../extensions/%s/templates templates/%s" \ + % (self.name, self.name)) + module.main.init() + self.enabled = True + self.save() + + def off(self): + if self.has_templates: + code, output = exec_cmd("rm -f templates/%s" % self.name) + print output + self.enabled = False + self.save() + module = __import__("mailng.extensions.%s" % self.name, + globals(), locals(), ['main', 'models']) + module.main.destroy() + diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index a29337ae3..65bc8a969 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -118,7 +118,6 @@ def param(app, definition): values = definition["values"] value = definition.has_key("value") \ and definition["value"] or definition["default"] - print name, values for v in values: selected = "" if value == v[0]: diff --git a/admin/urls.py b/admin/urls.py index 191e6950a..2736b07d9 100644 --- a/admin/urls.py +++ b/admin/urls.py @@ -20,6 +20,7 @@ urlpatterns = patterns('mailng.admin.views', (r'^settings/addperm/$', 'addpermission'), (r'^settings/delperm/(?P\d+)/(?P\w+)/$', 'deletepermission'), (r'^settings/extensions/$', 'viewextensions'), + (r'^settings/extensions/save/$', 'saveextensions'), (r'^settings/parameters/$', 'viewparameters'), (r'^settings/parameters/save/$', 'saveparameters'), ) diff --git a/admin/views.py b/admin/views.py index d58b4a719..e5b5e23ec 100644 --- a/admin/views.py +++ b/admin/views.py @@ -12,7 +12,7 @@ from django.contrib.auth.decorators \ import login_required, permission_required, user_passes_test from django.contrib.auth.models import User, Group from mailng import admin, main -from mailng.admin.models import Domain, Mailbox, Alias +from mailng.admin.models import Domain, Mailbox, Alias, Extension from forms import MailboxForm, DomainForm, AliasForm, PermissionForm from mailng.lib.authbackends import crypt_password from mailng.lib import _render, _ctx_ok, _ctx_ko @@ -447,6 +447,50 @@ def saveparameters(request): @user_passes_test(lambda u: u.is_superuser) def viewextensions(request): from mailng.extensions import list_extensions + from mailng.lib import tables + + class ExtensionsTable(tables.Table): + idkey = "id" + selection = tables.SelectionColumn("selection", width="4%", first=True) + _1_name = tables.Column("name", label=_("Name"), width="15%") + _2_version = tables.Column("version", label=_("Version"), width="6%") + _3_descr = tables.Column("description", label=_("Description")) + + exts = list_extensions() + for ext in exts: + try: + dbext = Extension.objects.get(name=ext["id"]) + ext["selection"] = dbext.enabled + except Extension.DoesNotExist: + dbext = Extension() + dbext.name = ext["id"] + dbext.enabled = False + dbext.has_templates = ext["templates"] + dbext.save() + ext["selection"] = False + + tbl = ExtensionsTable(exts) return _render(request, 'admin/extensions.html', { - "extensions" : list_extensions() + "extensions" : tbl.render(request) }) + +@login_required +@user_passes_test(lambda u: u.is_superuser) +def saveextensions(request): + from mailng import extensions + + actived_exts = Extension.objects.filter(enabled=True) + found = [] + for k, v in request.POST.iteritems(): + if k.startswith("select_"): + parts = k.split("_", 1) + dbext = Extension.objects.get(name=parts[1]) + if not dbext in actived_exts: + dbext.on() + else: + found += [dbext] + for ext in actived_exts: + if not ext in found: + ext.off() + + return HttpResponseRedirect(reverse(admin.views.viewextensions)) diff --git a/extensions/__init__.py b/extensions/__init__.py index 8dff92659..eb7fb41f5 100644 --- a/extensions/__init__.py +++ b/extensions/__init__.py @@ -2,29 +2,29 @@ import os import re -extensions = [] +extensions = {} def loadextensions(): - from django.conf import settings + from admin.models import Extension - expr = re.compile("mailng\.extensions\.(.+)") - for app in settings.INSTALLED_APPS: - m = expr.match(app) - if not m: - continue - module = __import__(m.group(1), globals(), locals(), ['main']) + exts = Extension.objects.filter(enabled=True) + for ext in exts: + module = __import__(ext.name, globals(), locals(), ['main']) module.main.init() - globals()['extensions'] += [module] + globals()['extensions'][ext] = module def loadmenus(): result = [] - for mod in extensions: + for name, mod in extensions.iteritems(): u = mod.main.urls() if u == (): continue result += (u,) return result +def isenabled(ext): + return extensions.has_key(ext) + def list_extensions(): basedir = "extensions" result = [] @@ -39,7 +39,13 @@ def list_extensions(): if isvalid: module = __import__(d, globals(), locals(), ['main']) try: - result += [module.main.infos()] + infos = module.main.infos() + infos["id"] = d + if os.path.isdir("%s/%s/templates" % (basedir, d)): + infos["templates"] = True + else: + infos["templates"] = False + result += [infos] except AttributeError: pass return result diff --git a/extensions/amavis_quarantine/main.py b/extensions/amavis_quarantine/main.py index 3530abb42..6baa63471 100644 --- a/extensions/amavis_quarantine/main.py +++ b/extensions/amavis_quarantine/main.py @@ -46,6 +46,10 @@ def init(): "/var/amavis/amavisd.sock", help="") +def destroy(): + events.unregister("UserMenuDisplay", menu) + parameters.unregister_app("amavis_quarantine") + def urls(): return (r'^mailng/quarantine/', include('mailng.extensions.amavis_quarantine.urls')) diff --git a/extensions/stats/main.py b/extensions/stats/main.py index cd0fe18d4..d3404dbff 100644 --- a/extensions/stats/main.py +++ b/extensions/stats/main.py @@ -41,6 +41,9 @@ def init(): parameters.register("stats", "IMG_ROOTDIR", "string", "/tmp/mailng", help=_("Path to directory where PNG files are stored")) +def destroy(): + events.unregister("AdminMenuDisplay", menu) + def urls(): return (r'^mailng/stats/', include('mailng.extensions.stats.urls')) diff --git a/extensions/webmail/imap_listing.py b/extensions/webmail/imap_listing.py index ba6e0bfce..ee1ceb946 100644 --- a/extensions/webmail/imap_listing.py +++ b/extensions/webmail/imap_listing.py @@ -16,7 +16,6 @@ from mailng.lib import tables, imap_utf7, parameters class WMtable(tables.Table): tableid = "emails" idkey = "imapid" -# selection = tables.SelectionColumn("selection", width="20px", first=True) _1_flags = tables.ImgColumn("flags", width="4%", header="") _2_subject = tables.Column("subject", label=_("Subject"), diff --git a/extensions/webmail/main.py b/extensions/webmail/main.py index d747fa877..84dd0a301 100644 --- a/extensions/webmail/main.py +++ b/extensions/webmail/main.py @@ -34,6 +34,11 @@ def init(): parameters.register("webmail", "SMTP_SECURED", "list_yesno", "no", help=_("Use a secured connection to access SMTP server")) +def destroy(): + events.unregister("UserMenuDisplay", menu) + events.unregister("UserLogin", userlogin) + parameters.unregister_app("webmail") + def infos(): return { "name" : "Webmail", @@ -139,14 +144,12 @@ def viewmail(request, folder, mail_id=None): def getmailcontent(request, folder, mail_id): msg = fetchmail(request, folder, mail_id, True) if "class" in msg.keys() and msg["class"] == "unseen": - #IMAPconnector(request).msg_read(*msg["imapid"].split("/")) IMAPconnector(request).msg_read(folder, mail_id) email = ImapEmail(msg, mode="html", links="1") try: pageid = request.session["page"] except KeyError: pageid = "1" - #folder, imapid = msg["imapid"].split("/") return _render(request, "common/viewmail.html", { "headers" : email.render_headers(folder, mail_id), "folder" : folder, "imapid" : mail_id, "mailbody" : email.body, diff --git a/lib/events.py b/lib/events.py index 4e9dcf493..5fe839387 100644 --- a/lib/events.py +++ b/lib/events.py @@ -27,6 +27,13 @@ def register(event, callback): callbacks[event].append(callback) return 1 +def unregister(event, callback): + if not event in events: + return False + if not callbacks.has_key(event): + return False + callbacks[event].remove(callback) + def raiseEvent(event, **kwargs): if not event in events or not event in callbacks.keys(): return 0 diff --git a/lib/parameters.py b/lib/parameters.py index b6c9d78bc..41ed6a529 100644 --- a/lib/parameters.py +++ b/lib/parameters.py @@ -35,6 +35,12 @@ def register(app, name, type="string", deflt=None, help=None, **kwargs): for k, v in kwargs.iteritems(): _params[app][name][k] = v +def unregister_app(app): + if not _params.has_key(app): + return False + del _params[app] + return True + def save(app, name, value): if not app in _params.keys() or not name in _params[app].keys(): raise NotDefined(app, name) diff --git a/lib/tables.py b/lib/tables.py index a987dd5f2..98b322c1f 100644 --- a/lib/tables.py +++ b/lib/tables.py @@ -19,13 +19,14 @@ class Column: label = getattr(self, "name") return label + class SelectionColumn(Column): def __str__(self): return "" - def render(self, value): - return "" \ - % (self.name, value) + def render(self, value, selection): + return "" \ + % (self.name, value, value, selection and "checked" or "") class ImgColumn(Column): sortable = False @@ -56,33 +57,28 @@ class Table(object): nrow = {"id" : row[self.idkey], "cols" : []} for c in self.columns: newcol = {"name" : c.name} - try: - newcol["width"] = getattr(c, "width") - except AttributeError: - pass - try: - newcol["align"] = getattr(c, "align") - except: - pass + for key in ["width", "align", "cssclass"]: + try: + newcol[key] = getattr(c, key) + except AttributeError: + newcol[key] = "" if isinstance(c, SelectionColumn): - newcol["value"] = c.render(row[self.idkey]) - newcol["safe"] = True - - if isinstance(c, ImgColumn): + selection = row.has_key(c.name) and row[c.name] or False + newcol["value"] = c.render(row[self.idkey], selection) + newcol["safe"] = True + elif isinstance(c, ImgColumn): key = "img_%s" % c.name if key in row.keys(): newcol["value"] = c.render(row[key]) newcol["safe"] = True - - if row.has_key(c.name): - try: - cssclass = getattr(c, "cssclass") - except AttributeError: - cssclass = "" + elif row.has_key(c.name): if "class" in row.keys(): - cssclass += (cssclass != "") and ", %s" % row["class"] or row["class"] - newcol["value"] = self.parse(c.name, row[c.name]) - newcol["class"] = cssclass + newcol["cssclass"] += newcol["cssclass"] != "" \ + and ", %s" % row["class"] or row["class"] + try: + newcol["value"] = self.parse(c.name, row[c.name]) + except AttributeError: + newcol["value"] = row[c.name] nrow["cols"] += [newcol] self.rows += [nrow] diff --git a/templates/admin/extensions.html b/templates/admin/extensions.html index 44c58e708..71355d7d1 100644 --- a/templates/admin/extensions.html +++ b/templates/admin/extensions.html @@ -5,10 +5,9 @@ {% endblock %} {% block setcontent %} {% load i18n %} - - -{% for ext in extensions %} - -{% endfor %} -
{% trans "Name" %}{% trans "Version" %}{% trans "Description" %}
{{ ext.name }}{{ ext.version }}{{ ext.description }}
+
+ {{ extensions }} +
+ +
{% endblock %} diff --git a/templates/common/table_body.html b/templates/common/table_body.html index 1b5ee2138..c218d8902 100644 --- a/templates/common/table_body.html +++ b/templates/common/table_body.html @@ -2,7 +2,7 @@ {% for row in table.rows %} {% for cell in row.cols %} - + {% if cell.safe %}{{ cell.value|safe }}{% else %}{{ cell.value }}{% endif %} {% endfor %} diff --git a/urls.py b/urls.py index e6e5b4f1d..84d60f9c4 100644 --- a/urls.py +++ b/urls.py @@ -5,7 +5,7 @@ from django.conf import settings # from django.contrib import admin # admin.autodiscover() -from mailng.extensions import loadextensions, loadmenus +from mailng.extensions import loadextensions, loadmenus, isenabled from mailng.lib import parameters loadextensions() @@ -27,6 +27,7 @@ urlpatterns = patterns('', (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', {'packages': ('mailng',),}) ) + menus = loadmenus() if menus != (): urlpatterns += patterns('', *menus) @@ -38,7 +39,7 @@ if settings.DEBUG: {'document_root': settings.STATIC_ROOTDIR}) ) # Stats - if 'mailng.extensions.stats' in settings.INSTALLED_APPS: + if isenabled('stats'): urlpatterns += patterns( '', (r'^img/stats/(?P.*)/$', 'django.views.static.serve',