Compare commits
1 Commits
main
...
419_Réorga
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2afafc1338 |
9
src/agenda_culturel/forms/__init__.py
Normal file
9
src/agenda_culturel/forms/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from .utils import *
|
||||
from .user import *
|
||||
from .category import *
|
||||
from .tag import *
|
||||
from .event import *
|
||||
from .place import *
|
||||
from .message import *
|
||||
from .imports import *
|
||||
from .special_period import *
|
57
src/agenda_culturel/forms/category.py
Normal file
57
src/agenda_culturel/forms/category.py
Normal file
@ -0,0 +1,57 @@
|
||||
import logging
|
||||
|
||||
from django.forms import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
Form,
|
||||
HiddenInput,
|
||||
ModelForm,
|
||||
)
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from ..models import (
|
||||
CategorisationRule,
|
||||
Event,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CategorisationRuleImportForm(ModelForm):
|
||||
required_css_class = "required"
|
||||
|
||||
class Meta:
|
||||
model = CategorisationRule
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
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)
|
||||
]
|
@ -1,285 +1,43 @@
|
||||
import logging
|
||||
import os
|
||||
from string import ascii_uppercase as auc
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files import File
|
||||
from django.forms import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
ChoiceField,
|
||||
EmailField,
|
||||
Form,
|
||||
HiddenInput,
|
||||
ModelChoiceField,
|
||||
ModelForm,
|
||||
MultipleChoiceField,
|
||||
MultipleHiddenInput,
|
||||
RadioSelect,
|
||||
Textarea,
|
||||
TextInput,
|
||||
URLField,
|
||||
ValidationError,
|
||||
FileField,
|
||||
formset_factory,
|
||||
)
|
||||
from django.utils.formats import localize
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
|
||||
|
||||
from .models import (
|
||||
CategorisationRule,
|
||||
from . import (
|
||||
GroupFormMixin,
|
||||
MultipleChoiceFieldAcceptAll,
|
||||
DynamicArrayWidgetTags,
|
||||
DynamicArrayWidgetURLs,
|
||||
SimpleContactForm,
|
||||
)
|
||||
from ..models import (
|
||||
Category,
|
||||
Event,
|
||||
Message,
|
||||
Place,
|
||||
RecurrentImport,
|
||||
Tag,
|
||||
UserProfile,
|
||||
SpecialPeriod,
|
||||
)
|
||||
from .templatetags.event_extra import event_field_verbose_name, field_to_html
|
||||
from .templatetags.utils_extra import int_to_abc
|
||||
|
||||
from .models.constants import (
|
||||
from ..models.constants import (
|
||||
TITLE_ISSUE_DATE_IMPORTATION,
|
||||
TITLE_ISSUE_TIME_IMPORTATION,
|
||||
PUBLICATION_ISSUE,
|
||||
)
|
||||
|
||||
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 is 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 g.toggle_field_name() not 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",
|
||||
"message",
|
||||
"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 name is not 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"] is 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 URLSubmissionSimpleForm(Form):
|
||||
url = URLField(max_length=512)
|
||||
|
||||
|
||||
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):
|
||||
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 MultipleChoiceFieldAcceptAll(MultipleChoiceField):
|
||||
def validate(self, value):
|
||||
# check if each element is without "/"
|
||||
for item in value:
|
||||
if "/" in item:
|
||||
raise ValidationError(_("The '/' character is not allowed."))
|
||||
from ..templatetags.event_extra import event_field_verbose_name, field_to_html
|
||||
from ..templatetags.utils_extra import int_to_abc
|
||||
|
||||
|
||||
class EventForm(GroupFormMixin, ModelForm):
|
||||
@ -570,131 +328,6 @@ class EventModerateForm(ModelForm):
|
||||
self.cleaned_data["tags"] = list(set(self.cleaned_data["tags"]))
|
||||
|
||||
|
||||
class BatchImportationForm(Form):
|
||||
required_css_class = "required"
|
||||
|
||||
data = CharField(
|
||||
label=_("Data"),
|
||||
widget=Textarea(attrs={"rows": "10"}),
|
||||
help_text=_("Supported formats: json, html."),
|
||||
required=True,
|
||||
)
|
||||
|
||||
category = ModelChoiceField(
|
||||
label=_("Category"),
|
||||
queryset=Category.objects.all().order_by("name"),
|
||||
help_text=_("Used only if data is html."),
|
||||
initial=None,
|
||||
required=False,
|
||||
)
|
||||
tags = MultipleChoiceField(
|
||||
label=_("Tags"),
|
||||
initial=None,
|
||||
choices=[],
|
||||
help_text=_("Used only if data is html."),
|
||||
required=False,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["tags"].choices = Tag.get_tag_groups(all=True)
|
||||
|
||||
|
||||
class FixDuplicates(Form):
|
||||
required_css_class = "required"
|
||||
|
||||
action = ChoiceField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
edup = kwargs.pop("edup", None)
|
||||
events = edup.get_duplicated()
|
||||
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"
|
||||
|
||||
@ -888,190 +521,96 @@ class MergeDuplicates(Form):
|
||||
return None
|
||||
|
||||
|
||||
class CategorisationForm(Form):
|
||||
class FixDuplicates(Form):
|
||||
required_css_class = "required"
|
||||
|
||||
action = ChoiceField()
|
||||
|
||||
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"])
|
||||
)
|
||||
edup = kwargs.pop("edup", None)
|
||||
events = edup.get_duplicated()
|
||||
len(events)
|
||||
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,
|
||||
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),
|
||||
)
|
||||
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,
|
||||
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]),
|
||||
)
|
||||
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)
|
||||
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:
|
||||
self.fields.pop("add_alias")
|
||||
if self.instance.exact_location:
|
||||
self.fields["place"].initial = self.instance.exact_location
|
||||
self.fields["add_alias"].initial = False
|
||||
return None
|
||||
|
||||
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"></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")
|
||||
|
||||
|
||||
class UserProfileForm(ModelForm):
|
||||
|
||||
user = CharField(widget=HiddenInput())
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class SpecialPeriodForm(ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = SpecialPeriod
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
"start_date": TextInput(attrs={"type": "date"}),
|
||||
"end_date": TextInput(attrs={"type": "date"}),
|
||||
}
|
||||
|
||||
|
||||
class SpecialPeriodFileForm(Form):
|
||||
periodtype = ChoiceField(
|
||||
label=_("Period type"),
|
||||
required=True,
|
||||
choices=SpecialPeriod.PERIODTYPE.choices,
|
||||
)
|
||||
file = FileField(label=_("ICAL file"), required=True)
|
||||
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
|
61
src/agenda_culturel/forms/imports.py
Normal file
61
src/agenda_culturel/forms/imports.py
Normal file
@ -0,0 +1,61 @@
|
||||
from django.forms import (
|
||||
CharField,
|
||||
Form,
|
||||
ModelChoiceField,
|
||||
ModelForm,
|
||||
MultipleChoiceField,
|
||||
Textarea,
|
||||
)
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from ..models import (
|
||||
Category,
|
||||
RecurrentImport,
|
||||
Tag,
|
||||
)
|
||||
|
||||
|
||||
class BatchImportationForm(Form):
|
||||
required_css_class = "required"
|
||||
|
||||
data = CharField(
|
||||
label=_("Data"),
|
||||
widget=Textarea(attrs={"rows": "10"}),
|
||||
help_text=_("Supported formats: json, html."),
|
||||
required=True,
|
||||
)
|
||||
|
||||
category = ModelChoiceField(
|
||||
label=_("Category"),
|
||||
queryset=Category.objects.all().order_by("name"),
|
||||
help_text=_("Used only if data is html."),
|
||||
initial=None,
|
||||
required=False,
|
||||
)
|
||||
tags = MultipleChoiceField(
|
||||
label=_("Tags"),
|
||||
initial=None,
|
||||
choices=[],
|
||||
help_text=_("Used only if data is html."),
|
||||
required=False,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["tags"].choices = Tag.get_tag_groups(all=True)
|
||||
|
||||
|
||||
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)
|
35
src/agenda_culturel/forms/message.py
Normal file
35
src/agenda_culturel/forms/message.py
Normal file
@ -0,0 +1,35 @@
|
||||
from django.forms import (
|
||||
HiddenInput,
|
||||
ModelForm,
|
||||
)
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from ..models import (
|
||||
Message,
|
||||
)
|
||||
|
||||
|
||||
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")
|
116
src/agenda_culturel/forms/place.py
Normal file
116
src/agenda_culturel/forms/place.py
Normal file
@ -0,0 +1,116 @@
|
||||
from django.forms import (
|
||||
BooleanField,
|
||||
Form,
|
||||
ModelChoiceField,
|
||||
ModelForm,
|
||||
TextInput,
|
||||
)
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from . import GroupFormMixin
|
||||
from ..models import (
|
||||
Place,
|
||||
)
|
||||
|
||||
|
||||
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")
|
||||
if self.instance.exact_location:
|
||||
self.fields["place"].initial = self.instance.exact_location
|
||||
self.fields["add_alias"].initial = False
|
||||
|
||||
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"></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")
|
32
src/agenda_culturel/forms/special_period.py
Normal file
32
src/agenda_culturel/forms/special_period.py
Normal file
@ -0,0 +1,32 @@
|
||||
from django.forms import (
|
||||
ChoiceField,
|
||||
Form,
|
||||
ModelForm,
|
||||
TextInput,
|
||||
FileField,
|
||||
)
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from ..models import (
|
||||
SpecialPeriod,
|
||||
)
|
||||
|
||||
|
||||
class SpecialPeriodForm(ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = SpecialPeriod
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
"start_date": TextInput(attrs={"type": "date"}),
|
||||
"end_date": TextInput(attrs={"type": "date"}),
|
||||
}
|
||||
|
||||
|
||||
class SpecialPeriodFileForm(Form):
|
||||
periodtype = ChoiceField(
|
||||
label=_("Period type"),
|
||||
required=True,
|
||||
choices=SpecialPeriod.PERIODTYPE.choices,
|
||||
)
|
||||
file = FileField(label=_("ICAL file"), required=True)
|
56
src/agenda_culturel/forms/tag.py
Normal file
56
src/agenda_culturel/forms/tag.py
Normal file
@ -0,0 +1,56 @@
|
||||
from django.forms import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
Form,
|
||||
HiddenInput,
|
||||
ModelForm,
|
||||
)
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from ..models import (
|
||||
Tag,
|
||||
)
|
||||
|
||||
|
||||
class TagForm(ModelForm):
|
||||
required_css_class = "required"
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = [
|
||||
"name",
|
||||
"description",
|
||||
"message",
|
||||
"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 name is not 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"] is True
|
18
src/agenda_culturel/forms/user.py
Normal file
18
src/agenda_culturel/forms/user.py
Normal file
@ -0,0 +1,18 @@
|
||||
from django.forms import (
|
||||
CharField,
|
||||
HiddenInput,
|
||||
ModelForm,
|
||||
)
|
||||
|
||||
from ..models import (
|
||||
UserProfile,
|
||||
)
|
||||
|
||||
|
||||
class UserProfileForm(ModelForm):
|
||||
|
||||
user = CharField(widget=HiddenInput())
|
||||
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = "__all__"
|
182
src/agenda_culturel/forms/utils.py
Normal file
182
src/agenda_culturel/forms/utils.py
Normal file
@ -0,0 +1,182 @@
|
||||
from django.forms import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
EmailField,
|
||||
Form,
|
||||
ModelChoiceField,
|
||||
MultipleChoiceField,
|
||||
Textarea,
|
||||
URLField,
|
||||
ValidationError,
|
||||
formset_factory,
|
||||
)
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
|
||||
|
||||
from ..models import (
|
||||
Category,
|
||||
Tag,
|
||||
)
|
||||
|
||||
|
||||
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 is 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 g.toggle_field_name() not in data:
|
||||
fields = self.get_fields_in_group(g)
|
||||
for f in fields:
|
||||
self.cleaned_data[f.name] = None
|
||||
|
||||
return result
|
||||
|
||||
|
||||
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 URLSubmissionSimpleForm(Form):
|
||||
url = URLField(max_length=512)
|
||||
|
||||
|
||||
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):
|
||||
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 MultipleChoiceFieldAcceptAll(MultipleChoiceField):
|
||||
def validate(self, value):
|
||||
# check if each element is without "/"
|
||||
for item in value:
|
||||
if "/" in item:
|
||||
raise ValidationError(_("The '/' character is not allowed."))
|
Loading…
x
Reference in New Issue
Block a user