From f359fab6b44e34ca3ef2bfffa89b3417a49cce41 Mon Sep 17 00:00:00 2001 From: imperosol Date: Sun, 16 Nov 2025 15:04:30 +0100 Subject: [PATCH 1/2] style: class for -like form submit buttons --- core/static/core/colors.scss | 2 ++ core/static/core/forms.scss | 28 +++++++++++++++++++++++++++ core/static/core/header.scss | 21 ++++---------------- core/static/core/style.scss | 1 - core/templates/core/base/header.jinja | 4 +++- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/core/static/core/colors.scss b/core/static/core/colors.scss index 9ed493ba..490690d9 100644 --- a/core/static/core/colors.scss +++ b/core/static/core/colors.scss @@ -21,6 +21,8 @@ $secondary-neutral-dark-color: hsl(40, 57.6%, 17%); $white-color: hsl(219.6, 20.8%, 98%); $black-color: hsl(0, 0%, 17%); +$red-text-color: #eb2f06; +$hovered-red-text-color: #ff4d4d; $faceblue: hsl(221, 44%, 41%); $twitblue: hsl(206, 82%, 63%); diff --git a/core/static/core/forms.scss b/core/static/core/forms.scss index d761331c..e1793a69 100644 --- a/core/static/core/forms.scss +++ b/core/static/core/forms.scss @@ -745,4 +745,32 @@ form { background-repeat: no-repeat; background-size: var(--nf-input-size); } + + &.no-margin { + margin:0; + } + + // a submit input that should look like a regular + input[type="submit"], button { + &.link-like { + color: $primary-dark-color; + &:hover { + color: $primary-light-color; + } + + &.link-red { + color: $red-text-color; + &:hover { + color: $hovered-red-text-color; + } + } + font-weight: normal; + font-size: 100%; + margin: auto; + background: none; + border: none; + cursor: pointer; + padding: 0; + } + } } diff --git a/core/static/core/header.scss b/core/static/core/header.scss index 7eca52f9..01e70403 100644 --- a/core/static/core/header.scss +++ b/core/static/core/header.scss @@ -5,9 +5,6 @@ $text-color: white; $background-color-hovered: #283747; -$red-text-color: #eb2f06; -$hovered-red-text-color: #ff4d4d; - .header { box-sizing: border-box; background-color: $deepblue; @@ -251,12 +248,15 @@ $hovered-red-text-color: #ff4d4d; justify-content: flex-start; } + a { + color: $text-color; + } + a, button { font-size: 100%; margin: 0; text-align: right; - color: $text-color; margin-top: auto; &:hover { @@ -268,19 +268,6 @@ $hovered-red-text-color: #ff4d4d; margin: 0; display: inline; } - - #logout-form button { - color: $red-text-color; - - &:hover { - color: $hovered-red-text-color; - } - - background: none; - border: none; - cursor: pointer; - padding: 0; - } } } } diff --git a/core/static/core/style.scss b/core/static/core/style.scss index 7522666b..b48aa7c1 100644 --- a/core/static/core/style.scss +++ b/core/static/core/style.scss @@ -519,7 +519,6 @@ th { td { margin: 5px; border-collapse: collapse; - vertical-align: top; overflow: hidden; text-overflow: ellipsis; diff --git a/core/templates/core/base/header.jinja b/core/templates/core/base/header.jinja index cb47a47c..de0169b9 100644 --- a/core/templates/core/base/header.jinja +++ b/core/templates/core/base/header.jinja @@ -61,7 +61,9 @@ {% trans %}Tools{% endtrans %}
{% csrf_token %} - +
From 32e1f09d469b92d320f0f47a6a6a26a9a99d11d6 Mon Sep 17 00:00:00 2001 From: imperosol Date: Sun, 16 Nov 2025 15:05:10 +0100 Subject: [PATCH 2/2] prevent csrf on `MembershipSetOldView` --- club/tests/test_membership.py | 31 +++++++++++++++++++++++++++- club/views.py | 29 +++++++------------------- core/templates/core/user_clubs.jinja | 19 ++++++++++++++--- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/club/tests/test_membership.py b/club/tests/test_membership.py index a3c0be50..2420043d 100644 --- a/club/tests/test_membership.py +++ b/club/tests/test_membership.py @@ -7,7 +7,7 @@ from django.conf import settings from django.contrib.auth.models import Permission from django.core.cache import cache from django.db.models import Max -from django.test import TestCase +from django.test import Client, TestCase from django.urls import reverse from django.utils.timezone import localdate, localtime, now from model_bakery import baker @@ -532,6 +532,35 @@ class TestMembership(TestClub): assert new_board == initial_board +@pytest.mark.django_db +def test_membership_set_old(client: Client): + membership = baker.make(Membership, end_date=None, user=(subscriber_user.make())) + client.force_login(membership.user) + response = client.post( + reverse("club:membership_set_old", kwargs={"membership_id": membership.id}) + ) + assertRedirects( + response, reverse("core:user_clubs", kwargs={"user_id": membership.user_id}) + ) + membership.refresh_from_db() + assert membership.end_date == localdate() + + +@pytest.mark.django_db +def test_membership_delete(client: Client): + user = baker.make(User, is_superuser=True) + membership = baker.make(Membership) + client.force_login(user) + url = reverse("club:membership_delete", kwargs={"membership_id": membership.id}) + response = client.get(url) + assert response.status_code == 200 + response = client.post(url) + assertRedirects( + response, reverse("core:user_clubs", kwargs={"user_id": membership.user_id}) + ) + assert not Membership.objects.filter(id=membership.id).exists() + + @pytest.mark.django_db class TestJoinClub: @pytest.fixture(autouse=True) diff --git a/club/views.py b/club/views.py index 5a3535a6..9077d0d7 100644 --- a/club/views.py +++ b/club/views.py @@ -34,7 +34,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.http import Http404, HttpResponseRedirect, StreamingHttpResponse +from django.http import Http404, StreamingHttpResponse from django.shortcuts import get_object_or_404, redirect from django.urls import reverse, reverse_lazy from django.utils import timezone @@ -43,6 +43,7 @@ from django.utils.timezone import now from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ from django.views.generic import DetailView, ListView, View +from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import CreateView, DeleteView, UpdateView from club.forms import ( @@ -544,33 +545,17 @@ class ClubCreateView(PermissionRequiredMixin, CreateView): permission_required = "club.add_club" -class MembershipSetOldView(CanEditMixin, DetailView): - """Set a membership as beeing old.""" +class MembershipSetOldView(CanEditMixin, SingleObjectMixin, View): + """Set a membership as being old.""" model = Membership pk_url_kwarg = "membership_id" - def get(self, request, *args, **kwargs): + def post(self, *_args, **_kwargs): self.object = self.get_object() self.object.end_date = timezone.now() self.object.save() - return HttpResponseRedirect( - reverse( - "club:club_members", - args=self.args, - kwargs={"club_id": self.object.club.id}, - ) - ) - - def post(self, request, *args, **kwargs): - self.object = self.get_object() - return HttpResponseRedirect( - reverse( - "club:club_members", - args=self.args, - kwargs={"club_id": self.object.club.id}, - ) - ) + return redirect("core:user_clubs", user_id=self.object.user_id) class MembershipDeleteView(PermissionRequiredMixin, DeleteView): @@ -582,7 +567,7 @@ class MembershipDeleteView(PermissionRequiredMixin, DeleteView): permission_required = "club.delete_membership" def get_success_url(self): - return reverse_lazy("core:user_clubs", kwargs={"user_id": self.object.user.id}) + return reverse_lazy("core:user_clubs", kwargs={"user_id": self.object.user_id}) class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView): diff --git a/core/templates/core/user_clubs.jinja b/core/templates/core/user_clubs.jinja index 51b6827f..bd365239 100644 --- a/core/templates/core/user_clubs.jinja +++ b/core/templates/core/user_clubs.jinja @@ -17,7 +17,9 @@ {% trans %}Description{% endtrans %} {% trans %}Since{% endtrans %} - + {% if user.has_perm("club.delete_membership") %} + + {% endif %} @@ -28,7 +30,16 @@ {{ m.description }} {{ m.start_date }} {% if m.can_be_edited_by(user) %} - {% trans %}Mark as old{% endtrans %} + +
+ {% csrf_token %} + +
+ {% endif %} {% if user.has_perm("club.delete_membership") %} {% trans %}Delete{% endtrans %} @@ -48,7 +59,9 @@ {% trans %}Description{% endtrans %} {% trans %}From{% endtrans %} {% trans %}To{% endtrans %} - + {% if user.has_perm("club.delete_membership") %} + + {% endif %}