Important update of the plugin engine.

New plugin that allows the handling of an Amavisd-new SQL quarantine.

NB: a DB connections manager has been added inside the lib because
Django does not support multiple connections at the same time. The
main consequence is that we don't use an ORM and that we have to
rewrite a small wrapper that supports several DB (mysql, postgresql, etc.).
Adding a dependancy to an existing ORM module (like SQLalchemy) is
heavy, we don't want that for now.
This commit is contained in:
Antoine Nguyen
2009-06-26 17:13:35 +02:00
parent fc74d34c1d
commit fcab20b608
17 changed files with 227 additions and 19 deletions
+2 -1
View File
@@ -16,7 +16,8 @@ class MailboxForm(forms.ModelForm):
quota = forms.IntegerField(label=_("Quota"), required=False)
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Confirmation"), widget=forms.PasswordInput)
enabled = forms.BooleanField(label=gender("Enabled", "f"), required=False)
enabled = forms.BooleanField(label=gender("Enabled", "f"), required=False,
initial=True)
class Meta:
model = Mailbox
+1 -1
View File
@@ -18,7 +18,7 @@ class Domain(models.Model):
def create_dir(self):
path = "%s/%s" % (settings.STORAGE_PATH, self.name)
return exec_as_vuser("mkdir %s" % path)
return exec_as_vuser("mkdir -p %s" % path)
def rename_dir(self, newname):
code, output = exec_pipe("sudo -u %s mv %s/%s %s/%s" \
+9
View File
@@ -2,6 +2,8 @@
import os
import re
extensions = []
def loadextensions():
from django.conf import settings
@@ -12,3 +14,10 @@ def loadextensions():
continue
module = __import__(m.group(1), globals(), locals(), ['main'])
module.main.init()
globals()['extensions'] += [module]
def loadmenus():
result = ()
for mod in extensions:
result += mod.main.urls()
return result
+64
View File
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
"""
"""
from django.shortcuts import get_object_or_404, render_to_response
from django.conf.urls.defaults import *
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from django.core.paginator import Paginator
from mailng.lib import events
from mailng.lib import _render, _ctx_ok, _ctx_ko
from mailng.lib import db
from mailng.admin.models import Mailbox
def init():
events.register("UserMenuDisplay", menu)
def urls():
return (r'^mailng/quarantine/',
include('mailng.extensions.amavis_quarantine.urls'))
def menu(**kwargs):
return [
{"name" : _("Quarantine"),
"url" : reverse(index),
"img" : "/static/pics/quarantine.png"}
]
def index(request):
if not request.user.is_superuser:
mb = Mailbox.objects.get(user=request.user.id)
filter = "AND maddr.email='%s'" % mb.full_address
else:
filter = ""
cursor = db.getconnection("amavis_quarantine").cursor()
cursor.execute("""
SELECT msgs.from_addr, maddr.email, msgs.subject, msgs.content, quarantine.mail_id
FROM quarantine, maddr, msgrcpt, msgs
WHERE quarantine.mail_id=msgrcpt.mail_id
AND msgrcpt.rid=maddr.id
AND msgrcpt.mail_id=msgs.mail_id
AND quarantine.chunk_ind=1
%s
""" % filter)
emails = []
rows = cursor.fetchall()
for row in rows:
emails.append({"from" : row[0], "to" : row[1],
"subject" : row[2], "content" : row[3],
"mailid" : row[4]})
# cursor.execute("SELECT count(*) AS cnt FROM quarantine")
# row = cursor.fetchone()
# cnt = row[0]
paginator = Paginator(emails, 10)
try:
pagenum = int(request.GET.get('page', '1'))
except ValueError:
pagenum = 1
page = paginator.page(pagenum)
return _render(request, 'amavis_quarantine/index.html', {
"count" : len(rows), "emails" : page
})
@@ -0,0 +1,71 @@
{% extends "layout.html" %}
{% load i18n %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="/static/stylesheets/box.css" />
{% endblock %}
{% block content %}
<style type="text/css">
#quarmenu {
float: left;
width: 100%;
margin: 5px 0;
}
#navbar {
float: right;
}
#actionbar {
float: left;
}
td {
font-size: smaller;
height: 40px;
}
</style>
<div class="header">
<h1>{% trans "Quarantine" %}</h1>
{% if messages %}
<div id="infos">
{% for m in messages %}
{{ m }}
{% endfor %}
</div>
{% endif %}
</div>
<div id="subcontent">
<b> Il y a {{ count }} messages dans la quarantaine.</b>
<div id="quarmenu">
<div id="actionbar">
<input type="button" name="release" value="{% trans "Release" %}" />
<input type="button" name="delete" value="{% trans "Delete" %}" />
<input type="button" name="deleteall" value="{% trans "Delete all" %}" />
</div>
<div id="navbar">
{% if emails.has_previous %}
<a href="?page={{ emails.previous_page_number }}">{% trans "Previous" %}</a>
{% endif %}
{% if emails.has_next %}
<a href="?page={{ emails.next_page_number }}">{% trans "Next" %}</a>
{% endif %}
</div>
</div>
<table>
<tr>
<th></th>
<th width="150px">{% trans "From" %}</th>
<th width="150px">{% trans "To" %}</th>
<th>{% trans "Subject" %}</th>
</tr>
{% for m in emails.object_list %}
<tr class="{% cycle 'odd' 'even' %}">
<td><input type="checkbox" name="selection" id="selection" /></td>
<td>{{ m.from }}</td><td>{{ m.to }}</td><td>{{ m.subject }}</td></tr>
{% endfor %}
</table>
</div>
{% endblock %}
+5
View File
@@ -0,0 +1,5 @@
from django.conf.urls.defaults import *
urlpatterns = patterns('mailng.extensions.amavis_quarantine.main',
(r'^$', 'index'),
)
+3
View File
@@ -16,6 +16,9 @@ def init():
events.register("CreateMailbox", onCreateMailbox)
events.register("DeleteMailbox", onDeleteMailbox)
def urls():
return ()
def onCreateDomain(**kwargs):
dom = kwargs["dom"]
transport = Transport()
+22
View File
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""
"""
connections = {}
def mysql_newconnection(host, dbname, login, password):
import MySQLdb
return MySQLdb.connect(host=host, db=dbname, user=login, passwd=password)
def getconnection(name):
if not name in connections.keys():
from django.conf import settings
cfg = settings.DB_CONNECTIONS[name]
if cfg["driver"] == "mysql":
conn = mysql_newconnection(cfg["host"], cfg["dbname"], cfg["login"],
cfg["password"])
connections[name] = conn
return connections[name]
+11 -1
View File
@@ -9,7 +9,9 @@ core application and additional components.
events = ["CreateDomain",
"DeleteDomain",
"CreateMailbox",
"DeleteMailbox"]
"DeleteMailbox",
"UserMenuDisplay"]
callbacks = {}
@@ -27,3 +29,11 @@ def raiseEvent(event, **kwargs):
for callback in callbacks[event]:
callback(**kwargs)
return 1
def raiseQueryEvent(event, **kwargs):
result = []
if not event in events or not event in callbacks.keys():
return result
for callback in callbacks[event]:
result += callback(**kwargs)
return result
+24 -1
View File
@@ -1,10 +1,33 @@
from django import template
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from mailng import main
from mailng.lib import events
register = template.Library()
@register.simple_tag
def menu(selection, perms):
entries = [
{"name" : "autoreply",
"url" : reverse(main.views.autoreply),
"img" : "/static/pics/auto-reply.png",
"label" : _("Auto-reply message")},
{"name" : "changepwd",
"url" : reverse(main.views.changepassword),
"img" : "/static/pics/edit.png",
"label" : _("Change password"),
"class" : "boxed",
"rel" : "{handler:'iframe',size:{x:350,y:220}}"}
]
return render_to_string('main/menu.html',
{"selection" : selection, "perms" : perms})
{"selection" : selection, "entries" : entries,
"perms" : perms})
@register.simple_tag
def loadextmenu(perms):
menu = events.raiseQueryEvent("UserMenuDisplay")
return render_to_string('main/menulist.html',
{"menu" : menu, "perms" : perms})
Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

+1 -2
View File
@@ -59,7 +59,6 @@ body {
float: left;
width: 100%;
margin: 15px 0;
/*height: 600px;*/
}
#loginbox {
@@ -129,11 +128,11 @@ body {
#content {
float: right;
width: 750px;
height: 400px;
background: white;
border: 1px solid #333;
padding: 5px;
margin: 0 0 0 15px;
min-height: 400px;
}
#content2 {
+2 -2
View File
@@ -18,7 +18,7 @@ div.row label {
padding: 3px 0;
}
form input {
input {
border: 1px solid #cccccc;
color: inherit;
/*background-color: #e9e9e9;*/
@@ -26,7 +26,7 @@ form input {
padding: 2px;
}
form input:hover {
input:hover {
color: inherit;
background-color: #ffffcc;
}
+2
View File
@@ -1,4 +1,5 @@
{% load i18n %}
{% load main_extras %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
@@ -47,6 +48,7 @@
<img src="/static/pics/user.png" border="0" />{% trans "Preferences" %}</a>
</li>
{% endif %}
{% loadextmenu perms %}
<li>
{% url django.contrib.auth.views.logout_then_login as logout_url %}
<a href="{{ logout_url }}">
+8 -10
View File
@@ -1,15 +1,13 @@
{% load i18n %}
<ul>
<li {% ifequal selection "autoreply" %}class="selected"{% endifequal %}>
{% url main.views.autoreply as ar_url %}
<a href="{{ ar_url }}"><img src="/static/pics/auto-reply.png" border="0"/>
{% trans "Auto-reply message" %}
{% for entry in entries %}
<li {% ifequal selection entry.name %}class="selected"{% endifequal %}>
<a href="{{ entry.url }}"
{% if entry.class %}class={{ entry.class }}{% endif %}
{% if entry.rel %}rel={{ entry.rel }}{% endif %}>
<img src="{{ entry.img }}" border="0" />
{{ entry.label }}
</a>
</li>
{% url main.views.changepassword as chpwd_url %}
<li><a href="{{ chpwd_url }}"
class="boxed" rel="{handler:'iframe',size:{x:350,y:220}}">
<img src="/static/pics/edit.png" border="0"/>
{% trans "Change password" %}</a>
</li>
{% endfor %}
</ul>
+2 -1
View File
@@ -5,7 +5,7 @@ from django.conf import settings
# from django.contrib import admin
# admin.autodiscover()
from mailng.extensions import loadextensions
from mailng.extensions import loadextensions, loadmenus
loadextensions()
urlpatterns = patterns('',
@@ -26,6 +26,7 @@ urlpatterns = patterns('',
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog',
{'packages': ('mailng',),})
)
urlpatterns += patterns('', loadmenus())
if settings.DEBUG:
urlpatterns += patterns(