877 lines
29 KiB
Python
877 lines
29 KiB
Python
from django.forms import (
|
||
ModelForm,
|
||
ValidationError,
|
||
TextInput,
|
||
Form,
|
||
URLField,
|
||
MultipleHiddenInput,
|
||
Textarea,
|
||
CharField,
|
||
ChoiceField,
|
||
RadioSelect,
|
||
MultipleChoiceField,
|
||
BooleanField,
|
||
HiddenInput,
|
||
ModelChoiceField,
|
||
EmailField
|
||
)
|
||
from django.forms import formset_factory
|
||
|
||
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
|
||
|
||
from .utils import PlaceGuesser
|
||
from .models import (
|
||
Event,
|
||
RecurrentImport,
|
||
CategorisationRule,
|
||
Place,
|
||
Category,
|
||
Tag,
|
||
Message
|
||
)
|
||
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.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"]
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
if "name" in kwargs["initial"]:
|
||
self.fields["name"].widget = 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 SimpleContactForm(GroupFormMixin, Form):
|
||
email = EmailField(
|
||
label=_("Your email"),
|
||
help_text=_("Your email address"),
|
||
max_length=254,
|
||
required=False
|
||
)
|
||
|
||
comments = CharField(
|
||
label=_("Comments"),
|
||
help_text=_("Your message for the moderation team (comments, clarifications, requests...)"),
|
||
widget=Textarea,
|
||
max_length=2048,
|
||
required=False
|
||
)
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
is_authenticated = "is_authenticated" in kwargs and kwargs["is_authenticated"]
|
||
super().__init__(*args, **kwargs)
|
||
|
||
if not is_authenticated:
|
||
self.add_group('communication',
|
||
_('Receive notification of publication or leave a message for moderation'),
|
||
maskable=True,
|
||
default_masked=True)
|
||
self.fields["email"].group_id = 'communication'
|
||
self.fields["comments"].group_id = 'communication'
|
||
else:
|
||
del self.fields["email"]
|
||
del self.fields["comments"]
|
||
|
||
|
||
|
||
|
||
class URLSubmissionForm(GroupFormMixin, 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):
|
||
is_authenticated = kwargs.pop("is_authenticated", False)
|
||
super().__init__(*args, **kwargs)
|
||
self.fields["tags"].choices = Tag.get_tag_groups(all=True)
|
||
|
||
self.add_group('event', _('Event'))
|
||
self.fields["url"].group_id = 'event'
|
||
self.fields["category"].group_id = 'event'
|
||
self.fields["tags"].group_id = 'event'
|
||
|
||
|
||
class URLSubmissionFormWithContact(SimpleContactForm, URLSubmissionForm):
|
||
pass
|
||
|
||
URLSubmissionFormSet = formset_factory(URLSubmissionForm, extra=9, min_num=1)
|
||
|
||
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)
|
||
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'
|
||
|
||
self.add_group('urls', _('URLs'))
|
||
self.fields["reference_urls"].group_id = 'urls'
|
||
|
||
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 ((not 'local_image' in self.cleaned_data) or (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 EventFormWithContact(SimpleContactForm, EventForm):
|
||
pass
|
||
|
||
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, (e.start_time.strftime('%H:%M') + " : " if e.start_time else "") + 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(e.pk), _("Value of version {}").format(e.pk)) if e != self.event else
|
||
("event_" + str(e.pk), _("Value of the selected version"))
|
||
for e in self.events
|
||
]
|
||
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>"
|
||
)
|
||
for step in e.chronology_dates():
|
||
if step["data"] == "created_date":
|
||
result += '<li><em>Création</em> le ' + localize(step["timestamp"]) + ' par ' + str(step["user"]) + '</li>'
|
||
if step["data"] == "modified_date":
|
||
result += '<li><em>Dernière modification</em> le ' + localize(step["timestamp"])
|
||
if e.modified_by_user:
|
||
result += ' par ' + e.modified_by_user.username
|
||
else:
|
||
result += ' par import récurrent'
|
||
result += '</li>'
|
||
|
||
if step["data"] == "moderated_date":
|
||
result += '<li><em>Dernière modération</em> le ' + localize(step["timestamp"])
|
||
if e.moderated_by_user:
|
||
result += ' par ' + e.moderated_by_user.username
|
||
result += '</li>'
|
||
if step["data"] == "imported_date":
|
||
result += '<li><em>Dernière importation</em> le ' + localize(step["timestamp"])
|
||
if e.imported_by_user:
|
||
result += ' par ' + e.imported_by_user.username
|
||
else:
|
||
result += ' par import récurrent'
|
||
result += '</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 checked and 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 MessageForm(ModelForm):
|
||
|
||
class Meta:
|
||
model = Message
|
||
fields = ["subject", "name", "email", "message", "related_event"]
|
||
widgets = {"related_event": HiddenInput(), "user": HiddenInput() }
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.event = kwargs.pop("event", False)
|
||
self.internal = kwargs.pop("internal", False)
|
||
super().__init__(*args, **kwargs)
|
||
self.fields['related_event'].required = False
|
||
if self.internal:
|
||
self.fields.pop("name")
|
||
self.fields.pop("email")
|
||
|
||
class MessageEventForm(ModelForm):
|
||
|
||
class Meta:
|
||
model = Message
|
||
fields = ["message"]
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
super().__init__(*args, **kwargs)
|
||
self.fields["message"].label = _("Add a comment") |