manager: custom fields edition (#63285)
This commit is contained in:
parent
4cfeb33d63
commit
597d88cce7
|
@ -1837,6 +1837,22 @@ class EventsType(models.Model):
|
|||
def base_slug(self):
|
||||
return slugify(self.label)
|
||||
|
||||
def get_custom_fields(self):
|
||||
custom_fields = []
|
||||
if not isinstance(self.custom_fields, list):
|
||||
return custom_fields
|
||||
for values in self.custom_fields:
|
||||
if not isinstance(values, dict):
|
||||
continue
|
||||
complete = True
|
||||
for k in ['varname', 'label', 'field_type']:
|
||||
if not values.get(k):
|
||||
complete = False
|
||||
break
|
||||
if complete:
|
||||
custom_fields.append(values)
|
||||
return custom_fields
|
||||
|
||||
|
||||
class BookingColor(models.Model):
|
||||
COLOR_COUNT = 8
|
||||
|
|
|
@ -29,7 +29,7 @@ from django.contrib.auth.models import Group
|
|||
from django.core.exceptions import FieldDoesNotExist
|
||||
from django.db import transaction
|
||||
from django.db.models import DurationField, ExpressionWrapper, F
|
||||
from django.forms import ValidationError
|
||||
from django.forms import ValidationError, formset_factory
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.six import StringIO
|
||||
|
@ -48,6 +48,7 @@ from chrono.agendas.models import (
|
|||
Booking,
|
||||
Desk,
|
||||
Event,
|
||||
EventsType,
|
||||
MeetingType,
|
||||
Person,
|
||||
Resource,
|
||||
|
@ -330,6 +331,40 @@ class EventForm(NewEventForm):
|
|||
return self.instance
|
||||
|
||||
|
||||
class EventsTypeForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = EventsType
|
||||
fields = ['label', 'slug']
|
||||
|
||||
|
||||
class CustomFieldForm(forms.Form):
|
||||
varname = forms.SlugField(label=_('Field slug'), required=False)
|
||||
label = forms.CharField(label=_('Field label'), required=False)
|
||||
field_type = forms.ChoiceField(
|
||||
label=_('Field type'),
|
||||
choices=[
|
||||
('', '-------'),
|
||||
('text', _('Text')),
|
||||
('textarea', _('Textarea')),
|
||||
('bool', _('Boolean')),
|
||||
],
|
||||
required=False,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if cleaned_data.get('varname') and not cleaned_data.get('label'):
|
||||
self.add_error('label', _('This field is required.'))
|
||||
if cleaned_data.get('varname') and not cleaned_data.get('field_type'):
|
||||
self.add_error('field', _('This field is required.'))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
CustomFieldFormSet = formset_factory(CustomFieldForm)
|
||||
|
||||
|
||||
class BookingCheckFilterSet(django_filters.FilterSet):
|
||||
class Meta:
|
||||
model = Booking
|
||||
|
|
|
@ -31,4 +31,21 @@ $(function() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ($('#add-custom-field-form').length) {
|
||||
var property_forms = $('.custom-field-form');
|
||||
var total_form = $('#id_form-TOTAL_FORMS');
|
||||
var form_num = property_forms.length - 1;
|
||||
$('#add-custom-field-form').on('click', function() {
|
||||
var new_form = $(property_forms[0]).clone();
|
||||
var form_regex = RegExp(`form-(\\d){1}-`,'g');
|
||||
form_num++;
|
||||
new_form.html(new_form.html().replace(form_regex, `form-${form_num}-`));
|
||||
new_form.appendTo('#custom-field-forms tbody');
|
||||
$('#id_form-' + form_num + '-varname').val('');
|
||||
$('#id_form-' + form_num + '-label').val('');
|
||||
$('#id_form-' + form_num + '-field_type').val('');
|
||||
total_form.val(form_num + 1);
|
||||
})
|
||||
}
|
||||
});
|
||||
|
|
|
@ -23,6 +23,32 @@
|
|||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
{% if object.pk %}
|
||||
<h3>{% trans "Custom fields" %}</h3>
|
||||
{{ formset.management_form }}
|
||||
<table id="custom-field-forms">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for field in formset.0 %}
|
||||
<th class="column-{{ field.name }}{% if field.required %} required{% endif %}">{{ field.label }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for sub_form in formset %}
|
||||
<tr class='custom-field-form'>
|
||||
{% for field in sub_form %}
|
||||
<td class="field-{{ field.name }}">
|
||||
{{ field.errors.as_ul }}
|
||||
{{ field }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<button id="add-custom-field-form" type="button">{% trans "Add another custom field" %}</button>
|
||||
{% endif %}
|
||||
<div class="buttons">
|
||||
<button class="submit-button">{% trans "Save" %}</button>
|
||||
<a class="cancel" href="{% url 'chrono-manager-events-type-list' %}">{% trans 'Cancel' %}</a>
|
||||
|
|
|
@ -105,6 +105,7 @@ from .forms import (
|
|||
BookingAbsenceReasonForm,
|
||||
BookingCancelForm,
|
||||
BookingCheckFilterSet,
|
||||
CustomFieldFormSet,
|
||||
DeskExceptionsImportForm,
|
||||
DeskForm,
|
||||
EventCancelForm,
|
||||
|
@ -850,6 +851,37 @@ class EventsTypeEditView(UpdateView):
|
|||
def get_success_url(self):
|
||||
return reverse('chrono-manager-events-type-list')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
data = None
|
||||
if self.request.method == 'POST':
|
||||
data = self.request.POST
|
||||
context['formset'] = CustomFieldFormSet(
|
||||
data=data,
|
||||
initial=self.get_object().get_custom_fields(),
|
||||
)
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object = form.save(commit=False) # save object and update cache only once
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
formset = CustomFieldFormSet(data=self.request.POST)
|
||||
if form.is_valid() and formset.is_valid():
|
||||
response = self.form_valid(form)
|
||||
self.object.custom_fields = []
|
||||
for sub_data in formset.cleaned_data:
|
||||
if not sub_data.get('varname'):
|
||||
continue
|
||||
self.object.custom_fields.append(sub_data)
|
||||
self.object.save()
|
||||
return response
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
events_type_edit = EventsTypeEditView.as_view()
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ def test_add_events_type(app, admin_user):
|
|||
assert resp.location.endswith('/manage/events-types/')
|
||||
assert events_type.label == 'Foo bar'
|
||||
assert events_type.slug == 'foo-bar'
|
||||
assert events_type.custom_fields == []
|
||||
|
||||
|
||||
def test_add_events_type_as_manager(app, manager_user):
|
||||
|
@ -56,6 +57,83 @@ def test_edit_events_type(app, admin_user):
|
|||
events_type.refresh_from_db()
|
||||
assert events_type.label == 'Foo bar baz'
|
||||
assert events_type.slug == 'baz2'
|
||||
assert events_type.custom_fields == []
|
||||
|
||||
|
||||
def test_edit_events_type_custom_fields(app, admin_user):
|
||||
events_type = EventsType.objects.create(label='Foo bar')
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
|
||||
resp.form['form-0-varname'] = 'foo'
|
||||
resp.form['form-0-label'] = 'Foo'
|
||||
resp.form['form-0-field_type'] = 'text'
|
||||
resp = resp.form.submit()
|
||||
assert resp.status_code == 302
|
||||
events_type.refresh_from_db()
|
||||
assert events_type.custom_fields == [
|
||||
{'varname': 'foo', 'label': 'Foo', 'field_type': 'text'},
|
||||
]
|
||||
|
||||
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
|
||||
assert resp.form['form-TOTAL_FORMS'].value == '2'
|
||||
assert resp.form['form-0-varname'].value == 'foo'
|
||||
assert resp.form['form-0-label'].value == 'Foo'
|
||||
assert resp.form['form-0-field_type'].value == 'text'
|
||||
assert resp.form['form-1-varname'].value == ''
|
||||
assert resp.form['form-1-label'].value == ''
|
||||
assert resp.form['form-1-field_type'].value == ''
|
||||
resp.form['form-0-label'] = 'Foo-bis'
|
||||
resp.form['form-1-varname'] = 'blah'
|
||||
resp.form['form-1-label'] = 'Blah'
|
||||
resp.form['form-1-field_type'] = 'bool'
|
||||
resp = resp.form.submit()
|
||||
assert resp.status_code == 302
|
||||
events_type.refresh_from_db()
|
||||
assert events_type.custom_fields == [
|
||||
{'varname': 'foo', 'label': 'Foo-bis', 'field_type': 'text'},
|
||||
{'varname': 'blah', 'label': 'Blah', 'field_type': 'bool'},
|
||||
]
|
||||
|
||||
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
|
||||
assert resp.form['form-TOTAL_FORMS'].value == '3'
|
||||
assert resp.form['form-0-varname'].value == 'foo'
|
||||
assert resp.form['form-0-label'].value == 'Foo-bis'
|
||||
assert resp.form['form-0-field_type'].value == 'text'
|
||||
assert resp.form['form-1-varname'].value == 'blah'
|
||||
assert resp.form['form-1-label'].value == 'Blah'
|
||||
assert resp.form['form-1-field_type'].value == 'bool'
|
||||
assert resp.form['form-2-varname'].value == ''
|
||||
assert resp.form['form-2-label'].value == ''
|
||||
assert resp.form['form-2-field_type'].value == ''
|
||||
resp.form['form-0-varname'] = 'foo'
|
||||
resp.form['form-0-label'] = 'Foo'
|
||||
resp.form['form-1-varname'] = ''
|
||||
resp = resp.form.submit()
|
||||
assert resp.status_code == 302
|
||||
events_type.refresh_from_db()
|
||||
assert events_type.custom_fields == [
|
||||
{'varname': 'foo', 'label': 'Foo', 'field_type': 'text'},
|
||||
]
|
||||
|
||||
# custom_fields contains anything (bad format or incomplete)
|
||||
bad_values = [
|
||||
{'foo': 'bar'},
|
||||
['foo', 'bar'],
|
||||
[['foo', 'bar']],
|
||||
[{'foo': 'bar'}],
|
||||
[{'varname': '', 'label': 'Foo', 'field_type': 'text'}],
|
||||
[{'varname': 'foo', 'label': '', 'field_type': 'text'}],
|
||||
[{'varname': 'foo', 'label': 'Foo', 'field_type': ''}],
|
||||
]
|
||||
for bad_value in bad_values:
|
||||
events_type.custom_fields = bad_value
|
||||
events_type.save()
|
||||
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
|
||||
assert resp.form['form-TOTAL_FORMS'].value == '1'
|
||||
assert resp.form['form-0-varname'].value == ''
|
||||
assert resp.form['form-0-label'].value == ''
|
||||
assert resp.form['form-0-field_type'].value == ''
|
||||
|
||||
|
||||
def test_edit_events_type_as_manager(app, manager_user):
|
||||
|
|
Loading…
Reference in New Issue