mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-19 05:58:34 +02:00
"ajax+hash" based navigation for the webmail.
This commit is contained in:
@@ -13,12 +13,14 @@ from mailng.lib import tables, imap_utf7, parameters
|
||||
|
||||
|
||||
class WMtable(tables.Table):
|
||||
tableid = "emails"
|
||||
idkey = "imapid"
|
||||
selection = tables.SelectionColumn("selection", first=True)
|
||||
subject = tables.Column("subject", label=_("Subject"))
|
||||
from_ = tables.Column("from", label=_("From"))
|
||||
date = tables.Column("date", label=_("Date"))
|
||||
flags = tables.ImgColumn("flags")
|
||||
selection = tables.SelectionColumn("selection", width="20px", first=True)
|
||||
_1_flags = tables.ImgColumn("flags", width="40px")
|
||||
_2_subject = tables.Column("subject", width="50%", label=_("Subject"))
|
||||
_3_from_ = tables.Column("from", width="20%", label=_("From"))
|
||||
_4_date = tables.Column("date", width="20%", label=_("Date"))
|
||||
|
||||
|
||||
def parse(self, header, value):
|
||||
try:
|
||||
|
||||
+29
-24
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
import sys
|
||||
from django.http import HttpResponse
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import simplejson
|
||||
@@ -33,41 +34,46 @@ def menu(**kwargs):
|
||||
return []
|
||||
return [
|
||||
{"name" : _("Webmail"),
|
||||
"url" : reverse(folder),
|
||||
"url" : reverse(index),
|
||||
"img" : "/static/pics/webmail.png"}
|
||||
]
|
||||
|
||||
def userlogin(**kwargs):
|
||||
kwargs["request"].session["password"] = kwargs["password"]
|
||||
|
||||
def folder_ajax(request, name, pageid, menu=False):
|
||||
def getctx(status, **kwargs):
|
||||
callername = sys._getframe(1).f_code.co_name
|
||||
ctx = {"status" : status, "callback" : callername}
|
||||
for kw, v in kwargs.iteritems():
|
||||
ctx[kw] = v
|
||||
return ctx
|
||||
|
||||
@login_required
|
||||
def folder(request, name, updatenav=True):
|
||||
if not name:
|
||||
name = "INBOX"
|
||||
if updatenav:
|
||||
pageid = request.GET.has_key("page") and int(request.GET["page"]) or 1
|
||||
request.session["folder"] = name
|
||||
request.session["page"] = pageid
|
||||
lst = ImapListing(request.user.username, request.session["password"],
|
||||
reverse(folder, args=[name]), folder=name)
|
||||
page = lst.paginator.getpage(pageid)
|
||||
name, folder=name)
|
||||
page = lst.paginator.getpage(request.session["page"])
|
||||
if page:
|
||||
content = lst.fetch(request, page.id_start, page.id_stop)
|
||||
navbar = lst.render_navbar(page)
|
||||
else:
|
||||
content = "Empty folder"
|
||||
navbar = ""
|
||||
ctx = {"status" : "ok", "listing" : content, "navbar" : navbar}
|
||||
if menu or "menu" in request.GET.keys():
|
||||
from templatetags.webextras import listing_menu
|
||||
ctx["menu"] = listing_menu("", request.user.get_all_permissions())
|
||||
ctx = getctx("ok", listing=content, navbar=navbar,
|
||||
menu=listing_menu("", request.user.get_all_permissions()))
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
def folder(request, name=None):
|
||||
if not name:
|
||||
name = "INBOX"
|
||||
pageid = request.GET.has_key("page") and int(request.GET["page"]) or 1
|
||||
request.session["folder"] = name
|
||||
request.session["page"] = pageid
|
||||
if "mode" in request.GET.keys() and request.GET["mode"] == "ajax":
|
||||
return folder_ajax(request, name, pageid)
|
||||
def index(request):
|
||||
lst = ImapListing(request.user.username, request.session["password"],
|
||||
reverse(folder, args=[name]), folder=name)
|
||||
return lst.render(request, pageid=int(pageid))
|
||||
"INBOX", folder="INBOX")
|
||||
return lst.render(request, empty=True)
|
||||
|
||||
def fetchmail(request, folder, mail_id, all=False):
|
||||
global mbc
|
||||
@@ -84,14 +90,14 @@ def viewmail(request, folder, mail_id=None):
|
||||
header = fetchmail(request, folder, mail_id)
|
||||
if header:
|
||||
from templatetags.webextras import viewm_menu
|
||||
menu = viewm_menu("", request.session["folder"], mail_id,
|
||||
request.session["page"],
|
||||
pageid = request.session.has_key("page") and request.session["page"] or "1"
|
||||
menu = viewm_menu("", folder, mail_id, pageid,
|
||||
request.user.get_all_permissions())
|
||||
folder, imapid = header["imapid"].split("/")
|
||||
mailcontent = render_to_string("webmail/viewmail.html", {
|
||||
"header" : header, "folder" : folder, "imapid" : imapid
|
||||
})
|
||||
ctx = {"status" : "ok", "menu" : menu, "listing" : mailcontent}
|
||||
ctx = getctx("ok", menu=menu, listing=mailcontent)
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
@@ -122,8 +128,7 @@ def compose(request):
|
||||
s = smtplib.SMTP(parameters.get("webmail", "SMTP_SERVER"))
|
||||
s.sendmail(msg['From'], [msg['To']], msg.as_string())
|
||||
s.quit()
|
||||
return folder_ajax(request, request.session["folder"],
|
||||
request.session["page"], True)
|
||||
return folder(request, request.session["folder"], False)
|
||||
|
||||
form = ComposeMailForm()
|
||||
form.fields["from_"].initial = request.user.username
|
||||
@@ -132,5 +137,5 @@ def compose(request):
|
||||
content = render_to_string("webmail/compose.html", {
|
||||
"form" : form
|
||||
})
|
||||
ctx = {"status" : "ok", "menu" : menu, "listing" : content}
|
||||
ctx = getctx("ok", menu=menu, listing=content)
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@@ -44,9 +44,16 @@
|
||||
margin-right: 2px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
tr.table-tr-selected {
|
||||
background: red;
|
||||
}
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
updatelisting = function(resp, overflow) {
|
||||
if (!$defined(overflow)) {
|
||||
overflow = "auto";
|
||||
}
|
||||
if ($defined(resp.menu)) {
|
||||
$("menubar").set("html", resp.menu);
|
||||
}
|
||||
@@ -60,46 +67,68 @@
|
||||
}
|
||||
|
||||
{% url extensions.webmail.main.folder as view_url %}
|
||||
loadMail = function(event) {
|
||||
viewmail = function(event) {
|
||||
event.stop();
|
||||
if (event.target.parentNode.id) {
|
||||
var url = "{{ view_url }}" + event.target.parentNode.id;
|
||||
var url = event.target.parentNode.id + "/";
|
||||
event.target.parentNode.setStyle("background", "#ffffcc");
|
||||
new Request.JSON({url: url, onSuccess: function(resp) {
|
||||
updatelisting(resp, "hidden");
|
||||
$$("a[name=back]").addEvent("click", loadFolder);
|
||||
setDivHeight("mailcontent", $("table-container").getSize().y, 0);
|
||||
}}).get();
|
||||
location.hash = url;
|
||||
}
|
||||
}
|
||||
register_callback("viewmail", function(resp) {
|
||||
updatelisting(resp, "hidden");
|
||||
$$("a[name=back]").addEvent("click", loadFolder);
|
||||
setDivHeight("mailcontent", $("table-container").getSize().y, 0);
|
||||
});
|
||||
|
||||
loadCompose = function(event) {
|
||||
event.stop();
|
||||
new Request.JSON({url: event.target, onSuccess: function(resp) {
|
||||
updatelisting(resp, "hidden");
|
||||
$$("a[name=back]").addEvent("click", loadFolder);
|
||||
$$("a[name=sendmail]").addEvent("click", function(evt) {
|
||||
evt.stop();
|
||||
$("composemail").set("send", {
|
||||
onSuccess: function(res) {
|
||||
updatelisting(JSON.decode(res));
|
||||
$$("tr").addEvent("click", loadMail);
|
||||
}
|
||||
});
|
||||
$("composemail").send();
|
||||
});
|
||||
setDivHeight("id_body", $("mailheader").getSize().y + 20, 0);
|
||||
}}).get();
|
||||
location.hash = "compose/";
|
||||
}
|
||||
register_callback("compose", function(resp) {
|
||||
updatelisting(resp, "hidden");
|
||||
$$("a[name=back]").addEvent("click", loadFolder);
|
||||
$$("a[name=sendmail]").addEvent("click", function(evt) {
|
||||
evt.stop();
|
||||
$("composemail").set("send", {
|
||||
onSuccess: function(res) {
|
||||
updatelisting(JSON.decode(res));
|
||||
$$("tr").addEvent("click", viewmail);
|
||||
}
|
||||
});
|
||||
$("composemail").send();
|
||||
});
|
||||
setDivHeight("id_body", $("mailheader").getSize().y + 20, 0);
|
||||
});
|
||||
|
||||
loadFolder = function(event, obj) {
|
||||
obj = typeof(obj) != "undefined" ? obj : event.target;
|
||||
if (!$defined(obj)) {
|
||||
obj = event.target;
|
||||
}
|
||||
event.stop();
|
||||
new Request.JSON({url: obj.get("href"), onSuccess: function(resp) {
|
||||
updatelisting(resp, "auto");
|
||||
$$("tr").addEvent("click", loadMail);
|
||||
}}).get({"mode" : "ajax"});
|
||||
location.hash = obj.get("href");
|
||||
}
|
||||
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, {
|
||||
onComplete: function(item) {
|
||||
item.dispose();
|
||||
delete(item);
|
||||
}
|
||||
});
|
||||
});
|
||||
$$("tr").addEvent("dblclick", viewmail);
|
||||
});
|
||||
|
||||
navbarcallback = function(event) {
|
||||
loadFolder(event, event.target.getParent());
|
||||
@@ -117,9 +146,9 @@
|
||||
loadFolder(event, event.target);
|
||||
}
|
||||
|
||||
ajaxListener(updatelisting);
|
||||
$$("a[name=loadfolder]").addEvent("click", foldercallback);
|
||||
$("navbar").getChildren("a").addEvent("click", navbarcallback);
|
||||
$$("tr").addEvent("click", loadMail);
|
||||
$$("a[name=compose]").addEvent("click", loadCompose);
|
||||
{% endblock %}
|
||||
{% block middletitle %}{% trans "Webmail" %}{% endblock %}
|
||||
@@ -133,7 +162,7 @@
|
||||
<ul>
|
||||
{% for fd in folders %}
|
||||
{% url extensions.webmail.main.folder fd.name as fdurl %}
|
||||
<a href="{{ fdurl }}" name="loadfolder">
|
||||
<a href="{{ fd.name }}" name="loadfolder">
|
||||
<li {% ifequal selection fd.name %}class="selected"{% endifequal %}>
|
||||
{% if fd.icon %}<img src="/static/pics/{{ fd.icon }}" />
|
||||
{% else %}<img src="/static/pics/folder.png" />
|
||||
|
||||
@@ -11,8 +11,7 @@ register = template.Library()
|
||||
def viewm_menu(selection, folder, mail_id, page_id, perms):
|
||||
entries = [
|
||||
{"name" : "back",
|
||||
"url" : reverse(webmail.main.folder, args=[folder]) \
|
||||
+ "?page=%s" % page_id + "&menu=1",
|
||||
"url" : folder + "?page=%s" % page_id + "&menu=1",
|
||||
"img" : "/static/pics/back.png",
|
||||
"label" : _("Back to list")},
|
||||
]
|
||||
@@ -25,8 +24,7 @@ def viewm_menu(selection, folder, mail_id, page_id, perms):
|
||||
def compose_menu(selection, folder, page_id, perms):
|
||||
entries = [
|
||||
{"name" : "back",
|
||||
"url" : reverse(webmail.main.folder, args=[folder]) \
|
||||
+ "?page=%s" % page_id + "&menu=1",
|
||||
"url" : folder + "?page=%s" % page_id + "&menu=1",
|
||||
"img" : "/static/pics/back.png",
|
||||
"label" : _("Back to list")},
|
||||
{"name" : "sendmail",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from django.conf.urls.defaults import *
|
||||
|
||||
urlpatterns = patterns('mailng.extensions.webmail.main',
|
||||
url(r'^$', "folder", name="index"),
|
||||
(r'^$', "index"),
|
||||
(r'^compose/$', "compose"),
|
||||
(r'^(?P<folder>.+)/(?P<mail_id>[\w\-\+]+)/content/$', 'getmailcontent'),
|
||||
(r'^(?P<folder>.+)/(?P<mail_id>[\w\-\+]+)/$', 'viewmail'),
|
||||
url(r'^(?P<name>.+)/$', "folder", name="folder"),
|
||||
(r'^(?P<name>.+)/$', "folder"),
|
||||
)
|
||||
|
||||
@@ -102,10 +102,13 @@ class EmailListing:
|
||||
|
||||
def render(self, request, pageid=1, **kwargs):
|
||||
page = self.paginator.getpage(pageid)
|
||||
if not page:
|
||||
listing = "Empty folder"
|
||||
if "empty" in kwargs.keys() and not kwargs["empty"]:
|
||||
if not page:
|
||||
listing = "Empty folder"
|
||||
else:
|
||||
listing = self.fetch(request, page.id_start, page.id_stop)
|
||||
else:
|
||||
listing = self.fetch(request, page.id_start, page.id_stop)
|
||||
listing = ""
|
||||
elapsed = kwargs.has_key("start") and time.time() - kwargs["start"] or 0
|
||||
return _render(request, self.tpl, {
|
||||
"listing" : listing, "elapsed" : elapsed,
|
||||
|
||||
+3
-1
@@ -32,6 +32,8 @@ class ImgColumn(Column):
|
||||
return "<img src='%s' />" % value
|
||||
|
||||
class Table(object):
|
||||
tableid = ""
|
||||
|
||||
def __init__(self, rows={}):
|
||||
self.columns = []
|
||||
for m in inspect.getmembers(self):
|
||||
@@ -67,5 +69,5 @@ class Table(object):
|
||||
|
||||
def render(self, request):
|
||||
return render_to_string("common/tables.html", {
|
||||
"table" : self
|
||||
"table" : self, "tableid" : self.tableid
|
||||
})
|
||||
|
||||
@@ -6,6 +6,45 @@ window.addEvent('domready', function(){
|
||||
});
|
||||
});
|
||||
|
||||
callbacks = {};
|
||||
|
||||
register_callback = function(name, callback) {
|
||||
callbacks[name] = callback;
|
||||
}
|
||||
|
||||
current_anchor = null;
|
||||
|
||||
check_anchor = function() {
|
||||
if (current_anchor == document.location.hash) {
|
||||
return;
|
||||
}
|
||||
current_anchor = document.location.hash;
|
||||
if (!current_anchor) {
|
||||
query = "INBOX/"; // Default location
|
||||
} else {
|
||||
var splits = current_anchor.substring(1).split('?');
|
||||
var section = splits[0];
|
||||
delete splits[0];
|
||||
var params = splits.join('&');
|
||||
if (section[section.length - 1] != '/') {
|
||||
section += "/";
|
||||
}
|
||||
var query = section;
|
||||
if (params != "") {
|
||||
query += "?" + params;
|
||||
}
|
||||
}
|
||||
new Request.JSON({url: query, onSuccess: function(resp) {
|
||||
callback = ($defined(resp.callback)) ? resp.callback : "default";
|
||||
callbacks[callback](resp);
|
||||
}}).get();
|
||||
}
|
||||
|
||||
ajaxListener = function(defcallback) {
|
||||
register_callback("default", defcallback);
|
||||
check_anchor.periodical(300);
|
||||
}
|
||||
|
||||
parse_menubar = function(id) {
|
||||
if ($(id)) {
|
||||
$(id).getElements('li.dropdown').each( function( elem ){
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,22 @@
|
||||
<table>
|
||||
<tr>
|
||||
{% for c in table.columns %}
|
||||
<th>{{ c|safe }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% for row in table.rows %}
|
||||
<tr id="{{ row.id }}" class="{% cycle 'odd' 'even' %}">
|
||||
{% for cell in row.cols %}
|
||||
<table id="{{ tableid }}">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for c in table.columns %}
|
||||
<th width="{{ c.width }}">{{ c|safe }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in table.rows %}
|
||||
<tr id="{{ row.id }}" class="{% cycle 'odd' 'even' %}">
|
||||
{% for cell in row.cols %}
|
||||
{% if cell.safe %}
|
||||
<td>{{ cell.value|safe }}</td>
|
||||
<td width="{{ c.width }}">{{ cell.value|safe }}</td>
|
||||
{% else %}
|
||||
<td class="{{ cell.class }}">{{ cell.value }}</td>
|
||||
<td width="{{ c.width }}" class="{{ cell.class }}">{{ cell.value }}</td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="/static/stylesheets/SqueezeBox.css" />
|
||||
<script src="/static/js/mootools.js" type="text/javascript"></script>
|
||||
<script src="/static/js/mootools-more.js" type="text/javascript"></script>
|
||||
<script src="/static/js/drag.group.js" type="text/javascript"></script>
|
||||
<script src="/static/js/SqueezeBox.js" type="text/javascript"></script>
|
||||
<script src="/static/js/common.js" type="text/javascript"></script>
|
||||
{% url django.views.i18n.javascript_catalog as catalog_url %}
|
||||
|
||||
Reference in New Issue
Block a user