diff --git a/extensions/webmail/imap_listing.py b/extensions/webmail/imap_listing.py index eae4295b9..671394131 100644 --- a/extensions/webmail/imap_listing.py +++ b/extensions/webmail/imap_listing.py @@ -7,7 +7,7 @@ from datetime import datetime, timedelta from email.header import decode_header import email.utils from django.utils.translation import ugettext as _ -from mailng.lib import decode, tables, imap_utf7 +from mailng.lib import decode, tables, imap_utf7, Singleton from mailng.lib.email_listing import MBconnector, EmailListing from mailng.lib import tables, imap_utf7, parameters @@ -17,7 +17,8 @@ class WMtable(tables.Table): idkey = "imapid" selection = tables.SelectionColumn("selection", width="20px", first=True) _1_flags = tables.ImgColumn("flags", width="40px") - _2_subject = tables.Column("subject", width="50%", label=_("Subject")) + _2_subject = tables.Column("subject", width="50%", label=_("Subject"), + cssclass="draggable") _3_from_ = tables.Column("from", width="20%", label=_("From")) _4_date = tables.Column("date", width="20%", label=_("Date")) @@ -83,11 +84,23 @@ class IMAPheader(object): res += " " res += part[0].strip(" ") return decode(res) - -class IMAPconnector(MBconnector): + +class IMAPconnector(object): + __metaclass__ = Singleton + + def __init__(self, request=None, user=None, password=None): + self.address = parameters.get("webmail", "IMAP_SERVER") + if request: + status, msg = self.login(request.user.username, + request.session["password"]) + else: + status, msg = self.login(user, password) + if not status: + # Gestion d'erreur + pass + def login(self, user, passwd): import imaplib - try: self.m = imaplib.IMAP4_SSL(self.address) self.m.login(user, passwd) @@ -125,6 +138,18 @@ class IMAPconnector(MBconnector): folder, id = imapid.split("/") self.m.store(id, "+FLAGS", "\\Seen") + def move(self, msgset, oldfolder, newfolder): + self.m.select(self._encodefolder(oldfolder), True) + self.m.copy(msgset, newfolder) + self.m.store(msgset, "+FLAGS", "\\Deleted") + + def empty(self, folder): + self.m.select(self._encodefolder(folder)) + typ, data = self.m.search(None, 'ALL') + for num in data[0].split(): + self.m.store(num, "+FLAGS", "\\Deleted") + self.m.expunge() + def fetch(self, start=None, stop=None, folder=None, all=False): if not start and not stop: return [] @@ -164,10 +189,7 @@ class ImapListing(EmailListing): tbltype = WMtable def __init__(self, user, password, baseurl=None, **kwargs): - self.mbc = IMAPconnector(parameters.get("webmail", "IMAP_SERVER"), 143) - status, text = self.mbc.login(user, password) - if not status: - print "Login error: %s" % text + self.mbc = IMAPconnector(user=user, password=password) EmailListing.__init__(self, **kwargs) if baseurl: self.paginator.baseurl = baseurl diff --git a/extensions/webmail/main.py b/extensions/webmail/main.py index c02b0c938..228ffb6c2 100644 --- a/extensions/webmail/main.py +++ b/extensions/webmail/main.py @@ -15,8 +15,6 @@ from imap_listing import * from forms import * from templatetags.webextras import * -mbc = None - def init(): events.register("UserMenuDisplay", menu) events.register("UserLogin", userlogin) @@ -66,7 +64,7 @@ def folder(request, name, updatenav=True): content = "Empty folder" navbar = "" ctx = getctx("ok", listing=content, navbar=navbar, - menu=listing_menu("", request.user.get_all_permissions())) + menu=listing_menu("", name, request.user.get_all_permissions())) return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") @login_required @@ -76,11 +74,7 @@ def index(request): return lst.render(request, empty=True) def fetchmail(request, folder, mail_id, all=False): - global mbc - if not mbc: - mbc = IMAPconnector(parameters.get("webmail", "IMAP_SERVER"), 143) - mbc.login(request.user.username, request.session["password"]) - res = mbc.fetch(start=mail_id, folder=folder, all=all) + res = IMAPconnector(request).fetch(start=mail_id, folder=folder, all=all) if len(res): return res[0] return None @@ -106,9 +100,7 @@ def getmailcontent(request, folder, mail_id): msg = fetchmail(request, folder, mail_id, True) if "class" in msg.keys() and msg["class"] == "unseen": - global mbc - mbc.msgseen(msg["imapid"]) - + IMAPconnector(request).msgseen(msg["imapid"]) email = Email(msg, mode="html", links="1") return email.render(request) @@ -139,3 +131,20 @@ def compose(request): }) ctx = getctx("ok", menu=menu, listing=content) return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + +@login_required +def move(request): + for arg in ["msgset", "to"]: + if not request.GET.has_key(arg): + return + folder, id = request.GET["msgset"].split("/") + mbc = IMAPconnector(request) + mbc.move(id, folder, request.GET["to"]) + return HttpResponse(simplejson.dumps({"status" : "ok"}), + mimetype="application/json") + +@login_required +def empty(request, name): + mbc = IMAPconnector(request) + mbc.empty(name) + return folder(request, name, False) diff --git a/extensions/webmail/templates/index.html b/extensions/webmail/templates/index.html index d44d1aca1..cadaf1d63 100644 --- a/extensions/webmail/templates/index.html +++ b/extensions/webmail/templates/index.html @@ -45,8 +45,20 @@ border: 0; } -tr.table-tr-selected { - background: red; +td { + position: relative; +} + +.draggable { + cursor: move; +} + +.dragging { + background: #6AB0C5; +} + +tr { + cursor: pointer; } {% endblock %} {% block js %} @@ -64,6 +76,22 @@ tr.table-tr-selected { $("navbar").getChildren("a").addEvent("click", navbarcallback); } $$("a[name=compose]").addEvent("click", loadCompose); + $$("a[name=empty]").addEvent("click", empty); + } + empty = function(event) { + event.stop(); + new Request.JSON({url : event.target.get("href"), onSuccess : function(resp) { + updatelisting(resp); + }}).get(); + } + + select = function(obj) { + $$("a[name=loadfolder]").each(function(item) { + item.getParent().removeClass("selected"); + item.getParent().addClass("droppable"); + }); + obj.getParent().removeClass("droppable"); + obj.getParent().addClass("selected"); } {% url extensions.webmail.main.folder as view_url %} @@ -110,20 +138,62 @@ tr.table-tr-selected { } register_callback("folder", function(resp) { updatelisting(resp, "auto"); - var tbl = new HtmlTable($("emails"), { - selectable: true - }); - var drag_group = new Drag.Group({ - }); - tbl.addEvent("rowFocus", function(obj) { - var clone = obj.clone() - .setStyles(obj.getCoordinates()) - .setStyles({"opacity": 0.7, "position": "absolute"}) - .inject(document.body); - drag_group.add(clone, { + /*var drag_group = new Drag.Group({ + filter: function(item) { return item.hasClass("active"); }, + drag: { onComplete: function(item) { item.dispose(); delete(item); + }, + onEnter: function(item, droppable) { + alert(droppable); + } + } + });*/ + $$("td[class*=draggable]").each(function(item) { + item.makeDraggable({ + droppables: ".droppable", + }).addEvents({ + "enter" : function(item, droppable) { + droppable.addClass("dragging"); + }, + "leave" : function(item, droppable) { + droppable.removeClass("dragging"); + }, + "drop" : function(item, droppable, event) { + item.dispose(); + delete(item); + if (!droppable) { + this.element = this.oldelement; + return; + } + droppable.removeClass("dragging"); + this.oldelement.getParent().dispose(); + to = droppable.getFirst("a").get("href"); + {% url extensions.webmail.main.move as moveurl %} + new Request.JSON({url: "{{ moveurl }}", onSuccess: function(resp) { + + }}).get({msgset : this.oldelement.getParent().get("id"), + to : to}); + }, + "beforeStart": function(item) { + var clone = item.clone() + .setStyles({"position" : "absolute", + "opacity" : 0.6, + "width" : "300px", + "border" : "1px solid #333", + "background" : "white"}) + .setStyles(item.getCoordinates()) + .inject(document.body); + this.oldelement = this.element; + this.element = clone; + }, + "cancel": function(item) { + if ($defined(this.oldelement)) { + item.dispose(); + delete(item); + this.element = this.oldelement; + } } }); }); @@ -134,13 +204,6 @@ tr.table-tr-selected { loadFolder(event, event.target.getParent()); } - select = function(obj) { - $$("a[name=loadfolder]").each(function(item) { - item.getParent().setStyle("background", "white"); - }); - obj.getParent().setStyle("background", "#FFFFCC"); - } - foldercallback = function(event) { select(event.target); loadFolder(event, event.target); @@ -154,7 +217,7 @@ tr.table-tr-selected { {% block middletitle %}{% trans "Webmail" %}{% endblock %} {% block menu %} {% load webextras %} - {% listing_menu "" perms %} + {% listing_menu "" "INBOX" perms %} {% endblock %} {% block leftcol %}
@@ -162,13 +225,14 @@ tr.table-tr-selected {
diff --git a/extensions/webmail/templatetags/webextras.py b/extensions/webmail/templatetags/webextras.py index c6f2b6e0f..c53b279db 100644 --- a/extensions/webmail/templatetags/webextras.py +++ b/extensions/webmail/templatetags/webextras.py @@ -37,13 +37,20 @@ def compose_menu(selection, folder, page_id, perms): "perms" : perms}) @register.simple_tag -def listing_menu(selection, perms): +def listing_menu(selection, folder, perms): entries = [ {"name" : "compose", "url" : reverse(webmail.main.compose), "img" : "/static/pics/edit.png", "label" : _("New message")} ] + if folder in ["Trash"]: + entries += [ + {"name" : "empty", + "url" : reverse(webmail.main.empty, args=[folder]), + "img" : "/static/pics/clear.png", + "label" : _("Empty trash")} + ] return render_to_string("main/menu.html", {"selection" : selection, "entries" : entries, diff --git a/extensions/webmail/urls.py b/extensions/webmail/urls.py index 5f155aa52..7c2988714 100644 --- a/extensions/webmail/urls.py +++ b/extensions/webmail/urls.py @@ -3,6 +3,8 @@ from django.conf.urls.defaults import * urlpatterns = patterns('mailng.extensions.webmail.main', (r'^$', "index"), (r'^compose/$', "compose"), + (r'^move/$', "move"), + (r'^empty/(?P.+)/', "empty"), (r'^(?P.+)/(?P[\w\-\+]+)/content/$', 'getmailcontent'), (r'^(?P.+)/(?P[\w\-\+]+)/$', 'viewmail'), (r'^(?P.+)/$', "folder"), diff --git a/lib/__init__.py b/lib/__init__.py index 25b629862..e02f23c13 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -78,3 +78,14 @@ def getoption(name, default=None): except AttributeError: res = default return res + + +class Singleton(type): + def __init__(cls, name, bases, dict): + super(Singleton, cls).__init__(name, bases, dict) + cls.instance = None + + def __call__(cls, *args, **kwargs): + if cls.instance is None: + cls.instance = super(Singleton, cls).__call__(*args, **kwargs) + return cls.instance diff --git a/lib/tables.py b/lib/tables.py index 4dfe23281..8ec897eeb 100644 --- a/lib/tables.py +++ b/lib/tables.py @@ -58,10 +58,12 @@ class Table(object): nrow["cols"] += ["."] continue if row.has_key(c.name): - if "class" in row.keys(): - cssclass = row["class"] - else: + try: + cssclass = getattr(c, "cssclass") + except AttributeError: cssclass = "" + if "class" in row.keys(): + cssclass += (cssclass != "") and ", %s" % row["class"] or row["class"] nrow["cols"] += [{"value" : self.parse(c.name, row[c.name]), "class" : cssclass}] diff --git a/static/pics/clear.png b/static/pics/clear.png new file mode 100644 index 000000000..e6c8e8b9f Binary files /dev/null and b/static/pics/clear.png differ