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 = $('