mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
* New "import data" feature (only supports mailboxes for now)
* Some javascript cleanup into the admin. application see #191
This commit is contained in:
@@ -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"]
|
||||
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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"):
|
||||
|
||||
+1
-3
@@ -6,6 +6,7 @@ urlpatterns = patterns('modoboa.admin.views',
|
||||
(r'^domains/new/', 'newdomain'),
|
||||
(r'^domains/(?P<dom_id>\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<dom_id>\d+)/rawmboxes/$', 'mailboxes_raw'),
|
||||
|
||||
#url(r'^domains/(?P<dom_id>\d+)/aliases/$', "aliases", name="full-aliases"),
|
||||
#url(r'^domains/(?P<dom_id>\d+)/aliases/(?P<mbox_id>\d+)/$', "aliases", name="mbox-aliases"),
|
||||
|
||||
(r'^settings/$', 'viewparameters'),
|
||||
(r'^settings/parameters/$', 'viewparameters'),
|
||||
(r'^settings/parameters/save/$', 'saveparameters'),
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ div#footer {
|
||||
}
|
||||
|
||||
#logo img {
|
||||
height: 90px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
#test {
|
||||
|
||||
+63
-38
@@ -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
|
||||
});
|
||||
});
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 580 B |
Binary file not shown.
|
After Width: | Height: | Size: 619 B |
Binary file not shown.
|
After Width: | Height: | Size: 250 B |
@@ -5,7 +5,8 @@
|
||||
{% block content %}
|
||||
<div class="title"><h1>{{ title }}</h1></div>
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
<form action="{{ action }}" method="POST" id="{{ formid }}" name="{{ formid }}">
|
||||
<form action="{{ action }}" enctype="{{ enctype }}"
|
||||
method="POST" id="{{ formid }}" name="{{ formid }}">
|
||||
{% include "common/genericform.html" %}
|
||||
{% block extrafields %}{% endblock %}
|
||||
<div class="spacer"></div>
|
||||
|
||||
@@ -1,50 +1,11 @@
|
||||
{% extends "nlayout.html" %}
|
||||
{% load i18n admin_extras libextras %}
|
||||
{% block html_head %}
|
||||
<script src="{{ MEDIA_URL }}js/domains.js" type="text/javascript"></script>
|
||||
<script src="{{ MEDIA_URL }}js/confirmation.js" type="text/javascript"></script>
|
||||
<script src="{{ MEDIA_URL }}js/domains.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
window.addEvent("domready", function() {
|
||||
var rooturl = "{% url index %}";
|
||||
|
||||
$$("a[name=new]").addEvent("click", function(evt) {
|
||||
var type = $("objects").get("name");
|
||||
var sizes = $("objects").get("rel").split(" ");
|
||||
|
||||
evt.stop();
|
||||
SqueezeBox.open(rooturl + type + "/new/", {
|
||||
size: {x: parseInt(sizes[0]), y: parseInt(sizes[1])},
|
||||
handler: 'iframe'
|
||||
});
|
||||
});
|
||||
|
||||
$$("a[name=remove]").addEvent("click", function(evt) {
|
||||
evt.stop();
|
||||
if (objtable._selectedRows.length == 0) {
|
||||
return;
|
||||
}
|
||||
var type = $("objects").get("name");
|
||||
var selection = "";
|
||||
|
||||
objtable._selectedRows.each(function(item) {
|
||||
var options = JSON.decode(item.get("rel"));
|
||||
|
||||
if ($defined(options) && $defined(options.ui_disabled) && options.ui_disabled == "True") {
|
||||
return;
|
||||
}
|
||||
if (selection != "") {
|
||||
selection += ",";
|
||||
}
|
||||
selection += item.get("id");
|
||||
});
|
||||
if (selection == "") {
|
||||
return;
|
||||
}
|
||||
new Confirmation(rooturl + type + "/delete/", "selection=" + selection,
|
||||
"{% trans 'Remove this selection?' %}", {
|
||||
{% if deloptions %}checkboxes: { {% join deloptions %} }{% endif %}
|
||||
});
|
||||
});
|
||||
new Domains("{% url index %}"{% if deloptions %}, { {% join deloptions %} } {% endif %});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
{% extends "layout_simple.html" %}
|
||||
{% load i18n %}
|
||||
{% block html_head %}
|
||||
{{ block.super }}
|
||||
<style>
|
||||
#id_sepcar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
#import_result, #import_status {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#import_result {
|
||||
color: green;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
function import_cb(status, msg) {
|
||||
$("import_status").setStyle("display", "none");
|
||||
$("submit").set("disabled", false);
|
||||
if (status == "ok") {
|
||||
parent.location.reload();
|
||||
parent.SqueezeBox.close();
|
||||
} else {
|
||||
$("import_result").set("html", "");
|
||||
$("error").set("html", msg);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEvent("domready", function() {
|
||||
$("importform").addEvent("submit", function(evt) {
|
||||
if ($("id_sourcefile").value == "") {
|
||||
evt.stop();
|
||||
return;
|
||||
}
|
||||
$("import_status").setStyle("display", "block");
|
||||
$("submit").set("disabled", true);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="title"><h1>{% trans "Import data" %}</h1></div>
|
||||
<div id="error" class="error">{{ error }}</div>
|
||||
<form action="{% url admin.views.importdata %}" enctype="multipart/form-data"
|
||||
target="import_target"
|
||||
method="POST" id="importform" name="importform">
|
||||
{% include "common/genericform.html" %}
|
||||
<div class="spacer"></div>
|
||||
<div class="lastrow" align="center">
|
||||
<input type="submit" id="submit" name="submit" value="{% trans 'Import' %}" />
|
||||
</div>
|
||||
</form>
|
||||
<iframe id="import_target" name="import_target" src="#"
|
||||
style="width:0;height:0;border:0px solid #fff;"></iframe>
|
||||
<div id="import_status" style="display:none">
|
||||
<img src="{{MEDIA_URL}}css/spinner.gif" /> {% trans "Importing..." %}
|
||||
</div>
|
||||
<div id="import_result"></div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head profile="http://www.w3.org/2005/10/profile">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<script type="text/javascript">
|
||||
parent.import_cb("{{ status }}", "{{ msg }}");
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user