Compare commits

..

1 Commits

Author SHA1 Message Date
SebF
2afafc1338 réorganisation des forms en module 2025-05-07 10:49:03 +02:00
10 changed files with 657 additions and 552 deletions

View 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 *

View 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)
]

View File

@ -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),
)
]
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,
)
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)
]
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
class EventAddPlaceForm(Form):
required_css_class = "required"
def is_action_no_duplicates(self):
return self.cleaned_data["action"] == "NotDuplicates"
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 is_action_select(self):
return self.cleaned_data["action"].startswith("Select")
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)
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

View 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)

View 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")

View 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")

View 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)

View 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

View 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__"

View 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."))