798 lines
26 KiB
Python
798 lines
26 KiB
Python
from django.forms import (
|
|
ModelForm,
|
|
ValidationError,
|
|
TextInput,
|
|
Form,
|
|
URLField,
|
|
MultipleHiddenInput,
|
|
Textarea,
|
|
CharField,
|
|
ChoiceField,
|
|
RadioSelect,
|
|
MultipleChoiceField,
|
|
BooleanField,
|
|
HiddenInput,
|
|
ModelChoiceField,
|
|
)
|
|
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
|
|
|
|
from .utils import PlaceGuesser
|
|
from .models import (
|
|
Event,
|
|
RecurrentImport,
|
|
CategorisationRule,
|
|
Place,
|
|
Category,
|
|
Tag,
|
|
ContactMessage
|
|
)
|
|
from django.conf import settings
|
|
from django.core.files import File
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
from string import ascii_uppercase as auc
|
|
from .templatetags.utils_extra import int_to_abc
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.timezone import localtime
|
|
from django.utils.formats import localize
|
|
from .templatetags.event_extra import event_field_verbose_name, field_to_html
|
|
import os
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class GroupFormMixin:
|
|
|
|
template_name = 'agenda_culturel/forms/div_group.html'
|
|
|
|
class FieldGroup:
|
|
|
|
def __init__(self, id, label, display_label=False, maskable=False, default_masked=True):
|
|
self.id = id
|
|
self.label = label
|
|
self.display_label = display_label
|
|
self.maskable = maskable
|
|
self.default_masked = default_masked
|
|
|
|
def toggle_field_name(self):
|
|
return 'group_' + self.id
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.groups = []
|
|
|
|
def add_group(self, *args, **kwargs):
|
|
self.groups.append(GroupFormMixin.FieldGroup(*args, **kwargs))
|
|
if self.groups[-1].maskable:
|
|
self.fields[self.groups[-1].toggle_field_name()] = BooleanField(required=False)
|
|
self.fields[self.groups[-1].toggle_field_name()].toggle_group = True
|
|
|
|
def get_fields_in_group(self, g):
|
|
return [f for f in self.visible_fields() if not hasattr(f.field, "toggle_group") and hasattr(f.field, "group_id") and f.field.group_id == g.id]
|
|
|
|
def get_no_group_fields(self):
|
|
return [f for f in self.visible_fields() if not hasattr(f.field, "toggle_group") and (not hasattr(f.field, "group_id") or f.field.group_id == None)]
|
|
|
|
def fields_by_group(self):
|
|
return [(g, self.get_fields_in_group(g)) for g in self.groups] + [(GroupFormMixin.FieldGroup("other", _("Other")), self.get_no_group_fields())]
|
|
|
|
def clean(self):
|
|
result = super().clean()
|
|
|
|
if result:
|
|
data = dict(self.data)
|
|
# for each masked group, we remove data
|
|
for g in self.groups:
|
|
if g.maskable and not g.toggle_field_name() in data:
|
|
fields = self.get_fields_in_group(g)
|
|
for f in fields:
|
|
self.cleaned_data[f.name] = None
|
|
|
|
return result
|
|
|
|
class TagForm(ModelForm):
|
|
required_css_class = 'required'
|
|
|
|
class Meta:
|
|
model = Tag
|
|
fields = ["name", "description", "in_included_suggestions", "in_excluded_suggestions", "principal"]
|
|
widgets = {
|
|
"name": HiddenInput()
|
|
}
|
|
|
|
class TagRenameForm(Form):
|
|
required_css_class = 'required'
|
|
|
|
name = CharField(
|
|
label=_('Name of new tag'),
|
|
required=True
|
|
)
|
|
|
|
force = BooleanField(
|
|
label=_('Force renaming despite the existence of events already using the chosen tag.'),
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
force = kwargs.pop("force", False)
|
|
name = kwargs.pop("name", None)
|
|
super().__init__(*args, **kwargs)
|
|
if not (force or (not len(args) == 0 and 'force' in args[0])):
|
|
del self.fields["force"]
|
|
if not name is None and self.fields["name"].initial is None:
|
|
self.fields["name"].initial = name
|
|
|
|
|
|
def is_force(self):
|
|
return "force" in self.fields and self.cleaned_data["force"] == True
|
|
|
|
class URLSubmissionForm(Form):
|
|
required_css_class = 'required'
|
|
|
|
url = URLField(max_length=512)
|
|
category = ModelChoiceField(
|
|
label=_("Category"),
|
|
queryset=Category.objects.all().order_by("name"),
|
|
initial=None,
|
|
required=False,
|
|
)
|
|
tags = MultipleChoiceField(
|
|
label=_("Tags"),
|
|
initial=None,
|
|
choices=[],
|
|
required=False
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields["tags"].choices = Tag.get_tag_groups(all=True)
|
|
|
|
|
|
|
|
|
|
class DynamicArrayWidgetURLs(DynamicArrayWidget):
|
|
template_name = "agenda_culturel/widgets/widget-urls.html"
|
|
|
|
|
|
class DynamicArrayWidgetTags(DynamicArrayWidget):
|
|
template_name = "agenda_culturel/widgets/widget-tags.html"
|
|
|
|
|
|
class RecurrentImportForm(ModelForm):
|
|
required_css_class = 'required'
|
|
|
|
defaultTags = MultipleChoiceField(
|
|
label=_("Tags"),
|
|
initial=None,
|
|
choices=[],
|
|
required=False
|
|
)
|
|
|
|
class Meta:
|
|
model = RecurrentImport
|
|
fields = "__all__"
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields["defaultTags"].choices = Tag.get_tag_groups(all=True)
|
|
|
|
|
|
class CategorisationRuleImportForm(ModelForm):
|
|
required_css_class = 'required'
|
|
|
|
class Meta:
|
|
model = CategorisationRule
|
|
fields = "__all__"
|
|
|
|
|
|
class EventForm(GroupFormMixin, ModelForm):
|
|
required_css_class = 'required'
|
|
|
|
old_local_image = CharField(widget=HiddenInput(), required=False)
|
|
simple_cloning = CharField(widget=HiddenInput(), required=False)
|
|
|
|
tags = MultipleChoiceField(
|
|
label=_("Tags"),
|
|
initial=None,
|
|
choices=[],
|
|
required=False
|
|
)
|
|
|
|
|
|
class Meta:
|
|
model = Event
|
|
exclude = [
|
|
"imported_date",
|
|
"modified_date",
|
|
"moderated_date",
|
|
"import_sources",
|
|
"image",
|
|
"moderated_by_user",
|
|
"modified_by_user",
|
|
"created_by_user",
|
|
"imported_by_user"
|
|
]
|
|
widgets = {
|
|
"start_day": TextInput(
|
|
attrs={
|
|
"type": "date",
|
|
"onchange": "update_datetimes(event);",
|
|
"onfocus": "this.oldvalue = this.value;",
|
|
}
|
|
),
|
|
"start_time": TextInput(
|
|
attrs={
|
|
"type": "time",
|
|
"onchange": "update_datetimes(event);",
|
|
"onfocus": "this.oldvalue = this.value;",
|
|
}
|
|
),
|
|
"end_day": TextInput(attrs={"type": "date"}),
|
|
"end_time": TextInput(attrs={"type": "time"}),
|
|
"other_versions": HiddenInput(),
|
|
"uuids": MultipleHiddenInput(),
|
|
"reference_urls": DynamicArrayWidgetURLs(),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
is_authenticated = kwargs.pop("is_authenticated", False)
|
|
self.cloning = kwargs.pop("is_cloning", False)
|
|
self.simple_cloning = kwargs.pop("is_simple_cloning", False)
|
|
super().__init__(*args, **kwargs)
|
|
if not is_authenticated:
|
|
del self.fields["status"]
|
|
del self.fields["organisers"]
|
|
self.fields['category'].queryset = self.fields['category'].queryset.order_by('name')
|
|
self.fields['category'].empty_label = None
|
|
self.fields['category'].initial = Category.get_default_category()
|
|
self.fields['tags'].choices = Tag.get_tag_groups(all=True)
|
|
|
|
# set groups
|
|
self.add_group('main', _('Main fields'))
|
|
self.fields['title'].group_id = 'main'
|
|
|
|
self.add_group('start', _('Start of event'))
|
|
self.fields['start_day'].group_id = 'start'
|
|
self.fields['start_time'].group_id = 'start'
|
|
|
|
self.add_group('end', _('End of event'))
|
|
self.fields['end_day'].group_id = 'end'
|
|
self.fields['end_time'].group_id = 'end'
|
|
|
|
self.add_group('recurrences',
|
|
_('This is a recurring event'),
|
|
maskable=True,
|
|
default_masked=not (self.instance and
|
|
self.instance.recurrences and
|
|
self.instance.recurrences.rrules and
|
|
len(self.instance.recurrences.rrules) > 0))
|
|
|
|
self.fields['recurrences'].group_id = 'recurrences'
|
|
|
|
self.add_group('details', _('Details'))
|
|
self.fields['description'].group_id = 'details'
|
|
if is_authenticated:
|
|
self.fields['organisers'].group_id = 'details'
|
|
|
|
self.add_group('location', _('Location'))
|
|
self.fields['location'].group_id = 'location'
|
|
self.fields['exact_location'].group_id = 'location'
|
|
|
|
self.add_group('illustration', _('Illustration'))
|
|
self.fields['local_image'].group_id = 'illustration'
|
|
self.fields['image_alt'].group_id = 'illustration'
|
|
|
|
|
|
if is_authenticated:
|
|
self.add_group('meta-admin', _('Meta information'))
|
|
self.fields['category'].group_id = 'meta-admin'
|
|
self.fields['tags'].group_id = 'meta-admin'
|
|
self.fields['status'].group_id = 'meta-admin'
|
|
else:
|
|
self.add_group('meta', _('Meta information'))
|
|
self.fields['category'].group_id = 'meta'
|
|
self.fields['tags'].group_id = 'meta'
|
|
|
|
def is_clone_from_url(self):
|
|
return self.cloning
|
|
|
|
def is_simple_clone_from_url(self):
|
|
return self.simple_cloning
|
|
|
|
def clean_end_day(self):
|
|
start_day = self.cleaned_data.get("start_day")
|
|
end_day = self.cleaned_data.get("end_day")
|
|
|
|
if end_day is not None and start_day is not None and end_day < start_day:
|
|
raise ValidationError(_("The end date must be after the start date."))
|
|
|
|
return end_day
|
|
|
|
def clean_end_time(self):
|
|
start_day = self.cleaned_data.get("start_day")
|
|
end_day = self.cleaned_data.get("end_day")
|
|
start_time = self.cleaned_data.get("start_time")
|
|
end_time = self.cleaned_data.get("end_time")
|
|
|
|
# same day
|
|
if start_day is not None and (end_day is None or start_day == end_day):
|
|
# both start and end time are defined
|
|
if start_time is not None and end_time is not None:
|
|
if start_time > end_time:
|
|
raise ValidationError(
|
|
_("The end time cannot be earlier than the start time.")
|
|
)
|
|
|
|
return end_time
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
|
|
# when cloning an existing event, we need to copy the local image
|
|
if self.cleaned_data['local_image'] is None and \
|
|
not self.cleaned_data['old_local_image'] is None and \
|
|
self.cleaned_data['old_local_image'] != "":
|
|
basename = self.cleaned_data['old_local_image']
|
|
old = settings.MEDIA_ROOT + "/" + basename
|
|
if os.path.isfile(old):
|
|
self.cleaned_data['local_image'] = File(name=basename, file=open(old, "rb"))
|
|
|
|
|
|
class MultipleChoiceFieldAcceptAll(MultipleChoiceField):
|
|
def validate(self, value):
|
|
pass
|
|
|
|
|
|
class EventModerateForm(ModelForm):
|
|
required_css_class = 'required'
|
|
|
|
tags = MultipleChoiceField(
|
|
label=_("Tags"),
|
|
help_text=_('Select tags from existing ones.'),
|
|
required=False
|
|
)
|
|
|
|
new_tags = MultipleChoiceFieldAcceptAll(
|
|
label=_("New tags"),
|
|
help_text=_('Create new labels (sparingly). Note: by starting your tag with the characters “TW:”, you''ll create a “trigger warning” tag, and the associated events will be announced as such.'),
|
|
widget=DynamicArrayWidget(),
|
|
required=False
|
|
)
|
|
|
|
class Meta:
|
|
model = Event
|
|
fields = [
|
|
"status",
|
|
"category",
|
|
"organisers",
|
|
"exact_location",
|
|
"tags"
|
|
]
|
|
widgets = {
|
|
"status": RadioSelect
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['category'].queryset = self.fields['category'].queryset.order_by('name')
|
|
self.fields['category'].empty_label = None
|
|
self.fields['category'].initial = Category.get_default_category()
|
|
self.fields['tags'].choices = Tag.get_tag_groups(all=True)
|
|
|
|
def clean_new_tags(self):
|
|
return list(set(self.cleaned_data.get("new_tags")))
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
|
|
if self.cleaned_data['tags'] is None:
|
|
self.cleaned_data['tags'] = []
|
|
|
|
if not self.cleaned_data.get('new_tags') is None:
|
|
self.cleaned_data['tags'] += self.cleaned_data.get('new_tags')
|
|
|
|
self.cleaned_data['tags'] = list(set(self.cleaned_data['tags']))
|
|
|
|
|
|
class BatchImportationForm(Form):
|
|
required_css_class = 'required'
|
|
|
|
json = CharField(
|
|
label="JSON",
|
|
widget=Textarea(attrs={"rows": "10"}),
|
|
help_text=_("JSON in the format expected for the import."),
|
|
required=True,
|
|
)
|
|
|
|
|
|
class FixDuplicates(Form):
|
|
required_css_class = 'required'
|
|
|
|
action = ChoiceField()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
edup = kwargs.pop("edup", None)
|
|
events = edup.get_duplicated()
|
|
nb_events = len(events)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
choices = []
|
|
initial = None
|
|
for i, e in enumerate(events):
|
|
if e.status != Event.STATUS.TRASH or e.modified():
|
|
msg = ""
|
|
if e.local_version():
|
|
msg = _(" (locally modified version)")
|
|
if e.status != Event.STATUS.TRASH:
|
|
initial = "Select-" + str(e.pk)
|
|
if e.pure_import():
|
|
msg = _(" (synchronized on import version)")
|
|
choices += [
|
|
(
|
|
"Select-" + str(e.pk),
|
|
_("Select {} as representative version.").format(auc[i] + msg)
|
|
)
|
|
]
|
|
|
|
for i, e in enumerate(events):
|
|
if e.status != Event.STATUS.TRASH and e.local_version():
|
|
choices += [
|
|
(
|
|
"Update-" + str(e.pk),
|
|
_("Update {} using some fields from other versions (interactive mode).").format(auc[i])
|
|
)
|
|
]
|
|
|
|
|
|
extra = ""
|
|
if edup.has_local_version():
|
|
extra = _(" Warning: a version is already locally modified.")
|
|
|
|
if initial is None:
|
|
initial = "Merge"
|
|
choices += [
|
|
("Merge", _("Create a new version by merging (interactive mode).") + extra)
|
|
]
|
|
for i, e in enumerate(events):
|
|
if e.status != Event.STATUS.TRASH:
|
|
choices += [
|
|
(
|
|
"Remove-" + str(e.pk),
|
|
_("Make {} independent.").format(auc[i]))
|
|
]
|
|
choices += [("NotDuplicates", _("Make all versions independent."))]
|
|
|
|
self.fields["action"].choices = choices
|
|
self.fields["action"].initial = initial
|
|
|
|
def is_action_no_duplicates(self):
|
|
return self.cleaned_data["action"] == "NotDuplicates"
|
|
|
|
def is_action_select(self):
|
|
return self.cleaned_data["action"].startswith("Select")
|
|
|
|
def is_action_update(self):
|
|
return self.cleaned_data["action"].startswith("Update")
|
|
|
|
def is_action_remove(self):
|
|
return self.cleaned_data["action"].startswith("Remove")
|
|
|
|
def get_selected_event_code(self):
|
|
if self.is_action_select() or self.is_action_remove() or self.is_action_update():
|
|
return int(self.cleaned_data["action"].split("-")[-1])
|
|
else:
|
|
return None
|
|
|
|
def get_selected_event(self, edup):
|
|
selected = self.get_selected_event_code()
|
|
for e in edup.get_duplicated():
|
|
if e.pk == selected:
|
|
return e
|
|
return None
|
|
|
|
|
|
class SelectEventInList(Form):
|
|
required_css_class = 'required'
|
|
|
|
event = ChoiceField(label=_('Event'))
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
events = kwargs.pop("events", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.fields["event"].choices = [
|
|
(e.pk, str(e.start_day) + " " + e.title + ((", " + e.location) if e.location else "")) for e in events
|
|
]
|
|
|
|
|
|
class MergeDuplicates(Form):
|
|
required_css_class = 'required'
|
|
|
|
checkboxes_fields = ["reference_urls", "description", "tags"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.duplicates = kwargs.pop("duplicates", None)
|
|
self.event = kwargs.pop("event", None)
|
|
self.events = list(self.duplicates.get_duplicated())
|
|
nb_events = len(self.events)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
if self.event:
|
|
choices = [("event_" + str(self.event.pk), _("Value of the selected version"))] + \
|
|
[
|
|
("event_" + str(e.pk), _("Value of version {}").format(e.pk)) for e in self.events if e != self.event
|
|
]
|
|
else:
|
|
choices = [
|
|
("event_" + str(e.pk), _("Value of version {}").format(e.pk)) for e in self.events
|
|
]
|
|
|
|
for f in self.duplicates.get_items_comparison():
|
|
if not f["similar"]:
|
|
if f["key"] in MergeDuplicates.checkboxes_fields:
|
|
self.fields[f["key"]] = MultipleChoiceField(choices=choices)
|
|
self.fields[f["key"]].initial = choices[0][0]
|
|
else:
|
|
self.fields[f["key"]] = ChoiceField(
|
|
widget=RadioSelect, choices=choices
|
|
)
|
|
self.fields[f["key"]].initial = choices[0][0]
|
|
|
|
def as_grid(self):
|
|
result = '<div class="grid">'
|
|
for i, e in enumerate(self.events):
|
|
result += '<div class="grid entete-badge">'
|
|
result += '<div class="badge-large">' + int_to_abc(i) + "</div>"
|
|
result += "<ul>"
|
|
result += (
|
|
'<li><a href="' + e.get_absolute_url() + '">' + e.title + "</a></li>"
|
|
)
|
|
result += (
|
|
"<li>Création : " + localize(localtime(e.created_date)) + "</li>"
|
|
)
|
|
result += (
|
|
"<li>Dernière modification : "
|
|
+ localize(localtime(e.modified_date))
|
|
+ "</li>"
|
|
)
|
|
if e.imported_date:
|
|
result += (
|
|
"<li>Dernière importation : "
|
|
+ localize(localtime(e.imported_date))
|
|
+ "</li>"
|
|
)
|
|
result += "</ul>"
|
|
result += "</div>"
|
|
result += "</div>"
|
|
|
|
for e in self.duplicates.get_items_comparison():
|
|
key = e["key"]
|
|
result += "<h3>" + event_field_verbose_name(e["key"]) + "</h3>"
|
|
if e["similar"]:
|
|
result += (
|
|
'<div class="comparison-item">Identique :'
|
|
+ str(field_to_html(e["values"], e["key"]))
|
|
+ "</div>"
|
|
)
|
|
else:
|
|
result += "<fieldset>"
|
|
if key in self.errors:
|
|
result += '<div class="message error"><ul>'
|
|
for err in self.errors[key]:
|
|
result += "<li>" + err + "</li>"
|
|
result += "</ul></div>"
|
|
result += '<div class="grid comparison-item">'
|
|
if hasattr(self, "cleaned_data"):
|
|
checked = self.cleaned_data.get(key)
|
|
else:
|
|
checked = self.fields[key].initial
|
|
|
|
i = 0
|
|
if self.event:
|
|
idx = self.events.index(self.event)
|
|
result += self.comparison_item(key, i, e["values"][idx], self.fields[e["key"]].choices[idx], self.event, checked)
|
|
i += 1
|
|
|
|
for (v, radio, ev) in zip(e["values"], self.fields[e["key"]].choices, self.events):
|
|
if self.event is None or ev != self.event:
|
|
result += self.comparison_item(key, i, v, radio, ev, checked)
|
|
i += 1
|
|
result += "</div></fieldset>"
|
|
|
|
return mark_safe(result)
|
|
|
|
def comparison_item(self, key, i, v, radio, ev, checked):
|
|
result = '<div class="duplicated">'
|
|
id = "id_" + key + "_" + str(ev.pk)
|
|
value = "event_" + str(ev.pk)
|
|
|
|
result += '<input id="' + id + '" name="' + key + '"'
|
|
if key in MergeDuplicates.checkboxes_fields:
|
|
result += ' type="checkbox"'
|
|
if value in checked:
|
|
result += " checked"
|
|
else:
|
|
result += ' type="radio"'
|
|
if checked == value:
|
|
result += " checked"
|
|
result += ' value="' + value + '"'
|
|
result += ">"
|
|
result += (
|
|
'<div class="badge-small">'
|
|
+ int_to_abc(i)
|
|
+ "</div>")
|
|
result += "<div>"
|
|
if key == "image":
|
|
result += str(field_to_html(ev.local_image, "local_image")) + "</div>"
|
|
result += "<div>Lien d'import : "
|
|
|
|
result += (str(field_to_html(v, key)) + "</div>")
|
|
result += "</div>"
|
|
return result
|
|
|
|
|
|
def get_selected_events(self, key):
|
|
value = self.cleaned_data.get(key)
|
|
if key not in self.fields:
|
|
return None
|
|
else:
|
|
if isinstance(value, list):
|
|
selected = [int(v.split("_")[-1]) for v in value]
|
|
result = []
|
|
for s in selected:
|
|
for e in self.duplicates.get_duplicated():
|
|
if e.pk == s:
|
|
result.append(e)
|
|
break
|
|
return result
|
|
else:
|
|
selected = int(value.split("_")[-1])
|
|
for e in self.duplicates.get_duplicated():
|
|
if e.pk == selected:
|
|
return e
|
|
|
|
return None
|
|
|
|
|
|
class CategorisationForm(Form):
|
|
required_css_class = 'required'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if "events" in kwargs:
|
|
events = kwargs.pop("events", None)
|
|
else:
|
|
events = []
|
|
for f in args[0]:
|
|
if "_" not in f:
|
|
if f + "_cat" in args[0]:
|
|
events.append(
|
|
(Event.objects.get(pk=int(f)), args[0][f + "_cat"])
|
|
)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
for e, c in events:
|
|
self.fields[str(e.pk)] = BooleanField(
|
|
initial=False,
|
|
label=_("Apply category {} to the event {}").format(c, e.title),
|
|
required=False,
|
|
)
|
|
self.fields[str(e.pk) + "_cat"] = CharField(initial=c, widget=HiddenInput())
|
|
|
|
def get_validated(self):
|
|
return [
|
|
(e, self.cleaned_data.get(e + "_cat"))
|
|
for e in self.fields
|
|
if "_" not in e and self.cleaned_data.get(e)
|
|
]
|
|
|
|
|
|
class EventAddPlaceForm(Form):
|
|
required_css_class = 'required'
|
|
|
|
place = ModelChoiceField(
|
|
label=_("Place"),
|
|
queryset=Place.objects.all().order_by("name"),
|
|
empty_label=_("Create a missing place"),
|
|
required=False,
|
|
)
|
|
add_alias = BooleanField(initial=True, required=False)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.instance = kwargs.pop("instance", False)
|
|
super().__init__(*args, **kwargs)
|
|
if self.instance.location:
|
|
self.fields["add_alias"].label = _(
|
|
'Add "{}" to the aliases of the place'
|
|
).format(self.instance.location)
|
|
else:
|
|
self.fields.pop("add_alias")
|
|
|
|
def modified_event(self):
|
|
return self.cleaned_data.get("place")
|
|
|
|
def save(self):
|
|
if self.cleaned_data.get("place"):
|
|
place = self.cleaned_data.get("place")
|
|
self.instance.exact_location = place
|
|
self.instance.save(update_fields=["exact_location"])
|
|
if self.cleaned_data.get("add_alias"):
|
|
if place.aliases:
|
|
place.aliases.append(self.instance.location.strip())
|
|
else:
|
|
place.aliases = [self.instance.location.strip()]
|
|
place.save()
|
|
|
|
return self.instance
|
|
|
|
|
|
class PlaceForm(GroupFormMixin, ModelForm):
|
|
required_css_class = 'required'
|
|
|
|
apply_to_all = BooleanField(
|
|
initial=True,
|
|
label=_(
|
|
"On saving, use aliases to detect all matching events with missing place"
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
class Meta:
|
|
model = Place
|
|
fields = "__all__"
|
|
widgets = {"location": TextInput()}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.add_group('header', _('Header'))
|
|
self.fields['name'].group_id = 'header'
|
|
|
|
|
|
self.add_group('address', _('Address'))
|
|
self.fields['address'].group_id = 'address'
|
|
self.fields['postcode'].group_id = 'address'
|
|
self.fields['city'].group_id = 'address'
|
|
self.fields['location'].group_id = 'address'
|
|
|
|
self.add_group('meta', _('Meta'))
|
|
self.fields['aliases'].group_id = 'meta'
|
|
|
|
self.add_group('information', _('Information'))
|
|
self.fields['description'].group_id = 'information'
|
|
|
|
def as_grid(self):
|
|
result = ('<div class="grid"><div>'
|
|
+ super().as_p()
|
|
+ '''</div><div><div class="map-widget">
|
|
<div id="map_location" style="width: 100%; aspect-ratio: 16/9"></div>
|
|
<p>Cliquez pour ajuster la position GPS</p></div>
|
|
<input type="checkbox" role="switch" id="lock_position">Verrouiller la position</lock>
|
|
<script>
|
|
document.getElementById("lock_position").onclick = function() {
|
|
const field = document.getElementById("id_location");
|
|
if (this.checked)
|
|
field.setAttribute("readonly", true);
|
|
else
|
|
field.removeAttribute("readonly");
|
|
}
|
|
</script>
|
|
</div></div>''')
|
|
|
|
return mark_safe(result)
|
|
|
|
def apply(self):
|
|
return self.cleaned_data.get("apply_to_all")
|
|
|
|
class ContactMessageForm(ModelForm):
|
|
|
|
class Meta:
|
|
model = ContactMessage
|
|
fields = ["subject", "name", "email", "message", "related_event"]
|
|
widgets = {"related_event": HiddenInput()}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.event = kwargs.pop("event", False)
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['related_event'].required = False
|