diff --git a/faqs/__init__.py b/faqs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/faqs/forms.py b/faqs/forms.py new file mode 100644 index 0000000..3c8aa41 --- /dev/null +++ b/faqs/forms.py @@ -0,0 +1,28 @@ +from translated_fields import language_code_formfield_callback + +from django import forms + +from .models import Faq + + +class FaqForm(forms.ModelForm): + formfield_callback = language_code_formfield_callback + + class Meta: + model = Faq + fields = [ + *Faq.title.fields, + "anchor", + *Faq.description.fields, + *Faq.content.fields, + ] + widgets = { + "description_en": forms.Textarea( + attrs={"rows": 4, "class": "is-family-monospace"} + ), + "description_fr": forms.Textarea( + attrs={"rows": 4, "class": "is-family-monospace"} + ), + "content_en": forms.Textarea(attrs={"class": "is-family-monospace"}), + "content_fr": forms.Textarea(attrs={"class": "is-family-monospace"}), + } diff --git a/faqs/migrations/0001_initial.py b/faqs/migrations/0001_initial.py new file mode 100644 index 0000000..a326379 --- /dev/null +++ b/faqs/migrations/0001_initial.py @@ -0,0 +1,66 @@ +# Generated by Django 3.2.4 on 2021-06-15 08:34 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Faq", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title_fr", models.CharField(max_length=255, verbose_name="titre")), + ( + "title_en", + models.CharField(blank=True, max_length=255, verbose_name="titre"), + ), + ("description_fr", models.TextField(verbose_name="description")), + ( + "description_en", + models.TextField(blank=True, verbose_name="description"), + ), + ("content_fr", models.TextField(blank=True, verbose_name="contenu")), + ("content_en", models.TextField(blank=True, verbose_name="contenu")), + ( + "last_modified", + models.DateField(auto_now=True, verbose_name="mise à jour"), + ), + ("anchor", models.CharField(max_length=20, verbose_name="ancre")), + ( + "author", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="faqs", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "permissions": [("is_author", "Can create faqs")], + }, + ), + migrations.AddConstraint( + model_name="faq", + constraint=models.UniqueConstraint( + fields=("anchor",), name="unique_faq_anchor" + ), + ), + ] diff --git a/faqs/migrations/__init__.py b/faqs/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/faqs/mixins.py b/faqs/mixins.py new file mode 100644 index 0000000..8a09157 --- /dev/null +++ b/faqs/mixins.py @@ -0,0 +1,14 @@ +from django.contrib.auth.mixins import PermissionRequiredMixin + + +class AdminOnlyMixin(PermissionRequiredMixin): + """Restreint l'accès aux admins""" + + permission_required = "faqs.is_author" + + +class CreatorOnlyMixin(AdminOnlyMixin): + """Restreint l'accès à l'auteur""" + + def get_queryset(self): + return super().get_queryset().filter(author=self.request.user) diff --git a/faqs/models.py b/faqs/models.py new file mode 100644 index 0000000..f972c61 --- /dev/null +++ b/faqs/models.py @@ -0,0 +1,32 @@ +from translated_fields import TranslatedFieldWithFallback + +from django.contrib.auth import get_user_model +from django.db import models +from django.utils.translation import gettext_lazy as _ + +User = get_user_model() + + +class Faq(models.Model): + title = TranslatedFieldWithFallback( + models.CharField(_("titre"), blank=False, max_length=255) + ) + description = TranslatedFieldWithFallback( + models.TextField(_("description"), blank=False) + ) + content = TranslatedFieldWithFallback(models.TextField(_("contenu"), blank=True)) + + author = models.ForeignKey( + User, related_name="faqs", null=True, on_delete=models.SET_NULL + ) + last_modified = models.DateField(_("mise à jour"), auto_now=True) + + anchor = models.CharField(_("ancre"), max_length=20) + + class Meta: + permissions = [ + ("is_author", "Can create faqs"), + ] + constraints = [ + models.UniqueConstraint(fields=["anchor"], name="unique_faq_anchor") + ] diff --git a/faqs/templates/faqs/faq.html b/faqs/templates/faqs/faq.html new file mode 100644 index 0000000..79d5c5c --- /dev/null +++ b/faqs/templates/faqs/faq.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} +{% load i18n markdown %} + + +{% block content %} + +
+ {# Titre de la FAQ #} +
+

{{ faq.title }}

+
+ +
+ {# Date de dernière modification #} +
+ {% blocktrans with maj=faq.last_modified|date:'d/m/Y' %}Mis à jour le {{ maj }}{% endblocktrans %} +
+ + {# Lien vers la page d'édition #} + {% if faq.author == user %} + + {% endif %} +
+
+
+ +{# Description #} +
+
{{ faq.description|markdown|safe }}
+
+ +{# Contenu #} +
+ {{ faq.content|markdown|safe }} +
+ +{% endblock %} diff --git a/faqs/templates/faqs/faq_create.html b/faqs/templates/faqs/faq_create.html new file mode 100644 index 0000000..c1c52b4 --- /dev/null +++ b/faqs/templates/faqs/faq_create.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% load i18n %} + + +{% block content %} + +{% for error in form.non_field_errors %} +
+ {{ error }} +
+{% endfor %} + +

{% trans "Nouvelle FAQ" %}

+
+ +{% url 'faq.list' as r_url %} +{% include "forms/common-form.html" with c_size="is-9" errors=False %} + +{% endblock %} + diff --git a/faqs/templates/faqs/faq_edit.html b/faqs/templates/faqs/faq_edit.html new file mode 100644 index 0000000..4b31145 --- /dev/null +++ b/faqs/templates/faqs/faq_edit.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% load i18n %} + + +{% block content %} + +{% for error in form.non_field_errors %} +
+ {{ error }} +
+{% endfor %} + +

{% trans "Modification de la FAQ" %}

+
+ +{% url 'faq.view' faq.anchor as r_url %} +{% include "forms/common-form.html" with c_size="is-9" errors=False %} + +{% endblock %} + diff --git a/faqs/templates/faqs/faq_list.html b/faqs/templates/faqs/faq_list.html new file mode 100644 index 0000000..5eefaf0 --- /dev/null +++ b/faqs/templates/faqs/faq_list.html @@ -0,0 +1,48 @@ +{% extends "base.html" %} +{% load i18n markdown %} + + +{% block content %} + +
+
+
+

{% trans "Liste des FAQ" %}

+
+
+ + {% if perms.faqs.is_author %} +
+ +
+ {% endif %} +
+
+ +{% for f in faq_list %} +
+
+ {{ f.title }} +
+ + {% if f.description %} +
+
+ {{ f.description|markdown|safe }} +
+
+ {% endif %} +
+{% if not forloop.last %} +
+{% endif %} +{% endfor %} + +{% endblock %} diff --git a/faqs/urls.py b/faqs/urls.py new file mode 100644 index 0000000..e89febf --- /dev/null +++ b/faqs/urls.py @@ -0,0 +1,12 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + # Admin views + path("create", views.FaqCreateView.as_view(), name="faq.create"), + path("edit/", views.FaqEditView.as_view(), name="faq.edit"), + # Public views + path("", views.FaqListView.as_view(), name="faq.list"), + path("view/", views.FaqView.as_view(), name="faq.view"), +] diff --git a/faqs/views.py b/faqs/views.py new file mode 100644 index 0000000..7857e12 --- /dev/null +++ b/faqs/views.py @@ -0,0 +1,54 @@ +from django.contrib.messages.views import SuccessMessageMixin +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ +from django.views.generic import CreateView, DetailView, ListView, UpdateView + +from .forms import FaqForm +from .mixins import AdminOnlyMixin, CreatorOnlyMixin +from .models import Faq + +# ############################################################################# +# Administration Views +# ############################################################################# + + +class FaqCreateView(AdminOnlyMixin, SuccessMessageMixin, CreateView): + model = Faq + form_class = FaqForm + success_message = _("Faq créée avec succès !") + template_name = "faqs/faq_create.html" + + def get_success_url(self): + return reverse("faq.view", args=[self.object.anchor]) + + def form_valid(self, form): + form.instance.author = self.request.user + + return super().form_valid(form) + + +class FaqEditView(CreatorOnlyMixin, SuccessMessageMixin, UpdateView): + model = Faq + form_class = FaqForm + slug_field = "anchor" + success_message = _("Faq modifiée avec succès !") + template_name = "faqs/faq_edit.html" + + def get_success_url(self): + return reverse("faq.view", args=[self.object.anchor]) + + +# ############################################################################# +# Public Views +# ############################################################################# + + +class FaqListView(ListView): + model = Faq + template_name = "faqs/faq_list.html" + + +class FaqView(DetailView): + model = Faq + template_name = "faqs/faq.html" + slug_field = "anchor" diff --git a/kadenios/settings/common.py b/kadenios/settings/common.py index bbbb328..6143d80 100644 --- a/kadenios/settings/common.py +++ b/kadenios/settings/common.py @@ -55,6 +55,7 @@ INSTALLED_APPS = [ "kadenios.apps.IgnoreSrcStaticFilesConfig", "shared", "elections", + "faqs", "authens", ] diff --git a/kadenios/urls.py b/kadenios/urls.py index 4c81a39..e8ace9b 100644 --- a/kadenios/urls.py +++ b/kadenios/urls.py @@ -8,6 +8,7 @@ urlpatterns = [ path("", HomeView.as_view(), name="kadenios"), path("admin/", admin.site.urls), path("elections/", include("elections.urls")), + path("faqs/", include("faqs.urls")), path("auth/", include("shared.auth.urls")), path("authens/", include("authens.urls")), path("i18n/", include("django.conf.urls.i18n")), diff --git a/shared/locale/en/LC_MESSAGES/django.mo b/shared/locale/en/LC_MESSAGES/django.mo index bd0732d..087532a 100644 Binary files a/shared/locale/en/LC_MESSAGES/django.mo and b/shared/locale/en/LC_MESSAGES/django.mo differ diff --git a/shared/locale/en/LC_MESSAGES/django.po b/shared/locale/en/LC_MESSAGES/django.po index 51af167..5d4b062 100644 --- a/shared/locale/en/LC_MESSAGES/django.po +++ b/shared/locale/en/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-05-28 10:58+0200\n" -"PO-Revision-Date: 2021-05-28 10:59+0200\n" +"POT-Creation-Date: 2021-06-18 19:34+0200\n" +"PO-Revision-Date: 2021-06-18 21:19+0200\n" "Last-Translator: Test Translator \n" "Language-Team: \n" "Language: en\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 2.4.3\n" +"X-Generator: Poedit 3.0\n" #: elections/forms.py:19 msgid "Impossible de faire débuter l'élection dans le passé" @@ -31,20 +31,22 @@ msgid "" "Le mail d'annonce a déjà été envoyé, il n'est pas possible d'ouvrir " "l'élection à tout le monde" msgstr "" +"The announcement email has already been sent, it is not possible to open the " +"election to everyone" -#: elections/forms.py:55 +#: elections/forms.py:63 msgid "Sélectionnez un fichier .csv" msgstr "Select a .csv file" -#: elections/forms.py:65 +#: elections/forms.py:73 msgid "Extension de fichier invalide, il faut un fichier au format CSV." msgstr "Invalid file extension, a CSV file is required." -#: elections/forms.py:72 +#: elections/forms.py:80 msgid "Objet" msgstr "Subject" -#: elections/forms.py:94 +#: elections/forms.py:102 msgid "" "L'abréviation est optionnelle et sert à identifier plus facilement les " "différentes options. Elle est affiché sans espaces et en majuscules." @@ -52,116 +54,120 @@ msgstr "" "The abbreviation is optional and serves to identify the different options " "more easily. It is displayed without spaces and in capital letters." -#: elections/forms.py:105 +#: elections/forms.py:113 msgid "Supprimer le vote de {} ({}) ?" msgstr "Delete the vote of {} ({}) ?" -#: elections/forms.py:110 elections/templates/elections/election_admin.html:191 -#: elections/templates/elections/election_admin.html:218 +#: elections/forms.py:118 elections/templates/elections/election_admin.html:243 +#: elections/templates/elections/election_admin.html:270 #: elections/templates/elections/election_voters.html:82 msgid "Supprimer" msgstr "Delete" -#: elections/forms.py:110 +#: elections/forms.py:118 msgid "Non" msgstr "No" -#: elections/forms.py:110 +#: elections/forms.py:118 msgid "Oui" msgstr "Yes" -#: elections/models.py:31 +#: elections/models.py:33 msgid "nom" msgstr "name" -#: elections/models.py:32 +#: elections/models.py:34 msgid "nom bref" msgstr "short name" -#: elections/models.py:34 +#: elections/models.py:36 faqs/models.py:15 msgid "description" msgstr "description" -#: elections/models.py:37 +#: elections/models.py:39 msgid "date et heure de début" msgstr "start date and time" -#: elections/models.py:38 +#: elections/models.py:40 msgid "date et heure de fin" msgstr "end date and time" -#: elections/models.py:41 +#: elections/models.py:42 +msgid "visible au public" +msgstr "visible to everyone" + +#: elections/models.py:45 msgid "conditions de vote" msgstr "voting requirements" -#: elections/models.py:45 +#: elections/models.py:49 msgid "restreint le vote à une liste de personnes" msgstr "restricts the vote to a list of people" -#: elections/models.py:49 +#: elections/models.py:53 msgid "mail avec les identifiants envoyé" msgstr "mail with credentials sent" -#: elections/models.py:66 +#: elections/models.py:70 msgid "résultats publics" msgstr "results published" -#: elections/models.py:67 +#: elections/models.py:71 msgid "dépouillée" msgstr "counted" -#: elections/models.py:69 +#: elections/models.py:73 msgid "archivée" msgstr "archived" -#: elections/models.py:79 +#: elections/models.py:77 +msgid "Peut administrer des élections" +msgstr "Can manage elections" + +#: elections/models.py:87 msgid "question" msgstr "question" -#: elections/models.py:81 +#: elections/models.py:90 msgid "type de question" msgstr "type of question" -#: elections/models.py:88 +#: elections/models.py:97 msgid "nombre maximal de votes reçus" msgstr "maximal number of votes received" -#: elections/models.py:139 +#: elections/models.py:154 msgid "texte" msgstr "text" -#: elections/models.py:140 +#: elections/models.py:155 msgid "abréviation" msgstr "abbreviation" -#: elections/models.py:142 +#: elections/models.py:157 msgid "option gagnante" msgstr "winning option" -#: elections/models.py:150 +#: elections/models.py:165 msgid "nombre de votes reçus" msgstr "number of votes received" -#: elections/models.py:177 +#: elections/models.py:192 msgid "rang de l'option" msgstr "option's ranking" -#: elections/models.py:193 +#: elections/models.py:208 msgid "votes supplémentaires" msgstr "extra votes" -#: elections/models.py:209 +#: elections/models.py:224 msgid "Nom et Prénom" msgstr "Name and surname" -#: elections/models.py:232 elections/tests/test_models.py:57 +#: elections/models.py:247 elections/tests/test_models.py:57 msgid "identifiants spécifiques" msgstr "dedicated credentials" -#: elections/models.py:236 -msgid "Peut administrer des élections" -msgstr "Can manage elections" - #: elections/staticdefs.py:26 elections/tests/test_models.py:56 msgid "mot de passe" msgstr "password" @@ -235,7 +241,7 @@ msgid "Élection en cours" msgstr "Election in progress" #: elections/templates/elections/election.html:52 -#: elections/templates/elections/election_list.html:72 +#: elections/templates/elections/election_list.html:83 msgid "Administrer" msgstr "Manage" @@ -280,70 +286,92 @@ msgstr "Login with credentials" msgid "Connexion via CAS" msgstr "Login via CAS" -#: elections/templates/elections/election.html:189 +#: elections/templates/elections/election.html:194 msgid "A voté" msgstr "Voted" -#: elections/templates/elections/election_admin.html:21 -#: elections/templates/elections/election_admin.html:282 -#: elections/templates/elections/election_admin.html:291 -msgid "Rajouter une question" -msgstr "Add a question" +#: elections/templates/elections/election_admin.html:51 +#: elections/templates/elections/election_list.html:58 +msgid "Élection invisible" +msgstr "Invisible election" -#: elections/templates/elections/election_admin.html:21 -msgid "Modifier la question" -msgstr "Change the question" +#: elections/templates/elections/election_admin.html:58 +msgid "Élection visible" +msgstr "Visible election" -#: elections/templates/elections/election_admin.html:26 -#: elections/templates/elections/election_admin.html:267 -#: elections/templates/elections/election_admin.html:278 -msgid "Rajouter une option" -msgstr "Add an option" - -#: elections/templates/elections/election_admin.html:26 -msgid "Modifier l'option" -msgstr "Change the option" - -#: elections/templates/elections/election_admin.html:53 +#: elections/templates/elections/election_admin.html:72 msgid "Actions" msgstr "Actions" -#: elections/templates/elections/election_admin.html:65 +#: elections/templates/elections/election_admin.html:84 +msgid "Vue classique" +msgstr "Classic view" + +#: elections/templates/elections/election_admin.html:95 +msgid "Rendre l'élection visible" +msgstr "Make the election visible" + +#: elections/templates/elections/election_admin.html:104 msgid "Exporter les votant·e·s" msgstr "Export the list of voters" -#: elections/templates/elections/election_admin.html:74 -#: elections/templates/elections/election_admin.html:200 -#: elections/templates/elections/election_admin.html:224 +#: elections/templates/elections/election_admin.html:113 +#: elections/templates/elections/election_admin.html:252 +#: elections/templates/elections/election_admin.html:276 +#: faqs/templates/faqs/faq.html:22 msgid "Modifier" msgstr "Edit" -#: elections/templates/elections/election_admin.html:83 +#: elections/templates/elections/election_admin.html:122 #: elections/templates/elections/upload_voters.html:27 msgid "Gestion de la liste de votant·e·s" msgstr "Management of the voters' list" -#: elections/templates/elections/election_admin.html:95 +#: elections/templates/elections/election_admin.html:134 #: elections/templates/elections/election_voters.html:48 msgid "Liste des votant·e·s" msgstr "Voters' list" -#: elections/templates/elections/election_admin.html:103 +#: elections/templates/elections/election_admin.html:142 msgid "Dépouiller" msgstr "Count" -#: elections/templates/elections/election_admin.html:115 +#: elections/templates/elections/election_admin.html:154 msgid "Publier" msgstr "Publish" -#: elections/templates/elections/election_admin.html:117 +#: elections/templates/elections/election_admin.html:156 msgid "Dépublier" msgstr "De-publish" -#: elections/templates/elections/election_admin.html:128 +#: elections/templates/elections/election_admin.html:166 +msgid "Télécharger les résultats" +msgstr "Download the results" + +#: elections/templates/elections/election_admin.html:175 msgid "Archiver" msgstr "Archive" +#: elections/templates/elections/election_admin.html:247 +msgid "Modifier la question" +msgstr "Change the question" + +#: elections/templates/elections/election_admin.html:276 +msgid "Modifier l'option" +msgstr "Change the option" + +#: elections/templates/elections/election_admin.html:315 +#: elections/templates/elections/election_admin.html:319 +#: elections/templates/elections/election_admin.html:330 +msgid "Rajouter une option" +msgstr "Add an option" + +#: elections/templates/elections/election_admin.html:334 +#: elections/templates/elections/election_admin.html:339 +#: elections/templates/elections/election_admin.html:343 +msgid "Rajouter une question" +msgstr "Add a question" + #: elections/templates/elections/election_ballots.html:18 #: elections/templates/elections/election_voters.html:40 #: elections/templates/elections/upload_voters.html:48 @@ -359,7 +387,7 @@ msgstr "Back" msgid "Liste des bulletins" msgstr "List of ballots" -#: elections/templates/elections/election_create.html:31 +#: elections/templates/elections/election_create.html:32 msgid "Création d'une élection" msgstr "Creating an election" @@ -371,19 +399,19 @@ msgstr "List of elections" msgid "Créer une élection" msgstr "Create an election" -#: elections/templates/elections/election_list.html:54 +#: elections/templates/elections/election_list.html:65 msgid "Élection dépouillée" msgstr "Election counted" -#: elections/templates/elections/election_list.html:60 +#: elections/templates/elections/election_list.html:71 msgid "Élection publiée" msgstr "Published election" -#: elections/templates/elections/election_list.html:66 +#: elections/templates/elections/election_list.html:77 msgid "Élection archivée" msgstr "Archived election" -#: elections/templates/elections/election_update.html:31 +#: elections/templates/elections/election_update.html:32 msgid "Modification d'une élection" msgstr "Editing an election" @@ -519,50 +547,50 @@ msgstr "Confirm" msgid "Annuler" msgstr "Cancel" -#: elections/templates/elections/vote/rank.html:150 +#: elections/templates/elections/vote/rank.html:154 msgid "Classement" msgstr "Ranking" -#: elections/templates/elections/vote/rank.html:151 +#: elections/templates/elections/vote/rank.html:155 #: elections/templates/elections/vote/select.html:24 msgid "Option(s) selectionnée(s)" msgstr "Selected option(s)" -#: elections/templates/elections/vote/rank.html:171 -#: elections/templates/elections/vote/rank.html:224 +#: elections/templates/elections/vote/rank.html:175 +#: elections/templates/elections/vote/rank.html:228 msgid "Utiliser le formulaire classique" msgstr "Use the traditional form" -#: elections/templates/elections/vote/rank.html:176 +#: elections/templates/elections/vote/rank.html:180 msgid "Utiliser le cliquer-déposer" msgstr "Use Drag & Drop" -#: elections/templates/elections/vote/rank.html:234 +#: elections/templates/elections/vote/rank.html:238 #, python-format msgid "Rang %(i)s" msgstr "Rank %(i)s" -#: elections/templates/elections/vote/rank.html:245 +#: elections/templates/elections/vote/rank.html:249 msgid "Ajouter un rang" msgstr "Add an rank" -#: elections/utils.py:207 +#: elections/utils.py:195 msgid "Vous devez sélectionnner une option." msgstr "You must select an option." -#: elections/utils.py:212 +#: elections/utils.py:200 msgid "Vous ne pouvez pas sélectionner plus d'une option." msgstr "You cannot select more than one option." -#: elections/utils.py:229 +#: elections/utils.py:217 msgid "Le classement maximal est {}." msgstr "The maximum ranking is {}." -#: elections/utils.py:233 +#: elections/utils.py:221 msgid "Le classement minimal est 1." msgstr "The minimum ranking is 1." -#: elections/utils.py:347 +#: elections/utils.py:335 msgid "" "Format invalide. Vérifiez que le fichier est bien formé (i.e. chaque ligne " "de la forme 'login,nom,email')." @@ -570,83 +598,132 @@ msgstr "" "Invalid format. Check that the file is properly formed (i.e. each line of " "the form 'login,name,e-mail')." -#: elections/utils.py:361 +#: elections/utils.py:349 msgid "La ligne {} n'a pas le bon nombre d'éléments." msgstr "The line {} has the wrong number of elements." -#: elections/utils.py:366 +#: elections/utils.py:354 msgid "Valeur manquante dans la ligne {} : 'login'." msgstr "Missing value in line {}: 'login'." -#: elections/utils.py:371 +#: elections/utils.py:359 msgid "Doublon dans les logins : lignes {} et {}." msgstr "Duplicate logins: lines {} and {}." -#: elections/utils.py:379 +#: elections/utils.py:367 msgid "Valeur manquante dans la ligne {} : 'nom'." msgstr "Missing value in line {}: 'name'." -#: elections/utils.py:385 +#: elections/utils.py:373 msgid "Adresse mail invalide à la ligne {} : '{}'." msgstr "Invalid e-mail address in line {}: '{}'." -#: elections/views.py:73 +#: elections/views.py:58 msgid "Élection créée avec succès !" msgstr "Election successfully created!" -#: elections/views.py:130 +#: elections/views.py:99 +msgid "Élection visible !" +msgstr "Election now visible!" + +#: elections/views.py:127 msgid "Liste de votant·e·s importée avec succès !" msgstr "Voters list successfully imported!" -#: elections/views.py:164 +#: elections/views.py:161 msgid "Mail d'annonce envoyé avec succès !" msgstr "Announcement e-mail sent successfully!" -#: elections/views.py:196 +#: elections/views.py:193 msgid "Élection modifiée avec succès !" msgstr "Election successfully modified!" -#: elections/views.py:279 +#: elections/views.py:276 msgid "Élection dépouillée avec succès !" msgstr "Election successfully counted!" -#: elections/views.py:305 +#: elections/views.py:302 msgid "Élection publiée avec succès !" msgstr "Election successfully published!" -#: elections/views.py:306 +#: elections/views.py:303 msgid "Élection dépubliée avec succès !" msgstr "Election successfully de-published!" -#: elections/views.py:318 +#: elections/views.py:330 msgid "Élection archivée avec succès !" msgstr "Election successfully archived!" -#: elections/views.py:350 +#: elections/views.py:362 msgid "Question modifiée avec succès !" msgstr "Question successfully modified!" -#: elections/views.py:362 +#: elections/views.py:374 msgid "Question supprimée !" msgstr "Question deleted!" -#: elections/views.py:400 +#: elections/views.py:412 msgid "Option modifiée avec succès !" msgstr "Option successfully modified!" -#: elections/views.py:412 +#: elections/views.py:424 msgid "Option supprimée !" msgstr "Option deleted!" -#: elections/views.py:566 +#: elections/views.py:578 msgid "Votre choix a bien été enregistré !" msgstr "Your choice has been recorded!" -#: kadenios/settings/common.py:138 +#: faqs/models.py:12 +msgid "titre" +msgstr "title" + +#: faqs/models.py:17 +msgid "contenu" +msgstr "content" + +#: faqs/models.py:22 +msgid "mise à jour" +msgstr "updated" + +#: faqs/models.py:24 +msgid "ancre" +msgstr "anchor" + +#: faqs/templates/faqs/faq.html:16 +#, python-format +msgid "Mis à jour le %(maj)s" +msgstr "Updated on %(maj)s" + +#: faqs/templates/faqs/faq_create.html:13 +msgid "Nouvelle FAQ" +msgstr "New FAQ" + +#: faqs/templates/faqs/faq_edit.html:13 +msgid "Modification de la FAQ" +msgstr "Editing an FAQ" + +#: faqs/templates/faqs/faq_list.html:10 +msgid "Liste des FAQ" +msgstr "List of FAQs" + +#: faqs/templates/faqs/faq_list.html:21 +msgid "Créer une FAQ" +msgstr "Create a FAQ" + +#: faqs/views.py:18 +msgid "Faq créée avec succès !" +msgstr "FAQ successfully created!" + +#: faqs/views.py:34 +msgid "Faq modifiée avec succès !" +msgstr "FAQ successfully modified!" + +#: kadenios/settings/common.py:140 msgid "Français" msgstr "French" -#: kadenios/settings/common.py:139 +#: kadenios/settings/common.py:141 msgid "Anglais" msgstr "English" @@ -698,20 +775,43 @@ msgstr "Password reset" msgid "Envoyer un mail" msgstr "Send an e-mail" -#: shared/templates/base.html:124 +#: shared/templates/authens/pwd_reset_email.txt:2 +#, python-format +msgid "" +"Quelqu'un (probablement vous) a demandé la réinitialisation du mot de passe " +"associé à cette addresse sur %(site_name)s." +msgstr "" + +#: shared/templates/authens/pwd_reset_email.txt:4 +msgid "" +"S'il s'agit bien de vous, vous pouvez vous rendre à l'adresse suivante pour " +"en choisir un nouveau : " +msgstr "" + +#: shared/templates/authens/pwd_reset_email.txt:8 +#, python-format +msgid "Pour information, votre nom d'utilisateur est le suivant : %(username)s" +msgstr "" + +#: shared/templates/authens/pwd_reset_email.txt:10 +#, python-format +msgid "L'équipe %(site_name)s" +msgstr "" + +#: shared/templates/base.html:122 msgid "Élections" msgstr "Elections" -#: shared/templates/base.html:136 +#: shared/templates/base.html:143 #, python-format msgid "Connecté·e en tant que %(name)s par %(connection)s" msgstr "Logged in as %(name)s via %(connection)s" -#: shared/templates/base.html:152 +#: shared/templates/base.html:159 msgid "Se connecter" msgstr "Log in" -#: shared/templates/base.html:235 +#: shared/templates/base.html:242 msgid "" "Développé par KDEns. En cas de pépin, contacter