wip: radicale extension.

This commit is contained in:
Antoine Nguyen
2014-06-06 09:04:59 +02:00
parent ad94dc937b
commit ef9319beb4
12 changed files with 123 additions and 41 deletions
@@ -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());
}
},
+8 -3
View File
@@ -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")
)
@@ -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
+2
View File
@@ -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)
@@ -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)
});
@@ -5,11 +5,14 @@
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/creation_wizard.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/listing.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/autocompleter.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}radicale/js/jquery.dynamicrule.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}radicale/js/radicale.js"></script>
<script type="text/javascript">
$(document).ready(function() {
radicale = new Radicale({});
radicale = new Radicale({
"username_list_url": "{% url 'usernames' %}"
});
});
</script>
{% endblock %}
@@ -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)
+1
View File
@@ -10,4 +10,5 @@ urlpatterns = patterns(
name='new_shared_calendar'),
url(r'^sharedcalendar/(?P<pk>\d+)/$', 'shared_calendar',
name='shared_calendar'),
url(r'^usernames/$', 'username_list', name='usernames'),
)
+47 -6
View File
@@ -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)
+17 -17
View File
@@ -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 =
$('<ul class="dropdown-menu autocompleter" data-toggle="dropdown" />');
@@ -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($('<li><a href="#" name="' + value
+ '">' + value + '</a></li>'));
this.$menu.append(
$('<li><a href="#" name="' + value + '">' + value + '</a></li>')
);
}
}, 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);
})(jQuery);