From 4fcc5b79110ce77f30d32c54343ab6b34991640a Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Fri, 9 Dec 2011 14:38:56 +0100 Subject: [PATCH] * New "import data" feature (only supports mailboxes for now) * Some javascript cleanup into the admin. application see #191 --- admin/forms.py | 10 +++ admin/models.py | 20 ++++++ admin/templatetags/admin_extras.py | 19 +++++- admin/urls.py | 4 +- admin/views.py | 34 ++++++++++ static/css/default.css | 2 +- static/js/domains.js | 101 +++++++++++++++++----------- static/pics/export.png | Bin 0 -> 580 bytes static/pics/import.png | Bin 0 -> 619 bytes static/pics/tools.png | Bin 0 -> 250 bytes templates/admin/adminform.html | 3 +- templates/admin/domains_header.html | 43 +----------- templates/admin/import.html | 65 ++++++++++++++++++ templates/admin/import_done.html | 11 +++ 14 files changed, 226 insertions(+), 86 deletions(-) create mode 100644 static/pics/export.png create mode 100644 static/pics/import.png create mode 100644 static/pics/tools.png create mode 100644 templates/admin/import.html create mode 100644 templates/admin/import_done.html diff --git a/admin/forms.py b/admin/forms.py index ece5c66f2..9567a7dc7 100644 --- a/admin/forms.py +++ b/admin/forms.py @@ -199,3 +199,13 @@ class DomainAdminForm(forms.Form): forms.ChoiceField(label=_("User"), required=True, choices=[("", _("Empty"))], help_text=_("Select a user in the list")) + +class ImportDataForm(forms.Form): + sourcefile = forms.FileField(label=_("Select a file")) + sepcar = forms.CharField(label=_("Separator"), max_length=1, required=False) + + def clean_sepcar(self): + if self.cleaned_data["sepcar"] == "": + return ";" + return self.cleaned_data["sepcar"] + diff --git a/admin/models.py b/admin/models.py index 1d3279e85..f0895b644 100644 --- a/admin/models.py +++ b/admin/models.py @@ -319,6 +319,26 @@ class Mailbox(DatesAware): self.quota = self.domain.quota super(Mailbox, self).save() + def create_from_csv(self, line): + """Create a new mailbox from a CSV file entry + + The expected order is the following:: + + [loginname, password, first name, last name, address] + + :param line: a list containing the expected information + """ + if len(line) < 5: + raise AdminError(_("Invalid line")) + mailbox, domname = split_mailbox(line[4]) + self.address = mailbox + try: + self.domain = Domain.objects.get(name=domname) + except Domain.DoesNotExist: + raise AdminError(_("Cannot import this mailbox because the associated domain does not exist")) + self.name = "%s %s" % (line[2], line[3]) + self.save(password=line[1], enabled=True) + def delete(self, *args, **kwargs): keepdir = False if kwargs.has_key("keepdir"): diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 296ae16b1..71ba2dd8c 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -93,7 +93,13 @@ def domains_menu(selection, user, domid): else: disabled = False - entries += [{"name" : "new", + entries += [ + {"name" : "actions", + "label" : _("Actions"), + "img" : static_url("pics/tools.png"), + "class" : "menubardropdown", + "menu" : [ + {"name" : "new", "label" : _("New"), "img" : static_url("pics/add.png"), "disabled" : disabled}, @@ -101,7 +107,16 @@ def domains_menu(selection, user, domid): "label" : _("Remove"), "img" : static_url("pics/remove.png"), "disabled" : disabled - }] + }, + {"name" : "import", + "label" : _("Import data"), + "img" : static_url("pics/import.png"), + "class" : "boxed", + "rel" : "{handler:'iframe',size:{x:370,y:250}}", + "url" : reverse(admin.views.importdata)} + ] + }, + ] entries += [{"separator" : True}] if user.has_perm("admin.view_domains"): diff --git a/admin/urls.py b/admin/urls.py index 922d614f3..51c7ebc35 100644 --- a/admin/urls.py +++ b/admin/urls.py @@ -6,6 +6,7 @@ urlpatterns = patterns('modoboa.admin.views', (r'^domains/new/', 'newdomain'), (r'^domains/(?P\d+)/edit/$', 'editdomain'), (r'^domains/delete/$', 'deldomain'), + (r'^domains/import/$', 'importdata'), (r'^domaliases/$', "domaliases"), (r'^domaliases/new/', 'newdomalias'), @@ -25,9 +26,6 @@ urlpatterns = patterns('modoboa.admin.views', (r'^domains/(?P\d+)/rawmboxes/$', 'mailboxes_raw'), - #url(r'^domains/(?P\d+)/aliases/$', "aliases", name="full-aliases"), - #url(r'^domains/(?P\d+)/aliases/(?P\d+)/$', "aliases", name="mbox-aliases"), - (r'^settings/$', 'viewparameters'), (r'^settings/parameters/$', 'viewparameters'), (r'^settings/parameters/save/$', 'saveparameters'), diff --git a/admin/views.py b/admin/views.py index 38ae790d3..a6f39b6d5 100644 --- a/admin/views.py +++ b/admin/views.py @@ -608,3 +608,37 @@ def saveextensions(request): ext.save() messages.info(request, _("Modifications applied."), fail_silently=True) return HttpResponseRedirect(reverse(admin.views.viewextensions)) + +@login_required +@user_passes_test(lambda u: u.is_superuser) +def importdata(request, tplname="admin/import.html"): + if request.method == "POST": + error = None + form = ImportDataForm(request.POST, request.FILES) + if form.is_valid(): + import csv + reader = csv.reader(request.FILES['sourcefile'], + delimiter=form.cleaned_data['sepcar']) + cpt = 0 + try: + for row in reader: + mb = Mailbox() + mb.create_from_csv(row) + cpt += 1 + except AdminError, e: + error = str(e) + else: + messages.info(request, _("%d mailboxes imported successfully" % cpt)) + return _render(request, "admin/import_done.html", { + "status" : "ok", "msg" : "" + }) + + return _render(request, "admin/import_done.html", { + "status" : "ko", "msg" : error + }) + + form = ImportDataForm() + ctx = dict(title=_("Import data"), action="", formid="importform", + enctype="multipart/form-data", form=form, submit_label=_("Import")) + return _render(request, tplname, ctx) + diff --git a/static/css/default.css b/static/css/default.css index b3c827783..ecf374c79 100644 --- a/static/css/default.css +++ b/static/css/default.css @@ -142,7 +142,7 @@ div#footer { } #logo img { - height: 90px; + height: 70px; } #test { diff --git a/static/js/domains.js b/static/js/domains.js index 991d4be06..c757d9f3c 100644 --- a/static/js/domains.js +++ b/static/js/domains.js @@ -1,41 +1,66 @@ -window.addEvent('domready', function() { - var handlers = new Array(); - - if ($defined($("subcontent"))) { - SqueezeBox.assign($("subcontent").getElements('a[class=boxed]'), { - parse: 'rel' +var Domains = new Class({ + initialize: function(rooturl, deloptions) { + this.rooturl = rooturl; + if (deloptions != undefined) { + this.deloptions = deloptions; + } else { + this.deloptions = new Array(); + } + if ($("subcontent") != undefined) { + SqueezeBox.assign($("subcontent").getElements('a[class=boxed]'), { + parse: 'rel' + }); + } + setDivHeight("content", 20, 0); + this.objtable = new HtmlTable($("objects_table"), { + selectable: true }); + + $$("a[name=new]").addEvent("click", this.new_clickcb.bind(this)); + $$("a[name=remove]").addEvent("click", this.remove_clickcb.bind(this)); + + }, + + new_clickcb: function(evt) { + var type = $("objects").get("name"); + var sizes = $("objects").get("rel").split(" "); + + evt.stop(); + SqueezeBox.open(this.rooturl + type + "/new/", { + size: {x: parseInt(sizes[0]), y: parseInt(sizes[1])}, + handler: 'iframe' + }); + }, + + remove_clickcb: function(evt) { + evt.stop(); + if (this.objtable._selectedRows.length == 0) { + return; + } + var type = $("objects").get("name"); + var selection = ""; + + this.objtable._selectedRows.each(function(item) { + var options = JSON.decode(item.get("rel")); + + if (options != undefined && options.ui_disabled != undefined && + options.ui_disabled == "True") { + return; + } + if (selection != "") { + selection += ","; + } + selection += item.get("id"); + }); + if (selection == "") { + return; + } + new Confirmation( + this.rooturl + type + "/delete/", + "selection=" + selection, + gettext('Remove this selection?'), { + checkboxes: this.deloptions + } + ); } - - $(document.body).addEvent('click', function(event) { - var target = event.target; - if (target.get('tag') == 'a') { - var name = target.get('name'); - if (name == 'undefined') - return; - if (typeof(handlers[name]) != 'undefined') { - if (!handlers[name](target, target.get('href'))) { - new Event(event).stop(); - } - } - } - }); - - $(document.body).addEvent('submit', function(event) { - target = event.target; - if (target.get('tag') == 'form') { - name = target.get('name'); - if (name == 'undefined') - return; - if (typeof(handlers[name]) != 'undefined') { - new Event(event).stop(); - handlers[name](target); - } - } - }); - setDivHeight("content", 20, 0); - - objtable = new HtmlTable($("objects_table"), { - selectable: true - }); }); diff --git a/static/pics/export.png b/static/pics/export.png new file mode 100644 index 0000000000000000000000000000000000000000..227a91e5157f4cdbe7152f6fa18fcb065c170563 GIT binary patch literal 580 zcmV-K0=xZ*P)*d*cCcUDOO%d90}t+#`?>Dl!*y}rMdb#8jnu{gkXsV; zpvt63+27v87BizV$y3>0K5f3?l5(Sv==U4a0}&&AAw69lO*-4lzWo=VGzz1p$LgQ% z+1r?Xr!cvX(>K#X^MG!%2vtLy(K`>&AG${m5ATvwyh(bAC&%fH*U;eGLEWUB3)2dm z^Bq!+eYNsruANG8r<6xgh7?}A7)RTLmDrLKF?3QIZVZ&t6e`1=5UIGfX=SZec|{24 z+XJFW2S)!Pw9gdif@SDq+wgwtl!gjfB*Fa6oY35q0C+ zsrAaxX8bq4D8;qVQ8*wlKZJeO!tk7h(T^4!pRG8)SPKLgh2?);UMBG6T>UT6H)L3l SX`GV)0000$ZBe>tk-KY--CK9;-umPBHXOeXL>o`s-xQz{E&~ix z`H~>NUZGe?I<@JoTJ^-V!6zyHQFv)i|ms9 z9`2q{wWHpW+dk%(p^;^LLB+>C+#>&${Xb)W)H&AHPSau6*Z0?})n;zs`?YtO3}Z)6 zx0j}PspXq(yBrx9TAoxz@mNP*o4a?4P{YJk*QH*6{FS6@%4xGihAp3|Jejr0cH_#2 y`(_dh+1-op-rs?zvU}wX;pTvVJS&CUCrH)oQrm{7qg%wt=rK`BXxOT4U0FjT?c# rnsfGR%;0UExx=M%@ezYV*%^C-7}%>f2K)Q~+Qi`L>gTe~DWM4fbR%b_ literal 0 HcmV?d00001 diff --git a/templates/admin/adminform.html b/templates/admin/adminform.html index 2ca1b1d34..ac06a6c27 100644 --- a/templates/admin/adminform.html +++ b/templates/admin/adminform.html @@ -5,7 +5,8 @@ {% block content %}

{{ title }}

{% if error %}
{{ error }}
{% endif %} -
+ {% include "common/genericform.html" %} {% block extrafields %}{% endblock %}
diff --git a/templates/admin/domains_header.html b/templates/admin/domains_header.html index dadee263c..3b831e1f3 100644 --- a/templates/admin/domains_header.html +++ b/templates/admin/domains_header.html @@ -1,50 +1,11 @@ {% extends "nlayout.html" %} {% load i18n admin_extras libextras %} {% block html_head %} - + {% endblock %} diff --git a/templates/admin/import.html b/templates/admin/import.html new file mode 100644 index 000000000..0e721558f --- /dev/null +++ b/templates/admin/import.html @@ -0,0 +1,65 @@ +{% extends "layout_simple.html" %} +{% load i18n %} +{% block html_head %} +{{ block.super }} + + + +{% endblock %} +{% block content %} +

{% trans "Import data" %}

+
{{ error }}
+ + {% include "common/genericform.html" %} +
+
+ +
+
+ + +
+{% endblock %} diff --git a/templates/admin/import_done.html b/templates/admin/import_done.html new file mode 100644 index 000000000..cc7881a74 --- /dev/null +++ b/templates/admin/import_done.html @@ -0,0 +1,11 @@ + + + + + + + +