wip: Radicale extension

Access rules definition works!
This commit is contained in:
Antoine Nguyen
2014-05-02 16:01:18 +02:00
parent f45f372ba5
commit 7a9df7de99
13 changed files with 154 additions and 67 deletions
+1 -6
View File
@@ -344,15 +344,10 @@ class AccountForm(TabForms):
def __init__(self, request, *args, **kwargs):
self.user = request.user
self.forms = [
<<<<<<< HEAD
dict(id="general", title=_("General"), cls=AccountFormGeneral,
new_args=[self.user], mandatory=True),
=======
dict(id="general", title=_("General"),
formtpl="admin/account_general_form.html",
cls=AccountFormGeneral,
new_args=[user], mandatory=True),
>>>>>>> master
new_args=[self.user], mandatory=True),
dict(id="mail", title=_("Mail"), formtpl="admin/mailform.html",
cls=AccountFormMail),
dict(id="perms", title=_("Permissions"), formtpl="admin/permsform.html",
@@ -143,32 +143,6 @@ def editaccount(request, accountid, tplname="common/tabforms.html"):
events.raiseEvent("FillAccountInstances", request.user, account, instances)
return AccountForm(request, instances=instances).process()
# if request.method == "POST":
# classes = {}
# form = AccountForm(request.user, request.POST,
# instances=instances, classes=classes)
# account.oldgroup = account.group
# if form.is_valid(mandatory_only=True):
# form.save_general_form()
# if form.is_valid(optional_only=True):
# events.raiseEvent("AccountModified", account, form.account)
# form.save()
# return render_to_json_response(_("Account updated"))
# return render_to_json_response({'form_errors': form.errors}, status=400)
# ctx = {
# 'title': account.username,
# 'formid': 'accountform',
# 'action': reverse(editaccount, args=[accountid]),
# 'action_label': _('Update'),
# 'action_classes': 'submit',
# 'tabs': AccountForm(request.user, instances=instances)
# }
# active_tab_id = request.GET.get("active_tab", "default")
# if active_tab_id != "default":
# ctx["tabs"].active_id = active_tab_id
# return render(request, tplname, ctx)
@login_required
@permission_required("core.delete_user")
+79 -8
View File
@@ -3,7 +3,7 @@ Radicale extension forms.
"""
from django import forms
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib.webutils import render_to_json_response
from modoboa.lib.formutils import WizardForm, TabForms, DynamicForm
from modoboa.extensions.radicale.models import UserCalendar, SharedCalendar
@@ -29,9 +29,17 @@ class SharedCalendarForm(forms.ModelForm):
class RightsForm(forms.Form, DynamicForm):
"""
"""
username = forms.CharField(required=False)
read_access = forms.BooleanField(initial=False, label=_("Read"))
write_access = forms.BooleanField(initial=False, label=_("Write"))
username = forms.CharField(
required=False, widget=forms.widgets.TextInput(
attrs={"placeholder": ugettext_lazy("Username")}
)
)
read_access = forms.BooleanField(
initial=False, label=_("Read"), required=False
)
write_access = forms.BooleanField(
initial=False, label=_("Write"), required=False
)
def __init__(self, *args, **kwargs):
from django.http import QueryDict
@@ -43,13 +51,65 @@ class RightsForm(forms.Form, DynamicForm):
self.calendar = None
super(RightsForm, self).__init__(*args, **kwargs)
if self.calendar:
cpt = 1
for rule in self.calendar.rules.all():
self._create_field(
forms.EmailField, "username_%d" % cpt,
rule.mailbox.full_address
)
self._create_field(
forms.BooleanField, "read_access_%d" % cpt, rule.read
)
self._create_field(
forms.BooleanField, "write_access_%d" % cpt, rule.write
)
cpt += 1
if args and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "username", forms.EmailField)
self._load_from_qdict(args[0], "read_access", forms.BooleanField)
self._load_from_qdict(args[0], "write_access", forms.BooleanField)
def _create_field(self, typ, name, value=None, pos=None):
def save(self):
"""Custom save method.
"""
"""
super(RightsForm, self)._create_field(typ, name, value, pos)
import re
from modoboa.lib.emailutils import split_mailbox
from modoboa.lib.exceptions import BadRequest
from modoboa.extensions.admin.models import Mailbox
from .models import AccessRule
usernames = {}
for name, value in self.cleaned_data.iteritems():
if not name.startswith("username") or not value:
continue
res = re.match(r"[^_]+_(\d+)$", name)
pos = int(res.group(1)) if res else None
usernames[value] = pos
for rule in self.calendar.rules.select_related().all():
if not rule.mailbox.full_address in usernames:
rule.delete()
for username, pos in usernames.iteritems():
local_part, domname = split_mailbox(username)
try:
mbox = Mailbox.objects.get(
address=local_part, domain__name=domname
)
except Mailbox.DoesNotExist:
raise BadRequest(_("Mailbox %s does not exist"))
if pos:
raccess = self.cleaned_data.get("read_access_%d" % pos, False)
waccess = self.cleaned_data.get("write_access_%d" % pos, False)
else:
raccess = self.cleaned_data.get("read_access", False)
waccess = self.cleaned_data.get("write_access", False)
acr, created = AccessRule.objects.get_or_create(
mailbox=mbox, calendar=self.calendar
)
acr.read = raccess
acr.write = waccess
acr.save()
class UserCalendarWizard(WizardForm):
@@ -96,7 +156,18 @@ class UserCalendarEditionForm(TabForms):
super(UserCalendarEditionForm, self).__init__(*args, **kwargs)
def extra_context(self, context):
calendar = self.instances["general"]
context.update({
"title": self.instances["general"].name,
"formid": "ucal_form"
"formid": "ucal_form",
"action": reverse("user_calendar", args=[calendar.id])
})
def save(self):
"""Custom save method.
"""
calendar = self.forms[0]["instance"].save()
self.forms[1]["instance"].save()
def done(self):
return render_to_json_response(_("Calendar updated"))
+5 -1
View File
@@ -115,7 +115,11 @@ class AccessRule(models.Model):
mailbox = models.ForeignKey("admin.Mailbox")
read = models.BooleanField(default=False)
write = models.BooleanField(default=False)
calendar = models.ForeignKey(UserCalendar)
calendar = models.ForeignKey(UserCalendar, related_name="rules")
last_update = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('mailbox', 'calendar')
def __str__(self):
access = "r" if self.read else ""
@@ -0,0 +1,12 @@
#original_row {
margin-left: 100px;
}
.access-rule {
margin-top: 5px;
margin-left: 100px;
}
.addrule, .delrule {
margin-left: 10px;
}
@@ -52,11 +52,14 @@
$btn.find("i").removeClass("icon-plus").addClass("icon-trash");
$btn.removeClass("addrule").addClass("delrule");
$clone.find("#id_username")
.attr("id", "id_username_" + this.rules_counter);
.attr("id", "id_username_" + this.rules_counter)
.attr("name", "username_" + this.rules_counter);
$clone.find("#id_read_access")
.attr("id", "id_read_access_" + this.rules_counter);
.attr("id", "id_read_access_" + this.rules_counter)
.attr("name", "read_access_" + this.rules_counter);
$clone.find("#id_write_access")
.attr("id", "id_write_access_" + this.rules_counter);
.attr("id", "id_write_access_" + this.rules_counter)
.attr("name", "write_access_" + this.rules_counter);
$clone.insertAfter(this.$element);
this.rules_counter++;
this.reset_original_rule();
@@ -69,7 +72,7 @@
event.preventDefault();
var $link = get_target(event, "a");
$link.parents("tr").remove();
$link.parent().remove();
}
};
@@ -49,6 +49,13 @@ Radicale.prototype = {
edit_calendar_cb: function() {
$("#original_row").dynamicrule();
$('.submit').on('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
formid: "ucal_form",
reload_on_success: false,
success_cb: $.proxy(this.reload_listing, this)
});
}, this));
},
add_shared_calendar_cb: function() {
@@ -0,0 +1,7 @@
{% load i18n %}
<div class="form-inline access-rule">
{{ username }}
<label class="checkbox">{{ read_access }} {% trans "Read" %}</label>
<label class="checkbox">{{ write_access }} {% trans "Write" %}</label>
<a href="#" class="delrule"><i class="icon-trash"></i></a>
</div>
@@ -1,5 +1,6 @@
{% extends "twocols.html" %}{% load i18n radicale_tags %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}radicale/css/radicale.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.sortable.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/creation_wizard.js"></script>
@@ -1,23 +1,12 @@
{% load lib_tags %}
{% load lib_tags radicale_tags %}
{% if error %}<div class="alert alert-error">{{ error }}</div>{% endif %}
<form action="{{ action }}" method="POST" id="{{ formid }}">
{% csrf_token %}
<table class="table table-condensed">
<thead>
<tr>
<th>{{ form.username.label }}</th>
<th>{{ form.read_access.label }}</th>
<th>{{ form.write_access.label }}</th>
<th></th>
</tr>
</thead>
<tbody>
<tr id="original_row">
<td>{{ form.username }}</td>
<td>{{ form.read_access }}</td>
<td>{{ form.write_access }}</td>
<td><a href="#" class="addrule"><i class="icon-plus"></i></a></td>
</tr>
</tbody>
</table>
<div class="form-inline" id="original_row">
{{ form.username }}
<label class="checkbox">{{ form.read_access }} {{ form.read_access.label }}</label>
<label class="checkbox">{{ form.write_access }} {{ form.write_access.label }}</label>
<a href="#" class="addrule"><i class="icon-plus"></i></a>
</div>
{% render_rule_fields form %}
</form>
@@ -73,3 +73,28 @@ def calendar_actions(calendar):
else:
actions[0]["url"] = reverse("shared_calendar", args=[calendar.id])
return render_actions(actions)
@register.simple_tag
def render_rule_fields(form):
"""Render access rules for a given calendar.
"""
from django.forms import forms
cpt = 1
result = ""
while True:
fname = "username_%d" % cpt
if not fname in form.fields:
break
rfieldname = "read_access_%d" % cpt
wfieldname = "write_access_%d" % cpt
result += render_to_string('radicale/accessrule.html', {
"username": forms.BoundField(form, form.fields[fname], fname),
"read_access": forms.BoundField(
form, form.fields[rfieldname], rfieldname),
"write_access": forms.BoundField(
form, form.fields[wfieldname], wfieldname)
})
cpt += 1
return result
+1 -1
View File
@@ -58,7 +58,7 @@ def user_calendar(request, pk):
ucal = UserCalendar.objects.get(pk=pk)
except UserCalendar.DoesNotExist:
raise NotFound
instances = {"general": ucal, "rights": None}
instances = {"general": ucal, "rights": ucal}
if request.method == "DELETE":
# Check ownership
ucal.delete()
+1 -2
View File
@@ -8,7 +8,6 @@ from django.forms import ChoiceField
from django.forms.widgets import RadioSelect, RadioInput
from django.forms.fields import CharField, Field
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape
@@ -67,7 +66,7 @@ class WizardStep(object):
class WizardForm(object):
"""Custom wizard.
"""
__metaclass__ = abc.ABCMeta
__metaclass__ = abc.ABCMeta
template_name = "common/wizard_forms.html"