From 2744282fd87e9719c54da5796ec55dc10fb9cde3 Mon Sep 17 00:00:00 2001 From: Sli Date: Wed, 14 Jan 2026 11:30:04 +0100 Subject: [PATCH] fix: bad value for blank vote and better flow for invalid form * Add an error message when looking at a public election without being logged in * Add correct value for blank vote on single vote field * Redirect to view with an error message if an invalid form has been submitted --- election/forms.py | 3 +- .../templates/election/election_detail.jinja | 49 +++++++++++-------- election/views.py | 21 ++++++-- locale/fr/LC_MESSAGES/django.po | 11 ++++- 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/election/forms.py b/election/forms.py index 4532b85c..944222ed 100644 --- a/election/forms.py +++ b/election/forms.py @@ -60,8 +60,6 @@ class CandidateForm(forms.ModelForm): class VoteForm(forms.Form): def __init__(self, election: Election, user: User, *args, **kwargs): super().__init__(*args, **kwargs) - if not election.can_vote(user): - return for role in election.roles.all(): cand = role.candidatures if role.max_choice > 1: @@ -74,6 +72,7 @@ class VoteForm(forms.Form): required=False, widget=forms.RadioSelect(), empty_label=_("Blank vote"), + blank=True, ) diff --git a/election/templates/election/election_detail.jinja b/election/templates/election/election_detail.jinja index b450e2c0..1236d77e 100644 --- a/election/templates/election/election_detail.jinja +++ b/election/templates/election/election_detail.jinja @@ -14,6 +14,11 @@ {% block content %}

{{ election.title }}

+ {% if user.is_anonymous %} +
+ {% trans %}You are not logged in, candidate pictures won't display for privacy reasons.{% endtrans %} +
+ {% endif %}

{{ election.description }}


@@ -117,7 +122,7 @@ {%- if role.max_choice == 1 and show_vote_buttons %}
{% set input_id = "blank_vote_" + role.id|string %} - + @@ -185,26 +190,28 @@
-
- {%- if (election.can_candidate(user) and election.is_candidature_active) or (user.can_edit(election) and election.is_vote_editable) %} - {% trans %}Candidate{% endtrans %} - {%- endif %} - {%- if election.is_vote_editable %} - {% trans %}Add a new list{% endtrans %} - {%- endif %} - {%- if user.can_edit(election) %} - {% if election.is_vote_editable %} - {% trans %}Add a new role{% endtrans %} - {% endif %} - {% trans %}Edit{% endtrans %} - {%- endif %} - {%- if user.is_root %} - {% trans %}Delete{% endtrans %} - {%- endif %} -
- {%- if show_vote_buttons %} + {% if not user.is_anonymous %}
- + {%- if (election.can_candidate(user) and election.is_candidature_active) or (user.can_edit(election) and election.is_vote_editable) %} + {% trans %}Candidate{% endtrans %} + {%- endif %} + {%- if election.is_vote_editable %} + {% trans %}Add a new list{% endtrans %} + {%- endif %} + {%- if user.can_edit(election) %} + {% if election.is_vote_editable %} + {% trans %}Add a new role{% endtrans %} + {% endif %} + {% trans %}Edit{% endtrans %} + {%- endif %} + {%- if user.is_root %} + {% trans %}Delete{% endtrans %} + {%- endif %}
- {%- endif %} + {%- if show_vote_buttons %} +
+ +
+ {%- endif %} + {% endif %} {% endblock %} diff --git a/election/views.py b/election/views.py index 189c34d5..addadb1a 100644 --- a/election/views.py +++ b/election/views.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING from cryptography.utils import cached_property from django.conf import settings +from django.contrib import messages from django.contrib.auth.mixins import ( LoginRequiredMixin, PermissionRequiredMixin, @@ -10,8 +11,9 @@ from django.contrib.auth.mixins import ( from django.core.exceptions import PermissionDenied from django.db import transaction from django.db.models import QuerySet -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.urls import reverse, reverse_lazy +from django.utils.translation import gettext_lazy as _ from django.views.generic import DetailView, ListView from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView @@ -53,7 +55,7 @@ class ElectionListArchivedView(CanViewMixin, ListView): class ElectionDetailView(CanViewMixin, DetailView): - """Details an election responsability by responsability.""" + """Details an election responsibility by responsibility.""" model = Election template_name = "election/election_detail.jinja" @@ -83,7 +85,7 @@ class ElectionDetailView(CanViewMixin, DetailView): return super().get(request, *arg, **kwargs) def get_context_data(self, **kwargs): - """Add additionnal data to the template.""" + """Add additional data to the template.""" user: User = self.request.user return super().get_context_data(**kwargs) | { "election_form": VoteForm(self.object, user), @@ -101,7 +103,7 @@ class ElectionDetailView(CanViewMixin, DetailView): class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView): - """Alows users to vote.""" + """Allows users to vote.""" form_class = VoteForm template_name = "election/election_detail.jinja" @@ -111,6 +113,9 @@ class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView): return get_object_or_404(Election, pk=self.kwargs["election_id"]) def test_func(self): + if not self.election.can_vote(self.request.user): + return False + groups = set(self.election.vote_groups.values_list("id", flat=True)) if ( settings.SITH_GROUP_SUBSCRIBERS_ID in groups @@ -150,11 +155,17 @@ class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView): self.vote(data) return super().form_valid(form) + def form_invalid(self, form): + messages.error(self.request, _("Form is invalid")) + return redirect( + reverse("election:detail", kwargs={"election_id": self.election.id}), + ) + def get_success_url(self, **kwargs): return reverse_lazy("election:detail", kwargs={"election_id": self.election.id}) def get_context_data(self, **kwargs): - """Add additionnal data to the template.""" + """Add additional data to the template.""" kwargs = super().get_context_data(**kwargs) kwargs["object"] = self.election kwargs["election"] = self.election diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index d98fcd5b..a4c9f8b4 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-19 23:10+0100\n" +"POT-Creation-Date: 2026-01-14 11:34+0100\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Maréchal \n" @@ -4108,6 +4108,11 @@ msgstr "Candidater" msgid "Candidature are closed for this election" msgstr "Les candidatures sont fermées pour cette élection" +#: election/templates/election/election_detail.jinja +msgid "" +"You are not logged in, candidate pictures won't display for privacy reasons." +msgstr "Vous n'êtes pas connecté, les photos des candidats ne s'afficheront pas pour des raisons de respect de la vie privée." + #: election/templates/election/election_detail.jinja msgid "Polls close " msgstr "Votes fermés" @@ -4183,6 +4188,10 @@ msgstr "au" msgid "Polls open from" msgstr "Votes ouverts du" +#: election/views.py +msgid "Form is invalid" +msgstr "Formulaire invalide" + #: forum/models.py msgid "is a category" msgstr "est une catégorie"