diff --git a/club/forms.py b/club/forms.py
index 76687103..5bc00e7e 100644
--- a/club/forms.py
+++ b/club/forms.py
@@ -28,7 +28,14 @@ from django.db.models.functions import Lower
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
-from club.models import Club, ClubRole, Mailing, MailingSubscription, Membership
+from club.models import (
+ Club,
+ ClubLink,
+ ClubRole,
+ Mailing,
+ MailingSubscription,
+ Membership,
+)
from core.models import User
from core.views.forms import SelectDateTime
from core.views.widgets.ajax_select import (
@@ -39,6 +46,26 @@ from counter.models import Counter, Selling
from counter.schemas import SaleFilterSchema
+class ClubLinkForm(forms.ModelForm):
+ error_css_class = "error"
+ required_css_class = "required"
+
+ class Meta:
+ model = ClubLink
+ fields = ["url", "name", "link_type"]
+ widgets = {
+ "url": forms.URLInput(
+ {"pattern": "https://.*", "placeholder": "https://monlien.com"}
+ ),
+ "link_type": forms.HiddenInput(),
+ }
+
+
+ClubLinkFormSet = forms.inlineformset_factory(
+ Club, ClubLink, ClubLinkForm, extra=0, can_delete_extra=False
+)
+
+
class ClubEditForm(forms.ModelForm):
error_css_class = "error"
required_css_class = "required"
@@ -48,6 +75,20 @@ class ClubEditForm(forms.ModelForm):
fields = ["address", "logo", "short_description"]
widgets = {"short_description": forms.Textarea()}
+ def __init__(self, *args, prefix: str | None = None, instance=None, **kwargs):
+ super().__init__(*args, prefix=prefix, instance=instance, **kwargs)
+ self.link_formset = ClubLinkFormSet(
+ *args, instance=self.instance, prefix="link", **kwargs
+ )
+
+ def is_valid(self):
+ return super().is_valid() and self.link_formset.is_valid()
+
+ def save(self, commit=True): # noqa: FBT002
+ res = super().save(commit=commit)
+ self.link_formset.save(commit=commit)
+ return res
+
class ClubAdminEditForm(ClubEditForm):
admin_fields = ["name", "parent", "is_active"]
diff --git a/club/templates/club/edit_club.jinja b/club/templates/club/edit_club.jinja
index d70e2140..d1d6aa2c 100644
--- a/club/templates/club/edit_club.jinja
+++ b/club/templates/club/edit_club.jinja
@@ -1,9 +1,60 @@
{% extends "core/base.jinja" %}
+{% block additional_js %}
+
+{% endblock %}
+
{% block title %}
{% trans name=object %}Edit {{ name }}{% endtrans %}
{% endblock %}
+{% macro link_form(form) %}
+
+ {{ form.non_field_errors() }}
+
+ {%- if form.DELETE -%}
+
+ {{ form.DELETE.as_field_group() }}
+
+ {%- else -%}
+
+
+ {% trans %}Remove link{% endtrans %}
+
+ {%- endif -%}
+ {{ form.link_type|add_attr(":value=linkType.id") }}
+ {%- for field in form.hidden_fields() -%}
+ {%- if field != form.link_type -%}
+ {{ field }}
+ {%- endif -%}
+ {%- endfor -%}
+
+{% endmacro %}
+
+
{% block content %}
{% trans name=object %}Edit {{ name }}{% endtrans %}
@@ -17,7 +68,7 @@
and explicitly separate them from the non-admin ones,
with some help text.
Non-admin users will only see the regular form fields,
- so they don't need thoses explanations #}
+ so they don't need those explanations #}
{% trans %}Club properties{% endtrans %}
{% trans trimmed %}
@@ -25,7 +76,7 @@
Only admin users can see and edit them.
{% endtrans %}
-
+
{% for field_name in form.admin_fields %}
{% set field = form[field_name] %}
@@ -36,11 +87,13 @@
{# Remove the the admin fields from the form.
The remaining non-admin fields will be rendered
at once with a simple {{ form.as_p() }} #}
- {% set _ = form.fields.pop(field_name) %}
+ {% do form.fields.pop(field_name) %}
{% endfor %}
+ {% endif %}
-
{% trans %}Club informations{% endtrans %}
+
{% trans %}Club informations{% endtrans %}
+ {% if form.admin_fields %}
{% trans trimmed %}
The following form fields are linked to the basic description of a club.
@@ -48,7 +101,45 @@
{% endtrans %}
{% endif %}
- {{ form.as_p() }}
-
+
+ {{ form.as_p() }}
+
+
+
{% trans %}Club links{% endtrans %}
+
+ {{ form.link_formset.management_form }}
+
+ {%- for f in form.link_formset.forms -%}
+ {{ link_form(f) }}
+ {%- endfor -%}
+
+
+ {{ link_form(form.link_formset.empty_form) }}
+
+
+ {% trans trimmed %}
+ Note: if the icon of one of your links doesn't exist yet,
+ you can ask the info team to add it.
+ {% endtrans %}
+
+
+
+ {% trans %}Add link{% endtrans %}
+
+
+
+
+ {% trans %}Save{% endtrans %}
+
{% endblock content %}
+
+{% block script %}
+
+{% endblock %}
diff --git a/club/tests/test_edit.py b/club/tests/test_edit.py
index b4d9e2b1..c8a03b61 100644
--- a/club/tests/test_edit.py
+++ b/club/tests/test_edit.py
@@ -21,7 +21,13 @@ def test_club_board_member_cannot_edit_club_properties(client: Client):
client.force_login(user)
res = client.post(
reverse("club:club_edit", kwargs={"club_id": club.id}),
- {"name": "new name", "is_active": False, "address": "new address"},
+ {
+ "name": "new name",
+ "is_active": False,
+ "address": "new address",
+ "link-TOTAL_FORMS": 0,
+ "link-INITIAL_FORMS": 0,
+ },
)
# The request should success,
# but admin-only fields shouldn't be taken into account
diff --git a/club/views.py b/club/views.py
index cce60e8c..2e8f831f 100644
--- a/club/views.py
+++ b/club/views.py
@@ -33,6 +33,7 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import NON_FIELD_ERRORS, PermissionDenied, ValidationError
from django.core.paginator import InvalidPage, Paginator
from django.db.models import F, Q, Sum
+from django.db.models.functions import Length
from django.http import Http404, StreamingHttpResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy
@@ -60,7 +61,7 @@ from club.forms import (
MailingForm,
SellingsForm,
)
-from club.models import Club, Mailing, MailingSubscription, Membership
+from club.models import Club, LinkType, Mailing, MailingSubscription, Membership
from com.models import Poster
from com.views import (
PosterCreateBaseView,
@@ -570,6 +571,11 @@ class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
return ClubAdminEditForm
return ClubEditForm
+ def get_context_data(self, **kwargs):
+ return super().get_context_data(**kwargs) | {
+ "link_types": list(LinkType.objects.order_by(Length("url_base").desc()))
+ }
+
class ClubCreateView(PermissionRequiredMixin, CreateView):
"""Create a club (for the Sith admin)."""
diff --git a/core/templatetags/renderer.py b/core/templatetags/renderer.py
index 927acf54..eef8bb3c 100644
--- a/core/templatetags/renderer.py
+++ b/core/templatetags/renderer.py
@@ -103,7 +103,7 @@ def add_attr(field: BoundField, attr: str):
if "=" not in d:
attrs["class"] = d
else:
- key, val = d.split("=")
+ key, val = d.split("=", maxsplit=1)
attrs[key] = val
return field.as_widget(attrs=attrs)