diff --git a/extensions/webmail/imaputils.py b/extensions/webmail/imaputils.py index 232dd6d63..1fc972598 100644 --- a/extensions/webmail/imaputils.py +++ b/extensions/webmail/imaputils.py @@ -356,7 +356,7 @@ class IMAPconnector(object): return sorted(result, key=itemgetter("name")) @capability('LIST-EXTENDED', '_listfolders_simple') - def _listmboxes(self, topmailbox='', mailboxes=[]): + def _listmboxes(self, topmailbox='', mailboxes=[], until_mailbox=None): pattern = ("%s.%%" % topmailbox.encode("imap4-utf-7")) if len(topmailbox) else "%" resp = self._cmd("LIST", "", pattern, "RETURN", "(CHILDREN)") newmboxes = [] @@ -381,10 +381,25 @@ class IMAPconnector(object): if r'\HasChildren' in flags: descr["path"] = name descr["sub"] = [] + if until_mailbox and until_mailbox.startswith(name): + self._listmboxes(name, descr["sub"], until_mailbox) + from operator import itemgetter mailboxes += sorted(newmboxes, key=itemgetter("name")) - def getmboxes(self, user, topmailbox='', unseen_messages=True): + def getmboxes(self, user, topmailbox='', until_mailbox=None, unseen_messages=True): + """Returns a list of mailboxes for a particular user + + By default, only the first level of mailboxes under + ``topmailbox`` is returned. If ``until_mailbox`` is specified, + all levels needed to access this mailbox will be returned. + + :param user: a ``User`` instance + :param topmailbox: the mailbox where to start in the tree + :param until_mailbox: the deepest needed mailbox + :param unseen_messages: include unseen messages counters or not + :return: a list + """ if len(topmailbox): md_mailboxes = [] else: @@ -395,7 +410,11 @@ class IMAPconnector(object): {"name" : parameters.get_user(user, "SENT_FOLDER")}, {"name" : parameters.get_user(user, "TRASH_FOLDER"), "class" : "icon-trash"}] - self._listmboxes(topmailbox, md_mailboxes) + if until_mailbox: + name, parent = separate_mailbox(until_mailbox) + if parent: + until_mailbox = parent + self._listmboxes(topmailbox, md_mailboxes, until_mailbox) if unseen_messages: for mb in md_mailboxes: @@ -577,6 +596,19 @@ class IMAPconnector(object): ) return data[int(mailid)] +def separate_mailbox(fullname, sep="."): + """Split a mailbox name + + If a separator is found in ``fullname``, this function returns the + corresponding name and parent mailbox 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 + def get_imapconnector(request): """Simple shortcut to create a connector diff --git a/extensions/webmail/static/js/webmail.js b/extensions/webmail/static/js/webmail.js index 622cf2b98..e2236b000 100644 --- a/extensions/webmail/static/js/webmail.js +++ b/extensions/webmail/static/js/webmail.js @@ -84,6 +84,8 @@ Webmail.prototype = { this.select_parent_mailbox), $(document).on("click", "div[class*=clickbox]", $.proxy(this.mbox_state_callback, this)); + $(document).on("click", "a[name=newmbox]", + $.proxy(this.new_mailbox, this)); $(document).on("click", "a[name=editmbox]", $.proxy(this.edit_mbox, this)); $(document).on("click", "a[name=removembox]", @@ -133,11 +135,10 @@ Webmail.prototype = { * Simple helper to retrieve the currently selected mailbox. */ get_current_mailbox: function() { - if (this.navobject.params.action != "listmailbox") { - return null; + if (this.navobject.getparam("action") != "listmailbox") { + return undefined; } - return (this.navobject.params.name) - ? this.navobject.params.name : "INBOX"; + return this.navobject.getparam("name", "INBOX"); }, /* @@ -256,6 +257,42 @@ Webmail.prototype = { } }, + /* + * Inject a new *clickbox* somewhere in the tree + */ + inject_clickbox: function($container) { + $container.prepend($("
", {'class' : 'clickbox collapsed'})); + }, + + /* + * Injects a single mailbox somewhere in the tree + */ + inject_mailbox: function($parent, mailbox, linkname, unseen) { + var $li = $("
  • ", { + name: mailbox, + 'class': "droppable" + }); + var $link = $("", { + name: linkname, + 'class': "block", + href: mailbox + }); + var parts = mailbox.split("."); + var linkcontent = " "; + var displayname = linkcontent + parts[parts.length - 1]; + + $li.append($link); + $parent.append($li); + + if (unseen != undefined) { + $link.addClass("unseen"); + $link.html(displayname + " (" + unseen + ")"); + this.unseen_counters[$link.attr("href")] = unseen; + } else { + $link.html(displayname); + } + }, + /* * Inject new mailboxes under a given parent in the tree. */ @@ -267,30 +304,16 @@ Webmail.prototype = { var $plink = $parent.children("a"); $parent.append($ul); + if (!$parent.children("div").length) { + this.inject_clickbox($parent); + } for (var i = 0; i < mboxes.length; i++) { - var mbname = mboxes[i].name; - var $li = $("
  • ", { - name: mbname, - 'class': "droppable" - }); - var $link = $("", { - name: $plink.attr("name"), - 'class': "block", - href: mbname - }); - var parts = mbname.split("."); - var linkcontent = " "; - var displayname = linkcontent + parts[parts.length - 1]; - - $li.append($link); - $ul.append($li); - - if (mboxes[i].unseen != undefined) { - $link.addClass("unseen"); - $link.html(displayname + " (" + mboxes[i].unseen + ")"); - this.unseen_counters[$link.attr("href")] = mboxes[i].unseen; - } else { - $link.html(displayname); + if ($parent.find('li[name="' + mboxes[i].name + '"]').length) { + continue; + } + this.inject_mailbox($ul, mboxes[i].name, $plink.attr("name"), mboxes[i].unseen); + if (mboxes[i].sub != undefined) { + this.inject_clickbox($('li[name="' + mboxes[i].name + '"]')); } } this.init_droppables($ul.find(".droppable"), true); @@ -300,10 +323,14 @@ Webmail.prototype = { /* * Download sub-mailboxes from the server */ - get_mailboxes: function(parent) { + get_mailboxes: function(parent, async) { + if (async === undefined) { + async = true; + } $.ajax({ url: this.options.submboxes_url, dataType: 'json', + async: async, data: "topmailbox=" + parent.attr("name"), success: $.proxy(function(data) { if (data.status == "ko") { @@ -341,29 +368,58 @@ Webmail.prototype = { }, /* - * Select a particular mailbox + * Unselect every selected mailbox + */ + reset_mb_selection: function() { + $("a[name=loadfolder]").parent().removeClass("active").addClass("droppable"); + }, + + /* + * Select a particular mailbox (one already present in the DOM) */ select_mailbox: function(obj) { - var $obj = $(obj); + var $obj = (typeof obj != "string") ? $(obj) : $('a[href="' + obj + '"]'); - $("a[name=loadfolder]").each(function() { - var $this = $(this); - $this.parent().removeClass("active"); - $this.parent().addClass("droppable"); - }); + this.reset_mb_selection(); + $obj.parents("ul").addClass("visible"); $obj.parent().removeClass("droppable"); $obj.parent().addClass("active"); + }, - // $$(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" - // ); - // }); - // }); + /* + * Tries to select a particular sub-mailbox and loads it from the + * server if it's not present in the DOM. (nb: all parents needed + * to access this mailbox are also loaded) + */ + load_and_select_mailbox: function(mailbox) { + if (mailbox.indexOf(".") == -1) { + this.select_mailbox(mailbox); + return; + } + this.reset_mb_selection(); + + var parts = mailbox.split("."); + var curmb = parts[0], lastmb = ""; + + for (var i = 1; i < parts.length; i++) { + lastmb = curmb; + curmb += "." + parts[i]; + + var $link = $('a[href="' + curmb + '"]'); + var $container = $('li[name="' + lastmb + '"]'); + + if ($link.length) { + if ($container.children("div").hasClass("collapsed")) { + this.toggle_mbox_state($container.children("div"), $container.children("ul")); + } + continue; + } + + this.get_mailboxes($container, false); + $container.children("div").addClass("expanded"); + $container.children("ul").addClass("visible"); + } + $('li[name="' + mailbox + '"]').addClass("active"); }, select_parent_mailbox: function(e) { @@ -372,11 +428,17 @@ Webmail.prototype = { $(this).parent().addClass("active"); }, + new_mailbox: function(e) { + this.poller.pause(); + }, + /* * Edit the currently selected mailbox. Opens a modal box that * permits to change mailbox's name and more. */ edit_mbox: function(e) { + this.poller.pause(); + var $link = get_target(e, "a"); if ($link.hasClass("disabled")) { e.preventDefault(); @@ -385,7 +447,7 @@ Webmail.prototype = { var $selected = $("#folders li.active").children("a"); modalbox(e, undefined, $link.attr("href") + "?name=" + $selected.attr("href"), - $.proxy(this.mboxform_cb, this)); + $.proxy(this.mboxform_cb, this), $.proxy(this.mboxform_close, this)); }, /* @@ -393,13 +455,13 @@ Webmail.prototype = { */ remove_mbox: function(e) { var $link = get_target(e, "a"); - e.preventDefault(); if ($link.hasClass("disabled")) return; if (!confirm(gettext("Remove the selected mailbox?"))) { return; } + this.poller.pause(); var $selected = $("#folders li.active").children("a"); $.ajax({ @@ -407,12 +469,9 @@ Webmail.prototype = { dataType: 'json', success: $.proxy(function(data) { if (data.status == "ok") { + this.remove_mbox_from_tree(this.navobject.getparam("name")); + this.poller.resume(); $("body").notify("success", gettext("Mailbox removed"), 2000); - if (this.navobject.getparam("action") == "listmailbox") { - $('li[name="' + this.navobject.getparam("name") + '"]').remove(); - this.select_mailbox($("a[href=INBOX]")); - this.navobject.setparam("name", "INBOX").update(); - } } else { $("body").notify("error", data.respmsg); } @@ -420,6 +479,54 @@ Webmail.prototype = { }); }, + /* + * Remove a mailbox from the tree. + * + * It is a client-side action, no request is sent to the server. + */ + remove_mbox_from_tree: function(mailbox) { + var $container = (typeof mailbox === "string") ? $('li[name="' + mailbox + '"]') : mailbox; + var $parent = $container.parent("ul"); + + if ($parent.children("li").length == 1) { + $parent.siblings("div").remove(); + $parent.remove(); + } else { + $container.remove(); + } + if (this.navobject.getparam("action") == "listmailbox") { + this.select_mailbox($("a[href=INBOX]")); + this.navobject.setparam("name", "INBOX").update(); + } + }, + + /* + * Rename a mailbox (client-side) + * If needed, the mailbox will be moved to its new location. + */ + rename_mailbox: function(oldname, newname, oldparent, newparent) { + var pattern = (oldparent) ? oldparent + "." + oldname : oldname; + var $link = $("#folders").find('a[href="' + pattern + '"]'); + + if (oldname != newname) { + var $i = $link.children("i"); + + $link.html(newname); + $link.parent("li").attr("name", pattern); + $link.prepend($i); + $link.attr("href", pattern); + } + if (oldparent != newparent) { + var newlocation = (newparent != "") ? newparent + "." + newname : newname; + this.remove_mbox_from_tree($link.parent("li")); + if (this.navobject.getparam("action") == "listmailbox") { + this.navobject.setparam("name", newlocation).update(); + } else { + this.select_mailbox("INBOX"); + } + } + }, + send_mark_request: function(e) { e.preventDefault(); if (!this.htmltable.current_selection().length) { @@ -487,13 +594,15 @@ Webmail.prototype = { * mailbox. (activated when clicking on a mailbox's name) */ _listmailbox_loader: function(event, obj) { - event.preventDefault(); + if (event) { + event.preventDefault(); + } if (obj === undefined) { obj = $(event.target); } this.navobject.reset().setparams({ action: "listmailbox", - name: gethref(obj) + name: obj.attr("href") }).update(); }, @@ -573,6 +682,9 @@ Webmail.prototype = { //window.removeEvents("resize"); var curmb = this.get_current_mailbox(); + if (!$('li[name="' + curmb + '"]').hasClass("active")) { + this.load_and_select_mailbox(curmb); + } this.enable_mb_actions(); for (var idx in this.options.ro_mboxes) { if (curmb == this.options.ro_mboxes[idx]) { @@ -617,7 +729,7 @@ Webmail.prototype = { $("a[name=sendmail]").attr("disabled", null); return; } - history.go(-2); + this._listmailbox_loader(null, $("#folders").find("li[class*=active]").children("a")); $("body").notify("success", gettext("Message sent"), 2000); }, @@ -654,20 +766,62 @@ Webmail.prototype = { }, /* - * Mailbox form + * Mailbox form initialization */ mboxform_cb: function() { + $("#mboxform").find("input").keypress(function(e) { + if (e.which == 13) e.preventDefault(); + }); $(".submit").one('click', $.proxy(function(e) { var $link = $("#folders2 li.active").children("a"); simple_ajax_form_post(e, { - formid: "mboxform", - error_cb: this.mboxform_cb, - extradata: ($link.length) ? "parent_folder=" + $link.attr("href") : "" + reload_on_success: false, + formid: "mboxform", + error_cb: this.mboxform_cb, + extradata: ($link.length) ? "parent_folder=" + $link.attr("href") : "", + success_cb: $.proxy(this.mboxform_success, this) }); }, this)); }, + mboxform_success: function(data) { + if (data.oldmb === undefined) { + var $parent; + + if (data.parent) { + $parent = $("#folders").find('li[name="' + data.parent + '"]'); + if (!$parent.length) { + return; + } + if (!$parent.children("div").length) { + this.inject_clickbox($parent); + } + var $ul = $parent.children("ul"); + if (!$ul.length) { + return; + } + $parent = $ul; + } else { + $parent = $("#mboxes_container").children("ul"); + } + this.inject_mailbox($parent, data.newmb, "loadfolder"); + } else if (data.newmb) { + this.rename_mailbox(data.oldmb, data.newmb, data.oldparent, data.newparent); + } + this.poller.resume(); + }, + + /* + * Just a simple 'onclose' callback to check if the poller has + * been resumed (useful when the user has cancelled the action) + */ + mboxform_close: function() { + if (this.poller.paused) { + this.poller.resume(); + } + }, + /* * Attachments form */ diff --git a/extensions/webmail/templates/webmail/folder.html b/extensions/webmail/templates/webmail/folder.html index eea27a94d..717997f59 100644 --- a/extensions/webmail/templates/webmail/folder.html +++ b/extensions/webmail/templates/webmail/folder.html @@ -4,7 +4,7 @@
    {% endblock %} diff --git a/extensions/webmail/templates/webmail/folders.html b/extensions/webmail/templates/webmail/folders.html index a3a5136b2..a25c3389f 100644 --- a/extensions/webmail/templates/webmail/folders.html +++ b/extensions/webmail/templates/webmail/folders.html @@ -1,8 +1,8 @@ {% load i18n webextras %}
    {% mboxes_menu %}
    diff --git a/extensions/webmail/templatetags/webextras.py b/extensions/webmail/templatetags/webextras.py index 721e32c19..355c3fd53 100644 --- a/extensions/webmail/templatetags/webextras.py +++ b/extensions/webmail/templatetags/webextras.py @@ -5,6 +5,7 @@ from django.core.urlresolvers import reverse from django.conf import settings from modoboa.extensions import webmail from modoboa.extensions.webmail.lib import IMAPheader +from modoboa.extensions.webmail.imaputils import separate_mailbox from modoboa.lib import parameters from modoboa.lib.webutils import static_url @@ -118,22 +119,23 @@ def listmailbox_menu(selection, folder, user): )) @register.simple_tag -def print_folders(folders, selected=None, withunseen=False, selectonly=False): +def print_mailboxes(tree, selected=None, withunseen=False, selectonly=False): """Display a tree of mailboxes and sub-mailboxes - + :param tree: the mailboxes to display """ result = "" - for fd in folders: + for mbox in tree: cssclass = "" - name = fd["path"] if fd.has_key("sub") else fd["name"] - label = fd["name"] + name = mbox["path"] if mbox.has_key("sub") else mbox["name"] + label = separate_mailbox(mbox["name"])[0] if selected == name: cssclass = "active" result += "
  • \n" % (name, cssclass) - if fd.has_key("sub"): + if mbox.has_key("sub"): if selected is not None and selected != name and selected.count(name): + print "%s is visible" % name ul_state = "visible" div_state = "expanded" else: @@ -143,20 +145,20 @@ def print_folders(folders, selected=None, withunseen=False, selectonly=False): cssclass = "block" extra_attrs = "" - if withunseen and fd.has_key("unseen"): - label += " (%d)" % fd["unseen"] + if withunseen and mbox.has_key("unseen"): + label += " (%d)" % mbox["unseen"] cssclass += " unseen" - extra_attrs = ' data-toggle="%d"' % fd["unseen"] - iclass = fd["class"] if fd.has_key("class") else "icon-folder-close" + extra_attrs = ' data-toggle="%d"' % mbox["unseen"] + iclass = mbox["class"] if mbox.has_key("class") else "icon-folder-close" result += """ %s -""" % (fd.has_key("path") and fd["path"] or fd["name"], cssclass, +""" % (mbox.has_key("path") and mbox["path"] or mbox["name"], cssclass, 'selectfolder' if selectonly else 'loadfolder', extra_attrs, iclass, label) - if fd.has_key("sub") and len(fd["sub"]): - result += "\n" + if mbox.has_key("sub") and len(mbox["sub"]): + result += "\n" result += "
  • \n" return result @@ -169,6 +171,7 @@ def mboxes_menu(): "title" : _("Create a new mailbox"), "modal" : True, "modalcb" : "webmail.mboxform_cb", + "closecb" : "webmail.mboxform_close", "class" : "btn btn-mini"}, {"name" : "editmbox", "url" : reverse(webmail.views.editfolder), diff --git a/extensions/webmail/views.py b/extensions/webmail/views.py index 04d102a6f..e85cc11a2 100644 --- a/extensions/webmail/views.py +++ b/extensions/webmail/views.py @@ -229,39 +229,28 @@ def compact(request, name): imapc.compact(name) return ajax_simple_response(dict(status="ok")) -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 @needs_mailbox() def newfolder(request, tplname="webmail/folder.html"): mbc = IMAPconnector(user=request.user.username, password=request.session["password"]) - ctx = {"title" : _("Create a new folder"), + ctx = {"title" : _("Create a new mailbox"), "formid" : "mboxform", - "action" : reverse(newfolder), + "action" : reverse(newfolder), "action_label" : _("Create"), "action_classes" : "submit", "withunseen" : False, "selectonly" : True} - ctx["folders"] = mbc.getmboxes(request.user) + ctx["mboxes"] = mbc.getmboxes(request.user) if request.method == "POST": form = FolderForm(request.POST) if form.is_valid(): pf = request.POST.get("parent_folder", None) mbc.create_folder(form.cleaned_data["name"], pf) - return ajax_response(request, ajaxnav=True, respmsg=_("Folder created")) + return ajax_simple_response(dict( + status="ok", respmsg=_("Mailbox created"), newmb=form.cleaned_data["name"], parent=pf + )) ctx["form"] = form ctx["selected"] = None @@ -283,34 +272,39 @@ def editfolder(request, tplname="webmail/folder.html"): "action_classes" : "submit", "withunseen" : False, "selectonly" : True} - ctx["folders"] = mbc.getmboxes(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"]) - extra = {} + oldname, oldparent = separate_mailbox(request.POST["oldname"]) + res = dict(status="ok", respmsg=_("Mailbox updated")) 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"]) mbc.rename_folder(request.POST["oldname"], newname) - extra["url"] = newname - return ajax_response(request, ajaxnav=True, - respmsg=_("Folder modified"), **extra) + res["oldmb"] = oldname + res["newmb"] = form.cleaned_data["name"] + res["oldparent"] = oldparent + res["newparent"] = pf + del request.session["mbox"] + return ajax_simple_response(res) + ctx["mboxes"] = mbc.getmboxes(request.user) ctx["form"] = form return ajax_response(request, status="ko", template=tplname, **ctx) name = request.GET.get("name", None) if name is None: raise WebmailError(_("Invalid request")) - shortname, parent = separate_folder(name) + shortname, parent = separate_mailbox(name) + ctx["mboxes"] = mbc.getmboxes(request.user, until_mailbox=parent) ctx["form"] = FolderForm() - ctx["form"].fields["oldname"].initial = shortname - ctx["form"].fields["name"].initial = name + ctx["form"].fields["oldname"].initial = name + ctx["form"].fields["name"].initial = shortname ctx["selected"] = parent return _render(request, tplname, ctx) @@ -323,6 +317,8 @@ def delfolder(request): mbc = IMAPconnector(user=request.user.username, password=request.session["password"]) mbc.delete_folder(name) + if request.session.has_key("mbox"): + del request.session["mbox"] return ajax_response(request) @login_required @@ -404,7 +400,7 @@ def render_mboxes_list(request, imapc): curmbox = request.session.get("mbox", "INBOX") return _render_to_string(request, "webmail/folders.html", { "selected" : curmbox, - "folders" : imapc.getmboxes(request.user), + "mboxes" : imapc.getmboxes(request.user), "withunseen" : True }) @@ -438,13 +434,11 @@ def listmailbox(request, defmailbox="INBOX"): :return: a dictionnary """ mbox = request.GET.get("name", defmailbox) - request.session["mbox"] = mbox set_nav_params(request) - lst = ImapListing(request.user, request.session["password"], baseurl="?action=listmailbox&name=%s&" % mbox, folder=mbox, **request.session["navparams"]) - + request.session["mbox"] = mbox return lst.render(request, request.session["pageid"]) def render_compose(request, form, posturl, email=None, insert_signature=False): diff --git a/lib/templatetags/libextras.py b/lib/templatetags/libextras.py index 7418d2d56..40330c6cd 100644 --- a/lib/templatetags/libextras.py +++ b/lib/templatetags/libextras.py @@ -140,6 +140,7 @@ def render_link(linkdef, mdclass=None): t = Template(""" {% if link.img %}{% endif %} diff --git a/static/js/common.js b/static/js/common.js index 6a7074e40..5bdfed32b 100644 --- a/static/js/common.js +++ b/static/js/common.js @@ -3,30 +3,6 @@ $(document).ready(function() { $(document).on('click', 'a[data-toggle="ajaxmodal-autowidth"]', modalbox_autowidth); $(document).on('click', 'a[name=sendspam]', simple_ajax_request); $(document).on('click', 'a[name=sendvirus]', simple_ajax_request); - - //parse_menubar('#topmenubar'); - //parse_menubar('#menubar'); - - /*infobox = new InfoBox({ - parent : $("menubar"), - message : "Loading" - });*/ - - /* Specific javascript code for the demo... not the best place */ - /*$$("a[name=sendspam], a[name=sendvirus]").addEvent("click", function(evt) { - evt.stop(); - new Request.JSON({ - url: this.get("href"), - onSuccess: function(response) { - if (response.status == "ok") { - infobox.info(gettext("Message sent")); - infobox.hide(1); - } else { - infobox.error(response.respmsg); - } - } - }).get(); - });*/ }); var media_url = ""; diff --git a/static/js/history.js b/static/js/history.js index 76a788379..27056fbbb 100644 --- a/static/js/history.js +++ b/static/js/history.js @@ -97,7 +97,13 @@ History.prototype = { var res = this.base; if (this.paramslength() != 0) { - res += "?" + $.param(this.params); + res += "?"; + $.each(this.params, function(key, value) { + if (res != "?") { + res += "&"; + } + res += key + "=" + encodeURIComponent(value); + }); } return res; }, @@ -144,9 +150,9 @@ History.prototype = { return this; }, - getparam: function(name) { + getparam: function(name, defvalue) { if (this.params[name] === undefined) { - return ""; + return (defvalue != undefined) ? defvalue : undefined; } return this.params[name]; }, @@ -201,7 +207,7 @@ History.prototype = { return; } delete(this.force); - this.from_string(location.hash); + this.from_string(decodeURIComponent(location.hash)); if (!this.serialized) { location.hash = this.options.deflocation; return; @@ -222,6 +228,10 @@ History.prototype = { } catch (e) { resp = { status: 'ko', respmsg: gettext('Internal Error') }; } + if (resp.status == "ko") { + $("body").notify("error", resp.respmsg); + return; + } var callback = (resp.callback != undefined) ? resp.callback : "default"; this.callbacks[callback](resp); }, this) diff --git a/static/js/poller.js b/static/js/poller.js index b715ca333..7beba47ea 100644 --- a/static/js/poller.js +++ b/static/js/poller.js @@ -20,11 +20,30 @@ Poller.prototype = { this.url = url; this.options = $.extend({}, this.defaults, options); this.running_request = false; + this.paused = false; this.reset(); }, reset: function() { - setTimeout($.proxy(this.send_request, this), this.options.interval); + if (!this.paused) { + this.tid = setTimeout($.proxy(this.send_request, this), this.options.interval); + } + }, + + pause: function() { + this.paused = true; + if (!this.tid) { + return; + } + clearTimeout(this.tid); + }, + + resume: function() { + if (!this.paused) { + return; + } + this.paused = false; + this.reset(); }, send_request: function() { diff --git a/static/js/utils.js b/static/js/utils.js index a227fccfe..ee5806efd 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -2,10 +2,12 @@ * Collection of functions that are accessible to every page. */ -function modalbox(e, css, defhref, defcb) { +function modalbox(e, css, defhref, defcb, defclosecb) { e.preventDefault(); - var href = (defhref != undefined) ? defhref : $(this).attr('href'); - var modalcb = (defcb != undefined) ? defcb : $(this).attr('modalcb'); + var $this = $(this); + var href = (defhref != undefined) ? defhref : $this.attr('href'); + var modalcb = (defcb != undefined) ? defcb : $this.attr('modalcb'); + var closecb = (defclosecb != undefined) ? defclosecb : $this.attr("closecb"); if (href.indexOf('#') == 0) { $(href).modal('open'); @@ -21,15 +23,14 @@ function modalbox(e, css, defhref, defcb) { var $div = $('') .one('shown', function() { if (modalcb != undefined) { - if (typeof modalcb === "function") { - modalcb(); - } else { - eval(modalcb + '()'); - } + if (typeof modalcb === "function") modalcb(); else eval(modalcb + '()'); } }) .on('hidden', function() { $("#modalbox").remove(); + if (closecb != undefined) { + if (typeof closecb === "function") closecb(); else eval(closecb + '()'); + } }) .modal();