From ccf24f490512f55b82a296f07efba902c2a8d9ed Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 4 Apr 2012 16:18:22 +0200 Subject: [PATCH] wip: Quarantine 'self-service' First commit. --- extensions/amavis_quarantine/__init__.py | 11 +++ extensions/amavis_quarantine/lib.py | 24 ++++++ .../static/css/selfservice.css | 20 +++++ .../static/js/selfservice.js | 35 ++++++++ .../viewmail_selfservice.html | 30 +++++++ .../templatetags/amextras.py | 22 ++++- extensions/amavis_quarantine/urls.py | 2 +- extensions/amavis_quarantine/views.py | 82 +++++++++++++++++-- templates/common/viewmail.html | 1 - 9 files changed, 215 insertions(+), 12 deletions(-) create mode 100644 extensions/amavis_quarantine/static/css/selfservice.css create mode 100644 extensions/amavis_quarantine/static/js/selfservice.js create mode 100644 extensions/amavis_quarantine/templates/amavis_quarantine/viewmail_selfservice.html diff --git a/extensions/amavis_quarantine/__init__.py b/extensions/amavis_quarantine/__init__.py index f5358ce9d..396232842 100644 --- a/extensions/amavis_quarantine/__init__.py +++ b/extensions/amavis_quarantine/__init__.py @@ -43,6 +43,8 @@ def load(): help=_("Path to the PDP server socket (if unix mode)")) parameters.register_admin("USER_CAN_RELEASE", type="list_yesno", deflt="no", help=_("Allow users to directly release their messages")) + parameters.register_admin("SELF_SERVICE", type="list_yesno", deflt="no", + help=_("Activate the 'self-service' mode")) parameters.register_user( "MESSAGES_PER_PAGE", type="int", deflt=40, @@ -67,3 +69,12 @@ def menu(target, user): ] return [] +@events.observe("CreateMailbox") +def create_user_record(user, mailbox): + from models import Users + + u = Users() + u.email = mailbox.full_address + u.fullname = mailbox.user.fullname + u.local = "1" + u.save() diff --git a/extensions/amavis_quarantine/lib.py b/extensions/amavis_quarantine/lib.py index afc5e29d5..650ee3816 100644 --- a/extensions/amavis_quarantine/lib.py +++ b/extensions/amavis_quarantine/lib.py @@ -2,8 +2,32 @@ import socket import re import struct import string +from functools import wraps from modoboa.lib import parameters +def selfservice(ssfunc=None): + """Decorator used to expose views to the 'self-service' feature + + The 'self-service' feature allows users to act on quarantined + messages without beeing authenticated. + + This decorator only acts as a 'router'. + + :param ssfunc: the function to call if the 'self-service' + pre-requisites are satisfied + """ + def decorator(f): + @wraps(f) + def wrapped_f(request, *args, **kwargs): + if request.user.is_authenticated(): + return f(request, *args, **kwargs) + if parameters.get_admin("SELF_SERVICE") == "no": + from django.contrib.auth.views import redirect_to_login + return redirect_to_login(reverse(f, args=args)) + return ssfunc(request, *args, **kwargs) + return wrapped_f + return decorator + class AMrelease(object): def __init__(self): mode = parameters.get_admin("AM_PDP_MODE") diff --git a/extensions/amavis_quarantine/static/css/selfservice.css b/extensions/amavis_quarantine/static/css/selfservice.css new file mode 100644 index 000000000..bfab53b9a --- /dev/null +++ b/extensions/amavis_quarantine/static/css/selfservice.css @@ -0,0 +1,20 @@ +#menubar { + padding-left: 15px; +} + +#listing { + position: absolute; + padding: 0px; + right: 0; + left: 0; + bottom: 0; + overflow: hidden; +} + +#mailcontent { + border: 0; + margin: 0; + padding: 0; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/extensions/amavis_quarantine/static/js/selfservice.js b/extensions/amavis_quarantine/static/js/selfservice.js new file mode 100644 index 000000000..8aecf6d04 --- /dev/null +++ b/extensions/amavis_quarantine/static/js/selfservice.js @@ -0,0 +1,35 @@ +function send_action(e, message) { + var $link = $(e.target); + + e.preventDefault(); + if (!confirm(message)) { + return; + } + $.ajax({ + url: $link.attr("href"), + dataType: 'json', + success: function(data) { + if (data.status == "ok") { + $("body").notify("success", data.respmsg, 2000); + } else { + $("body").notify("error", data.respmsg); + } + } + }); +} + +function release(e) { + send_action(e, gettext("Release this message?")); +} + +function remove(e) { + send_action(e, gettext("Delete this message?")); +} + +$(document).ready(function() { + $("#listing").css({ + top: $("#menubar").outerHeight() + 10 + }); + $(document).on("click", "a[name=release]", release); + $(document).on("click", "a[name=delete]", remove); +}); \ No newline at end of file diff --git a/extensions/amavis_quarantine/templates/amavis_quarantine/viewmail_selfservice.html b/extensions/amavis_quarantine/templates/amavis_quarantine/viewmail_selfservice.html new file mode 100644 index 000000000..8dbcacc86 --- /dev/null +++ b/extensions/amavis_quarantine/templates/amavis_quarantine/viewmail_selfservice.html @@ -0,0 +1,30 @@ + +{% load i18n libextras amextras %} + + + + Modoboa + + + + + + + + + + + {% url django.views.i18n.javascript_catalog as catalog_url %} + + + + + +
+ +
{{ content }}
+
+ + diff --git a/extensions/amavis_quarantine/templatetags/amextras.py b/extensions/amavis_quarantine/templatetags/amextras.py index 1a2ce3342..8ab4ba15f 100644 --- a/extensions/amavis_quarantine/templatetags/amextras.py +++ b/extensions/amavis_quarantine/templatetags/amextras.py @@ -52,6 +52,26 @@ def viewm_menu(user, mail_id, rcpt): return render_to_string('common/buttons_list.html', {"entries" : entries}) +@register.simple_tag +def viewm_menu_simple(user, mail_id, rcpt, secret_id=""): + entries = [ + {"name" : "release", + "img" : "icon-ok", + "url" : reverse(amavis_quarantine.views.release, args=[mail_id]) \ + + ("?rcpt=%s" % rcpt \ + + (("&secret_id=%s" % secret_id) if secret_id != "" else "")), + "label" : _("Release")}, + {"name" : "delete", + "img" : "icon-remove", + "url" : reverse(amavis_quarantine.views.delete, args=[mail_id]) \ + + "?rcpt=%s" % rcpt \ + + ("&secret_id=%s" % secret_id if secret_id != "" else ""), + "label" : _("Delete")}, + ] + + return render_to_string('common/buttons_list.html', + {"entries" : entries}) + @register.simple_tag def quar_menu(user, nbrequests): entries = [ @@ -94,7 +114,7 @@ def quar_menu(user, nbrequests): "extraopts" : extraopts }) - if nbrequests != -1: + if nbrequests > 0: tpl = Template('
{{ label }}
') extracontent += tpl.render(Context(dict( label=_("%d pending requests" % nbrequests) diff --git a/extensions/amavis_quarantine/urls.py b/extensions/amavis_quarantine/urls.py index 9633389cc..80ef0138c 100644 --- a/extensions/amavis_quarantine/urls.py +++ b/extensions/amavis_quarantine/urls.py @@ -5,9 +5,9 @@ urlpatterns = patterns( (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'), + (r'^(?P[\w\-\+]+)/headers/$', 'viewheaders'), ) diff --git a/extensions/amavis_quarantine/views.py b/extensions/amavis_quarantine/views.py index 621d07442..d0ff5ef41 100644 --- a/extensions/amavis_quarantine/views.py +++ b/extensions/amavis_quarantine/views.py @@ -1,6 +1,6 @@ # coding: utf-8 import email -from django.http import HttpResponseRedirect, HttpResponse +from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.template import Template, Context from django.utils import simplejson from django.utils.translation import ugettext as _, ungettext @@ -9,9 +9,10 @@ from django.contrib.auth.decorators \ import login_required from django.db.models import Q from modoboa.lib import parameters -from modoboa.lib.webutils import _render, getctx, ajax_response +from modoboa.lib.exceptions import ModoboaException +from modoboa.lib.webutils import _render, getctx, ajax_response, ajax_simple_response from modoboa.admin.models import Mailbox -from lib import AMrelease +from lib import AMrelease, selfservice from templatetags.amextras import * from modoboa.lib.email_listing import parse_search_parameters from sql_listing import * @@ -116,7 +117,21 @@ def index(request): deflocation="listing/", defcallback="listing_cb", selection="quarantine" )) -@login_required +def getmailcontent_selfservice(request, mail_id): + from sql_listing import SQLemail + + qmails = Quarantine.objects.filter(mail=mail_id) + content = "" + for qm in qmails: + content += qm.mail_text + msg = email.message_from_string(content) + mail = SQLemail(msg, mformat="plain", links="0") + return _render(request, "common/viewmail.html", { + "headers" : mail.render_headers(), + "mailbody" : mail.body + }) + +@selfservice(getmailcontent_selfservice) def getmailcontent(request, mail_id): from sql_listing import SQLemail @@ -133,7 +148,21 @@ def getmailcontent(request, mail_id): "mailbody" : mail.body }) -@login_required +def viewmail_selfservice(request, mail_id, + tplname="amavis_quarantine/viewmail_selfservice.html"): + rcpt = request.GET.get("rcpt", None) + secret_id = request.GET.get("secret_id", "") + if rcpt is None: + raise Http404 + content = Template(""" + +""").render(Context(dict(mail_id=mail_id))) + + return _render(request, tplname, dict( + mail_id=mail_id, rcpt=rcpt, secret_id=secret_id, content=content + )) + +@selfservice(viewmail_selfservice) def viewmail(request, mail_id): if request.user.group != 'SimpleUsers': rcpt = request.GET["rcpt"] @@ -175,7 +204,19 @@ def check_mail_id(request, mail_id): mail_id = [mail_id] return mail_id -@login_required +def delete_selfservice(request, mail_id): + rcpt = request.GET.get("rcpt", None) + if rcpt is None: + raise ModoboaException(_("Invalid request")) + try: + msgrcpt = Msgrcpt.objects.get(mail=mail_id, rid__email=rcpt) + msgrcpt.rs = 'D' + msgrcpt.save() + except Msgrcpt.DoesNotExist: + raise ModoboaException(_("Invalid request")) + return ajax_simple_response(dict(status="ok", respmsg=_("Message deleted"))) + +@selfservice(delete_selfservice) def delete(request, mail_id): mail_id = check_mail_id(request, mail_id) if request.user.group == 'SimpleUsers': @@ -195,15 +236,38 @@ def delete(request, mail_id): return ajax_response(request, respmsg=message, url=__get_current_url(request)) -@login_required +def release_selfservice(request, mail_id): + rcpt = request.GET.get("rcpt", None) + secret_id = request.GET.get("secret_id", None) + if rcpt is None or secret_id is None: + raise ModoboaException(_("Invalid request")) + try: + msgrcpt = Msgrcpt.objects.get(mail=mail_id, rid__email=rcpt) + except Msgrcpt.DoesNotExist: + raise ModoboaException(_("Invalid request")) + if secret_id != msgrcpt.mail.secret_id: + raise ModoboaException(_("Invalid request")) + if parameters.get_admin("USER_CAN_RELEASE") == "no": + msgrcpt.rs = 'p' + msg = _("Request sent") + else: + amr = AMrelease() + result = amr.sendreq(mail_id, secret_id, rcpt) + if result: + rcpt.rs = 'R' + msg = _("Message released") + else: + raise ModoboaException(result) + msgrcpt.save() + return ajax_simple_response(dict(status="ok", respmsg=msg)) + +@selfservice(release_selfservice) def release(request, mail_id): mail_id = check_mail_id(request, mail_id) if request.user.group == 'SimpleUsers': mb = Mailbox.objects.get(user=request.user) - print mail_id msgrcpts = Msgrcpt.objects.filter(mail__in=mail_id, rid__email=mb.full_address) if parameters.get_admin("USER_CAN_RELEASE") == "no": - print msgrcpts msgrcpts.update(rs='p') message = ungettext("%(count)d request sent", "%(count)d requests sent", diff --git a/templates/common/viewmail.html b/templates/common/viewmail.html index a73ff8a9d..19c00dd9e 100644 --- a/templates/common/viewmail.html +++ b/templates/common/viewmail.html @@ -1,6 +1,5 @@ -{% load i18n %}