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/webmail/lib.py b/extensions/webmail/lib.py
index c5353aacd..2f31177f0 100644
--- a/extensions/webmail/lib.py
+++ b/extensions/webmail/lib.py
@@ -317,6 +317,13 @@ class IMAPconnector(object):
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":
diff --git a/extensions/webmail/static/css/folders.css b/extensions/webmail/static/css/folders.css
index 796f50419..14511c42f 100644
--- a/extensions/webmail/static/css/folders.css
+++ b/extensions/webmail/static/css/folders.css
@@ -31,13 +31,19 @@
.box ul {
list-style: none;
- padding: 5px 5px;
+ padding: 0 0 0 5px;
margin: 0;
+ background-color: #FFFFFF;
+}
+
+.box li ul {
+ padding: 0 0 0 8px;
}
.box li {
- margin-bottom: 4px;
- padding-left: 15px;
+ margin: 0;
+ background-position: 2px 2px;
+ position: relative;
}
.box li.selected {
@@ -45,10 +51,13 @@
}
.box a:link, .box a:visited {
- background: transparent;
color: black;
text-decoration: none;
- margin-left: 5px;
+}
+
+a.block {
+ display: block;
+ padding: 2px 10px 2px 30px;
}
.box a:hover {
@@ -69,24 +78,29 @@ img.aligned {
}
img.footer {
- float: none;
- vertical-align: bottom;
+ float: left;
+ margin-top: 10px;
+ margin-left: 18px;
}
li.inbox {
- background: url("/static/pics/overview.png") no-repeat;
+ background-image: url("/static/pics/overview.png");
+ background-repeat: no-repeat;
}
li.trash {
- background: url("/static/pics/trash.png") no-repeat;
+ background-image: url("/static/pics/trash.png");
+ background-repeat: no-repeat;
}
li.folder {
- background: url("/static/pics/folder.png") no-repeat;
+ background-image: url("/static/pics/folder.png");
+ background-repeat: no-repeat;
}
li.subfolders {
- background: url("/static/pics/subfolders.png") no-repeat;
+ background-image: url("/static/pics/folder.png");
+ background-repeat: no-repeat;
}
ul.hidden {
@@ -96,4 +110,22 @@ ul.hidden {
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 1928e05ac..19acbfa47 100644
--- a/extensions/webmail/static/css/webmail.css
+++ b/extensions/webmail/static/css/webmail.css
@@ -157,7 +157,6 @@ textarea:hover {
float: left;
}
-/* context menu specific */
#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; }
diff --git a/extensions/webmail/static/js/fdmenu.js b/extensions/webmail/static/js/fdmenu.js
index 2c540cae9..f178aa404 100644
--- a/extensions/webmail/static/js/fdmenu.js
+++ b/extensions/webmail/static/js/fdmenu.js
@@ -6,18 +6,17 @@ var FdMenu = new Class({
Implements: [Options],
options: {
- media_url: "",
modify_url: "",
delete_url: ""
},
add_entry: function(name, label, pic, url, onclick) {
var item = new Element("li");
- var picpath = this.options.media_url + "pics/" + pic;
+ var picpath = static_url("pics/" + pic);
this[name] = new Element("a")
.set("html", " " + label)
- .addEvent("click", onclick)
+ .addEvent("click", onclick.bind(this))
.inject(item);
item.inject(this.list);
},
@@ -36,28 +35,36 @@ var FdMenu = new Class({
size: {x: 350, y: 400},
handler: 'iframe'
});
- SqueezeBox.open(this.get("href"));
+ SqueezeBox.open(evt.target.get("href"));
+ $(document.body).fireEvent("click");
});
this.add_entry("delbutton", gettext("Delete folder"), "remove.png",
- this.options.delete_url, function(evt) {
- evt.stop();
- if (!confirm(gettext("Delete folder?"))) {
- return;
- }
- new Request.JSON({
- url: this.get("href"),
- method: "GET",
- onSuccess: function(resp) {
- if (resp.status == "ok") {
- infobox.info(gettext("Folder deleted."));
- var id = current_anchor.update.delay(500, current_anchor, 1);
- } else {
- infobox.error(gettext("Failed to delete folder"));
- infobox.hide(2);
- }
- }
- }).send();
- });
+ 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(".");
+ 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;
@@ -80,7 +87,9 @@ var FdMenu = new Class({
});
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() {
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
index 59d0f3492..912fc79a1 100644
--- a/extensions/webmail/templates/webmail/folder.html
+++ b/extensions/webmail/templates/webmail/folder.html
@@ -3,24 +3,11 @@
{% block html_head %}
+
+
+{{ block.super }}
+
{% endblock %}
{% block js %}
quota_pb = new ProgressBar({
@@ -14,168 +57,12 @@
displayID: "quotatext"
});
- wm_updatelisting = function(resp) {
- window.removeEvents("resize");
- updatelisting(resp);
- if ($defined(resp.folders)) {
- $("folders").set("html", resp.folders);
- $$("li[class*=clickable]").removeEvents();
- SqueezeBox.assign($$('a[class=boxed]'), {
- parse: 'rel'
- });
-
- var fdbuttons = $$("img[class*=footer]");
- var fdmenu = new FdMenu({
- media_url: "{{MEDIA_URL}}",
- modify_url: "{% url extensions.webmail.views.editfolder %}",
- delete_url: "{% url extensions.webmail.views.delfolder %}"
- });
-
- fdbuttons.addEvents({
- click: function(evt) {
- var parent = this.getParent();
- var fdname = parent.getFirst("a[name=loadfolder]").get("href");
-
- fdmenu.toggle(this, fdname);
- evt.stop();
- }
- });
- }
- select($$("a[href=" + current_anchor.getbaseurl() + "]"));
- $$("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);
@@ -183,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 }});
@@ -214,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");
}
@@ -319,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 (!selectedRows.length) {
@@ -380,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 782760d43..92feb0a69 100644
--- a/extensions/webmail/templatetags/webextras.py
+++ b/extensions/webmail/templatetags/webextras.py
@@ -119,34 +119,33 @@ def listing_menu(selection, folder, user):
def print_folders(folders, selected=None, withunseen=False, withmenu=False):
result = ""
- import pprint
- pprint.PrettyPrinter(indent=2).pprint(folders)
- print "END"
-
-
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 += "
" % settings.MEDIA_URL
-
- cssclass = ""
+ 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 += "