diff --git a/modoboa/extensions/admin/static/admin/js/admin.js b/modoboa/extensions/admin/static/admin/js/admin.js
index 0ed43c300..04a89070e 100644
--- a/modoboa/extensions/admin/static/admin/js/admin.js
+++ b/modoboa/extensions/admin/static/admin/js/admin.js
@@ -25,7 +25,7 @@ Admin.prototype = {
$(this).val("");
}).blur($.proxy(function(e) {
var $this = $(e.target);
- if ($this.val() == "") {
+ if ($this.val() === "") {
if (this.navobj.getparam("searchquery")) {
$this.val(this.navobj.getparam("searchquery"));
} else {
@@ -33,7 +33,7 @@ Admin.prototype = {
}
}
}, this));
- if (this.navobj.getparam("searchquery") != undefined) {
+ if (this.navobj.getparam("searchquery") !== undefined) {
$("#searchquery").val(this.navobj.getparam("searchquery"));
}
@@ -52,14 +52,14 @@ Admin.prototype = {
register_tag_handler: function(name, handler) {
this.tag_handlers[name] = handler;
- if (this.navobj.getparam(name + "filter") != undefined) {
+ if (this.navobj.getparam(name + "filter") !== undefined) {
var text = this.navobj.getparam(name + "filter");
$("#searchform").parent().after(this.make_tag(text, name));
}
},
generic_tag_handler: function(tag, $link) {
- if (this.navobj.getparam(tag + "filter") == undefined && $link.hasClass(tag)) {
+ if (this.navobj.getparam(tag + "filter") === undefined && $link.hasClass(tag)) {
var text = $link.attr("name");
this.navobj.setparam(tag + "filter", text).update();
$("#searchform").parent().after(this.make_tag(text, tag));
@@ -101,7 +101,7 @@ Admin.prototype = {
do_search: function(e) {
e.preventDefault();
var squery = $("#searchquery").val();
- if (squery != "") {
+ if (squery !== "") {
this.navobj.setparam("searchquery", squery);
} else {
this.navobj.delparam("searchquery");
@@ -112,7 +112,7 @@ Admin.prototype = {
importform_cb: function() {
$(".submit").one('click', function(e) {
e.preventDefault();
- if ($("#id_sourcefile").val() == "") {
+ if ($("#id_sourcefile").val() === "") {
return;
}
$("#import_status").css("display", "block");
@@ -305,13 +305,13 @@ Identities.prototype = {
},
grp_tag_handler: function(tag, $link) {
- if (this.navobj.getparam(tag + "filter") == undefined && $link.hasClass(tag)) {
+ if (this.navobj.getparam(tag + "filter") === undefined && $link.hasClass(tag)) {
var text = $link.attr("name");
this.navobj
.setparam("idtfilter", "account")
.setparam(tag + "filter", text)
.update();
- if ($("a[name=idt]").length == 0) {
+ if ($("a[name=idt]").length === 0) {
$("#searchform").parent().after(this.make_tag("account", "idt"));
}
$("#searchform").parent().after(this.make_tag(text, tag));
@@ -341,13 +341,13 @@ Identities.prototype = {
var $this = $(e.target);
var value = $this.val();
- if (value == "SimpleUsers" || value == "") {
+ if (value == "SimpleUsers" || value === "") {
this.simpleuser_mode();
} else {
this.normal_mode();
}
}, this));
- if (notrigger != undefined && notrigger) {
+ if (notrigger !== undefined && notrigger) {
return;
}
$("#id_role").trigger("change");
@@ -382,7 +382,7 @@ Identities.prototype = {
mailform_prefill: function() {
var $role = $("#id_role");
- if (!$role.length || $role.val() == "" || $role.val() == "SimpleUsers") {
+ if (!$role.length || $role.val() === "" || $role.val() == "SimpleUsers") {
$("#id_email").val($("#id_username").val());
}
},
diff --git a/modoboa/extensions/radicale/app_settings.py b/modoboa/extensions/radicale/app_settings.py
index 82e5783b9..f2ad2b9c4 100644
--- a/modoboa/extensions/radicale/app_settings.py
+++ b/modoboa/extensions/radicale/app_settings.py
@@ -1,8 +1,13 @@
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext_lazy
from django import forms
-from modoboa.lib.parameters import AdminParametersForm, UserParametersForm
-from modoboa.lib.formutils import SeparatorField, YesNoField, InlineRadioSelect
+from modoboa.lib.parameters import AdminParametersForm
class ParametersForm(AdminParametersForm):
app = "radicale"
+
+ rights_file_path = forms.CharField(
+ label=ugettext_lazy("Radicale rights file path"),
+ initial="/etc/radicale/rights",
+ help_text=ugettext_lazy("Path to file that contains rights definition")
+ )
diff --git a/modoboa/extensions/radicale/management/__init__.py b/modoboa/extensions/radicale/management/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/modoboa/extensions/radicale/management/commands/__init__.py b/modoboa/extensions/radicale/management/commands/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/modoboa/extensions/radicale/management/commands/generate_rights.py b/modoboa/extensions/radicale/management/commands/generate_rights.py
new file mode 100644
index 000000000..bf95cdf0f
--- /dev/null
+++ b/modoboa/extensions/radicale/management/commands/generate_rights.py
@@ -0,0 +1,10 @@
+from django.core.management.base import BaseCommand, CommandError
+
+
+class Command(BaseCommand):
+ help = ""
+
+ def handle(self, *args, **options):
+ """Command entry point.
+ """
+ pass
diff --git a/modoboa/extensions/radicale/models.py b/modoboa/extensions/radicale/models.py
index 389d59a48..11d94f259 100644
--- a/modoboa/extensions/radicale/models.py
+++ b/modoboa/extensions/radicale/models.py
@@ -112,6 +112,8 @@ class SharedCalendar(Calendar):
@python_2_unicode_compatible
class AccessRule(models.Model):
+ """Access rules to user calendars
+ """
mailbox = models.ForeignKey("admin.Mailbox")
read = models.BooleanField(default=False)
write = models.BooleanField(default=False)
diff --git a/modoboa/extensions/radicale/static/radicale/js/radicale.js b/modoboa/extensions/radicale/static/radicale/js/radicale.js
index 17a17dea8..4b4070751 100644
--- a/modoboa/extensions/radicale/static/radicale/js/radicale.js
+++ b/modoboa/extensions/radicale/static/radicale/js/radicale.js
@@ -47,8 +47,27 @@ Radicale.prototype = {
});
},
+ /**
+ * Retrieve a list of username from the server.
+ */
+ get_username_list: function() {
+ var result;
+
+ $.ajax({
+ url: this.options.username_list_url,
+ dataType: "json",
+ async: false
+ }).done(function(data) {
+ result = data;
+ });
+ return result;
+ },
+
edit_calendar_cb: function() {
$("#original_row").dynamicrule();
+ $("#id_username").autocompleter({
+ choices: $.proxy(this.get_username_list, this)
+ });
$('.submit').on('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
formid: "ucal_form",
@@ -58,10 +77,10 @@ Radicale.prototype = {
}, this));
},
- add_shared_calendar_cb: function() {
+ shared_calendar_cb: function() {
$('.submit').on('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
- formid: "newsharedcal_form",
+ formid: "sharedcal_form",
reload_on_success: false,
success_cb: $.proxy(this.reload_listing, this)
});
diff --git a/modoboa/extensions/radicale/templates/radicale/calendars.html b/modoboa/extensions/radicale/templates/radicale/calendars.html
index bbdb06597..e554951cc 100644
--- a/modoboa/extensions/radicale/templates/radicale/calendars.html
+++ b/modoboa/extensions/radicale/templates/radicale/calendars.html
@@ -5,11 +5,14 @@
+
{% endblock %}
diff --git a/modoboa/extensions/radicale/templatetags/radicale_tags.py b/modoboa/extensions/radicale/templatetags/radicale_tags.py
index 4bd5810c5..1fd7de54b 100644
--- a/modoboa/extensions/radicale/templatetags/radicale_tags.py
+++ b/modoboa/extensions/radicale/templatetags/radicale_tags.py
@@ -33,7 +33,7 @@ def radicale_left_menu(user):
"label": _("Add shared calendar"),
"img": "icon-plus",
"modal": True,
- "modalcb": "radicale.add_shared_calendar_cb",
+ "modalcb": "radicale.shared_calendar_cb",
"url": reverse("new_shared_calendar")},
]
return render_to_string('common/menulist.html', {
@@ -56,6 +56,7 @@ def calendar_modify_link(calendar):
linkdef["url"] = reverse(
"shared_calendar", args=[calendar.pk]
)
+ linkdef["modalcb"] = "radicale.shared_calendar_cb"
return render_link(linkdef)
diff --git a/modoboa/extensions/radicale/urls.py b/modoboa/extensions/radicale/urls.py
index e7855a63f..03a5d40de 100644
--- a/modoboa/extensions/radicale/urls.py
+++ b/modoboa/extensions/radicale/urls.py
@@ -10,4 +10,5 @@ urlpatterns = patterns(
name='new_shared_calendar'),
url(r'^sharedcalendar/(?P\d+)/$', 'shared_calendar',
name='shared_calendar'),
+ url(r'^usernames/$', 'username_list', name='usernames'),
)
diff --git a/modoboa/extensions/radicale/views.py b/modoboa/extensions/radicale/views.py
index de32cd586..b9bbd63ad 100644
--- a/modoboa/extensions/radicale/views.py
+++ b/modoboa/extensions/radicale/views.py
@@ -6,7 +6,7 @@ from itertools import chain
from django.shortcuts import render
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import (
- login_required
+ login_required, permission_required, user_passes_test
)
from django.utils.translation import ugettext as _
@@ -29,10 +29,13 @@ def index(request):
@login_required
def calendars(request, tplname="radicale/calendar_list.html"):
+ """Display calendars list.
+
+ The content depends on current user's role.
+ """
if request.user.group == "SimpleUsers":
- cals = UserCalendar.objects.filter(
- mailbox=request.user.mailbox_set.all()[0]
- )
+ mbox = request.user.mailbox_set.all()[0]
+ cals = UserCalendar.objects.filter(mailbox=mbox).select_related().all()
else:
cals = chain(
UserCalendar.objects.get_for_admin(request.user),
@@ -47,6 +50,8 @@ def calendars(request, tplname="radicale/calendar_list.html"):
@login_required
def new_user_calendar(request):
+ """Calendar creation view.
+ """
return UserCalendarWizard(request).process()
@@ -67,7 +72,10 @@ def user_calendar(request, pk):
@login_required
+@permission_required("radicale.add_sharedcalendar")
def new_shared_calendar(request):
+ """Shared calendar creation view.
+ """
if request.method == "POST":
form = SharedCalendarForm(request.POST)
if form.is_valid():
@@ -79,7 +87,7 @@ def new_shared_calendar(request):
form = SharedCalendarForm()
return render(request, "common/generic_modal_form.html", {
"form": form,
- "formid": "newsharedcal_form",
+ "formid": "sharedcal_form",
"title": _("New shared calendar"),
"action": reverse("new_shared_calendar"),
"action_classes": "submit",
@@ -88,6 +96,11 @@ def new_shared_calendar(request):
@login_required
+@permission_required("radicale.add_sharedcalendar")
+@user_passes_test(
+ lambda u: u.has_perm("radicale.change_sharedcalendar")
+ or u.has_perm("radicale.delete_sharedcalendar")
+)
def shared_calendar(request, pk):
"""
"""
@@ -100,4 +113,32 @@ def shared_calendar(request, pk):
if request.method == "DELETE":
scal.delete()
return render_to_json_response(_("Calendar removed"))
-
+ if request.method == "POST":
+ form = SharedCalendarForm(request.POST, instance=scal)
+ if form.is_valid():
+ form.save()
+ return render_to_json_response(_("Calendar updated"))
+ return render_to_json_response(
+ {"form_errors": form.errors}, status=400
+ )
+ form = SharedCalendarForm(instance=scal)
+ return render(request, "common/generic_modal_form.html", {
+ "form": form,
+ "formid": "sharedcal_form",
+ "title": scal.name,
+ "action": reverse("shared_calendar", args=[scal.pk]),
+ "action_classes": "submit",
+ "action_label": _("Submit")
+ })
+
+
+@login_required
+def username_list(request):
+ """Get the list of username the current user can see.
+ """
+ from modoboa.extensions.admin.models import Mailbox
+
+ result = []
+ for mb in Mailbox.objects.prefetch_related("user").all():
+ result.append(mb.user.username)
+ return render_to_json_response(result)
diff --git a/modoboa/static/js/autocompleter.js b/modoboa/static/js/autocompleter.js
index c726ba256..0b826cbe8 100644
--- a/modoboa/static/js/autocompleter.js
+++ b/modoboa/static/js/autocompleter.js
@@ -1,4 +1,6 @@
(function($) {
+ "use strict";
+
var Autocompleter = function(element, options) {
this.initialize(element, options);
};
@@ -9,13 +11,10 @@
initialize: function(element, options) {
this.$element = $(element);
this.options = $.extend({}, $.fn.autocompleter.defaults, options);
- this.choices = {
- value: null
- };
if (typeof this.options.choices === "function") {
- this.options.choices(this.choices);
+ this.choices = this.options.choices();
} else {
- this.choices.value = this.options.choices();
+ this.choices = this.options.choices;
}
this.$menu =
$('');
@@ -54,10 +53,11 @@
var exp = new RegExp("^" + pattern);
this.$menu.empty();
- $.each(this.choices.value, $.proxy(function(index, value) {
+ $.each(this.choices, $.proxy(function(index, value) {
if (exp.test(value)) {
- this.$menu.append($('' + value + ''));
+ this.$menu.append(
+ $('' + value + '')
+ );
}
}, this));
@@ -71,6 +71,7 @@
'z-index': 1051
});
if (this.$menu.children().length) {
+ this.$menu.children().first().addClass("active");
this.$menu.show();
} else {
this.$menu.hide();
@@ -84,7 +85,7 @@
select_entry: function(evt) {
if (this.$menu.is(":visible")) {
var $link;
- if (evt != undefined) {
+ if (evt !== undefined) {
evt.preventDefault();
$link = $(evt.target);
} else {
@@ -92,7 +93,7 @@
}
var curvalue = this.$element.val();
- if (curvalue == undefined || curvalue == "") {
+ if (curvalue === undefined || curvalue === "") {
if (this.options.empty_choice) {
this.options.empty_choice();
}
@@ -111,19 +112,18 @@
},
activate_next: function() {
- var active = this.$menu.find('.active').removeClass('active')
- , next = active.next();
+ var active = this.$menu.find('.active').removeClass('active'),
+ next = active.next();
if (!next.length) {
next = $(this.$menu.find('li').first());
}
-
next.addClass('active');
},
activate_prev: function() {
- var active = this.$menu.find('.active').removeClass('active')
- , prev = active.prev();
+ var active = this.$menu.find('.active').removeClass('active'),
+ prev = active.prev();
if (!prev.length) {
prev = this.$menu.find('li').last();
@@ -170,7 +170,7 @@
default:
this.check_user_input();
- };
+ }
}
};
@@ -194,4 +194,4 @@
'empty_choice' : null
};
-})(jQuery);
\ No newline at end of file
+})(jQuery);