diff --git a/doc/conf.py b/doc/conf.py
index 212ac06d9..1eec437de 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -91,7 +91,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
+html_theme = 'nature'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
diff --git a/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html b/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html
index edfbd2fab..b402bfc34 100644
--- a/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html
+++ b/extensions/postfix_autoreply/templates/postfix_autoreply/autoreply.html
@@ -16,6 +16,12 @@
{% endblock %}
diff --git a/extensions/postfix_autoreply/views.py b/extensions/postfix_autoreply/views.py
index 0a176e509..580076fc0 100644
--- a/extensions/postfix_autoreply/views.py
+++ b/extensions/postfix_autoreply/views.py
@@ -7,13 +7,13 @@ from django.contrib.auth.decorators \
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
-from modoboa.lib import _render, _ctx_ok, _ctx_ko, is_not_localadmin
+from modoboa.lib import _render, ajax_response, is_not_localadmin
from forms import *
from models import *
@login_required
@is_not_localadmin('boxerror')
-def autoreply(request):
+def autoreply(request, tplname="postfix_autoreply/autoreply.html"):
mb = Mailbox.objects.get(user=request.user.id)
try:
arm = ARmessage.objects.get(mbox=mb.id)
@@ -34,17 +34,13 @@ def autoreply(request):
arm.save()
messages.info(request, _("Auto reply message updated successfully."),
fail_silently=True)
- ctx = _ctx_ok(reverse(userprefs.views.index))
- else:
- ctx = _ctx_ko("postfix_autoreply/autoreply.html", {
- "form" : form, "error" : error
- })
- return HttpResponse(simplejson.dumps(ctx),
- mimetype="application/json")
+ return ajax_response(request, url=reverse(userprefs.views.index))
+ return ajax_response(request, status="ko", template=tplname,
+ form=form, error=error)
form = ARmessageForm(instance=arm)
if arm is not None:
form.fields['untildate'].initial = arm.untildate
- return _render(request, "postfix_autoreply/autoreply.html", {
+ return _render(request, tplname, {
"form" : form
})
diff --git a/extensions/webmail/forms.py b/extensions/webmail/forms.py
index 32c5ede87..1de6598e1 100644
--- a/extensions/webmail/forms.py
+++ b/extensions/webmail/forms.py
@@ -7,3 +7,5 @@ class ComposeMailForm(forms.Form):
cc = forms.CharField(label=_("Cc"), widget=forms.Textarea(), required=False)
subject = forms.CharField(label=_("Subject"), max_length=255)
+class FolderForm(forms.Form):
+ name = forms.CharField()
diff --git a/extensions/webmail/lib.py b/extensions/webmail/lib.py
index 087e78973..85cfcc753 100644
--- a/extensions/webmail/lib.py
+++ b/extensions/webmail/lib.py
@@ -257,7 +257,8 @@ class IMAPconnector(object):
def getfolders(self, user):
md_folders = [{"name" : "INBOX", "class" : "inbox"},
- {"name" : 'Drafts'},
+ {"name" : parameters.get_user(user, "DRAFTS_FOLDER"),
+ "class" : "drafts"},
{"name" : 'Junk'},
{"name" : parameters.get_user(user, "SENT_FOLDER")},
{"name" : parameters.get_user(user, "TRASH_FOLDER"),
@@ -309,6 +310,27 @@ class IMAPconnector(object):
self.m.select(self._encodefolder(folder))
self.m.expunge()
+ def create_folder(self, name, parent=None):
+ if parent is not None:
+ name = "%s.%s" % (parent, name)
+ typ, data = self.m.create(self._encodefolder(name))
+ if typ == "NO":
+ return False
+ return True
+
+ def rename_folder(self, oldname, newname):
+ typ, data = self.m.rename(self._encodefolder(oldname),
+ self._encodefolder(newname))
+ if typ == "NO":
+ return False
+ return True
+
+ def delete_folder(self, name):
+ typ, data = self.m.delete(self._encodefolder(name))
+ if typ == "NO":
+ return False
+ return True
+
def getquota(self, folder):
status, data = self.m.getquotaroot(self._encodefolder(folder))
if status == "OK":
diff --git a/extensions/webmail/main.py b/extensions/webmail/main.py
index 0ec493e98..1e1a1cff8 100644
--- a/extensions/webmail/main.py
+++ b/extensions/webmail/main.py
@@ -43,6 +43,9 @@ def init():
parameters.register_user("SENT_FOLDER", type="string", deflt="Sent",
label=_("Sent folder"),
help=_("Folder where copies of sent messages go"))
+ parameters.register_user("DRAFTS_FOLDER", type="string", deflt="Drafts",
+ label=_("Drafts folder"),
+ help=_("Folder where drafts go"))
parameters.register_user("SIGNATURE", type="text", deflt="",
label=_("Signature text"),
help=_("User defined email signature"))
diff --git a/extensions/webmail/static/css/folders.css b/extensions/webmail/static/css/folders.css
new file mode 100644
index 000000000..25400f3ef
--- /dev/null
+++ b/extensions/webmail/static/css/folders.css
@@ -0,0 +1,136 @@
+#folders {
+ float: left;
+ width: 150px;
+}
+
+#folders2 {
+ border: 1px solid #ccc;
+ background-color: #eee;
+ position: absolute;
+ top: 100px;
+ left: 20px;
+ right: 20px;
+ bottom: 40px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+#fdcreate {
+ position: absolute;
+ top: 370px;
+ left: 20px;
+ right: 20px;
+ text-align: center;
+}
+
+.box h3 {
+ font-size: small;
+ margin: 2px 0px 2px 0px;
+ text-align: center;
+}
+
+.box ul {
+ list-style: none;
+ padding: 0 0 0 5px;
+ margin: 0;
+ background-color: #FFFFFF;
+}
+
+.box li ul {
+ padding: 0 0 0 8px;
+}
+
+.box li {
+ margin: 0;
+ background-position: 2px 2px;
+ position: relative;
+}
+
+.box li.selected {
+ background-color: #FFFF77;
+}
+
+.box a:link, .box a:visited {
+ color: black;
+ text-decoration: none;
+}
+
+a.block {
+ display: block;
+ padding: 2px 10px 2px 30px;
+}
+
+.box a:hover {
+ color: #6AB0C5;
+ text-decoration: none;
+}
+
+.box img {
+ border: 0;
+}
+
+.box li.dragging {
+ background-color: #6AB0C5;
+}
+
+img.aligned {
+ float: right;
+}
+
+img.footer {
+ float: left;
+ margin-top: 10px;
+ margin-left: 18px;
+}
+
+li.inbox {
+ background-image: url("/static/pics/overview.png");
+ background-repeat: no-repeat;
+}
+
+li.trash {
+ background-image: url("/static/pics/trash.png");
+ background-repeat: no-repeat;
+}
+
+li.folder {
+ background-image: url("/static/pics/folder.png");
+ background-repeat: no-repeat;
+}
+
+li.drafts {
+ background-image: url("/static/pics/draft.png");
+ background-repeat: no-repeat;
+}
+
+li.subfolders {
+ background-image: url("/static/pics/folder.png");
+ background-repeat: no-repeat;
+}
+
+ul.hidden {
+ display: none;
+}
+
+ul.visible {
+ display: block;
+ padding-bottom: 0;
+}
+
+div.clickbox {
+ position: absolute;
+ left: 2px;
+ top: 2px;
+ width: 14px;
+ height: 14px;
+}
+
+div.collapsed {
+ background-image: url("/static/pics/collapsed.png");
+ background-repeat: no-repeat;
+}
+
+div.expanded {
+ background-image: url("/static/pics/expanded.png");
+ background-repeat: no-repeat;
+}
\ No newline at end of file
diff --git a/extensions/webmail/static/css/webmail.css b/extensions/webmail/static/css/webmail.css
index b87973c89..19acbfa47 100644
--- a/extensions/webmail/static/css/webmail.css
+++ b/extensions/webmail/static/css/webmail.css
@@ -12,11 +12,6 @@ html > body #listing {
bottom: 35px;
}
-#folders {
- float: left;
- width: 150px;
-}
-
/*thead tr {
position: relative;
top: expression(offsetParent.scrollTop);
@@ -50,60 +45,6 @@ html > body #listing {
-khtml-user-select: none; /* Webkit (Safari, Chrome) */
}
-.box h3 {
- font-size: small;
- margin: 2px 0px 2px 0px;
- text-align: center;
-}
-
-.box ul {
- list-style: none;
- padding: 5px 5px;
- margin: 0;
-}
-
-.box li {
- margin-bottom: 4px;
- padding-left: 20px;
-}
-
-.box li.selected {
- background-color: #FFFF77;
-}
-
-.box a:link, .box a:visited {
- background: transparent;
- color: black;
- text-decoration: none;
- margin-left: 5px;
-}
-
-.box a:hover {
- color: #6AB0C5;
- text-decoration: none;
-}
-
-.box img {
- float: left;
- border: 0;
-}
-
-li.inbox {
- background: url("/static/pics/overview.png") no-repeat;
-}
-
-li.trash {
- background: url("/static/pics/trash.png") no-repeat;
-}
-
-li.folder {
- background: url("/static/pics/folder.png") no-repeat;
-}
-
-li.subfolders {
- background: url("/static/pics/subfolders.png") no-repeat;
-}
-
th[class*=sortable] {
cursor: pointer;
/*-moz-box-shadow: 0px -15px 10px -10px rgba(0,0,0,0.6) inset;
@@ -124,16 +65,11 @@ th.selected {
position: static;
}
-.box li.dragging {
- background-color: #6AB0C5;
-}
-
ul.hidden {
display: none;
}
ul.visible {
- background-color: white;
display: block;
padding-bottom: 0;
}
@@ -219,4 +155,26 @@ textarea:hover {
#quotatext {
padding: 2px 0 0 2px;
float: left;
-}
\ No newline at end of file
+}
+
+#fdmenu { border:1px solid #999; padding:0; background:#eee; width:150px; display:none; }
+#fdmenu .separator { border-top:1px solid #999; }
+#fdmenu ul { list-style-type:none; margin:0; padding:0; }
+#fdmenu li { margin:0; padding:0; }
+#fdmenu li a { display:block; padding:5px 10px 5px 5px; font-size:12px; text-decoration:none; font-family:tahoma,arial,sans-serif; color:#000; }
+#fdmenu li a:hover { background-color:#ddd; }
+#fdmenu img { float:left; margin: 0 5px 0 0; }
+
+.folder_menu {
+ float: right;
+ opacity: 0.8;
+}
+
+.folder_btn {
+ float: right;
+ margin: 3px;
+}
+
+.folder_menubar {
+ clear: both;
+}
diff --git a/extensions/webmail/static/js/fdmenu.js b/extensions/webmail/static/js/fdmenu.js
new file mode 100644
index 000000000..c70f8dcbe
--- /dev/null
+++ b/extensions/webmail/static/js/fdmenu.js
@@ -0,0 +1,109 @@
+/*
+ * Simple dropdown menu plugin used for folders manipulation.
+ *
+ */
+var FdMenu = new Class({
+ Implements: [Options],
+
+ options: {
+ modify_url: "",
+ delete_url: ""
+ },
+
+ add_entry: function(name, label, pic, url, onclick) {
+ var item = new Element("li");
+ var picpath = static_url("pics/" + pic);
+
+ this[name] = new Element("a")
+ .set("html", "
" + label)
+ .addEvent("click", onclick.bind(this))
+ .inject(item);
+ item.inject(this.list);
+ },
+
+ initialize: function(options) {
+ this.setOptions(options);
+
+ this.container = new Element("div", {"id" : "fdmenu"})
+ .addClass("fdmenu");
+ this.list = new Element("ul");
+
+ this.add_entry("modbutton", gettext("Edit folder"),
+ "edit.png", this.options.modify_url, function(evt) {
+ evt.stop();
+ SqueezeBox.open(evt.target.get("href"), {
+ handler: "iframe",
+ size: {x: 350, y: 400}
+ });
+ $(document.body).fireEvent("click");
+ });
+
+ this.add_entry("delbutton", gettext("Delete folder"), "remove.png",
+ this.options.delete_url, function(evt) {
+ evt.stop();
+ $(document.body).fireEvent("click");
+ if (!confirm(gettext("Delete folder?"))) {
+ return;
+ }
+ new Request.JSON({
+ url: evt.target.get("href"),
+ method: "GET",
+ onSuccess: function(resp) {
+ if (resp.status == "ok") {
+ var dfolder = this.current_folder + "/";
+ infobox.info(gettext("Folder deleted."));
+ if (dfolder == current_anchor.base) {
+ var pfolder = ((this.current_folder.split(".")).slice(0, -1)).join(".");
+ if (pfolder == "") {
+ pfolder = "INBOX";
+ }
+ current_anchor.baseurl(pfolder, 1);
+ }
+ current_anchor.update.delay(500, current_anchor, 1);
+ } else {
+ infobox.error(gettext("Failed to delete folder"));
+ infobox.hide(2);
+ }
+ }.bind(this)
+ }).send();
+ }
+ );
+ this.list.inject(this.container);
+ this.shown = false;
+
+ $(document.body).addEvent("click", function() {
+ if (this.shown) {
+ this.hide();
+ }
+ }.bind(this));
+ //this.fx = new Fx.Slide(this.container);
+ },
+
+ show: function(origin, folder) {
+ var position = origin.getPosition();
+
+ this.container.setStyles({
+ position: "absolute",
+ top: position.y + 5,
+ left: position.x + 5,
+ display: "block"
+ });
+ this.container.inject(document.body);
+ this.shown = true;
+ this.current_folder = folder;
+ this.modbutton.set("href", this.options.modify_url + "?name=" + folder);
+ this.delbutton.set("href", this.options.delete_url + "?name=" + folder);
+ },
+
+ hide: function() {
+ this.container.setStyles({
+ display: "none"
+ });
+ this.container = this.container.dispose();
+ this.shown = false;
+ },
+
+ toggle: function(origin, folder) {
+ return (!this.shown) ? this.show(origin, folder) : this.hide();
+ }
+});
\ No newline at end of file
diff --git a/extensions/webmail/static/js/webmail.js b/extensions/webmail/static/js/webmail.js
new file mode 100644
index 000000000..9df19f5a4
--- /dev/null
+++ b/extensions/webmail/static/js/webmail.js
@@ -0,0 +1,227 @@
+/*
+ * Globals
+ */
+var rtimer = null;
+
+/*
+ * Enables folders opening/closing
+ *
+ * Available for folders with subfolders.
+ */
+function init_folders_browsing() {
+ $$("div[class*=clickbox]").addEvent("click", function(evt) {
+ var parent = this.getParent();
+ var ul = parent.getFirst("ul[name=" + parent.get("name") + "]");
+
+ if (!$defined(ul)) {
+ return;
+ }
+ if (ul.hasClass("hidden")) {
+ this.removeClass("collapsed").addClass("expanded");
+ ul.removeClass("hidden").addClass("visible");
+ } else {
+ this.removeClass("expanded").addClass("collapsed");
+ ul.removeClass("visible").addClass("hidden");
+ }
+ evt.stop();
+ });
+}
+
+/*
+ * Select a particular folder
+ */
+function select_folder(obj) {
+ $$("a[name=loadfolder]").each(function(item) {
+ item.getParent().removeClass("selected");
+ item.getParent().addClass("droppable");
+ });
+ obj.getParent().removeClass("droppable");
+ obj.getParent().addClass("selected");
+
+ $$(obj.getParents("ul[class*=hidden]")).each(function(item) {
+ item.removeClass("hidden");
+ item.addClass("visible");
+ $$("li[name=" + item.get("name") + "]").each(function(item) {
+ item.setStyle("background",
+ "url('" + static_url("pics/folder-open.png") + "') no-repeat");
+ });
+ });
+}
+
+/*
+ * Callback of the 'viewmail' action
+ */
+function viewmail_cb(resp) {
+ wm_updatelisting(resp, "hidden");
+ $$("a[name=back]").addEvent("click", loadFolder);
+ $$("a[name=reply]").addEvent("click", reply_loader);
+ $$("a[name=replyall]").addEvent("click", replyall_loader);
+ $$("a[name=forward]").addEvent("click", forward_loader);
+ $$("a[name=delete]").addEvent("click", function(event) {
+ var lnk = event.target;
+
+ event.stop();
+ new Request.JSON({
+ url: lnk.get("href"), onSuccess: function(resp) {
+ current_anchor.parse_string(resp.next).update();
+ select_folder($$("a[href=" + current_anchor.getbaseurl() + "]"));
+ }
+ }).get();
+ });
+ $$("a[name=activate_links]").addEvent("click", function(evt) {
+ evt.stop();
+ current_anchor.setparam("links", "1").update();
+ });
+ $$("a[name=disable_links]").addEvent("click", function(evt) {
+ evt.stop();
+ current_anchor.setparam("links", "0").update();
+ });
+ setDivHeight("mailcontent", 5, 0);
+}
+
+/*
+ * Loader of the 'compose' action (called when the associated button
+ * is clicked)
+ */
+function compose_loader(event) {
+ event.stop();
+ current_anchor.baseurl("compose").update();
+}
+
+/*
+ * Callback of the 'compose' action
+ */
+function compose_callback(resp) {
+ wm_updatelisting(resp);
+ window.addEvent("resize", function(evt) {
+ setDivHeight("id_body", $("mailheader").getSize().y, 0);
+ });
+ window.fireEvent("resize");
+
+ var editormode = resp.editor;
+ var editorid = "id_body";
+
+ if (editormode == "html") {
+ var instance = CKEDITOR.instances[editorid];
+ if (instance) {
+ CKEDITOR.remove(instance);
+ }
+ CKEDITOR.replace(editorid, {
+ customConfig: static_url("js/editor_config.js"),
+ height: $(editorid).getSize().y
+ });
+ }
+ $$("a[name=back]").addEvent("click", loadFolder);
+ $$("a[name=sendmail]").addEvent("click", function(evt) {
+ evt.stop();
+ $("composemail").set("send", {
+ onSuccess: function(resp) {
+ resp = JSON.decode(resp);
+ if (resp.status == "ko") {
+ wm_updatelisting(resp);
+ window.fireEvent("resize");
+ if ($defined(resp.error)) {
+ infobox.error(resp.error);
+ }
+ return;
+ }
+ current_anchor.parse_string(resp.url, true).setparams(navparams);
+ current_anchor.update();
+ }
+ });
+ if (editormode == "html") {
+ CKEDITOR.instances[editorid].updateElement();
+ }
+ $("composemail").send();
+ });
+}
+
+/*
+ * Loader of the 'reply' action (called when the associated button
+ * is clicked).
+ */
+function reply_loader(event) {
+ event.stop();
+ location.hash += event.target.get("href");
+}
+
+/*
+ * Loader of the 'replyall' action (called when the associated button
+ * is clicked).
+ */
+function replyall_loader(event) {
+ event.stop();
+ location.hash += event.target.get("href") + "?all=1";
+}
+
+/*
+ * Loader of the 'forward' action (called when the associated button
+ * is clicked).
+ */
+function forward_loader(event) {
+ event.stop();
+ location.hash += event.target.get("href");
+}
+
+/*
+ * Onclick callback used to load the content of a particular
+ * folder. (activated when clicking on a folder's name)
+ */
+function loadFolder(event, obj) {
+ event.stop();
+ if (!$defined(obj)) {
+ obj = $(event.target);
+ }
+ current_anchor.parse_string(gethref(obj), true).setparams(navparams);
+ current_anchor.update();
+}
+
+function foldercallback(event) {
+ select_folder(event.target);
+ loadFolder(event, event.target);
+}
+
+/*
+ * Automatic folder listing refresh
+ *
+ * Called at a regular frequency to make an asynchronous request that
+ * retrieves a folder's content (ie. mails)
+ */
+function refreshFolder() {
+ var query = current_anchor.serialized.substring(1);
+
+ clearInterval(rtimer);
+ infobox.show(gettext("Reloading..."), {
+ profile : "gray",
+ spinner : true
+ });
+ new Request.JSON({url: query, noCache : true, onSuccess: function(resp) {
+ var callback = ($defined(resp.callback)) ? resp.callback : "default";
+
+ callbacks[callback](resp);
+ infobox.info(gettext("Done"));
+ infobox.hide(1);
+ }}).get();
+}
+
+/*
+ * Simple AJAX request
+ *
+ * Initialize the infobox with a loading message and wait for a JSON
+ * encoded answer. The infobox is cleared by the request callback
+ * function.
+ */
+function simple_request(url, params) {
+ infobox.show(gettext("Waiting..."), {
+ profile: "gray",
+ spinner: true
+ });
+ new Request.JSON({
+ url : url,
+ onSuccess : function(response) {
+ get_callback("folder")(response);
+ infobox.info(gettext("Done"));
+ infobox.hide(1);
+ }
+ }).get(params);
+}
\ No newline at end of file
diff --git a/extensions/webmail/templates/webmail/folder.html b/extensions/webmail/templates/webmail/folder.html
new file mode 100644
index 000000000..912fc79a1
--- /dev/null
+++ b/extensions/webmail/templates/webmail/folder.html
@@ -0,0 +1,45 @@
+{% extends "layout_simple.html" %}
+{% load i18n %}
+{% block html_head %}
+
+
+
+
+{% endblock %}
+{% block content %}
+
{{ title }}
+
+{% endblock %}
diff --git a/extensions/webmail/templates/webmail/folders.html b/extensions/webmail/templates/webmail/folders.html
index ce21f6cf7..5956654a9 100644
--- a/extensions/webmail/templates/webmail/folders.html
+++ b/extensions/webmail/templates/webmail/folders.html
@@ -1,6 +1,13 @@
{% load i18n %}
{% load webextras %}
+{% if titlebar %}
+
+
+
{% trans "Folders" %}
+{% endif %}
- {% print_folders folders selected %}
+ {% print_folders folders selected withunseen withmenu %}
diff --git a/extensions/webmail/templates/webmail/index.html b/extensions/webmail/templates/webmail/index.html
index dd6c51c01..8fb326ba8 100644
--- a/extensions/webmail/templates/webmail/index.html
+++ b/extensions/webmail/templates/webmail/index.html
@@ -1,8 +1,51 @@
-
{% extends "common/email_listing.html" %}
{% load i18n %}
-{% block css %}
+{% block html_head %}
+
+
+
+{{ block.super }}
+
{% endblock %}
{% block js %}
quota_pb = new ProgressBar({
@@ -14,148 +57,12 @@
displayID: "quotatext"
});
- wm_updatelisting = function(resp) {
- window.removeEvents("resize");
- updatelisting(resp);
- if ($defined(resp.folders)) {
- $("folders").set("html", resp.folders);
- }
- select($$("a[href=" + current_anchor.getbaseurl() + "]"));
- $$("li[class*=clickable]").removeEvents();
- $$("li[class*=clickable]").addEvent("click", function(evt) {
- $$("ul[name=" + evt.target.get("name") + "]").each(function(item) {
- if (item.hasClass("hidden")) {
- evt.target.setStyle("background",
- "url('{{ MEDIA_URL }}pics/folder-open.png') no-repeat");
- item.removeClass("hidden").addClass("visible");
- } else {
- evt.target.setStyle("background",
- "url('{{ MEDIA_URL }}pics/subfolders.png') no-repeat");
- item.removeClass("visible").addClass("hidden");
- }
- });
- evt.stop();
- });
- if ($defined(resp.quota) && resp.quota != "-1") {
- quota_pb.set(resp.quota);
- $("quotabox").setStyle("display", "block");
- $("quotalabel").setStyle("display", "block");
- }
- $$("a[name=loadfolder]").addEvent("click", foldercallback);
- $$("a[name=compose]").addEvent("click", loadCompose);
- };
-
- 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");
-
- $$(obj.getParents("ul[class*=hidden]")).each(function(item) {
- item.removeClass("hidden");
- item.addClass("visible");
- $$("li[name=" + item.get("name") + "]").each(function(item) {
- item.setStyle("background", "url('{{ MEDIA_URL }}pics/folder-open.png') no-repeat");
- });
- });
- };
-
- register_callback("viewmail", function(resp) {
- wm_updatelisting(resp, "hidden");
- $$("a[name=back]").addEvent("click", loadFolder);
- $$("a[name=reply]").addEvent("click", reply);
- $$("a[name=replyall]").addEvent("click", replyall);
- $$("a[name=forward]").addEvent("click", forward);
- $$("a[name=delete]").addEvent("click", function(event) {
- var lnk = event.target;
-
- event.stop();
- new Request.JSON({url: lnk.get("href"), onSuccess: function(resp) {
- current_anchor.parse_string(resp.next).update();
- select($$("a[href=" + current_anchor.getbaseurl() + "]"));
- }}).get();
- });
- $$("a[name=activate_links]").addEvent("click", function(evt) {
- evt.stop();
- current_anchor.setparam("links", "1").update();
- });
- $$("a[name=disable_links]").addEvent("click", function(evt) {
- evt.stop();
- current_anchor.setparam("links", "0").update();
- });
- setDivHeight("mailcontent", 5, 0);
- });
-
- compose_callback = function(resp) {
- wm_updatelisting(resp);
- window.addEvent("resize", function(evt) {
- setDivHeight("id_body", $("mailheader").getSize().y, 0);
- });
- window.fireEvent("resize");
-
- var editormode = resp.editor;
- var editorid = "id_body";
- if (editormode == "html") {
- var instance = CKEDITOR.instances[editorid];
- if (instance) {
- CKEDITOR.remove(instance);
- }
- CKEDITOR.replace(editorid, {
- customConfig: "{{ MEDIA_URL }}/js/editor_config.js",
- height: $(editorid).getSize().y
- });
- }
- $$("a[name=back]").addEvent("click", loadFolder);
- $$("a[name=sendmail]").addEvent("click", function(evt) {
- evt.stop();
- $("composemail").set("send", {
- onSuccess: function(resp) {
- resp = JSON.decode(resp);
- if (resp.status == "ko") {
- wm_updatelisting(resp);
- window.fireEvent("resize");
- if ($defined(resp.error)) {
- infobox.error(resp.error);
- }
- return;
- }
- current_anchor.parse_string(resp.url, true).setparams(navparams);
- current_anchor.update();
- }
- });
- if (editormode == "html") {
- CKEDITOR.instances[editorid].updateElement();
- }
- $("composemail").send();
- });
- };
-
- loadCompose = function(event) {
- event.stop();
- current_anchor.baseurl("compose").update();
- };
+ register_callback("viewmail", viewmail_cb);
register_callback("compose", compose_callback);
-
- reply = function(event) {
- event.stop();
- location.hash += event.target.get("href");
- };
register_callback("reply", compose_callback);
- replyall = function(event) {
- event.stop();
- location.hash += event.target.get("href") + "?all=1";
- };
register_callback("replyall", compose_callback);
- forward = function(event) {
- event.stop();
- location.hash += event.target.get("href");
- };
register_callback("forward", compose_callback);
- var rtimer = null;
-
window.addEvent("pageRefresh", function() {
if (rtimer) {
clearInterval(rtimer);
@@ -163,30 +70,6 @@
}
});
- refreshFolder = function() {
- var query = current_anchor.serialized.substring(1);
- clearInterval(rtimer);
- infobox.show('{% trans "Reloading..." %}', {
- profile : "gray",
- spinner : true
- });
- new Request.JSON({url: query, noCache : true, onSuccess: function(resp) {
- callback = ($defined(resp.callback)) ? resp.callback : "default";
- callbacks[callback](resp);
- infobox.info('{% trans "Done" %}');
- infobox.hide(1);
- }}).get();
- };
-
- loadFolder = function(event, obj) {
- event.stop();
- if (!$defined(obj)) {
- obj = $(event.target);
- }
- current_anchor.parse_string(gethref(obj), true).setparams(navparams);
- current_anchor.update();
- };
-
register_callback("folder", function(resp) {
wm_updatelisting(resp);
rtimer = refreshFolder.periodical({{ refreshrate }});
@@ -194,7 +77,7 @@
if (navparams.has("order")) {
src = navparams.get("order").charAt(0) == "-" ? "down_arrow.png" : "up_arrow.png";
th = $$("th[id=" + navparams.get("order").substring(1) + "]");
- th.getElement("img").set("src", "{{ MEDIA_URL }}pics/" + src);
+ th.getElement("img").set("src", static_url("pics/" + src));
th.getElement("img").setStyle("display", "block");
th.addClass("selected");
}
@@ -299,20 +182,6 @@
});
});
- simple_request = function(url, params) {
- infobox.show(gettext("Waiting..."), {
- profile: "gray",
- spinner: true
- });
- new Request.JSON({url : url,
- onSuccess : function(response) {
- get_callback("folder")(response);
- infobox.info(gettext("Done"));
- infobox.hide(1);
- }
- }).get(params);
- };
-
buildmarkrequest = function(evt) {
evt.stop();
if (!emails_table._selectedRows.length) {
@@ -360,12 +229,6 @@
current_anchor.setparams(navparams).update();
});
});
-
- foldercallback = function(event) {
- select(event.target);
- loadFolder(event, event.target);
- };
-
{% endblock %}
{% block middletitle %}{% trans "Webmail" %}{% endblock %}
{% block leftcol %}
diff --git a/extensions/webmail/templatetags/webextras.py b/extensions/webmail/templatetags/webextras.py
index 06879cf07..92feb0a69 100644
--- a/extensions/webmail/templatetags/webextras.py
+++ b/extensions/webmail/templatetags/webextras.py
@@ -116,29 +116,36 @@ def listing_menu(selection, folder, user):
return menu + searchbar
@register.simple_tag
-def print_folders(folders, selected=None):
+def print_folders(folders, selected=None, withunseen=False, withmenu=False):
result = ""
+
for fd in folders:
- if fd.has_key("class"):
- cssclass = fd["class"]
- else:
- cssclass = "folder"
- name = ""
- if fd.has_key("sub"):
- cssclass += " clickable"
- name = fd["path"]
+ cssclass = fd["class"] if fd.has_key("class") else "folder"
+ name = fd["path"] if fd.has_key("sub") else fd["name"]
label = fd["name"]
- if selected == label:
+ if selected == name:
cssclass += " selected"
result += "\n" % (name, cssclass)
-
- cssclass = ""
- if fd.has_key("unseen"):
+ if fd.has_key("sub") and len(fd["sub"]):
+ if selected is not None and selected != name and selected.count(name):
+ ul_state = "visible"
+ div_state = "expanded"
+ else:
+ ul_state = "hidden"
+ div_state = "collapsed"
+ result += "
" % div_state
+ if withmenu:
+ result += "" \
+ % settings.MEDIA_URL
+
+ cssclass = "block"
+ if withunseen and fd.has_key("unseen"):
label += " (%d)" % fd["unseen"]
- cssclass = "unseen"
+ cssclass += " unseen"
result += "%s\n" \
% (fd.has_key("path") and fd["path"] or fd["name"], cssclass, label)
if fd.has_key("sub") and len(fd["sub"]):
- result += "" % (fd["path"]) + print_folders(fd["sub"], selected) + "
\n"
+ result += "" % (fd["path"], ul_state) \
+ + print_folders(fd["sub"], selected, withunseen, withmenu) + "
\n"
result += "\n"
return result
diff --git a/extensions/webmail/urls.py b/extensions/webmail/urls.py
index 1799bac60..201341160 100644
--- a/extensions/webmail/urls.py
+++ b/extensions/webmail/urls.py
@@ -7,6 +7,9 @@ urlpatterns = patterns('modoboa.extensions.webmail.views',
(r'^mark/(?P.+)/$', "mark"),
(r'^empty/(?P.+)/$', "empty"),
(r'^compact/(?P.+)/$', "compact"),
+ (r'^newfolder/$', "newfolder"),
+ (r'^editfolder/$', 'editfolder'),
+ (r'^delfolder/$', 'delfolder'),
(r'^(?P.+)/(?P[\w\-\+]+)/part/$', 'getattachment'),
(r'^(?P.+)/(?P[\w\-\+]+)/delete/$', 'delete'),
(r'^(?P.+)/(?P[\w\-\+]+)/content/$', 'getmailcontent'),
diff --git a/extensions/webmail/views.py b/extensions/webmail/views.py
index 43e71a95b..e7634e559 100644
--- a/extensions/webmail/views.py
+++ b/extensions/webmail/views.py
@@ -10,7 +10,8 @@ from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required
from modoboa.admin.models import Mailbox
from modoboa.lib import parameters, _render, _render_error, \
- getctx, is_not_localadmin, _render_to_string, split_mailbox
+ getctx, is_not_localadmin, _render_to_string, split_mailbox, \
+ ajax_response
from modoboa.lib.email_listing import parse_search_parameters, Paginator
from lib import *
from forms import *
@@ -63,9 +64,12 @@ def __render_common_components(request, folder_name, lst=None, content=None, men
% _("This folder contains no messages")
ret = {
- "folders" : render_to_string("webmail/folders.html", {
+ "folders" : _render_to_string(request, "webmail/folders.html", {
+ "titlebar" : True,
"selected" : request.session["folder"],
- "folders" : mbc.getfolders(request.user)
+ "folders" : mbc.getfolders(request.user),
+ "withmenu" : True,
+ "withunseen" : True
}),
"menu" : menu,
"listing" : content,
@@ -423,3 +427,97 @@ def forward(request, folder, mail_id):
email = ForwardModifier(msg, request.user, form, addrfull=True, links="1")
return render_compose(request, form, reverse(forward, args=[folder, mail_id]),
email)
+
+def separate_folder(fullname, sep="."):
+ """Split a folder name
+
+ If a separator is found in fullname, this function returns the
+ corresponding name and parent folder name.
+ """
+ if fullname.count("."):
+ parts = fullname.split(sep)
+ name = parts[-1]
+ parent = sep.join(parts[0:len(parts) - 1])
+ return name, parent
+ return fullname, None
+
+@login_required
+@is_not_localadmin()
+def newfolder(request, tplname="webmail/folder.html"):
+ mbc = IMAPconnector(user=request.user.username,
+ password=request.session["password"])
+ ctx = {"title" : _("Create a new folder"),
+ "fname" : "newfolder",
+ "submit_label" : _("Create"),
+ "withmenu" : False,
+ "withunseen" : False}
+ ctx["folders"] = mbc.getfolders(request.user)
+ if request.method == "POST":
+ form = FolderForm(request.POST)
+ if form.is_valid():
+ pf = request.POST.has_key("parent_folder") \
+ and request.POST["parent_folder"] or None
+ if mbc.create_folder(form.cleaned_data["name"], pf):
+ return ajax_response(request, ajaxnav=True)
+
+ ctx["form"] = form
+ ctx["selected"] = None
+ return ajax_response(request, status="ko", template=tplname, **ctx)
+
+ ctx["form"] = FolderForm()
+ ctx["selected"] = None
+ return _render(request, tplname, ctx)
+
+@login_required
+@is_not_localadmin()
+def editfolder(request, tplname="webmail/folder.html"):
+ mbc = IMAPconnector(user=request.user.username,
+ password=request.session["password"])
+ ctx = {"title" : _("Edit folder"),
+ "fname" : "editfolder",
+ "submit_label" : _("Update"),
+ "withmenu" : False,
+ "withunseen" : False}
+ ctx["folders"] = mbc.getfolders(request.user)
+ if request.method == "POST":
+ form = FolderForm(request.POST)
+ if form.is_valid():
+ pf = request.POST.has_key("parent_folder") \
+ and request.POST["parent_folder"] or None
+ ctx["selected"] = pf
+ oldname, oldparent = separate_folder(request.POST["oldname"])
+ try:
+ extra = {}
+ if form.cleaned_data["name"] != oldname \
+ or (pf is not None and pf != oldparent):
+ newname = form.cleaned_data["name"] if pf is None \
+ else "%s.%s" % (pf, form.cleaned_data["name"])
+ if not mbc.rename_folder(request.POST["oldname"], newname):
+ raise Exception
+ extra["url"] = newname
+ return ajax_response(request, ajaxnav=True, **extra)
+ except Exception:
+ pass
+ ctx["form"] = form
+ return ajax_response(request, status="ko", template=tplname, **ctx)
+
+ if not request.GET.has_key("name") or request.GET["name"] == "":
+ return
+ name = request.GET["name"]
+ ctx["oldname"] = name
+ name, parent = separate_folder(name)
+ ctx["form"] = FolderForm()
+ ctx["form"].fields["name"].initial = name
+ ctx["selected"] = parent
+ return _render(request, tplname, ctx)
+
+@login_required
+@is_not_localadmin()
+def delfolder(request):
+ if not request.GET.has_key("name") or request.GET["name"] == "":
+ return
+ mbc = IMAPconnector(user=request.user.username,
+ password=request.session["password"])
+ if mbc.delete_folder(request.GET["name"]):
+ return ajax_response(request)
+ return ajax_response(request, status="ko")
diff --git a/lib/__init__.py b/lib/__init__.py
index 476e28a3e..352cdb9ba 100644
--- a/lib/__init__.py
+++ b/lib/__init__.py
@@ -74,7 +74,7 @@ def getctx(status, level=1, callback=None, **kwargs):
ctx[kw] = v
return ctx
-def ajax_response(request, status="ok", url=None, template=None, **kwargs):
+def ajax_response(request, status="ok", url="", ajaxnav=False, template=None, **kwargs):
"""Ajax response shortcut
Simple shortcut that sends an JSON response. If a template is
@@ -94,10 +94,12 @@ def ajax_response(request, status="ok", url=None, template=None, **kwargs):
content = _render_to_string(request, template, ctx)
else:
content = ""
- return HttpResponse(simplejson.dumps({"status" : status,
- "content" : content,
- "url" : url}),
- mimetype="application/json")
+ jsonctx = {"status" : status, "content" : content}
+ if ajaxnav:
+ jsonctx["ajaxnav"] = True
+ if url != "":
+ jsonctx["url"] = url
+ return HttpResponse(simplejson.dumps(jsonctx), mimetype="application/json")
def decode(s, encodings=('utf8', 'latin1', 'windows-1252', 'ascii'), charset=None):
if charset is not None:
diff --git a/static/js/common.js b/static/js/common.js
index c8d5d6007..e85f4d928 100644
--- a/static/js/common.js
+++ b/static/js/common.js
@@ -8,9 +8,26 @@ window.addEvent('domready', function(){
});
});
+var media_url = "";
callbacks = {};
-register_callback = function(name, callback) {
+/*
+ * A simple function to initialize the value of the global variable
+ * 'media_url' (corresponding to django's MEDIA_URL variable).
+ */
+function set_media_url(url) {
+ media_url = url;
+}
+
+/*
+ * Shortcut function that construct an url from the media_url and the
+ * given value.
+ */
+function static_url(value) {
+ return media_url + value;
+}
+
+function register_callback(name, callback) {
callbacks[name] = callback;
};
@@ -18,170 +35,177 @@ get_callback = function(name) {
return callbacks[name];
};
-current_anchor = null;
+/*
+ * AJAX anchor based navigation
+ */
+var current_anchor = null;
-check_anchor = function() {
- if (current_anchor.serialized == location.hash
- && !$defined(current_anchor.force)) {
- return;
- }
- delete(current_anchor.force);
- current_anchor.from_string(location.hash);
- if (!current_anchor.serialized) {
- location.hash = current_anchor.deflocation;
- return;
- } else {
- query = current_anchor.serialized.substring(1);
- }
- infobox.show(gettext("Loading..."), {
- profile: "gray",
- spinner: true
- });
- new Request.JSON({url: query, noCache : true, onSuccess: function(resp) {
- var callback = ($defined(resp.callback)) ? resp.callback : "default";
- callbacks[callback](resp);
- infobox.info(gettext("Done"));
- infobox.hide(1);
- }}).get();
-};
+function check_anchor() {
+ if (current_anchor.serialized == location.hash
+ && !$defined(current_anchor.force)) {
+ return;
+ }
+ delete(current_anchor.force);
+ current_anchor.from_string(location.hash);
+ if (!current_anchor.serialized) {
+ location.hash = current_anchor.deflocation;
+ return;
+ }
-ajaxListener = function(deflocation, defcallback) {
- current_anchor = new HashWrapper(deflocation);
- register_callback("default", defcallback);
- check_anchor.periodical(300);
+ var query = current_anchor.serialized.substring(1);
+
+ infobox.show(gettext("Loading..."), {
+ profile: "gray",
+ spinner: true
+ });
+ new Request.JSON({url: query, noCache : true, onSuccess: function(resp) {
+ var callback = ($defined(resp.callback)) ? resp.callback : "default";
+ callbacks[callback](resp);
+ infobox.info(gettext("Done"));
+ infobox.hide(1);
+ }}).get();
};
/*
-*
-*/
+ * Initialize the AJAX anchor based navigation
+ */
+function ajaxListener(deflocation, defcallback) {
+ current_anchor = new HashWrapper(deflocation);
+ register_callback("default", defcallback);
+ check_anchor.periodical(300);
+};
+
+/*
+ *
+ */
function HashWrapper(deflocation) {
- this.deflocation = deflocation;
- this.params = $H();
- this.base = '';
- this.serialized = null;
+ this.deflocation = deflocation;
+ this.params = $H();
+ this.base = '';
+ this.serialized = null;
- this.reset = function() {
- this.base = null;
- this.params.empty();
- return this;
- };
+ this.reset = function() {
+ this.base = null;
+ this.params.empty();
+ return this;
+ };
- this.parse_string = function(value, reset) {
- var splits = (value.charAt(0) == '#')
- ? value.substring(1).split('?') : value.split('?');
+ this.parse_string = function(value, reset) {
+ var splits = (value.charAt(0) == '#')
+ ? value.substring(1).split('?') : value.split('?');
- if (splits.length == 0) {
- return this;
- }
- if (!$defined(reset)) {
- reset = false;
- }
- if (reset) {
- this.reset();
- }
- this.base = (splits[0] == "") ? this.deflocation : splits[0];
- /*if (this.base[this.base.length - 1] != '/') {
- this.base += '/';
- }*/
- var re = new RegExp("/$");
- if (!this.base.match(re)) {
- this.base += '/';
- }
- if (splits.length > 1) {
- params = splits[1].split('&');
- for (var i = 0; i < params.length; i++) {
- tmp = params[i].split('=');
- this.setparam(tmp[0], tmp[1]);
- }
- }
- return this;
- };
+ if (splits.length == 0) {
+ return this;
+ }
+ if (!$defined(reset)) {
+ reset = false;
+ }
+ if (reset) {
+ this.reset();
+ }
+ this.base = (splits[0] == "") ? this.deflocation : splits[0];
- this.from_string = function(value, reset) {
- this.parse_string(value);
- this.serialized = value;
- return this;
- };
+ var re = new RegExp("/$");
+ if (!this.base.match(re)) {
+ this.base += '/';
+ }
+ if (splits.length > 1) {
+ var params = splits[1].split('&');
+ for (var i = 0; i < params.length; i++) {
+ var tmp = params[i].split('=');
+ this.setparam(tmp[0], tmp[1]);
+ }
+ }
+ return this;
+ };
- this.serialize = function() {
- var res = this.base;
+ this.from_string = function(value, reset) {
+ this.parse_string(value);
+ this.serialized = value;
+ return this;
+ };
- if (this.params.getLength() != 0) {
- res += "?" + this.params.toQueryString();
- }
- return res;
- };
+ this.serialize = function() {
+ var res = this.base;
- this.update = function(force) {
- window.fireEvent("pageRefresh");
- location.hash = this.serialize();
- if ($defined(force)) {
- this.force = force;
- }
- };
+ if (this.params.getLength() != 0) {
+ res += "?" + this.params.toQueryString();
+ }
+ return res;
+ };
- this.deleteParam = function(str) {
- this.params.erase(str);
- };
+ this.update = function(force) {
+ window.fireEvent("pageRefresh");
+ location.hash = this.serialize();
+ if ($defined(force)) {
+ this.force = force;
+ }
+ };
- this.updateparams = function(str) {
- if (str.charAt(0) == '?') {
- str = str.substring(1);
- }
- var elems = str.split('&');
- for (var i = 0; i < elems.length; i++) {
- this.setparamfromstring(elems[i]);
- }
- };
+ this.deleteParam = function(str) {
+ this.params.erase(str);
+ };
- this.setparamfromstring = function(str) {
- var def = str.split('=');
- this.params.set(def[0], def[1]);
- };
+ this.updateparams = function(str) {
+ if (str.charAt(0) == '?') {
+ str = str.substring(1);
+ }
+ var elems = str.split('&');
+ for (var i = 0; i < elems.length; i++) {
+ this.setparamfromstring(elems[i]);
+ }
+ };
- this.setparam = function(name, value) {
- this.params.set(name, value);
- return this;
- };
+ this.setparamfromstring = function(str) {
+ var def = str.split('=');
+ this.params.set(def[0], def[1]);
+ };
- this.setparams = function(params) {
- this.params.extend(params);
- return this;
- };
+ this.setparam = function(name, value) {
+ this.params.set(name, value);
+ return this;
+ };
- this.getparam = function(name) {
- if (!this.params.has(name)) {
- return "";
- }
- return this.params.get(name);
- };
+ this.setparams = function(params) {
+ this.params.extend(params);
+ return this;
+ };
- this.baseurl = function(value) {
- this.reset();
- this.base = value;
- var re = new RegExp("/$");
- if (!this.base.match(re)) {
- this.base += '/';
- }
- return this;
- };
+ this.getparam = function(name) {
+ if (!this.params.has(name)) {
+ return "";
+ }
+ return this.params.get(name);
+ };
- this.addbaseurl = function(value) {
- newbase = this.base + value;
- return this.baseurl(newbase);
- };
+ this.baseurl = function(value, noreset) {
+ if (!$defined(noreset) || noreset == 0) {
+ this.reset();
+ }
+ this.base = value;
+ var re = new RegExp("/$");
+ if (!this.base.match(re)) {
+ this.base += '/';
+ }
+ return this;
+ };
- this.getbaseurl = function() {
- return this.base.substr(0, this.base.length - 1);
- };
+ this.addbaseurl = function(value) {
+ var newbase = this.base + value;
+ return this.baseurl(newbase);
+ };
- this.delparam = function(name) {
- if (!this.params.has(name)) {
- return false;
- }
- this.params.erase(name);
- return this;
- };
+ this.getbaseurl = function() {
+ return this.base.substr(0, this.base.length - 1);
+ };
+
+ this.delparam = function(name) {
+ if (!this.params.has(name)) {
+ return false;
+ }
+ this.params.erase(name);
+ return this;
+ };
};
parse_menubar = function(id) {
diff --git a/static/js/email_listing.js b/static/js/email_listing.js
new file mode 100644
index 000000000..12ab9572c
--- /dev/null
+++ b/static/js/email_listing.js
@@ -0,0 +1,25 @@
+/*
+ * Shared functions between the webmail and the quarantine plugin.
+ *
+ */
+
+function navbarcallback(event) {
+ event.stop();
+ current_anchor.setparams(navparams);
+ current_anchor.parse_string($(event.target).getParent().get("href")).update();
+}
+
+function updatelisting(resp) {
+ if ($defined(resp.menu)) {
+ $("menubar").set("html", resp.menu);
+ parse_menubar("menubar");
+ }
+ if ($defined(resp.listing)) {
+ $("listing").set("html", resp.listing);
+ }
+ if ($defined(resp.navbar)) {
+ $("navbar").set("html", resp.navbar);
+ $("navbar").getChildren("a").addEvent("click", navbarcallback);
+ }
+}
+
diff --git a/static/js/form.js b/static/js/form.js
index 15c4af0c3..a3936b17d 100644
--- a/static/js/form.js
+++ b/static/js/form.js
@@ -1,59 +1,84 @@
+var error_callbacks = {
+ mbaliasform: function() {
+ if ($("id_domain").value == 0) {
+ $("id_mboxes").set("html", "");
+ }
+ mbaliasform_init();
+ },
+ permform: function() {
+ if ($defined($("id_domain"))) {
+ $("id_domain").options[0].setProperty("selected", "selected");
+ domadminform_init();
+ }
+ }
+};
+
+var extra_data_callbacks = {
+
+};
+
+function register_callback(hash, name, callback) {
+ if ($defined(hash[name])) {
+ return false;
+ }
+ hash[name] = callback;
+ return true;
+}
+
+function register_error_callback(name, callback) {
+ return register_callback(error_callbacks, name, callback);
+}
+
+function register_extra_data_callback(name, callback) {
+ return register_callback(extra_data_callbacks, name, callback);
+}
+
+function ajax_submit(event) {
+ var form = event.target;
+ var myid = form.get("id");
+ var data = form.toQueryString();
+
+ event.stop();
+ if ($defined(extra_data_callbacks[myid])) {
+ if (data != "") {
+ data += "&";
+ }
+ data += extra_data_callbacks[myid]();
+ }
+
+ new Request({
+ method: form.get("method"),
+ data: data,
+ onSuccess: function(responseText) {
+ var response = JSON.decode(responseText);
+
+ if (response.status == "ko") {
+ $(document.body).set("html", response.content);
+ $(document.body).addEvent("submit", ajax_submit);
+ if ($defined(error_callbacks[myid])) {
+ error_callbacks[myid]();
+ }
+ } else {
+ if ($defined(response.ajaxnav)) {
+ if ($defined(response.url)) {
+ parent.current_anchor.baseurl(response.url, 1);
+ }
+ parent.current_anchor.update(1);
+ } else if ($defined(response.url)) {
+ parent.location.href = response.url;
+ }
+ parent.SqueezeBox.close();
+ }
+ },
+ onFailure: function(xhr) {
+ parent.document.getElement('iframe').setStyles({width: 800, height: 600});
+ $(document.body).set("html", xhr.responseText);
+ }
+ }).send();
+}
+
window.addEvent("domready", function() {
- ajax_submit = function(event, error_callbacks) {
- var form = event.target;
- var myid = form.get("id");
-
- event.stop();
- form.set('send', {
- onSuccess: function(responseText) {
- var response = JSON.decode(responseText);
-
- if (response.status == "ko") {
- $(document.body).set("html", response.content);
- $(document.body).addEvent("submit", default_submit);
- if ($defined(error_callbacks[myid])) {
- error_callbacks[myid]();
- }
- } else {
- if (response.url != "") {
- parent.location.href = response.url;
- }
- parent.SqueezeBox.close();
- }
- },
- onFailure: function(xhr) {
- parent.document.getElement('iframe').setStyles({width: 800, height: 600});
- $(document.body).set("html", xhr.responseText);
- }
- });
- form.send();
- };
-
- default_submit = function(event) {
- var error_callbacks = {
- mbaliasform: function() {
- if ($("id_domain").value == 0) {
- $("id_mboxes").set("html", "");
- }
- mbaliasform_init();
- },
- armform: function() {
- new Calendar({id_untildate: "Y-m-d"}, {
- tweak: {x: -100, y:-100}
- });
- },
- permform: function() {
- if ($defined($("id_domain"))) {
- $("id_domain").options[0].setProperty("selected", "selected");
- domadminform_init();
- }
- }
- };
-
- ajax_submit(event, error_callbacks);
- };
-
- $(document.body).addEvent("submit", default_submit);
+ $(document.body).addEvent("submit", ajax_submit);
$$("input[type!=submit]", "select").addEvents({
focus: function(evt) {
diff --git a/static/pics/collapsed.png b/static/pics/collapsed.png
new file mode 100644
index 000000000..07cc1ec7d
Binary files /dev/null and b/static/pics/collapsed.png differ
diff --git a/static/pics/draft.png b/static/pics/draft.png
new file mode 100644
index 000000000..663cbaddd
Binary files /dev/null and b/static/pics/draft.png differ
diff --git a/static/pics/expanded.png b/static/pics/expanded.png
new file mode 100644
index 000000000..1db079b79
Binary files /dev/null and b/static/pics/expanded.png differ
diff --git a/static/pics/go-down.png b/static/pics/go-down.png
new file mode 100644
index 000000000..adb59b0af
Binary files /dev/null and b/static/pics/go-down.png differ
diff --git a/templates/common/email_listing.html b/templates/common/email_listing.html
index eb23499c1..800326063 100644
--- a/templates/common/email_listing.html
+++ b/templates/common/email_listing.html
@@ -5,6 +5,7 @@
+
{% block css %}{% endblock %}
{% url django.views.i18n.javascript_catalog as catalog_url %}
- {%block html_head %}{% endblock %}
+
+ {% block html_head %}{% endblock %}