"ajax+hash" based navigation for the webmail.

This commit is contained in:
tonio
2010-01-26 15:34:45 +01:00
parent 6a37664b43
commit 5d9099d33e
12 changed files with 1468 additions and 81 deletions
+1
View File
@@ -1,3 +1,4 @@
.*~$
^[^\.]+\.pyc
static/tmp
static/ckeditor
+7 -5
View File
@@ -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
View File
@@ -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")
+58 -29
View File
@@ -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" />
+2 -4
View File
@@ -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",
+2 -2
View File
@@ -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"),
)
+6 -3
View File
@@ -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
View File
@@ -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
})
+39
View File
@@ -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
+17 -13
View File
@@ -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>
+1
View File
@@ -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 %}