diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index aba3d217..2a43ffa3 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 1d68e1fe..bcd86e72 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" @@ -37,7 +37,7 @@ msgstr "Vous n'avez pas le droit de voir cette application." msgid "Select a payment method" msgstr "Sélectionnez un moyen de paiement" -#: cotisations/forms.py:73 cotisations/models.py:676 +#: cotisations/forms.py:73 cotisations/models.py:682 msgid "Member" msgstr "Adhérent" @@ -154,8 +154,8 @@ msgstr "Peut voir un objet facture" msgid "Can edit all the previous invoices" msgstr "Peut modifier toutes les factures précédentes" -#: cotisations/models.py:145 cotisations/models.py:431 cotisations/views.py:378 -#: cotisations/views.py:573 +#: cotisations/models.py:145 cotisations/models.py:456 cotisations/views.py:376 +#: cotisations/views.py:571 msgid "invoice" msgstr "facture" @@ -217,115 +217,115 @@ msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." msgid "There are no articles that you can buy." msgstr "Il n'y a pas d'articles que vous puissiez acheter." -#: cotisations/models.py:347 +#: cotisations/models.py:372 msgid "Can view a custom invoice object" msgstr "Peut voir un objet facture personnalisée" -#: cotisations/models.py:349 +#: cotisations/models.py:374 msgid "recipient" msgstr "destinataire" -#: cotisations/models.py:350 +#: cotisations/models.py:375 msgid "payment type" msgstr "type de paiement" -#: cotisations/models.py:351 +#: cotisations/models.py:376 msgid "address" msgstr "adresse" -#: cotisations/models.py:352 +#: cotisations/models.py:377 msgid "paid" msgstr "payé" -#: cotisations/models.py:353 +#: cotisations/models.py:378 msgid "remark" msgstr "remarque" -#: cotisations/models.py:358 +#: cotisations/models.py:383 msgid "Can view a cost estimate object" msgstr "Peut voir un objet devis" -#: cotisations/models.py:361 +#: cotisations/models.py:386 msgid "period of validity" msgstr "période de validité" -#: cotisations/models.py:396 +#: cotisations/models.py:421 msgid "You don't have the right to delete a cost estimate." msgstr "Vous n'avez pas le droit de supprimer un devis." -#: cotisations/models.py:402 +#: cotisations/models.py:427 msgid "The cost estimate has an invoice and can't be deleted." msgstr "Le devis a une facture et ne peut pas être supprimé." -#: cotisations/models.py:424 cotisations/models.py:682 -#: cotisations/models.py:940 +#: cotisations/models.py:449 cotisations/models.py:688 +#: cotisations/models.py:946 msgid "Connection" msgstr "Connexion" -#: cotisations/models.py:425 cotisations/models.py:683 -#: cotisations/models.py:941 +#: cotisations/models.py:450 cotisations/models.py:689 +#: cotisations/models.py:947 msgid "Membership" msgstr "Adhésion" -#: cotisations/models.py:426 cotisations/models.py:678 -#: cotisations/models.py:684 cotisations/models.py:942 +#: cotisations/models.py:451 cotisations/models.py:684 +#: cotisations/models.py:690 cotisations/models.py:948 msgid "Both of them" msgstr "Les deux" -#: cotisations/models.py:435 +#: cotisations/models.py:460 msgid "amount" msgstr "montant" -#: cotisations/models.py:438 +#: cotisations/models.py:463 msgid "article" msgstr "article" -#: cotisations/models.py:441 +#: cotisations/models.py:466 msgid "price" msgstr "prix" -#: cotisations/models.py:444 cotisations/models.py:696 +#: cotisations/models.py:469 cotisations/models.py:702 msgid "duration (in months)" msgstr "durée (en mois)" -#: cotisations/models.py:450 cotisations/models.py:702 +#: cotisations/models.py:475 cotisations/models.py:708 msgid "duration (in days, will be added to duration in months)" msgstr "durée (en jours, sera ajoutée à la durée en mois)" -#: cotisations/models.py:458 cotisations/models.py:716 -#: cotisations/models.py:953 +#: cotisations/models.py:483 cotisations/models.py:722 +#: cotisations/models.py:959 msgid "subscription type" msgstr "type de cotisation" -#: cotisations/models.py:463 +#: cotisations/models.py:488 msgid "Can view a purchase object" msgstr "Peut voir un objet achat" -#: cotisations/models.py:464 +#: cotisations/models.py:489 msgid "Can edit all the previous purchases" msgstr "Peut modifier tous les achats précédents" -#: cotisations/models.py:466 cotisations/models.py:947 +#: cotisations/models.py:491 cotisations/models.py:953 msgid "purchase" msgstr "achat" -#: cotisations/models.py:467 +#: cotisations/models.py:492 msgid "purchases" msgstr "achats" -#: cotisations/models.py:539 cotisations/models.py:736 +#: cotisations/models.py:545 cotisations/models.py:742 msgid "Duration must be specified for a subscription." msgstr "La durée doit être renseignée pour une cotisation." -#: cotisations/models.py:550 +#: cotisations/models.py:556 msgid "You don't have the right to edit a purchase." msgstr "Vous n'avez pas le droit de modifier un achat." -#: cotisations/models.py:556 +#: cotisations/models.py:562 msgid "You don't have the right to edit this user's purchases." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." -#: cotisations/models.py:565 +#: cotisations/models.py:571 msgid "" "You don't have the right to edit a purchase already controlled or " "invalidated." @@ -333,15 +333,15 @@ msgstr "" "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:580 +#: cotisations/models.py:586 msgid "You don't have the right to delete a purchase." msgstr "Vous n'avez pas le droit de supprimer un achat." -#: cotisations/models.py:586 +#: cotisations/models.py:592 msgid "You don't have the right to delete this user's purchases." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." -#: cotisations/models.py:593 +#: cotisations/models.py:599 msgid "" "You don't have the right to delete a purchase already controlled or " "invalidated." @@ -349,134 +349,134 @@ msgstr "" "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:609 +#: cotisations/models.py:615 msgid "You don't have the right to view someone else's purchase history." msgstr "" "Vous n'avez pas le droit de voir l'historique des achats d'un autre " "utilisateur." -#: cotisations/models.py:677 +#: cotisations/models.py:683 msgid "Club" msgstr "Club" -#: cotisations/models.py:687 +#: cotisations/models.py:693 msgid "designation" msgstr "désignation" -#: cotisations/models.py:690 +#: cotisations/models.py:696 msgid "unit price" msgstr "prix unitaire" -#: cotisations/models.py:708 +#: cotisations/models.py:714 msgid "type of users concerned" msgstr "type d'utilisateurs concernés" -#: cotisations/models.py:719 cotisations/models.py:820 +#: cotisations/models.py:725 cotisations/models.py:826 msgid "is available for every user" msgstr "est disponible pour chaque utilisateur" -#: cotisations/models.py:726 +#: cotisations/models.py:732 msgid "Can view an article object" msgstr "Peut voir un objet article" -#: cotisations/models.py:727 +#: cotisations/models.py:733 msgid "Can buy every article" msgstr "Peut acheter chaque article" -#: cotisations/models.py:734 +#: cotisations/models.py:740 msgid "Solde is a reserved article name." msgstr "Solde est un nom d'article réservé." -#: cotisations/models.py:759 +#: cotisations/models.py:765 msgid "You can't buy this article." msgstr "Vous ne pouvez pas acheter cet article." -#: cotisations/models.py:800 +#: cotisations/models.py:806 msgid "Can view a bank object" msgstr "Peut voir un objet banque" -#: cotisations/models.py:801 +#: cotisations/models.py:807 msgid "bank" msgstr "banque" -#: cotisations/models.py:802 +#: cotisations/models.py:808 msgid "banks" msgstr "banques" -#: cotisations/models.py:818 +#: cotisations/models.py:824 msgid "method" msgstr "moyen" -#: cotisations/models.py:825 +#: cotisations/models.py:831 msgid "is user balance" msgstr "est solde utilisateur" -#: cotisations/models.py:826 +#: cotisations/models.py:832 msgid "There should be only one balance payment method." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." -#: cotisations/models.py:832 +#: cotisations/models.py:838 msgid "Can view a payment method object" msgstr "Peut voir un objet moyen de paiement" -#: cotisations/models.py:833 +#: cotisations/models.py:839 msgid "Can use every payment method" msgstr "Peut utiliser chaque moyen de paiement" -#: cotisations/models.py:835 +#: cotisations/models.py:841 msgid "payment method" msgstr "moyen de paiement" -#: cotisations/models.py:836 +#: cotisations/models.py:842 msgid "payment methods" msgstr "moyens de paiement" -#: cotisations/models.py:875 cotisations/payment_methods/comnpay/views.py:62 +#: cotisations/models.py:881 cotisations/payment_methods/comnpay/views.py:62 #, python-format msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." -#: cotisations/models.py:885 +#: cotisations/models.py:891 msgid "The invoice was created." msgstr "La facture a été créée." -#: cotisations/models.py:905 +#: cotisations/models.py:911 msgid "You can't use this payment method." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." -#: cotisations/models.py:924 +#: cotisations/models.py:930 msgid "No custom payment methods." msgstr "Pas de moyens de paiement personnalisés." -#: cotisations/models.py:955 +#: cotisations/models.py:961 msgid "start date" msgstr "date de début" -#: cotisations/models.py:956 +#: cotisations/models.py:962 msgid "end date" msgstr "date de fin" -#: cotisations/models.py:960 +#: cotisations/models.py:966 msgid "Can view a subscription object" msgstr "Peut voir un objet cotisation" -#: cotisations/models.py:961 +#: cotisations/models.py:967 msgid "Can edit the previous subscriptions" msgstr "Peut modifier les cotisations précédentes" -#: cotisations/models.py:963 +#: cotisations/models.py:969 msgid "subscription" msgstr "cotisation" -#: cotisations/models.py:964 +#: cotisations/models.py:970 msgid "subscriptions" msgstr "cotisations" -#: cotisations/models.py:970 +#: cotisations/models.py:976 msgid "You don't have the right to edit a subscription." msgstr "Vous n'avez pas le droit de modifier une cotisation." -#: cotisations/models.py:979 +#: cotisations/models.py:985 msgid "" "You don't have the right to edit a subscription already controlled or " "invalidated." @@ -484,11 +484,11 @@ msgstr "" "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:991 +#: cotisations/models.py:997 msgid "You don't have the right to delete a subscription." msgstr "Vous n'avez pas le droit de supprimer une cotisation." -#: cotisations/models.py:998 +#: cotisations/models.py:1004 msgid "" "You don't have the right to delete a subscription already controlled or " "invalidated." @@ -496,7 +496,7 @@ msgstr "" "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1014 +#: cotisations/models.py:1020 msgid "You don't have the right to view someone else's subscription history." msgstr "" "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " @@ -784,7 +784,7 @@ msgstr "Contrôlé" #: cotisations/templates/cotisations/control.html:107 #: cotisations/templates/cotisations/delete.html:38 #: cotisations/templates/cotisations/edit_facture.html:64 -#: cotisations/views.py:168 cotisations/views.py:222 cotisations/views.py:278 +#: cotisations/views.py:170 cotisations/views.py:222 cotisations/views.py:276 msgid "Confirm" msgstr "Confirmer" @@ -921,7 +921,7 @@ msgstr "Rechargement de solde" msgid "Pay %(amount)s €" msgstr "Payer %(amount)s €" -#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1028 +#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1026 msgid "Pay" msgstr "Payer" @@ -933,11 +933,11 @@ msgstr "Créer une facture" msgid "Control the invoices" msgstr "Contrôler les factures" -#: cotisations/views.py:155 +#: cotisations/views.py:157 msgid "You need to choose at least one article." msgstr "Vous devez choisir au moins un article." -#: cotisations/views.py:169 +#: cotisations/views.py:171 msgid "New invoice" msgstr "Nouvelle facture" @@ -949,104 +949,104 @@ msgstr "Le devis a été créé." msgid "New cost estimate" msgstr "Nouveau devis" -#: cotisations/views.py:272 +#: cotisations/views.py:270 msgid "The custom invoice was created." msgstr "La facture personnalisée a été créée." -#: cotisations/views.py:282 +#: cotisations/views.py:280 msgid "New custom invoice" msgstr "Nouvelle facture personnalisée" -#: cotisations/views.py:357 cotisations/views.py:438 +#: cotisations/views.py:355 cotisations/views.py:436 msgid "The invoice was edited." msgstr "La facture a été modifiée." -#: cotisations/views.py:375 cotisations/views.py:570 +#: cotisations/views.py:373 cotisations/views.py:568 msgid "The invoice was deleted." msgstr "La facture a été supprimée." -#: cotisations/views.py:398 +#: cotisations/views.py:396 msgid "The cost estimate was edited." msgstr "Le devis a été modifié." -#: cotisations/views.py:405 +#: cotisations/views.py:403 msgid "Edit cost estimate" msgstr "Modifier le devis" -#: cotisations/views.py:419 +#: cotisations/views.py:417 msgid "An invoice was successfully created from your cost estimate." msgstr "Une facture a bien été créée à partir de votre devis." -#: cotisations/views.py:445 +#: cotisations/views.py:443 msgid "Edit custom invoice" msgstr "Modifier la facture personnalisée" -#: cotisations/views.py:507 +#: cotisations/views.py:505 msgid "The cost estimate was deleted." msgstr "Le devis a été supprimé." -#: cotisations/views.py:510 +#: cotisations/views.py:508 msgid "cost estimate" msgstr "devis" -#: cotisations/views.py:594 +#: cotisations/views.py:592 msgid "The article was created." msgstr "L'article a été créé." -#: cotisations/views.py:599 cotisations/views.py:673 cotisations/views.py:767 +#: cotisations/views.py:597 cotisations/views.py:671 cotisations/views.py:765 msgid "Add" msgstr "Ajouter" -#: cotisations/views.py:600 +#: cotisations/views.py:598 msgid "New article" msgstr "Nouvel article" -#: cotisations/views.py:617 +#: cotisations/views.py:615 msgid "The article was edited." msgstr "L'article a été modifié." -#: cotisations/views.py:622 cotisations/views.py:705 cotisations/views.py:791 +#: cotisations/views.py:620 cotisations/views.py:703 cotisations/views.py:789 msgid "Edit" msgstr "Modifier" -#: cotisations/views.py:623 +#: cotisations/views.py:621 msgid "Edit article" msgstr "Modifier l'article" -#: cotisations/views.py:640 +#: cotisations/views.py:638 msgid "The articles were deleted." msgstr "Les articles ont été supprimés." -#: cotisations/views.py:645 cotisations/views.py:744 cotisations/views.py:829 +#: cotisations/views.py:643 cotisations/views.py:742 cotisations/views.py:827 msgid "Delete" msgstr "Supprimer" -#: cotisations/views.py:646 +#: cotisations/views.py:644 msgid "Delete article" msgstr "Supprimer l'article" -#: cotisations/views.py:667 +#: cotisations/views.py:665 msgid "The payment method was created." msgstr "Le moyen de paiment a été créé." -#: cotisations/views.py:674 +#: cotisations/views.py:672 msgid "New payment method" msgstr "Nouveau moyen de paiement" -#: cotisations/views.py:699 +#: cotisations/views.py:697 msgid "The payment method was edited." msgstr "Le moyen de paiment a été modifié." -#: cotisations/views.py:706 +#: cotisations/views.py:704 msgid "Edit payment method" msgstr "Modifier le moyen de paiement" -#: cotisations/views.py:728 +#: cotisations/views.py:726 #, python-format msgid "The payment method %(method_name)s was deleted." msgstr "Le moyen de paiement %(method_name)s a été supprimé." -#: cotisations/views.py:735 +#: cotisations/views.py:733 #, python-format msgid "" "The payment method %(method_name)s can't be deleted because there are " @@ -1055,32 +1055,32 @@ msgstr "" "Le moyen de paiement %(method_name)s ne peut pas être supprimé car il y a " "des factures qui l'utilisent." -#: cotisations/views.py:745 +#: cotisations/views.py:743 msgid "Delete payment method" msgstr "Supprimer le moyen de paiement" -#: cotisations/views.py:762 +#: cotisations/views.py:760 msgid "The bank was created." msgstr "La banque a été créée." -#: cotisations/views.py:768 +#: cotisations/views.py:766 msgid "New bank" msgstr "Nouvelle banque" -#: cotisations/views.py:786 +#: cotisations/views.py:784 msgid "The bank was edited." msgstr "La banque a été modifiée." -#: cotisations/views.py:792 +#: cotisations/views.py:790 msgid "Edit bank" msgstr "Modifier la banque" -#: cotisations/views.py:814 +#: cotisations/views.py:812 #, python-format msgid "The bank %(bank_name)s was deleted." msgstr "La banque %(bank_name)s a été supprimée." -#: cotisations/views.py:820 +#: cotisations/views.py:818 #, python-format msgid "" "The bank %(bank_name)s can't be deleted because there are invoices using it." @@ -1088,22 +1088,22 @@ msgstr "" "La banque %(bank_name)s ne peut pas être supprimée car il y a des factures " "qui l'utilisent." -#: cotisations/views.py:830 +#: cotisations/views.py:828 msgid "Delete bank" msgstr "Supprimer la banque" -#: cotisations/views.py:864 +#: cotisations/views.py:862 msgid "Your changes have been properly taken into account." msgstr "Vos modifications ont correctement été prises en compte." -#: cotisations/views.py:996 +#: cotisations/views.py:994 msgid "You are not allowed to credit your balance." msgstr "Vous n'êtes pas autorisé à créditer votre solde." -#: cotisations/views.py:1027 +#: cotisations/views.py:1025 msgid "Refill your balance" msgstr "Recharger votre solde" -#: cotisations/views.py:1046 +#: cotisations/views.py:1044 msgid "Could not find a voucher for that invoice." msgstr "Impossible de trouver un reçu pour cette facture." diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 496f2f3f..966663b1 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -480,6 +480,21 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address): RadiusOption.get_cached_value("banned") != RadiusOption.REJECT, RadiusOption.get_attributes("banned_attributes", attributes_kwargs), ) + elif user.email_state == User.EMAIL_STATE_UNVERIFIED: + return ( + sw_name, + room, + u"Utilisateur suspendu (mail non confirme)", + getattr( + RadiusOption.get_cached_value("non_member_vlan"), + "vlan_id", + None, + ), + RadiusOption.get_cached_value("non_member") != RadiusOption.REJECT, + RadiusOption.get_attributes( + "non_member_attributes", attributes_kwargs + ), + ) elif not (user.is_connected() or user.is_whitelisted()): return ( sw_name, diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index f04f6bef..d7c38182 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -159,7 +159,7 @@ msgid "Statistics" msgstr "Statistiques" #: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 -#: logs/views.py:400 +#: logs/views.py:418 msgid "Actions performed" msgstr "Actions effectuées" @@ -260,65 +260,77 @@ msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" #: logs/views.py:288 +msgid "Users with a confirmed email" +msgstr "Utilisateurs ayant un mail confirmé" + +#: logs/views.py:294 +msgid "Users with an unconfirmed email" +msgstr "Utilisateurs ayant un mail non confirmé" + +#: logs/views.py:300 +msgid "Users pending email confirmation" +msgstr "Utilisateurs en attente de confirmation du mail" + +#: logs/views.py:306 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: logs/views.py:302 +#: logs/views.py:320 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: logs/views.py:319 +#: logs/views.py:337 msgid "IP range" msgstr "Plage d'IP" -#: logs/views.py:320 +#: logs/views.py:338 msgid "VLAN" msgstr "VLAN" -#: logs/views.py:321 +#: logs/views.py:339 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: logs/views.py:322 +#: logs/views.py:340 msgid "Number of assigned IP addresses" msgstr "Nombre d'adresses IP assignées" -#: logs/views.py:323 +#: logs/views.py:341 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: logs/views.py:324 +#: logs/views.py:342 msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: logs/views.py:339 +#: logs/views.py:357 msgid "Users (members and clubs)" msgstr "Utilisateurs (adhérents et clubs)" -#: logs/views.py:385 +#: logs/views.py:403 msgid "Topology" msgstr "Topologie" -#: logs/views.py:401 +#: logs/views.py:419 msgid "Number of actions" msgstr "Nombre d'actions" -#: logs/views.py:426 +#: logs/views.py:444 msgid "rights" msgstr "droits" -#: logs/views.py:455 +#: logs/views.py:473 msgid "actions" msgstr "actions" -#: logs/views.py:486 +#: logs/views.py:504 msgid "No model found." msgstr "Aucun modèle trouvé." -#: logs/views.py:492 +#: logs/views.py:510 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:499 +#: logs/views.py:517 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." diff --git a/logs/views.py b/logs/views.py index 7c509134..78971d18 100644 --- a/logs/views.py +++ b/logs/views.py @@ -284,6 +284,24 @@ def stats_general(request): _all_whitelisted.exclude(adherent__isnull=True).count(), _all_whitelisted.exclude(club__isnull=True).count(), ], + "email_state_verified_users": [ + _("Users with a confirmed email"), + User.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), + Adherent.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), + Club.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), + ], + "email_state_unverified_users": [ + _("Users with an unconfirmed email"), + User.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), + Adherent.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), + Club.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), + ], + "email_state_pending_users": [ + _("Users pending email confirmation"), + User.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), + Adherent.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), + Club.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), + ], "actives_interfaces": [ _("Active interfaces (with access to the network)"), _all_active_interfaces_count.count(), diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 049c01ad..94a700de 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -55,91 +55,91 @@ msgstr "Sélectionnez un type de machine" msgid "Automatic IPv4 assignment" msgstr "Assignation automatique IPv4" -#: machines/forms.py:177 +#: machines/forms.py:172 msgid "Current aliases" msgstr "Alias actuels" -#: machines/forms.py:199 +#: machines/forms.py:194 msgid "Machine type to add" msgstr "Type de machine à ajouter" -#: machines/forms.py:200 +#: machines/forms.py:195 msgid "Related IP type" msgstr "Type d'IP relié" -#: machines/forms.py:208 +#: machines/forms.py:203 msgid "Current machine types" msgstr "Types de machines actuels" -#: machines/forms.py:232 +#: machines/forms.py:227 msgid "IP type to add" msgstr "Type d'IP à ajouter" -#: machines/forms.py:260 +#: machines/forms.py:255 msgid "Current IP types" msgstr "Types d'IP actuels" -#: machines/forms.py:283 +#: machines/forms.py:278 msgid "Extension to add" msgstr "Extension à ajouter" -#: machines/forms.py:284 machines/templates/machines/aff_extension.html:37 +#: machines/forms.py:279 machines/templates/machines/aff_extension.html:37 msgid "A record origin" msgstr "Enregistrement A origin" -#: machines/forms.py:285 machines/templates/machines/aff_extension.html:39 +#: machines/forms.py:280 machines/templates/machines/aff_extension.html:39 msgid "AAAA record origin" msgstr "Enregistrement AAAA origin" -#: machines/forms.py:286 +#: machines/forms.py:281 msgid "SOA record to use" msgstr "Enregistrement SOA à utiliser" -#: machines/forms.py:287 +#: machines/forms.py:282 msgid "Sign with DNSSEC" msgstr "Signer avec DNSSEC" -#: machines/forms.py:295 +#: machines/forms.py:290 msgid "Current extensions" msgstr "Extensions actuelles" -#: machines/forms.py:337 +#: machines/forms.py:332 msgid "Current SOA records" msgstr "Enregistrements SOA actuels" -#: machines/forms.py:370 +#: machines/forms.py:365 msgid "Current MX records" msgstr "Enregistrements MX actuels" -#: machines/forms.py:405 +#: machines/forms.py:400 msgid "Current NS records" msgstr "Enregistrements NS actuels" -#: machines/forms.py:435 +#: machines/forms.py:430 msgid "Current TXT records" msgstr "Enregistrements TXT actuels" -#: machines/forms.py:465 +#: machines/forms.py:460 msgid "Current DNAME records" msgstr "Enregistrements DNAME actuels" -#: machines/forms.py:495 +#: machines/forms.py:490 msgid "Current SRV records" msgstr "Enregistrements SRV actuels" -#: machines/forms.py:526 +#: machines/forms.py:521 msgid "Current NAS devices" msgstr "Dispositifs NAS actuels" -#: machines/forms.py:559 +#: machines/forms.py:554 msgid "Current roles" msgstr "Rôles actuels" -#: machines/forms.py:601 +#: machines/forms.py:596 msgid "Current services" msgstr "Services actuels" -#: machines/forms.py:643 +#: machines/forms.py:638 msgid "Current VLANs" msgstr "VLANs actuels" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 4faf9166..201e577f 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/forms.py b/preferences/forms.py index 9ea9fca5..b296c0d4 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -66,8 +66,10 @@ class EditOptionalUserForm(ModelForm): self.fields["gpg_fingerprint"].label = _("GPG fingerprint") self.fields["all_can_create_club"].label = _("All can create a club") self.fields["all_can_create_adherent"].label = _("All can create a member") + self.fields["disable_emailnotyetconfirmed"].label = _("Delay before disabling accounts without a verified email") self.fields["self_adhesion"].label = _("Self registration") self.fields["shell_default"].label = _("Default shell") + self.fields["allow_set_password_during_user_creation"].label = _("Allow directly setting a password during account creation") class EditOptionalMachineForm(ModelForm): diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 0047c101..4c43a549 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -35,7 +35,7 @@ msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." #: preferences/forms.py:65 -#: preferences/templates/preferences/display_preferences.html:144 +#: preferences/templates/preferences/display_preferences.html:150 msgid "Telephone number required" msgstr "Numéro de téléphone requis" @@ -52,196 +52,207 @@ msgid "All can create a member" msgstr "Tous peuvent créer un adhérent" #: preferences/forms.py:69 +#: preferences/templates/preferences/display_preferences.html:134 +msgid "Delay before disabling accounts without a verified email" +msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" + +#: preferences/forms.py:70 #: preferences/templates/preferences/display_preferences.html:120 msgid "Self registration" msgstr "Autoinscription" -#: preferences/forms.py:70 +#: preferences/forms.py:71 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: preferences/forms.py:84 +#: preferences/forms.py:72 +msgid "Allow directly setting a password during account creation" +msgstr "" +"Permettre le choix d'un mot de passe directement lors de la création du " +"compte" + +#: preferences/forms.py:86 msgid "Possibility to set a password per machine" msgstr "Possibilité de mettre un mot de passe par machine" -#: preferences/forms.py:87 -#: preferences/templates/preferences/display_preferences.html:174 +#: preferences/forms.py:89 +#: preferences/templates/preferences/display_preferences.html:180 msgid "Maximum number of interfaces allowed for a standard user" msgstr "Nombre maximum d'interfaces autorisé pour un utilisateur standard" -#: preferences/forms.py:90 -#: preferences/templates/preferences/display_preferences.html:178 +#: preferences/forms.py:92 +#: preferences/templates/preferences/display_preferences.html:184 msgid "Maximum number of DNS aliases allowed for a standard user" msgstr "Nombre maximum d'alias DNS autorisé pour un utilisateur standard" -#: preferences/forms.py:92 +#: preferences/forms.py:94 msgid "IPv6 mode" msgstr "Mode IPv6" -#: preferences/forms.py:93 +#: preferences/forms.py:95 msgid "Can create a machine" msgstr "Peut créer une machine" -#: preferences/forms.py:136 +#: preferences/forms.py:138 msgid "General message in French" msgstr "Message général en français" -#: preferences/forms.py:137 +#: preferences/forms.py:139 msgid "General message in English" msgstr "Message général en anglais" -#: preferences/forms.py:139 +#: preferences/forms.py:141 #: preferences/templates/preferences/display_preferences.html:58 msgid "Number of results displayed when searching" msgstr "Nombre de résultats affichés lors de la recherche" -#: preferences/forms.py:142 +#: preferences/forms.py:144 msgid "Number of items per page, standard size (e.g. users)" msgstr "Nombre d'éléments par page, taille standard (ex : utilisateurs)" -#: preferences/forms.py:145 +#: preferences/forms.py:147 msgid "Number of items per page, large size (e.g. machines)" msgstr "Nombre d'éléments par page, taille importante (ex : machines)" -#: preferences/forms.py:148 +#: preferences/forms.py:150 #: preferences/templates/preferences/display_preferences.html:66 msgid "Time before expiration of the reset password link (in hours)" msgstr "" "Temps avant expiration du lien de réinitialisation de mot de passe (en " "heures)" -#: preferences/forms.py:150 +#: preferences/forms.py:152 #: preferences/templates/preferences/display_preferences.html:52 msgid "Website name" msgstr "Nom du site" -#: preferences/forms.py:151 +#: preferences/forms.py:153 #: preferences/templates/preferences/display_preferences.html:54 msgid "Email address for automatic emailing" msgstr "Adresse mail pour les mails automatiques" -#: preferences/forms.py:152 +#: preferences/forms.py:154 #: preferences/templates/preferences/display_preferences.html:76 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" -#: preferences/forms.py:153 +#: preferences/forms.py:155 #: preferences/templates/preferences/display_preferences.html:78 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: preferences/forms.py:166 +#: preferences/forms.py:168 msgid "Organisation name" msgstr "Nom de l'association" -#: preferences/forms.py:167 -#: preferences/templates/preferences/display_preferences.html:307 +#: preferences/forms.py:169 +#: preferences/templates/preferences/display_preferences.html:313 msgid "SIRET number" msgstr "Numéro SIRET" -#: preferences/forms.py:168 +#: preferences/forms.py:170 msgid "Address (line 1)" msgstr "Adresse (ligne 1)" -#: preferences/forms.py:169 +#: preferences/forms.py:171 msgid "Address (line 2)" msgstr "Adresse (ligne 2)" -#: preferences/forms.py:170 -#: preferences/templates/preferences/display_preferences.html:315 +#: preferences/forms.py:172 +#: preferences/templates/preferences/display_preferences.html:321 msgid "Contact email address" msgstr "Adresse mail de contact" -#: preferences/forms.py:171 -#: preferences/templates/preferences/display_preferences.html:319 +#: preferences/forms.py:173 +#: preferences/templates/preferences/display_preferences.html:325 msgid "Telephone number" msgstr "Numéro de téléphone" -#: preferences/forms.py:172 -#: preferences/templates/preferences/display_preferences.html:321 +#: preferences/forms.py:174 +#: preferences/templates/preferences/display_preferences.html:327 msgid "Usual name" msgstr "Nom d'usage" -#: preferences/forms.py:174 +#: preferences/forms.py:176 msgid "Account used for editing from /admin" msgstr "Compte utilisé pour les modifications depuis /admin" -#: preferences/forms.py:176 preferences/forms.py:321 +#: preferences/forms.py:178 preferences/forms.py:323 #: preferences/templates/preferences/aff_service.html:33 msgid "Description" msgstr "Description" -#: preferences/forms.py:190 +#: preferences/forms.py:192 msgid "Message for the French welcome email" msgstr "Message pour le mail de bienvenue en français" -#: preferences/forms.py:193 +#: preferences/forms.py:195 msgid "Message for the English welcome email" msgstr "Message pour le mail de bienvenue en anglais" -#: preferences/forms.py:207 +#: preferences/forms.py:209 msgid "Facebook URL" msgstr "URL du compte Facebook" -#: preferences/forms.py:208 +#: preferences/forms.py:210 msgid "Twitter URL" msgstr "URL du compte Twitter" -#: preferences/forms.py:209 -#: preferences/templates/preferences/display_preferences.html:482 +#: preferences/forms.py:211 +#: preferences/templates/preferences/display_preferences.html:488 msgid "Twitter account name" msgstr "Nom du compte Twitter" -#: preferences/forms.py:227 +#: preferences/forms.py:229 msgid "You chose to set vlan but did not set any VLAN." msgstr "" "Vous avez choisi de paramétrer vlan mais vous n'avez indiqué aucun VLAN." -#: preferences/forms.py:228 +#: preferences/forms.py:230 msgid "Please, choose a VLAN." msgstr "Veuillez choisir un VLAN." -#: preferences/forms.py:259 +#: preferences/forms.py:261 msgid "There is already a mandate taking place at the specified start date." msgstr "Il y a déjà un mandat ayant cours à la date de début renseignée." -#: preferences/forms.py:273 +#: preferences/forms.py:275 msgid "There is already a mandate taking place at the specified end date." msgstr "Il y a déjà un madant ayant cours à la date de fin renseignée." -#: preferences/forms.py:287 +#: preferences/forms.py:289 msgid "The specified dates overlap with an existing mandate." msgstr "Les dates renseignées se superposent avec un mandat existant." -#: preferences/forms.py:319 +#: preferences/forms.py:321 #: preferences/templates/preferences/aff_service.html:31 -#: preferences/templates/preferences/display_preferences.html:305 +#: preferences/templates/preferences/display_preferences.html:311 msgid "Name" msgstr "Nom" -#: preferences/forms.py:320 +#: preferences/forms.py:322 #: preferences/templates/preferences/aff_service.html:32 msgid "URL" msgstr "URL" -#: preferences/forms.py:322 +#: preferences/forms.py:324 #: preferences/templates/preferences/aff_service.html:34 msgid "Image" msgstr "Image" -#: preferences/forms.py:330 +#: preferences/forms.py:332 msgid "Current services" msgstr "Services actuels" -#: preferences/forms.py:419 +#: preferences/forms.py:421 msgid "Current email addresses" msgstr "Adresses mail actuelles" -#: preferences/forms.py:454 +#: preferences/forms.py:456 msgid "Current document templates" msgstr "Modèles de document actuels" -#: preferences/forms.py:484 +#: preferences/forms.py:486 msgid "Current attributes" msgstr "Attributs actuels" @@ -280,11 +291,19 @@ msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:111 +#: preferences/models.py:113 +msgid "" +"Users with an email address not yet confirmed will be disabled after this " +"number of days." +msgstr "" +"Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " +"après ce nombre de jours" + +#: preferences/models.py:117 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:116 +#: preferences/models.py:122 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -292,172 +311,183 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:121 +#: preferences/models.py:129 +msgid "" +"If True, users have the choice to receive an email containing a link to " +"reset their password during creation, or to directly set their password in " +"the page. If False, an email is always sent." +msgstr "" +"Si True, les utilisateurs ont le choix de recevoir un email avec un lien " +"pour changer leur mot de passe lors de la création de leur compte, ou alors " +"de choisir leur mot de passe immédiatement. Si False, un mail est toujours " +"envoyé." + +#: preferences/models.py:136 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:125 +#: preferences/models.py:140 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:126 +#: preferences/models.py:141 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:133 +#: preferences/models.py:148 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:151 +#: preferences/models.py:166 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:152 +#: preferences/models.py:167 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:153 +#: preferences/models.py:168 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:162 +#: preferences/models.py:177 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:172 +#: preferences/models.py:187 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:173 +#: preferences/models.py:188 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:193 preferences/models.py:651 +#: preferences/models.py:208 preferences/models.py:666 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:194 preferences/models.py:652 +#: preferences/models.py:209 preferences/models.py:667 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:200 +#: preferences/models.py:215 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:205 +#: preferences/models.py:220 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:211 +#: preferences/models.py:226 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:218 +#: preferences/models.py:233 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:224 +#: preferences/models.py:239 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:227 +#: preferences/models.py:242 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:230 +#: preferences/models.py:245 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:334 +#: preferences/models.py:349 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:335 +#: preferences/models.py:350 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:348 +#: preferences/models.py:363 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:350 +#: preferences/models.py:365 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:353 +#: preferences/models.py:368 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:357 +#: preferences/models.py:372 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:358 preferences/views.py:331 +#: preferences/models.py:373 preferences/views.py:331 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:359 -#: preferences/templates/preferences/display_preferences.html:201 +#: preferences/models.py:374 +#: preferences/templates/preferences/display_preferences.html:207 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:366 +#: preferences/models.py:381 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:369 +#: preferences/models.py:384 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:375 +#: preferences/models.py:390 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:376 +#: preferences/models.py:391 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:378 +#: preferences/models.py:393 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:385 +#: preferences/models.py:400 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:388 preferences/views.py:394 +#: preferences/models.py:403 preferences/views.py:394 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:391 +#: preferences/models.py:406 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:403 +#: preferences/models.py:418 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:409 +#: preferences/models.py:424 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:413 +#: preferences/models.py:428 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:414 preferences/views.py:276 +#: preferences/models.py:429 preferences/views.py:276 msgid "reminder" msgstr "rappel" -#: preferences/models.py:415 +#: preferences/models.py:430 msgid "reminders" msgstr "rappels" -#: preferences/models.py:436 +#: preferences/models.py:451 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -465,7 +495,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:444 +#: preferences/models.py:459 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -473,75 +503,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:459 +#: preferences/models.py:474 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:460 +#: preferences/models.py:475 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:480 +#: preferences/models.py:495 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:481 preferences/views.py:227 +#: preferences/models.py:496 preferences/views.py:227 msgid "service" msgstr "service" -#: preferences/models.py:482 +#: preferences/models.py:497 msgid "services" msgstr "services" -#: preferences/models.py:492 +#: preferences/models.py:507 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:498 +#: preferences/models.py:513 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:508 +#: preferences/models.py:523 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:510 +#: preferences/models.py:525 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:511 +#: preferences/models.py:526 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:519 preferences/views.py:634 +#: preferences/models.py:534 preferences/views.py:634 msgid "mandate" msgstr "mandat" -#: preferences/models.py:520 +#: preferences/models.py:535 msgid "mandates" msgstr "mandats" -#: preferences/models.py:521 +#: preferences/models.py:536 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:528 +#: preferences/models.py:543 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:529 +#: preferences/models.py:544 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:531 +#: preferences/models.py:546 msgid "start date" msgstr "date de début" -#: preferences/models.py:532 +#: preferences/models.py:547 msgid "end date" msgstr "date de fin" -#: preferences/models.py:545 +#: preferences/models.py:560 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -549,140 +579,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:560 +#: preferences/models.py:575 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:563 +#: preferences/models.py:578 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:564 +#: preferences/models.py:579 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:567 +#: preferences/models.py:582 msgid "Organisation" msgstr "Association" -#: preferences/models.py:574 +#: preferences/models.py:589 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:575 +#: preferences/models.py:590 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:593 +#: preferences/models.py:608 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:594 +#: preferences/models.py:609 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:608 +#: preferences/models.py:623 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:611 +#: preferences/models.py:626 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:616 +#: preferences/models.py:631 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:618 +#: preferences/models.py:633 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:623 +#: preferences/models.py:638 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:624 +#: preferences/models.py:639 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:628 preferences/views.py:587 +#: preferences/models.py:643 preferences/views.py:587 msgid "attribute" msgstr "attribut" -#: preferences/models.py:629 +#: preferences/models.py:644 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:631 +#: preferences/models.py:646 msgid "value" msgstr "valeur" -#: preferences/models.py:633 +#: preferences/models.py:648 msgid "comment" msgstr "commentaire" -#: preferences/models.py:634 +#: preferences/models.py:649 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:645 +#: preferences/models.py:660 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:646 -#: preferences/templates/preferences/display_preferences.html:279 +#: preferences/models.py:661 +#: preferences/templates/preferences/display_preferences.html:285 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:657 +#: preferences/models.py:672 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:658 +#: preferences/models.py:673 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:667 +#: preferences/models.py:682 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:675 +#: preferences/models.py:690 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:676 +#: preferences/models.py:691 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:682 +#: preferences/models.py:697 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:683 +#: preferences/models.py:698 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:689 +#: preferences/models.py:704 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:697 +#: preferences/models.py:712 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:698 +#: preferences/models.py:713 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:704 +#: preferences/models.py:719 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:705 +#: preferences/models.py:720 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:712 +#: preferences/models.py:727 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -690,87 +720,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:722 +#: preferences/models.py:737 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:723 +#: preferences/models.py:738 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:729 +#: preferences/models.py:744 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:730 +#: preferences/models.py:745 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:736 +#: preferences/models.py:751 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:744 +#: preferences/models.py:759 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:745 +#: preferences/models.py:760 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:751 +#: preferences/models.py:766 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:752 +#: preferences/models.py:767 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:758 +#: preferences/models.py:773 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:766 +#: preferences/models.py:781 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:767 +#: preferences/models.py:782 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:773 +#: preferences/models.py:788 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:774 +#: preferences/models.py:789 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:787 +#: preferences/models.py:802 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:788 +#: preferences/models.py:803 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:815 +#: preferences/models.py:830 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:819 +#: preferences/models.py:834 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:826 +#: preferences/models.py:841 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:832 +#: preferences/models.py:847 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:834 +#: preferences/models.py:849 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -778,19 +808,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:846 +#: preferences/models.py:861 msgid "template" msgstr "modèle" -#: preferences/models.py:847 +#: preferences/models.py:862 msgid "name" msgstr "nom" -#: preferences/models.py:850 +#: preferences/models.py:865 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:851 +#: preferences/models.py:866 msgid "document templates" msgstr "modèles de document" @@ -803,7 +833,7 @@ msgid "File" msgstr "Fichier" #: preferences/templates/preferences/aff_mailcontact.html:31 -#: preferences/templates/preferences/display_preferences.html:311 +#: preferences/templates/preferences/display_preferences.html:317 msgid "Address" msgstr "Adresse" @@ -983,12 +1013,12 @@ msgstr "Préférences générales" #: preferences/templates/preferences/display_preferences.html:46 #: preferences/templates/preferences/display_preferences.html:108 -#: preferences/templates/preferences/display_preferences.html:167 -#: preferences/templates/preferences/display_preferences.html:220 -#: preferences/templates/preferences/display_preferences.html:282 -#: preferences/templates/preferences/display_preferences.html:300 -#: preferences/templates/preferences/display_preferences.html:397 -#: preferences/templates/preferences/display_preferences.html:475 +#: preferences/templates/preferences/display_preferences.html:173 +#: preferences/templates/preferences/display_preferences.html:226 +#: preferences/templates/preferences/display_preferences.html:288 +#: preferences/templates/preferences/display_preferences.html:306 +#: preferences/templates/preferences/display_preferences.html:403 +#: preferences/templates/preferences/display_preferences.html:481 #: preferences/templates/preferences/edit_preferences.html:46 #: preferences/views.py:212 preferences/views.py:261 preferences/views.py:307 #: preferences/views.py:367 preferences/views.py:431 preferences/views.py:496 @@ -1053,232 +1083,244 @@ msgstr "Tous les utilisateurs sont actifs par défault" msgid "Allow archived users to log in" msgstr "Autoriser les utilisateurs archivés à se connecter" -#: preferences/templates/preferences/display_preferences.html:133 +#: preferences/templates/preferences/display_preferences.html:132 +msgid "Allow directly entering a password during account creation" +msgstr "" +"Permettre le choix d'un mot de passe directement lors de la création du " +"compte" + +#: preferences/templates/preferences/display_preferences.html:135 +#, fuzzy, python-format +#| msgid "%(delete_notyetactive)s days" +msgid "%(disable_emailnotyetconfirmed)s days" +msgstr "%(delete_notyetactive)s jours" + +#: preferences/templates/preferences/display_preferences.html:139 msgid "Users general permissions" msgstr "Permissions générales des utilisateurs" -#: preferences/templates/preferences/display_preferences.html:136 +#: preferences/templates/preferences/display_preferences.html:142 msgid "Default shell for users" msgstr "Interface en ligne de commande par défaut pour les utilisateurs" -#: preferences/templates/preferences/display_preferences.html:138 +#: preferences/templates/preferences/display_preferences.html:144 msgid "Users can edit their shell" msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande" -#: preferences/templates/preferences/display_preferences.html:142 +#: preferences/templates/preferences/display_preferences.html:148 msgid "Users can edit their room" msgstr "Les utilisateurs peuvent modifier leur chambre" -#: preferences/templates/preferences/display_preferences.html:148 +#: preferences/templates/preferences/display_preferences.html:154 msgid "GPG fingerprint field" msgstr "Champ empreinte GPG" -#: preferences/templates/preferences/display_preferences.html:159 +#: preferences/templates/preferences/display_preferences.html:165 msgid "Machine preferences" msgstr "Préférences de machine" -#: preferences/templates/preferences/display_preferences.html:172 +#: preferences/templates/preferences/display_preferences.html:178 msgid "Password per machine" msgstr "Mot de passe par machine" -#: preferences/templates/preferences/display_preferences.html:180 +#: preferences/templates/preferences/display_preferences.html:186 msgid "Default Time To Live (TTL) for CNAME, A and AAAA records." msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA." -#: preferences/templates/preferences/display_preferences.html:184 +#: preferences/templates/preferences/display_preferences.html:190 msgid "IPv6 support" msgstr "Support de l'IPv6" -#: preferences/templates/preferences/display_preferences.html:186 +#: preferences/templates/preferences/display_preferences.html:192 msgid "Creation of machines" msgstr "Création de machines" -#: preferences/templates/preferences/display_preferences.html:196 +#: preferences/templates/preferences/display_preferences.html:202 msgid "Topology preferences" msgstr "Préférences de topologie" -#: preferences/templates/preferences/display_preferences.html:203 +#: preferences/templates/preferences/display_preferences.html:209 msgid "Add a RADIUS key" msgstr "Ajouter une clé RADIUS" -#: preferences/templates/preferences/display_preferences.html:213 +#: preferences/templates/preferences/display_preferences.html:219 msgid "Configuration of switches" msgstr "Configuration de commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:226 +#: preferences/templates/preferences/display_preferences.html:232 msgid "Web management, activated in case of automatic provision" msgstr "Gestion web, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:228 +#: preferences/templates/preferences/display_preferences.html:234 msgid "REST management, activated in case of automatic provision" msgstr "Gestion REST, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:235 +#: preferences/templates/preferences/display_preferences.html:241 msgid "Provision of configuration for switches" msgstr "Provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:238 +#: preferences/templates/preferences/display_preferences.html:244 msgid "Switches with automatic provision" msgstr "Commutateurs réseau avec provision automatique" -#: preferences/templates/preferences/display_preferences.html:239 -#: preferences/templates/preferences/display_preferences.html:243 -#: preferences/templates/preferences/display_preferences.html:247 -#: preferences/templates/preferences/display_preferences.html:255 -#: preferences/templates/preferences/display_preferences.html:259 -#: preferences/templates/preferences/display_preferences.html:269 +#: preferences/templates/preferences/display_preferences.html:245 +#: preferences/templates/preferences/display_preferences.html:249 +#: preferences/templates/preferences/display_preferences.html:253 +#: preferences/templates/preferences/display_preferences.html:261 +#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:275 msgid "OK" msgstr "OK" -#: preferences/templates/preferences/display_preferences.html:239 -#: preferences/templates/preferences/display_preferences.html:243 -#: preferences/templates/preferences/display_preferences.html:247 -#: preferences/templates/preferences/display_preferences.html:269 +#: preferences/templates/preferences/display_preferences.html:245 +#: preferences/templates/preferences/display_preferences.html:249 +#: preferences/templates/preferences/display_preferences.html:253 +#: preferences/templates/preferences/display_preferences.html:275 msgid "Missing" msgstr "Manquant" -#: preferences/templates/preferences/display_preferences.html:242 +#: preferences/templates/preferences/display_preferences.html:248 msgid "IP range for the management of switches" msgstr "Plage d'IP pour la gestion des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:246 +#: preferences/templates/preferences/display_preferences.html:252 msgid "Server for the configuration of switches" msgstr "Serveur pour la configuration des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:250 +#: preferences/templates/preferences/display_preferences.html:256 msgid "Provision of configuration mode for switches" msgstr "Mode de provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:254 +#: preferences/templates/preferences/display_preferences.html:260 msgid "TFTP mode" msgstr "Mode TFTP" -#: preferences/templates/preferences/display_preferences.html:258 +#: preferences/templates/preferences/display_preferences.html:264 msgid "SFTP mode" msgstr "Mode SFTP" -#: preferences/templates/preferences/display_preferences.html:259 +#: preferences/templates/preferences/display_preferences.html:265 msgid "Missing credentials" msgstr "Identifiants manquants" -#: preferences/templates/preferences/display_preferences.html:263 +#: preferences/templates/preferences/display_preferences.html:269 msgid "Switch management credentials" msgstr "Identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:271 msgid "Add switch management credentials" msgstr "Ajouter des identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:276 +#: preferences/templates/preferences/display_preferences.html:282 msgid "RADIUS preferences" msgstr "Préférences RADIUS" -#: preferences/templates/preferences/display_preferences.html:285 +#: preferences/templates/preferences/display_preferences.html:291 msgid "Current RADIUS attributes" msgstr "Attributs RADIUS actuels" -#: preferences/templates/preferences/display_preferences.html:286 +#: preferences/templates/preferences/display_preferences.html:292 msgid "Add an attribute" msgstr "Ajouter un attribut" -#: preferences/templates/preferences/display_preferences.html:294 +#: preferences/templates/preferences/display_preferences.html:300 msgid "Information about the organisation" msgstr "Informations sur l'association" -#: preferences/templates/preferences/display_preferences.html:325 +#: preferences/templates/preferences/display_preferences.html:331 msgid "User object of the organisation" msgstr "Objet utilisateur de l'association" -#: preferences/templates/preferences/display_preferences.html:327 +#: preferences/templates/preferences/display_preferences.html:333 msgid "Description of the organisation" msgstr "Description de l'association" -#: preferences/templates/preferences/display_preferences.html:331 +#: preferences/templates/preferences/display_preferences.html:337 msgid "Mandates" msgstr "Mandats" -#: preferences/templates/preferences/display_preferences.html:334 +#: preferences/templates/preferences/display_preferences.html:340 msgid "Add a mandate" msgstr "Ajouter un mandat" -#: preferences/templates/preferences/display_preferences.html:343 +#: preferences/templates/preferences/display_preferences.html:349 msgid "Document templates" msgstr "Modèles de document" -#: preferences/templates/preferences/display_preferences.html:349 +#: preferences/templates/preferences/display_preferences.html:355 msgid "Add a document template" msgstr "Ajouter un modèle de document" -#: preferences/templates/preferences/display_preferences.html:353 +#: preferences/templates/preferences/display_preferences.html:359 msgid "Delete one or several document templates" msgstr " Supprimer un ou plusieurs modèles de document" -#: preferences/templates/preferences/display_preferences.html:362 +#: preferences/templates/preferences/display_preferences.html:368 msgid "Subscription preferences" msgstr "Préférences de cotisation" -#: preferences/templates/preferences/display_preferences.html:371 +#: preferences/templates/preferences/display_preferences.html:377 msgid "Send voucher by email" msgstr "Envoyer le reçu par mail" -#: preferences/templates/preferences/display_preferences.html:375 +#: preferences/templates/preferences/display_preferences.html:381 msgid "Invoices' template" msgstr "Modèle des factures" -#: preferences/templates/preferences/display_preferences.html:379 +#: preferences/templates/preferences/display_preferences.html:385 msgid "Vouchers' template" msgstr "Modèle des reçus" -#: preferences/templates/preferences/display_preferences.html:390 +#: preferences/templates/preferences/display_preferences.html:396 msgid "Message for emails" msgstr "Message pour les mails" -#: preferences/templates/preferences/display_preferences.html:403 +#: preferences/templates/preferences/display_preferences.html:409 msgid "Welcome email (in French)" msgstr "Mail de bienvenue (en français)" -#: preferences/templates/preferences/display_preferences.html:407 +#: preferences/templates/preferences/display_preferences.html:413 msgid "Welcome email (in English)" msgstr "Mail de bienvenue (en anglais)" -#: preferences/templates/preferences/display_preferences.html:417 +#: preferences/templates/preferences/display_preferences.html:423 msgid "Preferences for the membership's end email" msgstr "Préférences pour le mail de fin d'adhésion" -#: preferences/templates/preferences/display_preferences.html:423 +#: preferences/templates/preferences/display_preferences.html:429 msgid "Add a reminder" msgstr "Ajouter un rappel" -#: preferences/templates/preferences/display_preferences.html:434 +#: preferences/templates/preferences/display_preferences.html:440 msgid "List of services and homepage preferences" msgstr "Liste des services et préférences de page d'accueil" -#: preferences/templates/preferences/display_preferences.html:440 +#: preferences/templates/preferences/display_preferences.html:446 msgid "Add a service" msgstr "Ajouter un service" -#: preferences/templates/preferences/display_preferences.html:451 +#: preferences/templates/preferences/display_preferences.html:457 msgid "List of contact email addresses" msgstr "Liste des adresses mail de contact" -#: preferences/templates/preferences/display_preferences.html:457 +#: preferences/templates/preferences/display_preferences.html:463 msgid "Add an address" msgstr "Ajouter une adresse" -#: preferences/templates/preferences/display_preferences.html:459 +#: preferences/templates/preferences/display_preferences.html:465 msgid "Delete one or several addresses" msgstr "Supprimer une ou plusieurs adresses" -#: preferences/templates/preferences/display_preferences.html:468 +#: preferences/templates/preferences/display_preferences.html:474 msgid "Social networks" msgstr "Réseaux sociaux" -#: preferences/templates/preferences/display_preferences.html:480 +#: preferences/templates/preferences/display_preferences.html:486 msgid "Twitter account URL" msgstr "URL du compte Twitter" -#: preferences/templates/preferences/display_preferences.html:486 +#: preferences/templates/preferences/display_preferences.html:492 msgid "Facebook account URL" msgstr "URL du compte Facebook" diff --git a/preferences/migrations/0068_optionaluser_allow_set_password_during_user_creation.py b/preferences/migrations/0068_optionaluser_allow_set_password_during_user_creation.py new file mode 100644 index 00000000..63d9e4c9 --- /dev/null +++ b/preferences/migrations/0068_optionaluser_allow_set_password_during_user_creation.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-16 17:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0067_auto_20191120_0159'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='allow_set_password_during_user_creation', + field=models.BooleanField(default=False, help_text='If True, users have the choice to receive an email containing a link to reset their password during creation, or to directly set their password in the page. If False, an email is always sent.'), + ), + ] diff --git a/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py new file mode 100644 index 00000000..3cc12081 --- /dev/null +++ b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 00:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0068_optionaluser_allow_set_password_during_user_creation'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='disable_emailnotyetconfirmed', + field=models.IntegerField(default=2, help_text='Users with an email address not yet confirmed will be disabled after this number of days.') + ), + ] + diff --git a/preferences/models.py b/preferences/models.py index b8189384..e470303d 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -107,6 +107,12 @@ class OptionalUser(AclMixin, PreferencesModel): "Not yet active users will be deleted after this number of days." ), ) + disable_emailnotyetconfirmed = models.IntegerField( + default=2, + help_text=_( + "Users with an email address not yet confirmed will be disabled after this number of days." + ), + ) self_adhesion = models.BooleanField( default=False, help_text=_("A new user can create their account on Re2o.") ) @@ -117,6 +123,15 @@ class OptionalUser(AclMixin, PreferencesModel): " If False, only when a valid registration has been paid." ), ) + allow_set_password_during_user_creation = models.BooleanField( + default=False, + help_text=_( + "If True, users have the choice to receive an email containing" + " a link to reset their password during creation, or to directly" + " set their password in the page." + " If False, an email is always sent." + ), + ) allow_archived_connexion = models.BooleanField( default=False, help_text=_("If True, archived users are allowed to connect.") ) diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 8e00962e..8eb75918 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -128,6 +128,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Allow archived users to log in" %} {{ useroptions.allow_archived_connexion|tick }} + + {% trans "Allow directly entering a password during account creation" %} + {{ useroptions.allow_set_password_during_user_creation|tick }} + {% trans "Delay before disabling accounts without a verified email" %} + {% blocktrans with disable_emailnotyetconfirmed=useroptions.disable_emailnotyetconfirmed %}{{ disable_emailnotyetconfirmed }} days{% endblocktrans %} +

{% trans "Users general permissions" %}

diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 6f5dbd0b..92760212 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/utils.py b/re2o/utils.py index f4abc57c..348e5c55 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -117,6 +117,7 @@ def all_has_access(search_time=None, including_asso=True): search_time = timezone.now() filter_user = ( Q(state=User.STATE_ACTIVE) + & ~Q(email_state=User.EMAIL_STATE_UNVERIFIED) & ~Q( ban__in=Ban.objects.filter( Q(date_start__lt=search_time) & Q(date_end__gt=search_time) diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index ca0507ae..ce63a66e 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -82,34 +82,33 @@ msgstr "Ports" msgid "Switches" msgstr "Commutateurs réseau" -#: search/forms.py:62 search/forms.py:77 search/templates/search/search.html:29 +#: search/forms.py:62 search/forms.py:78 search/templates/search/search.html:29 #: search/templates/search/search.html:48 msgid "Search" msgstr "Rechercher" -#: search/forms.py:65 search/forms.py:80 +#: search/forms.py:65 search/forms.py:81 msgid "" -"Use « » and «,» to specify distinct words, «\"query\"» for" -" an exact search, «\\» to escape a character and «+» to" -" combine keywords." +"Use « » and «,» to specify distinct words, «\"query\"» for an exact search, " +"«\\» to escape a character and «+» to combine keywords." msgstr "" -"Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour" -" une recherche exacte, «\\» pour échapper un caractère et «+» pour" -" combiner des mots clés." +"Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour une " +"recherche exacte, «\\» pour échapper un caractère et «+» pour combiner des " +"mots clés." -#: search/forms.py:88 +#: search/forms.py:90 msgid "Users filter" msgstr "Filtre utilisateurs" -#: search/forms.py:95 +#: search/forms.py:97 msgid "Display filter" msgstr "Filtre affichage" -#: search/forms.py:101 +#: search/forms.py:103 msgid "Start date" msgstr "Date de début" -#: search/forms.py:102 +#: search/forms.py:104 msgid "End date" msgstr "Date de fin" @@ -117,47 +116,47 @@ msgstr "Date de fin" msgid "Search results" msgstr "Résultats de la recherche" -#: search/templates/search/index.html:33 +#: search/templates/search/index.html:34 msgid "Results among users:" msgstr "Résultats parmi les utilisateurs :" -#: search/templates/search/index.html:37 +#: search/templates/search/index.html:44 msgid "Results among clubs:" msgstr "Résultats parmi les clubs :" -#: search/templates/search/index.html:41 +#: search/templates/search/index.html:54 msgid "Results among machines:" msgstr "Résultats parmi les machines :" -#: search/templates/search/index.html:45 +#: search/templates/search/index.html:64 msgid "Results among invoices:" msgstr "Résultats parmi les factures :" -#: search/templates/search/index.html:49 +#: search/templates/search/index.html:74 msgid "Results among whitelists:" msgstr "Résultats parmi les accès à titre gracieux :" -#: search/templates/search/index.html:53 +#: search/templates/search/index.html:84 msgid "Results among bans:" msgstr "Résultats parmi les bannissements :" -#: search/templates/search/index.html:57 +#: search/templates/search/index.html:94 msgid "Results among rooms:" msgstr "Résultats parmi les chambres :" -#: search/templates/search/index.html:61 +#: search/templates/search/index.html:104 msgid "Results among ports:" msgstr "Résultats parmi les ports :" -#: search/templates/search/index.html:65 +#: search/templates/search/index.html:114 msgid "Results among switches:" msgstr "Résultats parmi les commutateurs réseau :" -#: search/templates/search/index.html:69 +#: search/templates/search/index.html:123 msgid "No result" msgstr "Pas de résultat" -#: search/templates/search/index.html:71 +#: search/templates/search/index.html:125 #, python-format msgid "Only the first %(max_result)s results are displayed in each category." msgstr "" @@ -171,3 +170,12 @@ msgstr "Recherche simple" #: search/templates/search/sidebar.html:35 msgid "Advanced search" msgstr "Recherche avancée" + +#~ msgid "Verified" +#~ msgstr "Confirmé" + +#~ msgid "Unverified" +#~ msgstr "Non-confirmé" + +#~ msgid "Waiting for email confirmation" +#~ msgstr "En attente de confirmation du mail" diff --git a/static/js/toggle_password_fields.js b/static/js/toggle_password_fields.js new file mode 100644 index 00000000..3c1f436f --- /dev/null +++ b/static/js/toggle_password_fields.js @@ -0,0 +1,24 @@ +/** This makes an checkbox toggle the appeareance of the + * password and password confirmations fields. + */ +function toggle_show_password_chkbox() { + var password1 = document.getElementById('id_Adherent-password1'); + var password2 = document.getElementById('id_Adherent-password2'); + + if (show_password_chkbox.checked) { + password1.parentElement.style.display = 'none'; + password2.parentElement.style.display = 'none'; + password1.required = false; + password2.required = false; + } else { + password1.parentElement.style.display = 'block'; + password2.parentElement.style.display = 'block'; + password1.required = true; + password2.required = true; + } +} + +var show_password_chkbox = document.getElementById('id_Adherent-init_password_by_mail'); +show_password_chkbox.onclick = toggle_show_password_chkbox; +toggle_show_password_chkbox(); + diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 80131b1d..5fb07ef3 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -339,19 +339,19 @@ msgstr "Si vous n'avez aucune idée de ce que vous avez fait :" msgid "Go back to a safe page" msgstr "Retourner à une page sécurisée" -#: templates/pagination.html:34 +#: templates/pagination.html:35 msgid "First" msgstr "Première page" -#: templates/pagination.html:40 +#: templates/pagination.html:41 msgid "Previous" msgstr "Précédent" -#: templates/pagination.html:60 +#: templates/pagination.html:61 msgid "Next" msgstr "Suivant" -#: templates/pagination.html:66 +#: templates/pagination.html:67 msgid "Last" msgstr "Dernière page" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 1ccfe87f..977d5325 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index ef0afb98..53df3972 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/forms.py b/users/forms.py index cef1e43a..8e60b698 100644 --- a/users/forms.py +++ b/users/forms.py @@ -3,9 +3,11 @@ # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -113,6 +115,7 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): """Changement du mot de passe""" user = super(PassForm, self).save(commit=False) user.set_password(self.cleaned_data.get("passwd1")) + user.state = User.STATE_NOT_YET_ACTIVE user.set_active() user.save() @@ -380,7 +383,42 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class AdherentCreationForm(AdherentForm): """Formulaire de création d'un user. AdherentForm auquel on ajoute une checkbox afin d'éviter les - doublons d'utilisateurs""" + doublons d'utilisateurs et, optionnellement, + un champ mot de passe""" + # Champ pour choisir si un lien est envoyé par mail pour le mot de passe + init_password_by_mail_info = _( + "If this options is set, you will receive a link to set" + " your initial password by email. If you do not have" + " any means of accessing your emails, you can disable" + " this option to set your password immediatly." + " You will still receive an email to confirm your address." + " Failure to confirm your address will result in an" + " automatic suspension of your account until you do." + ) + + init_password_by_mail = forms.BooleanField( + help_text=init_password_by_mail_info, + required=False, + initial=True + ) + init_password_by_mail.label = _("Send password reset link by email.") + + # Champs pour initialiser le mot de passe + # Validators are handled manually since theses fields aren't always required + password1 = forms.CharField( + required=False, + label=_("Password"), + widget=forms.PasswordInput, + #validators=[MinLengthValidator(8)], + max_length=255, + ) + password2 = forms.CharField( + required=False, + label=_("Password confirmation"), + widget=forms.PasswordInput, + #validators=[MinLengthValidator(8)], + max_length=255, + ) # Champ permettant d'éviter au maxium les doublons d'utilisateurs former_user_check_info = _( @@ -422,6 +460,53 @@ class AdherentCreationForm(AdherentForm): ) ) + # Remove password fields if option is disabled + if not OptionalUser.get_cached_value("allow_set_password_during_user_creation"): + self.fields.pop("init_password_by_mail") + self.fields.pop("password1") + self.fields.pop("password2") + + def clean_password1(self): + """Ignore ce champs si la case init_password_by_mail est décochée""" + send_email = self.cleaned_data.get("init_password_by_mail") + if send_email: + return None + + password1 = self.cleaned_data.get("password1") + if len(password1) < 8: + raise forms.ValidationError(_("Password must contain at least 8 characters.")) + + return password1 + + def clean_password2(self): + """Verifie que password1 et 2 sont identiques (si nécessaire)""" + send_email = self.cleaned_data.get("init_password_by_mail") + if send_email: + return None + + # Check that the two password entries match + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + raise forms.ValidationError(_("The passwords don't match.")) + + return password2 + + def save(self, commit=True): + """Set the user's password, if entered + Returns the user and a bool indicating whether + an email to init the password should be sent""" + # Save the provided password in hashed format + user = super(AdherentForm, self).save(commit=False) + + is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") + set_passwd = is_set_password_allowed and not self.cleaned_data.get("init_password_by_mail") + if set_passwd: + user.set_password(self.cleaned_data["password1"]) + + user.save() + return user + class AdherentEditForm(AdherentForm): """Formulaire d'édition d'un user. @@ -565,22 +650,17 @@ class EditServiceUserForm(ServiceUserForm): class StateForm(FormRevMixin, ModelForm): - """ Changement de l'état d'un user""" + """Change state of an user, and if its main email is verified or not""" class Meta: model = User - fields = ["state"] + fields = ["state", "email_state"] def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(StateForm, self).__init__(*args, prefix=prefix, **kwargs) - - def save(self, commit=True): - user = super(StateForm, self).save(commit=False) - if self.cleaned_data["state"]: - user.state = self.cleaned_data.get("state") - user.state_sync() - user.save() + self.fields["state"].label = _("State") + self.fields["email_state"].label = _("Email state") class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 7d630850..bf9d0f26 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -35,129 +35,150 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: users/forms.py:78 +#: users/forms.py:80 msgid "Current password" msgstr "Mot de passe actuel" -#: users/forms.py:81 users/forms.py:528 users/forms.py:556 +#: users/forms.py:83 users/forms.py:613 users/forms.py:641 msgid "New password" msgstr "Nouveau mot de passe" -#: users/forms.py:87 +#: users/forms.py:89 msgid "New password confirmation" msgstr "Confirmation du nouveau mot de passe" -#: users/forms.py:103 +#: users/forms.py:105 msgid "The new passwords don't match." msgstr "Les nouveaux mots de passe ne correspondent pas." -#: users/forms.py:109 +#: users/forms.py:111 msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." -#: users/forms.py:129 users/forms.py:186 users/models.py:1760 +#: users/forms.py:132 users/forms.py:189 users/forms.py:410 +#: users/models.py:1893 msgid "Password" msgstr "Mot de passe" -#: users/forms.py:135 users/forms.py:189 +#: users/forms.py:138 users/forms.py:192 users/forms.py:417 msgid "Password confirmation" msgstr "Confirmation du mot de passe" -#: users/forms.py:140 users/forms.py:229 +#: users/forms.py:143 users/forms.py:232 msgid "Is admin" msgstr "Est admin" -#: users/forms.py:153 +#: users/forms.py:156 msgid "You can't use an internal address as your external address." msgstr "" "Vous ne pouvez pas utiliser une adresse interne pour votre adresse externe." -#: users/forms.py:166 users/forms.py:209 +#: users/forms.py:169 users/forms.py:212 users/forms.py:491 msgid "The passwords don't match." msgstr "Les mots de passe ne correspondent pas." -#: users/forms.py:238 +#: users/forms.py:241 #, python-format msgid "User is admin: %s" msgstr "L'utilisateur est admin : %s" -#: users/forms.py:284 users/templates/users/aff_clubs.html:38 +#: users/forms.py:287 users/templates/users/aff_clubs.html:38 #: users/templates/users/aff_listright.html:63 #: users/templates/users/aff_listright.html:168 #: users/templates/users/aff_users.html:39 -#: users/templates/users/profil.html:178 users/templates/users/profil.html:342 -#: users/templates/users/profil.html:361 +#: users/templates/users/profil.html:204 users/templates/users/profil.html:368 +#: users/templates/users/profil.html:387 msgid "Username" msgstr "Pseudo" -#: users/forms.py:296 +#: users/forms.py:299 msgid "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" msgstr "" "Complètement archiver les utilisateurs ? ATTENTION: OPÉRATION CRITIQUE SI OUI" -#: users/forms.py:309 +#: users/forms.py:312 msgid "Impossible to archive users whose end access date is in the future." msgstr "" "Impossible d'archiver des utilisateurs dont la date de fin de connexion est " "dans le futur." -#: users/forms.py:324 users/templates/users/profil.html:167 -#: users/templates/users/profil.html:341 users/templates/users/profil.html:360 +#: users/forms.py:327 users/templates/users/aff_users.html:35 +#: users/templates/users/profil.html:193 users/templates/users/profil.html:367 +#: users/templates/users/profil.html:386 msgid "First name" msgstr "Prénom" -#: users/forms.py:325 users/templates/users/aff_users.html:37 -#: users/templates/users/profil.html:173 users/templates/users/profil.html:340 -#: users/templates/users/profil.html:359 +#: users/forms.py:328 users/templates/users/aff_users.html:37 +#: users/templates/users/profil.html:199 users/templates/users/profil.html:366 +#: users/templates/users/profil.html:385 msgid "Surname" msgstr "Nom" -#: users/forms.py:326 users/forms.py:466 users/models.py:1760 +#: users/forms.py:329 users/forms.py:551 users/models.py:1893 #: users/templates/users/aff_emailaddress.html:36 -#: users/templates/users/profil.html:183 +#: users/templates/users/profil.html:209 msgid "Email address" msgstr "Adresse mail" -#: users/forms.py:327 users/forms.py:464 users/forms.py:614 +#: users/forms.py:330 users/forms.py:549 users/forms.py:694 #: users/templates/users/aff_schools.html:37 -#: users/templates/users/profil.html:204 +#: users/templates/users/profil.html:230 msgid "School" msgstr "Établissement" -#: users/forms.py:328 users/forms.py:465 +#: users/forms.py:331 users/forms.py:550 #: users/templates/users/aff_serviceusers.html:34 -#: users/templates/users/profil.html:209 +#: users/templates/users/profil.html:235 msgid "Comment" msgstr "Commentaire" -#: users/forms.py:330 users/forms.py:468 +#: users/forms.py:333 users/forms.py:553 #: users/templates/users/aff_clubs.html:40 #: users/templates/users/aff_users.html:41 -#: users/templates/users/profil.html:188 +#: users/templates/users/profil.html:214 msgid "Room" msgstr "Chambre" -#: users/forms.py:331 users/forms.py:469 +#: users/forms.py:334 users/forms.py:554 msgid "No room" msgstr "Pas de chambre" -#: users/forms.py:332 users/forms.py:470 +#: users/forms.py:335 users/forms.py:555 msgid "Select a school" msgstr "Sélectionnez un établissement" -#: users/forms.py:348 +#: users/forms.py:351 msgid "Force the move?" msgstr "Forcer le déménagement ?" -#: users/forms.py:358 users/forms.py:765 +#: users/forms.py:361 users/forms.py:845 msgid "You can't use a {} address." msgstr "Vous ne pouvez pas utiliser une adresse {}." -#: users/forms.py:368 users/forms.py:493 +#: users/forms.py:371 users/forms.py:578 msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: users/forms.py:387 +#: users/forms.py:390 +msgid "" +"If this options is set, you will receive a link to set your initial password " +"by email. If you do not have any means of accessing your emails, you can " +"disable this option to set your password immediatly. You will still receive " +"an email to confirm your address. Failure to confirm your address will " +"result in an automatic suspension of your account until you do." +msgstr "" +"Si cette option est activée, vous recevrez un lien pour choisir votre mot de " +"passe par email. Si vous n'avez pas accès à vos mails, vous pouvez " +"désactiver cette option pour immédiatement entrer votre mot de passe. Vous " +"recevrez tous de même un email de confirmation. Si vous ne confirmez pas " +"votre adresse dans les délais impartis, votre connexion sera automatiquement " +"suspendue." + +#: users/forms.py:404 +msgid "Send password reset link by email." +msgstr "Envoyer le lien de modification du mot de passe par mail." + +#: users/forms.py:425 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -168,231 +189,255 @@ msgstr "" "passe oublié est à votre disposition. Si vous avez oublié votre login, " "contactez le support." -#: users/forms.py:394 +#: users/forms.py:432 msgid "I certify that I have not had an account before." msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte." -#: users/forms.py:419 +#: users/forms.py:457 msgid "I commit to accept the" msgstr "J'accepte les" -#: users/forms.py:421 +#: users/forms.py:459 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: users/forms.py:433 +#: users/forms.py:477 +msgid "Password must contain at least 8 characters." +msgstr "Le mot de passe doit contenir au moins 8 caractères." + +#: users/forms.py:518 msgid "Leave empty if you don't have any GPG key." msgstr "Laissez vide si vous n'avez pas de clé GPG." -#: users/forms.py:436 +#: users/forms.py:521 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: users/forms.py:463 users/templates/users/aff_clubs.html:36 +#: users/forms.py:548 users/templates/users/aff_clubs.html:36 #: users/templates/users/aff_serviceusers.html:32 msgid "Name" msgstr "Nom" -#: users/forms.py:471 +#: users/forms.py:556 msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" -#: users/forms.py:601 users/templates/users/aff_listright.html:38 +#: users/forms.py:662 users/templates/users/profil.html:277 +msgid "State" +msgstr "État" + +#: users/forms.py:663 +msgid "Email state" +msgstr "État du mail" + +#: users/forms.py:681 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" -#: users/forms.py:627 +#: users/forms.py:707 msgid "Shell name" msgstr "Nom de l'interface en ligne de commande" -#: users/forms.py:647 +#: users/forms.py:727 msgid "Name of the group of rights" msgstr "Nom du groupe de droits" -#: users/forms.py:659 +#: users/forms.py:739 msgid "GID. Warning: this field must not be edited after creation." msgstr "GID. Attention : ce champ ne doit pas être modifié après création." -#: users/forms.py:668 +#: users/forms.py:748 msgid "Current groups of rights" msgstr "Groupes de droits actuels" -#: users/forms.py:686 +#: users/forms.py:766 msgid "Current schools" msgstr "Établissements actuels" -#: users/forms.py:705 users/forms.py:720 users/templates/users/aff_bans.html:41 +#: users/forms.py:785 users/forms.py:800 users/templates/users/aff_bans.html:41 #: users/templates/users/aff_whitelists.html:41 msgid "End date" msgstr "Date de fin" -#: users/forms.py:735 +#: users/forms.py:815 msgid "Local part of the email address" msgstr "Partie locale de l'adresse mail" -#: users/forms.py:736 +#: users/forms.py:816 msgid "Can't contain @." msgstr "Ne peut pas contenir @." -#: users/forms.py:752 +#: users/forms.py:832 msgid "Main email address" msgstr "Adresse mail principale" -#: users/forms.py:754 +#: users/forms.py:834 msgid "Redirect local emails" msgstr "Rediriger les mails locaux" -#: users/forms.py:756 +#: users/forms.py:836 msgid "Use local emails" msgstr "Utiliser les mails locaux" -#: users/forms.py:808 +#: users/forms.py:888 msgid "This room is my room" msgstr "Il s'agit bien de ma chambre" -#: users/forms.py:813 +#: users/forms.py:893 msgid "This new connected device is mine" msgstr "Ce nouvel appareil connecté m'appartient" -#: users/models.py:105 +#: users/models.py:108 #, python-format msgid "The username \"%(label)s\" contains forbidden characters." msgstr "Le pseudo « %(label)s » contient des caractères interdits." -#: users/models.py:134 +#: users/models.py:137 msgid "Users must have an username." msgstr "Les utilisateurs doivent avoir un pseudo." -#: users/models.py:137 +#: users/models.py:140 msgid "Username should only contain [a-z0-9-]." msgstr "Le pseudo devrait seulement contenir [a-z0-9-]" -#: users/models.py:180 users/templates/users/aff_clubs.html:55 +#: users/models.py:184 users/templates/users/aff_clubs.html:55 #: users/templates/users/aff_users.html:57 -#: users/templates/users/profil.html:253 +#: users/templates/users/profil.html:279 msgid "Active" msgstr "Actif" -#: users/models.py:181 users/templates/users/aff_clubs.html:57 +#: users/models.py:185 users/templates/users/aff_clubs.html:57 #: users/templates/users/aff_users.html:59 -#: users/templates/users/profil.html:255 users/templates/users/profil.html:271 +#: users/templates/users/profil.html:281 users/templates/users/profil.html:297 msgid "Disabled" msgstr "Désactivé" -#: users/models.py:182 users/templates/users/profil.html:257 +#: users/models.py:186 users/templates/users/profil.html:283 msgid "Archived" msgstr "Archivé" -#: users/models.py:183 users/templates/users/profil.html:259 +#: users/models.py:187 users/templates/users/profil.html:285 msgid "Not yet active" msgstr "Pas encore adhéré" -#: users/models.py:184 users/templates/users/profil.html:261 +#: users/models.py:188 users/templates/users/profil.html:287 msgid "Fully archived" msgstr "Complètement archivé" -#: users/models.py:191 users/models.py:1416 +#: users/models.py:195 +msgid "Confirmed" +msgstr "Confirmé" + +#: users/models.py:196 +msgid "Not confirmed" +msgstr "Non confirmé" + +#: users/models.py:197 +msgid "Waiting for email confirmation" +msgstr "En attente de confirmation" + +#: users/models.py:204 users/models.py:1549 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." -#: users/models.py:197 +#: users/models.py:210 msgid "External email address allowing us to contact you." msgstr "Adresse mail externe nous permettant de vous contacter." -#: users/models.py:202 +#: users/models.py:215 msgid "" "Enable redirection of the local email messages to the main email address." msgstr "" "Activer la redirection des mails locaux vers l'adresse mail principale." -#: users/models.py:207 +#: users/models.py:220 msgid "Enable the local email account." msgstr "Activer le compte mail local" -#: users/models.py:216 +#: users/models.py:229 msgid "Comment, school year." msgstr "Commentaire, promotion." -#: users/models.py:225 +#: users/models.py:239 msgid "enable shortcuts on Re2o website" msgstr "activer les raccourcis sur le site de Re2o" -#: users/models.py:235 +#: users/models.py:250 msgid "Can change the password of a user" msgstr "Peut changer le mot de passe d'un utilisateur" -#: users/models.py:236 +#: users/models.py:251 msgid "Can edit the state of a user" msgstr "Peut changer l'état d'un utilisateur" -#: users/models.py:237 +#: users/models.py:252 msgid "Can force the move" msgstr "Peut forcer le déménagement" -#: users/models.py:238 +#: users/models.py:253 msgid "Can edit the shell of a user" msgstr "Peut modifier l'interface en ligne de commande d'un utilisateur" -#: users/models.py:241 +#: users/models.py:256 msgid "Can edit the groups of rights of a user (critical permission)" msgstr "" "Peut modifier les groupes de droits d'un utilisateur (permission critique)" -#: users/models.py:243 +#: users/models.py:258 msgid "Can edit all users, including those with rights" msgstr "" "Peut modifier tous les utilisateurs, y compris ceux possédant des droits" -#: users/models.py:244 +#: users/models.py:259 msgid "Can view a user object" msgstr "Peut voir un objet utilisateur" -#: users/models.py:246 +#: users/models.py:261 msgid "user (member or club)" msgstr "utilisateur (adhérent ou club)" -#: users/models.py:247 +#: users/models.py:262 msgid "users (members or clubs)" msgstr "utilisateurs (adhérents ou clubs)" -#: users/models.py:265 users/models.py:293 +#: users/models.py:280 users/models.py:308 users/models.py:318 msgid "Unknown type." msgstr "Type inconnu." -#: users/models.py:289 users/templates/users/aff_listright.html:75 +#: users/models.py:314 users/templates/users/aff_listright.html:75 #: users/templates/users/aff_listright.html:180 msgid "Member" msgstr "Adhérent" -#: users/models.py:291 +#: users/models.py:316 msgid "Club" msgstr "Club" -#: users/models.py:781 +#: users/models.py:891 msgid "Maximum number of registered machines reached." msgstr "Nombre maximum de machines enregistrées atteint." -#: users/models.py:783 +#: users/models.py:893 msgid "Re2o doesn't know wich machine type to assign." msgstr "Re2o ne sait pas quel type de machine attribuer." -#: users/models.py:806 users/templates/users/user_autocapture.html:64 +#: users/models.py:916 users/templates/users/user_autocapture.html:64 msgid "OK" msgstr "OK" -#: users/models.py:878 +#: users/models.py:1010 msgid "This user is archived." msgstr "Cet utilisateur est archivé." -#: users/models.py:892 users/models.py:946 +#: users/models.py:1024 users/models.py:1078 msgid "You don't have the right to edit this club." msgstr "Vous n'avez pas le droit de modifier ce club." -#: users/models.py:904 +#: users/models.py:1036 msgid "User with critical rights, can't be edited." msgstr "Utilisateur avec des droits critiques, ne peut être modifié." -#: users/models.py:911 +#: users/models.py:1043 msgid "" "Impossible to edit the organisation's user without the \"change_all_users\" " "right." @@ -400,61 +445,61 @@ msgstr "" "Impossible de modifier l'utilisateur de l'association sans le droit « " "change_all_users »." -#: users/models.py:923 users/models.py:961 +#: users/models.py:1055 users/models.py:1093 msgid "You don't have the right to edit another user." msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." -#: users/models.py:987 +#: users/models.py:1119 msgid "You don't have the right to change the room." msgstr "Vous n'avez pas le droit de changer la chambre." -#: users/models.py:1004 +#: users/models.py:1136 msgid "You don't have the right to change the state." msgstr "Vous n'avez pas le droit de changer l'état." -#: users/models.py:1024 +#: users/models.py:1156 msgid "You don't have the right to change the shell." msgstr "Vous n'avez pas le droit de changer l'interface en ligne de commande." -#: users/models.py:1041 users/models.py:1056 +#: users/models.py:1173 users/models.py:1188 msgid "Local email accounts must be enabled." msgstr "Les comptes mail locaux doivent être activés." -#: users/models.py:1071 +#: users/models.py:1203 msgid "You don't have the right to force the move." msgstr "Vous n'avez pas le droit de forcer le déménagement." -#: users/models.py:1086 +#: users/models.py:1218 msgid "You don't have the right to edit the user's groups of rights." msgstr "" "Vous n'avez pas le droit de modifier les groupes de droits d'un autre " "utilisateur." -#: users/models.py:1102 +#: users/models.py:1234 msgid "\"superuser\" right required to edit the superuser flag." msgstr "Droit « superuser » requis pour modifier le signalement superuser." -#: users/models.py:1127 +#: users/models.py:1259 msgid "You don't have the right to view this club." msgstr "Vous n'avez pas le droit de voir ce club." -#: users/models.py:1136 +#: users/models.py:1268 msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1151 users/models.py:1354 +#: users/models.py:1283 users/models.py:1487 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." -#: users/models.py:1168 +#: users/models.py:1300 msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: users/models.py:1190 +#: users/models.py:1323 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: users/models.py:1198 +#: users/models.py:1331 msgid "" "There is neither a local email address nor an external email address for " "this user." @@ -462,7 +507,7 @@ msgstr "" "Il n'y a pas d'adresse mail locale ni d'adresse mail externe pour cet " "utilisateur." -#: users/models.py:1205 +#: users/models.py:1338 msgid "" "You can't redirect your local emails if no external email address has been " "set." @@ -470,195 +515,195 @@ msgstr "" "Vous ne pouvez pas rediriger vos mails locaux si aucune adresse mail externe " "n'a été définie." -#: users/models.py:1225 +#: users/models.py:1358 msgid "member" msgstr "adhérent" -#: users/models.py:1226 +#: users/models.py:1359 msgid "members" msgstr "adhérents" -#: users/models.py:1243 +#: users/models.py:1376 msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:1267 +#: users/models.py:1400 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:1277 +#: users/models.py:1410 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:1307 +#: users/models.py:1440 msgid "club" msgstr "club" -#: users/models.py:1308 +#: users/models.py:1441 msgid "clubs" msgstr "clubs" -#: users/models.py:1319 +#: users/models.py:1452 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:1327 +#: users/models.py:1460 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:1420 +#: users/models.py:1553 msgid "Comment." msgstr "Commentaire." -#: users/models.py:1426 +#: users/models.py:1559 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:1427 users/views.py:332 +#: users/models.py:1560 users/views.py:356 msgid "service user" msgstr "utilisateur service" -#: users/models.py:1428 +#: users/models.py:1561 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:1432 +#: users/models.py:1565 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:1499 +#: users/models.py:1632 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:1500 +#: users/models.py:1633 msgid "school" msgstr "établissement" -#: users/models.py:1501 +#: users/models.py:1634 msgid "schools" msgstr "établissements" -#: users/models.py:1520 +#: users/models.py:1653 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:1526 +#: users/models.py:1659 msgid "Description." msgstr "Description." -#: users/models.py:1529 +#: users/models.py:1662 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:1530 +#: users/models.py:1663 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:1531 +#: users/models.py:1664 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:1576 +#: users/models.py:1709 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:1577 users/views.py:643 +#: users/models.py:1710 users/views.py:671 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:1578 +#: users/models.py:1711 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:1596 +#: users/models.py:1729 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:1597 +#: users/models.py:1730 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:1598 +#: users/models.py:1731 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:1608 +#: users/models.py:1741 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:1609 users/views.py:383 +#: users/models.py:1742 users/views.py:407 msgid "ban" msgstr "bannissement" -#: users/models.py:1610 +#: users/models.py:1743 msgid "bans" msgstr "bannissements" -#: users/models.py:1645 +#: users/models.py:1778 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:1693 +#: users/models.py:1826 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:1694 +#: users/models.py:1827 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1695 +#: users/models.py:1828 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1715 +#: users/models.py:1848 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:1912 +#: users/models.py:2046 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:1915 +#: users/models.py:2049 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:1920 +#: users/models.py:2054 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:1922 +#: users/models.py:2056 msgid "local email account" msgstr "compte mail local" -#: users/models.py:1923 +#: users/models.py:2057 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:1951 users/models.py:1986 users/models.py:2020 -#: users/models.py:2054 +#: users/models.py:2085 users/models.py:2120 users/models.py:2154 +#: users/models.py:2188 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:1956 +#: users/models.py:2090 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:1966 +#: users/models.py:2100 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:1992 +#: users/models.py:2126 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2012 +#: users/models.py:2146 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -666,13 +711,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2026 +#: users/models.py:2160 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2046 +#: users/models.py:2180 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -680,13 +725,13 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2060 +#: users/models.py:2194 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2069 +#: users/models.py:2203 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." @@ -712,18 +757,18 @@ msgstr "Fin de cotisation le" #: users/templates/users/aff_clubs.html:43 #: users/templates/users/aff_users.html:44 -#: users/templates/users/profil.html:266 +#: users/templates/users/profil.html:292 msgid "Internet access" msgstr "Accès Internet" #: users/templates/users/aff_clubs.html:44 -#: users/templates/users/aff_users.html:45 users/templates/users/profil.html:31 +#: users/templates/users/aff_users.html:45 users/templates/users/profil.html:33 msgid "Profile" msgstr "Profil" #: users/templates/users/aff_clubs.html:53 #: users/templates/users/aff_users.html:54 -#: users/templates/users/profil.html:228 +#: users/templates/users/profil.html:254 msgid "Not a member" msgstr "Non adhérent" @@ -821,10 +866,33 @@ msgid "Access group" msgstr "Group d'accès" #: users/templates/users/aff_shell.html:32 -#: users/templates/users/profil.html:307 +#: users/templates/users/profil.html:333 msgid "Shell" msgstr "Interface en ligne de commande" +#: users/templates/users/confirm_email.html:31 +#: users/templates/users/confirm_email.html:37 +#: users/templates/users/resend_confirmation_email.html:27 +msgid "Confirmation email" +msgstr "Email de confirmation" + +#: users/templates/users/confirm_email.html:38 +msgid "Confirm the email" +msgstr "Confirmer le mail" + +#: users/templates/users/confirm_email.html:38 +#, python-format +msgid "for %(name)s." +msgstr "pour %(name)s." + +#: users/templates/users/confirm_email.html:39 +#: users/templates/users/delete.html:36 +#: users/templates/users/mass_archive.html:36 +#: users/templates/users/resend_confirmation_email.html:35 users/views.py:628 +#: users/views.py:732 +msgid "Confirm" +msgstr "Confirmer" + #: users/templates/users/delete.html:29 msgid "Deletion of users" msgstr "Suppression d'utilisateurs" @@ -838,14 +906,8 @@ msgstr "" "Attention : voulez-vous vraiment supprimer cet objet %(objet_name)s " "( %(objet)s ) ?" -#: users/templates/users/delete.html:36 -#: users/templates/users/mass_archive.html:36 users/views.py:600 -#: users/views.py:704 -msgid "Confirm" -msgstr "Confirmer" - #: users/templates/users/index_ban.html:32 -#: users/templates/users/profil.html:438 users/templates/users/sidebar.html:58 +#: users/templates/users/profil.html:464 users/templates/users/sidebar.html:58 msgid "Bans" msgstr "Bannissements" @@ -916,7 +978,7 @@ msgid "Add a shell" msgstr "Ajouter une interface en ligne de commande" #: users/templates/users/index_whitelist.html:32 -#: users/templates/users/profil.html:463 users/templates/users/sidebar.html:64 +#: users/templates/users/profil.html:489 users/templates/users/sidebar.html:64 msgid "Whitelists" msgstr "Accès gracieux" @@ -941,233 +1003,254 @@ msgstr "" "débrancher et rebrancher votre câble Ethernet pour bénéficier d'une " "connexion filaire." -#: users/templates/users/profil.html:36 +#: users/templates/users/profil.html:38 #, python-format msgid "Welcome %(name)s %(surname)s" msgstr "Bienvenue %(name)s %(surname)s" -#: users/templates/users/profil.html:38 +#: users/templates/users/profil.html:40 #, python-format msgid "Profile of %(name)s %(surname)s" msgstr "Profil de %(name)s %(surname)s" -#: users/templates/users/profil.html:46 +#: users/templates/users/profil.html:47 +#, python-format +msgid "" +"Please confirm your email address before %(confirm_before_date)s, or your " +"account will be suspended." +msgstr "" +"Veuillez confirmer votre adresse mail avant le %(confirm_before_date)s, sous " +"peine de suspension de votre compte." + +#: users/templates/users/profil.html:50 +msgid "Didn't receive the email?" +msgstr "Vous n'avez pas reçu le mail ?" + +#: users/templates/users/profil.html:55 +msgid "Your account has been suspended, please confirm your email address." +msgstr "Votre compte a été suspendu, veuillez confirmer votre adresse mail." + +#: users/templates/users/profil.html:65 msgid "Your account has been banned." msgstr "Votre compte a été banni." -#: users/templates/users/profil.html:48 +#: users/templates/users/profil.html:67 #, python-format msgid "End of the ban: %(end_ban_date)s" msgstr "Fin du bannissement : %(end_ban_date)s" -#: users/templates/users/profil.html:53 +#: users/templates/users/profil.html:72 msgid "No connection" msgstr "Pas de connexion" -#: users/templates/users/profil.html:57 +#: users/templates/users/profil.html:77 +msgid "Resend the email" +msgstr "Renvoyer le mail" + +#: users/templates/users/profil.html:82 msgid "Pay for a connection" msgstr "Payer une connexion" -#: users/templates/users/profil.html:60 +#: users/templates/users/profil.html:85 msgid "Ask someone with the appropriate rights to pay for a connection." msgstr "" "Demandez à quelqu'un ayant les droits appropriés de payer une connexion." -#: users/templates/users/profil.html:66 +#: users/templates/users/profil.html:92 #, python-format msgid "Connection (until %(end_connection_date)s )" msgstr "Connexion (jusqu'au %(end_connection_date)s)" -#: users/templates/users/profil.html:70 +#: users/templates/users/profil.html:96 msgid "Extend the connection period" msgstr "Étendre la durée de connexion" -#: users/templates/users/profil.html:86 +#: users/templates/users/profil.html:112 msgid "Refill the balance" msgstr "Recharger le solde" -#: users/templates/users/profil.html:97 users/templates/users/profil.html:382 +#: users/templates/users/profil.html:123 users/templates/users/profil.html:408 msgid "Machines" msgstr "Machines" -#: users/templates/users/profil.html:101 users/templates/users/profil.html:113 -#: users/templates/users/profil.html:390 +#: users/templates/users/profil.html:127 users/templates/users/profil.html:139 +#: users/templates/users/profil.html:416 msgid "Add a machine" msgstr "Ajouter une machine" -#: users/templates/users/profil.html:109 users/templates/users/profil.html:397 +#: users/templates/users/profil.html:135 users/templates/users/profil.html:423 msgid "No machine" msgstr "Pas de machine" -#: users/templates/users/profil.html:128 +#: users/templates/users/profil.html:154 msgid "Detailed information" msgstr "Informations détaillées" -#: users/templates/users/profil.html:135 users/views.py:184 users/views.py:211 -#: users/views.py:228 users/views.py:245 users/views.py:317 users/views.py:371 -#: users/views.py:425 users/views.py:490 users/views.py:533 users/views.py:569 -#: users/views.py:629 users/views.py:675 +#: users/templates/users/profil.html:161 users/views.py:202 users/views.py:233 +#: users/views.py:252 users/views.py:269 users/views.py:341 users/views.py:395 +#: users/views.py:449 users/views.py:514 users/views.py:561 users/views.py:597 +#: users/views.py:657 users/views.py:703 msgid "Edit" msgstr "Modifier" -#: users/templates/users/profil.html:139 users/views.py:264 users/views.py:1001 +#: users/templates/users/profil.html:165 users/views.py:288 users/views.py:1035 msgid "Change the password" msgstr "Changer le mot de passe" -#: users/templates/users/profil.html:144 +#: users/templates/users/profil.html:170 msgid "Change the state" msgstr "Changer l'état" -#: users/templates/users/profil.html:150 +#: users/templates/users/profil.html:176 msgid "Edit the groups" msgstr "Modifier les groupes" -#: users/templates/users/profil.html:160 +#: users/templates/users/profil.html:186 msgid "Mailing" msgstr "Envoi de mails" -#: users/templates/users/profil.html:164 +#: users/templates/users/profil.html:190 msgid "Mailing disabled" msgstr "Envoi de mails désactivé" -#: users/templates/users/profil.html:192 +#: users/templates/users/profil.html:210 +msgid "Pending confirmation..." +msgstr "En attente de confirmation..." + +#: users/templates/users/profil.html:218 msgid "Connected" msgstr "Connecté" -#: users/templates/users/profil.html:193 +#: users/templates/users/profil.html:219 msgid "Pending connection..." msgstr "Connexion en attente..." -#: users/templates/users/profil.html:199 +#: users/templates/users/profil.html:225 msgid "Telephone number" msgstr "Numéro de téléphone" -#: users/templates/users/profil.html:214 +#: users/templates/users/profil.html:240 msgid "Registration date" msgstr "Date d'inscription" -#: users/templates/users/profil.html:219 +#: users/templates/users/profil.html:245 msgid "Last login" msgstr "Dernière connexion" -#: users/templates/users/profil.html:224 +#: users/templates/users/profil.html:250 msgid "End of membership" msgstr "Fin d'adhésion" -#: users/templates/users/profil.html:233 +#: users/templates/users/profil.html:259 msgid "Whitelist" msgstr "Accès gracieux" -#: users/templates/users/profil.html:237 users/templates/users/profil.html:280 +#: users/templates/users/profil.html:263 users/templates/users/profil.html:306 msgid "None" msgstr "Aucun" -#: users/templates/users/profil.html:242 +#: users/templates/users/profil.html:268 msgid "Ban" msgstr "Bannissement" -#: users/templates/users/profil.html:246 +#: users/templates/users/profil.html:272 msgid "Not banned" msgstr "Non banni" -#: users/templates/users/profil.html:251 -msgid "State" -msgstr "État" - -#: users/templates/users/profil.html:269 +#: users/templates/users/profil.html:295 #, python-format msgid "Active (until %(end_access)s)" msgstr "Actif (jusqu'au %(end_access)s)" -#: users/templates/users/profil.html:276 users/templates/users/sidebar.html:82 +#: users/templates/users/profil.html:302 users/templates/users/sidebar.html:82 msgid "Groups of rights" msgstr "Groupes de droits" -#: users/templates/users/profil.html:285 +#: users/templates/users/profil.html:311 msgid "Balance" msgstr "Solde" -#: users/templates/users/profil.html:292 +#: users/templates/users/profil.html:318 msgid "Refill" msgstr "Recharger" -#: users/templates/users/profil.html:300 +#: users/templates/users/profil.html:326 msgid "GPG fingerprint" msgstr "Empreinte GPG" -#: users/templates/users/profil.html:312 +#: users/templates/users/profil.html:338 msgid "Shortcuts enabled" msgstr "Raccourcis activés" -#: users/templates/users/profil.html:323 +#: users/templates/users/profil.html:349 msgid "Manage the club" msgstr "Gérer le club" -#: users/templates/users/profil.html:331 +#: users/templates/users/profil.html:357 msgid "Manage the admins and members" msgstr "Gérer les admins et les membres" -#: users/templates/users/profil.html:335 +#: users/templates/users/profil.html:361 msgid "Club admins" msgstr "Admins du clubs" -#: users/templates/users/profil.html:354 +#: users/templates/users/profil.html:380 msgid "Members" msgstr "Adhérents" -#: users/templates/users/profil.html:407 +#: users/templates/users/profil.html:433 msgid "Subscriptions" msgstr "Cotisations" -#: users/templates/users/profil.html:415 +#: users/templates/users/profil.html:441 msgid "Add a subscription" msgstr "Ajouter une cotisation" -#: users/templates/users/profil.html:420 +#: users/templates/users/profil.html:446 msgid "Edit the balance" msgstr "Modifier le solde" -#: users/templates/users/profil.html:429 +#: users/templates/users/profil.html:455 msgid "No invoice" msgstr "Pas de facture" -#: users/templates/users/profil.html:446 +#: users/templates/users/profil.html:472 msgid "Add a ban" msgstr "Ajouter un bannissement" -#: users/templates/users/profil.html:454 +#: users/templates/users/profil.html:480 msgid "No ban" msgstr "Pas de bannissement" -#: users/templates/users/profil.html:471 +#: users/templates/users/profil.html:497 msgid "Grant a whitelist" msgstr "Donner un accès gracieux" -#: users/templates/users/profil.html:479 +#: users/templates/users/profil.html:505 msgid "No whitelist" msgstr "Pas d'accès gracieux" -#: users/templates/users/profil.html:487 +#: users/templates/users/profil.html:513 msgid "Email settings" msgstr "Paramètres mail" -#: users/templates/users/profil.html:494 +#: users/templates/users/profil.html:520 msgid "Edit email settings" msgstr "Modifier les paramètres mail" -#: users/templates/users/profil.html:503 users/templates/users/profil.html:529 +#: users/templates/users/profil.html:529 users/templates/users/profil.html:555 msgid "Contact email address" msgstr "Adresse mail de contact" -#: users/templates/users/profil.html:507 +#: users/templates/users/profil.html:533 msgid "Enable the local email account" msgstr "Activer le compte mail local" -#: users/templates/users/profil.html:509 +#: users/templates/users/profil.html:535 msgid "Enable the local email redirection" msgstr "Activer la redirection mail locale" -#: users/templates/users/profil.html:513 +#: users/templates/users/profil.html:539 msgid "" "The contact email address is the email address to which we send emails to " "contact you. If you would like to use your external email address for that, " @@ -1179,10 +1262,18 @@ msgstr "" "externe pour cela, vous pouvez soit désactiver votre adresse mail locale " "soit activer la redirection des mails locaux." -#: users/templates/users/profil.html:518 +#: users/templates/users/profil.html:544 msgid "Add an email address" msgstr "Ajouter une adresse mail" +#: users/templates/users/resend_confirmation_email.html:33 +msgid "Re-send confirmation email?" +msgstr "Renvoyer l'email de confirmation ?" + +#: users/templates/users/resend_confirmation_email.html:34 +msgid "The confirmation email will be sent to" +msgstr "Le mail de confirmation sera envoyé à" + #: users/templates/users/sidebar.html:33 msgid "Create a club or organisation" msgstr "Créer un club ou une association" @@ -1242,143 +1333,160 @@ msgstr "Connecté avec l'appareil :" msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" -#: users/views.py:127 +#: users/views.py:133 +#, python-format +msgid "The user %s was created, a confirmation email was sent." +msgstr "L'utilisateur %s a été créé, un mail de confirmation a été envoyé." + +#: users/views.py:140 #, python-format msgid "The user %s was created, an email to set the password was sent." msgstr "" "L'utilisateur %s a été créé, un mail pour initialiser le mot de passe a été " "envoyé." -#: users/views.py:137 +#: users/views.py:153 msgid "Commit" msgstr "Valider" -#: users/views.py:156 +#: users/views.py:174 #, python-format msgid "The club %s was created, an email to set the password was sent." msgstr "" "Le club %s a été créé, un mail pour initialiser le mot de passe a été envoyé." -#: users/views.py:161 +#: users/views.py:179 msgid "Create a club" msgstr "Créer un club" -#: users/views.py:176 +#: users/views.py:194 msgid "The club was edited." msgstr "Le club a été modifié." -#: users/views.py:208 +#: users/views.py:226 msgid "The user was edited." msgstr "L'utilisateur a été modifié." -#: users/views.py:225 -msgid "The state was edited." -msgstr "L'état a été modifié." +#: users/views.py:229 +msgid "Sent a new confirmation email." +msgstr "Un nouveau mail de confirmation a été envoyé." -#: users/views.py:242 +#: users/views.py:247 +msgid "The states were edited." +msgstr "Les états ont été modifié." + +#: users/views.py:249 +msgid "An email to confirm the address was sent." +msgstr "Un mail pour confirmer l'adresse a été envoyé." + +#: users/views.py:266 msgid "The groups were edited." msgstr "Les groupes ont été modifiés." -#: users/views.py:261 users/views.py:998 +#: users/views.py:285 users/views.py:1032 msgid "The password was changed." msgstr "Le mot de passe a été changé." -#: users/views.py:276 +#: users/views.py:300 #, python-format msgid "%s was removed from the group." msgstr "%s a été retiré du groupe." -#: users/views.py:286 +#: users/views.py:310 #, python-format msgid "%s is no longer superuser." msgstr "%s n'est plus superutilisateur." -#: users/views.py:297 +#: users/views.py:321 msgid "The service user was created." msgstr "L'utilisateur service a été créé." -#: users/views.py:300 users/views.py:354 users/views.py:405 users/views.py:463 -#: users/views.py:551 users/views.py:614 users/views.py:657 +#: users/views.py:324 users/views.py:378 users/views.py:429 users/views.py:487 +#: users/views.py:579 users/views.py:642 users/views.py:685 msgid "Add" msgstr "Ajouter" -#: users/views.py:314 +#: users/views.py:338 msgid "The service user was edited." msgstr "L'utilisateur service a été modifié." -#: users/views.py:329 +#: users/views.py:353 msgid "The service user was deleted." msgstr "L'utilisateur service a été supprimé." -#: users/views.py:349 +#: users/views.py:373 msgid "The ban was added." msgstr "Le bannissement a été ajouté." -#: users/views.py:352 +#: users/views.py:376 msgid "Warning: this user already has an active ban." msgstr "Attention : cet utilisateur a déjà un bannissement actif." -#: users/views.py:368 +#: users/views.py:392 msgid "The ban was edited." msgstr "Le bannissement a été modifié." -#: users/views.py:381 +#: users/views.py:405 msgid "The ban was deleted." msgstr "Le bannissement a été supprimé." -#: users/views.py:398 +#: users/views.py:422 msgid "The whitelist was added." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:402 +#: users/views.py:426 msgid "Warning: this user already has an active whitelist." msgstr "Attention : cet utilisateur a déjà un accès gracieux actif." -#: users/views.py:422 +#: users/views.py:446 msgid "The whitelist was edited." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:437 +#: users/views.py:461 msgid "The whitelist was deleted." msgstr "L'accès gracieux a été supprimé." -#: users/views.py:442 +#: users/views.py:466 msgid "whitelist" msgstr "accès gracieux" -#: users/views.py:457 +#: users/views.py:481 msgid "The local email account was created." msgstr "Le compte mail local a été créé." -#: users/views.py:480 +#: users/views.py:504 msgid "The local email account was edited." msgstr "Le compte mail local a été modifié." -#: users/views.py:503 +#: users/views.py:527 msgid "The local email account was deleted." msgstr "Le compte mail local a été supprimé." -#: users/views.py:508 +#: users/views.py:532 msgid "email address" msgstr "adresse mail" -#: users/views.py:524 +#: users/views.py:548 msgid "The email settings were edited." msgstr "Les paramètres mail ont été modifiés." -#: users/views.py:548 +#: users/views.py:551 users/views.py:1077 +msgid "An email to confirm your address was sent." +msgstr "Un mail pour confirmer votre adresse a été envoyé." + +#: users/views.py:576 msgid "The school was added." msgstr "L'établissement a été ajouté." -#: users/views.py:566 +#: users/views.py:594 msgid "The school was edited." msgstr "L'établissement a été modifié." -#: users/views.py:588 +#: users/views.py:616 msgid "The school was deleted." msgstr "L'établissement a été supprimé." -#: users/views.py:593 +#: users/views.py:621 #, python-format msgid "" "The school %s is assigned to at least one user, impossible to delete it." @@ -1386,31 +1494,31 @@ msgstr "" "L'établissement %s est assigné à au moins un utilisateur, impossible de le " "supprimer." -#: users/views.py:611 +#: users/views.py:639 msgid "The shell was added." msgstr "L'interface en ligne de commande a été ajoutée." -#: users/views.py:626 +#: users/views.py:654 msgid "The shell was edited." msgstr "L'interface en ligne de commande a été modifiée." -#: users/views.py:641 +#: users/views.py:669 msgid "The shell was deleted." msgstr "L'interface en ligne de commande a été supprimée." -#: users/views.py:654 +#: users/views.py:682 msgid "The group of rights was added." msgstr "Le groupe de droits a été ajouté." -#: users/views.py:672 +#: users/views.py:700 msgid "The group of rights was edited." msgstr "Le groupe de droits a été modifié." -#: users/views.py:692 +#: users/views.py:720 msgid "The group of rights was deleted." msgstr "Le groupe de droits a été supprimé." -#: users/views.py:697 +#: users/views.py:725 #, python-format msgid "" "The group of rights %s is assigned to at least one user, impossible to " @@ -1419,32 +1527,37 @@ msgstr "" "Le groupe de droits %s est assigné à au moins un utilisateur, impossible de " "le supprimer." -#: users/views.py:733 +#: users/views.py:761 #, python-format msgid "%s users were archived." msgstr "%s utilisateurs ont été archivés." -#: users/views.py:962 +#: users/views.py:990 users/views.py:1073 msgid "The user doesn't exist." msgstr "L'utilisateur n'existe pas." -#: users/views.py:964 users/views.py:972 +#: users/views.py:992 users/views.py:1000 msgid "Reset" msgstr "Réinitialiser" -#: users/views.py:969 +#: users/views.py:997 msgid "An email to reset the password was sent." msgstr "Un mail pour réinitialiser le mot de passe a été envoyé." -#: users/views.py:984 +#: users/views.py:1015 msgid "Error: please contact an admin." msgstr "Erreur : veuillez contacter un admin." -#: users/views.py:1020 +#: users/views.py:1053 +#, python-format +msgid "The %s address was confirmed." +msgstr "L'adresse mail %s a été confirmée." + +#: users/views.py:1100 msgid "Incorrect URL, or already registered device." msgstr "URL incorrect, ou appareil déjà enregistré." -#: users/views.py:1032 +#: users/views.py:1112 msgid "" "Successful registration! Please disconnect and reconnect your Ethernet cable " "to get Internet access." @@ -1452,7 +1565,7 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." -#: users/views.py:1072 users/views.py:1096 users/views.py:1111 +#: users/views.py:1152 users/views.py:1176 users/views.py:1191 msgid "The mailing list doesn't exist." msgstr "La liste de diffusion n'existe pas." diff --git a/users/management/commands/clean_notyetactive.py b/users/management/commands/clean_notyetactive.py index 994abfc2..14f7c826 100644 --- a/users/management/commands/clean_notyetactive.py +++ b/users/management/commands/clean_notyetactive.py @@ -17,6 +17,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # from django.core.management.base import BaseCommand, CommandError +from django.db.models import Q from users.models import User from cotisations.models import Facture @@ -31,9 +32,9 @@ class Command(BaseCommand): def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value("delete_notyetactive") + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_delete = ( - User.objects.filter(state=User.STATE_NOT_YET_ACTIVE) + User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE)) .filter(registered__lte=timezone.now() - timedelta(days=days)) .exclude(facture__valid=True) .distinct() diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py new file mode 100644 index 00000000..85b12699 --- /dev/null +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -0,0 +1,43 @@ +# Copyright © 2017-2020 Jean-Romain Garnier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +from django.core.management.base import BaseCommand, CommandError + +from users.models import User +from cotisations.models import Facture +from preferences.models import OptionalUser +from datetime import timedelta + +from django.utils import timezone + + +class Command(BaseCommand): + help = "Disable users who haven't confirmed their email." + + def handle(self, *args, **options): + """First deleting invalid invoices, and then deleting the users""" + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") + users_to_disable = ( + User.objects.filter(email_state=User.EMAIL_STATE_PENDING) + .filter(email_change_date__lte=timezone.now() - timedelta(days=days)) + .distinct() + ) + print("Disabling " + str(users_to_disable.count()) + " users.") + + for user in users_to_disable: + user.email_state = User.EMAIL_STATE_UNVERIFIED + user.notif_disable() + user.save() diff --git a/users/migrations/0085_user_email_state.py b/users/migrations/0085_user_email_state.py new file mode 100644 index 00000000..92535565 --- /dev/null +++ b/users/migrations/0085_user_email_state.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-16 22:31 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0084_auto_20191120_0159'), + ] + + def flag_verified(apps, schema_editor): + db_alias = schema_editor.connection.alias + users = apps.get_model("users", "User") + users.objects.using(db_alias).all().update(email_state=0) + + def undo_flag_verified(apps, schema_editor): + return + + operations = [ + migrations.AddField( + model_name='user', + name='email_state', + field=models.IntegerField(choices=[(0, 'Verified'), (1, 'Unverified'), (2, 'Waiting for email confirmation')], default=2), + ), + migrations.RunPython(flag_verified, undo_flag_verified), + ] diff --git a/users/migrations/0086_user_email_change_date.py b/users/migrations/0086_user_email_change_date.py new file mode 100644 index 00000000..e8d9ea9d --- /dev/null +++ b/users/migrations/0086_user_email_change_date.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 00:00 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0085_user_email_state'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='email_change_date', + field=models.DateTimeField(default=None, null=True), + ), + ] diff --git a/users/migrations/0087_request_email.py b/users/migrations/0087_request_email.py new file mode 100644 index 00000000..3cb8d792 --- /dev/null +++ b/users/migrations/0087_request_email.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 20:10 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0086_user_email_change_date'), + ] + + operations = [ + migrations.AddField( + model_name='request', + name='email', + field=models.EmailField(blank=True, max_length=254, null=True), + ), + ] diff --git a/users/migrations/0088_auto_20200417_2312.py b/users/migrations/0088_auto_20200417_2312.py new file mode 100644 index 00000000..e9f82f7c --- /dev/null +++ b/users/migrations/0088_auto_20200417_2312.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 21:12 +from __future__ import unicode_literals + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0087_request_email'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email_change_date', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2020, 4, 17, 21, 12, 19, 739799, tzinfo=utc)), + preserve_default=False, + ), + ] diff --git a/users/migrations/0089_auto_20200418_0112.py b/users/migrations/0089_auto_20200418_0112.py new file mode 100644 index 00000000..f3711f0f --- /dev/null +++ b/users/migrations/0089_auto_20200418_0112.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 23:12 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0088_auto_20200417_2312'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email_state', + field=models.IntegerField(choices=[(0, 'Confirmed'), (1, 'Not confirmed'), (2, 'Waiting for email confirmation')], default=2), + ), + ] diff --git a/users/models.py b/users/models.py index 75a38aa9..36558a74 100755 --- a/users/models.py +++ b/users/models.py @@ -3,9 +3,11 @@ # Il se veut agnostique au réseau considéré, de manière à être installable # en quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -62,6 +64,7 @@ from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.db import transaction from django.utils import timezone +from datetime import timedelta from django.contrib.auth.models import ( AbstractBaseUser, BaseUserManager, @@ -144,6 +147,7 @@ class UserManager(BaseUserManager): ) user.set_password(password) + user.confirm_mail() if su: user.is_superuser = True user.save(using=self._db) @@ -184,6 +188,15 @@ class User( (4, _("Fully archived")), ) + EMAIL_STATE_VERIFIED = 0 + EMAIL_STATE_UNVERIFIED = 1 + EMAIL_STATE_PENDING = 2 + EMAIL_STATES = ( + (0, _("Confirmed")), + (1, _("Not confirmed")), + (2, _("Waiting for email confirmation")), + ) + surname = models.CharField(max_length=255) pseudo = models.CharField( max_length=32, @@ -217,6 +230,7 @@ class User( ) pwd_ntlm = models.CharField(max_length=255) state = models.IntegerField(choices=STATES, default=STATE_NOT_YET_ACTIVE) + email_state = models.IntegerField(choices=EMAIL_STATES, default=EMAIL_STATE_PENDING) registered = models.DateTimeField(auto_now_add=True) telephone = models.CharField(max_length=15, blank=True, null=True) uid_number = models.PositiveIntegerField(default=get_fresh_user_uid, unique=True) @@ -224,6 +238,7 @@ class User( shortcuts_enabled = models.BooleanField( verbose_name=_("enable shortcuts on Re2o website"), default=True ) + email_change_date = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = "pseudo" REQUIRED_FIELDS = ["surname", "email"] @@ -335,7 +350,7 @@ class User( def set_active(self): """Enable this user if he subscribed successfully one time before Reenable it if it was archived - Do nothing if disabed""" + Do nothing if disabled or waiting for email confirmation""" if self.state == self.STATE_NOT_YET_ACTIVE: if self.facture_set.filter(valid=True).filter( Q(vente__type_cotisation="All") | Q(vente__type_cotisation="Adhesion") @@ -388,7 +403,7 @@ class User( @cached_property def get_shadow_expire(self): """Return the shadow_expire value for the user""" - if self.state == self.STATE_DISABLED: + if self.state == self.STATE_DISABLED or self.email_state == self.EMAIL_STATE_UNVERIFIED: return str(0) else: return None @@ -481,6 +496,7 @@ class User( """ Renvoie si un utilisateur a accès à internet """ return ( self.state == User.STATE_ACTIVE + and self.email_state != User.EMAIL_STATE_UNVERIFIED and not self.is_ban() and (self.is_connected() or self.is_whitelisted()) ) or self == AssoOption.get_cached_value("utilisateur_asso") @@ -783,6 +799,90 @@ class User( ) return + def send_confirm_email_if_necessary(self, request): + """Update the user's email state: + * If the user changed email, it needs to be confirmed + * If they're not fully archived, send a confirmation email + + Returns whether an email was sent""" + # Only update the state if the email changed + if self.__original_email == self.email: + return False + + # If the user was previously in the PENDING or UNVERIFIED state, + # we can't update email_change_date otherwise it would push back + # their due date + # However, if the user is in the VERIFIED state, we reset the date + if self.email_state == self.EMAIL_STATE_VERIFIED: + self.email_change_date = timezone.now() + + # Remember that the user needs to confirm their email address again + self.email_state = self.EMAIL_STATE_PENDING + self.save() + + # Fully archived users shouldn't get an email, so stop here + if self.state == self.STATE_FULL_ARCHIVE: + return False + + # Send the email + self.confirm_email_address_mail(request) + return True + + def trigger_email_changed_state(self, request): + """Trigger an email, and changed values after email_state been manually updated""" + if self.email_state == self.EMAIL_STATE_VERIFIED: + return False + + self.email_change_date = timezone.now() + self.save() + + self.confirm_email_address_mail(request) + return True + + def confirm_email_before_date(self): + if self.email_state == self.EMAIL_STATE_VERIFIED: + return None + + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") + return self.email_change_date + timedelta(days=days) + + def confirm_email_address_mail(self, request): + """Prend en argument un request, envoie un mail pour + confirmer l'adresse""" + # Delete all older requests for this user, that aren't for this email + filter = Q(user=self) & Q(type=Request.EMAIL) & ~Q(email=self.email) + Request.objects.filter(filter).delete() + + # Create the request and send the email + req = Request() + req.type = Request.EMAIL + req.user = self + req.email = self.email + req.save() + + template = loader.get_template("users/email_confirmation_request") + context = { + "name": req.user.get_full_name(), + "asso": AssoOption.get_cached_value("name"), + "asso_mail": AssoOption.get_cached_value("contact"), + "site_name": GeneralOption.get_cached_value("site_name"), + "url": request.build_absolute_uri( + reverse("users:process", kwargs={"token": req.token}) + ), + "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), + "confirm_before_fr": self.confirm_email_before_date().strftime("%d/%m/%Y"), + "confirm_before_en": self.confirm_email_before_date().strftime("%Y-%m-%d"), + } + send_mail( + "Confirmation du mail de %(name)s / Email confirmation for " + "%(name)s" % {"name": AssoOption.get_cached_value("name")}, + template.render(context), + GeneralOption.get_cached_value("email_from"), + [req.user.email], + fail_silently=False, + ) + return + def autoregister_machine(self, mac_address, nas_type): """ Fonction appellée par freeradius. Enregistre la mac pour une machine inconnue sur le compte de l'user""" @@ -836,6 +936,24 @@ class User( ) return + def notif_disable(self): + """Envoi un mail de notification informant que l'adresse mail n'a pas été confirmée""" + template = loader.get_template("users/email_disable_notif") + context = { + "name": self.get_full_name(), + "asso_name": AssoOption.get_cached_value("name"), + "asso_email": AssoOption.get_cached_value("contact"), + "site_name": GeneralOption.get_cached_value("site_name"), + } + send_mail( + "Suspension automatique / Automatic suspension", + template.render(context), + GeneralOption.get_cached_value("email_from"), + [self.email], + fail_silently=False, + ) + return + def set_password(self, password): """ A utiliser de préférence, set le password en hash courrant et dans la version ntlm""" @@ -845,6 +963,10 @@ class User( self.pwd_ntlm = hashNT(password) return + def confirm_mail(self): + """Marque l'email de l'utilisateur comme confirmé""" + self.email_state = self.EMAIL_STATE_VERIFIED + @cached_property def email_address(self): if ( @@ -1190,6 +1312,7 @@ class User( "room": self.can_change_room, } self.__original_state = self.state + self.__original_email = self.email def clean(self, *args, **kwargs): """Check if this pseudo is already used by any mailalias. @@ -1771,6 +1894,7 @@ class Request(models.Model): type = models.CharField(max_length=2, choices=TYPE_CHOICES) token = models.CharField(max_length=32) user = models.ForeignKey("User", on_delete=models.CASCADE) + email = models.EmailField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True, editable=False) expires_at = models.DateTimeField() diff --git a/users/templates/users/confirm_email.html b/users/templates/users/confirm_email.html new file mode 100644 index 00000000..b00d3e88 --- /dev/null +++ b/users/templates/users/confirm_email.html @@ -0,0 +1,46 @@ +{% extends 'users/sidebar.html' %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017-2020 Gabriel Détraz +Copyright © 2017-2020 Lara Kermarec +Copyright © 2017-2020 Augustin Lemesle +Copyright © 2017-2020 Hugo Levy--Falk +Copyright © 2017-2020 Jean-Romain Garnier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load i18n %} + +{% block title %}{% trans "Confirmation email" %}{% endblock %} + +{% block content %} + +
+ {% csrf_token %} +

{% blocktrans %}Confirmation email{% endblocktrans %}

+

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for {{ name }}.{% endblocktrans %}

+ {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %} +
+
+
+
+{% endblock %} + diff --git a/users/templates/users/email_confirmation_request b/users/templates/users/email_confirmation_request new file mode 100644 index 00000000..8c5fe03d --- /dev/null +++ b/users/templates/users/email_confirmation_request @@ -0,0 +1,39 @@ +Bonjour {{ name }}, + +Vous trouverez ci-dessous une URL permettant de confirmer votre +adresse mail pour votre compte {{ site_name }}. Celui-ci vous permet de gérer l'ensemble +de vos équipements, votre compte, vos factures, et tous les services proposés sur le réseau. + + {{ url }} + +Contactez les administrateurs si vous n'êtes pas à l'origine de cette requête. + +Ce lien expirera dans {{ expire_in }} heures. +S'il a expiré, vous pouvez renvoyer un mail de confirmation depuis votre compte {{ site_name }}. + +/!\ Attention : Si vous ne confirmez pas votre email avant le {{ confirm_before_fr }}, votre compte sera suspendu. + +Respectueusement, + +L'équipe de {{ asso }} (contact : {{ asso_mail }}). + +--- + +Hello {{ name }}, + +You will find below an URL allowing you to confirm the email address of your account +on {{ site_name }}. It enables you to manage your devices, your account, your invoices, and all +the services offered on the network. + + {{ url }} + +Contact the administrators if you didn't request this. + +This link will expire in {{ expire_in }} hours. +If it has expired, you can send a new confirmation email from your account on {{ site_name }}. + +/!\ Warning: If you do not confirm your email before {{ confirm_before_en }}, your account will be suspended. + +Regards, + +The {{ asso }} team (contact: {{ asso_mail }}). diff --git a/users/templates/users/email_disable_notif b/users/templates/users/email_disable_notif new file mode 100644 index 00000000..5b2a90e0 --- /dev/null +++ b/users/templates/users/email_disable_notif @@ -0,0 +1,20 @@ +Bonjour {{ name }}, + +Votre connexion a été automatiquement suspendue car votre adresse mail n'a pas été confirmée. Vous pouvez renvoyer un mail de confirmation sur votre compte {{ site_name }} pour réactiver votre connexion. + +Pour de plus amples renseignements, contactez {{ asso_name }} à l'adresse {{ asso_mail }}. + +Respectueusement, +L'équipe de {{ asso_name }}. + +--- + +Hello {{ name }}, + +Your connection has automatically been suspended because you have not confirmed your email address. You can ask for a new confirmation email to be sent on your profil at {{ site_name }} to enable your connection. + + +For more information, contactez {{ asso_name }} at {{ asso_mail }}. + +Regards, +The {{ asso_name }} team. diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 0bd25f75..4fec3c18 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -4,9 +4,11 @@ Re2o est un logiciel d'administration développé initiallement au rezometz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. -Copyright © 2017 Gabriel Détraz -Copyright © 2017 Lara Kermarec -Copyright © 2017 Augustin Lemesle +Copyright © 2017-2020 Gabriel Détraz +Copyright © 2017-2020 Lara Kermarec +Copyright © 2017-2020 Augustin Lemesle +Copyright © 2017-2020 Hugo Levy--Falk +Copyright © 2017-2020 Jean-Romain Garnier This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,6 +40,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% blocktrans with name=users.name surname=users.surname %}Profile of {{ name }} {{ surname }}{% endblocktrans %}

{% endif %} + + +{% if users.email_state == users.EMAIL_STATE_PENDING %} +
+ {% blocktrans with confirm_before_date=users.confirm_email_before_date|date:"DATE_FORMAT" %}Please confirm your email address before {{ confirm_before_date }}, or your account will be suspended.{% endblocktrans %} +
+ + {% blocktrans %}Didn't receive the email?{% endblocktrans %} + +
+{% elif users.email_state == users.EMAIL_STATE_UNVERIFIED %} +
+ {% blocktrans %}Your account has been suspended, please confirm your email address.{% endblocktrans %} +
+{% endif %} + +
@@ -51,14 +70,21 @@ with this program; if not, write to the Free Software Foundation, Inc., {% elif not users.has_access %}
{% trans "No connection" %}
+
- {% can_create Facture %} - - {% trans "Pay for a connection" %} - - {% acl_else %} - {% trans "Ask someone with the appropriate rights to pay for a connection." %} - {% acl_end %} + {% if users.email_state == users.EMAIL_STATE_UNVERIFIED %} + + {% trans "Resend the email" %} + + {% else %} + {% can_create Facture %} + + {% trans "Pay for a connection" %} + + {% acl_else %} + {% trans "Ask someone with the appropriate rights to pay for a connection." %} + {% acl_end %} + {% endif %}
{% else %} @@ -181,7 +207,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Email address" %}
-
{{ users.email }}
+
{{ users.email }}{% if users.email_state != users.EMAIL_STATE_VERIFIED %}
{% trans "Pending confirmation..." %}{% endif %}
diff --git a/users/templates/users/resend_confirmation_email.html b/users/templates/users/resend_confirmation_email.html new file mode 100644 index 00000000..ab226374 --- /dev/null +++ b/users/templates/users/resend_confirmation_email.html @@ -0,0 +1,42 @@ +{% extends 'users/sidebar.html' %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2020 Jean-Romain Garnier + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load i18n %} + +{% block title %}{% trans "Confirmation email" %}{% endblock %} + +{% block content %} + +
+ {% csrf_token %} +

{% blocktrans %}Re-send confirmation email?{% endblocktrans %}

+

{% blocktrans %}The confirmation email will be sent to{% endblocktrans %} {{ email }}.

+ {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %} +
+
+
+
+{% endblock %} + diff --git a/users/urls.py b/users/urls.py index 1cd303e6..a1579a29 100644 --- a/users/urls.py +++ b/users/urls.py @@ -3,9 +3,11 @@ # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -42,6 +44,7 @@ urlpatterns = [ url(r"^state/(?P[0-9]+)$", views.state, name="state"), url(r"^groups/(?P[0-9]+)$", views.groups, name="groups"), url(r"^password/(?P[0-9]+)$", views.password, name="password"), + url(r"^confirm_email/(?P[0-9]+)$", views.resend_confirmation_email, name="resend-confirmation-email"), url( r"^del_group/(?P[0-9]+)/(?P[0-9]+)$", views.del_group, diff --git a/users/views.py b/users/views.py index 3fb4d472..28fb107c 100644 --- a/users/views.py +++ b/users/views.py @@ -3,9 +3,11 @@ # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -119,26 +121,42 @@ def new_user(request): user = AdherentCreationForm(request.POST or None, user=request.user) GTU_sum_up = GeneralOption.get_cached_value("GTU_sum_up") GTU = GeneralOption.get_cached_value("GTU") + is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") + if user.is_valid(): user = user.save() - user.reset_passwd_mail(request) - messages.success( - request, - _("The user %s was created, an email to set the password was sent.") - % user.pseudo, - ) + + if user.password: + user.send_confirm_email_if_necessary(request) + messages.success( + request, + _("The user %s was created, a confirmation email was sent.") + % user.pseudo, + ) + else: + user.reset_passwd_mail(request) + messages.success( + request, + _("The user %s was created, an email to set the password was sent.") + % user.pseudo, + ) + return redirect(reverse("users:profil", kwargs={"userid": str(user.id)})) - return form( - { - "userform": user, - "GTU_sum_up": GTU_sum_up, - "GTU": GTU, - "showCGU": True, - "action_name": _("Commit"), - }, - "users/user.html", - request, - ) + + # Anonymous users are allowed to create new accounts + # but they should be treated differently + params = { + "userform": user, + "GTU_sum_up": GTU_sum_up, + "GTU": GTU, + "showCGU": True, + "action_name": _("Commit"), + } + + if is_set_password_allowed: + params["load_js_file"] = "/static/js/toggle_password_fields.js" + + return form(params, "users/user.html", request) @login_required @@ -204,8 +222,12 @@ def edit_info(request, user, userid): ) if user_form.is_valid(): if user_form.changed_data: - user_form.save() + user = user_form.save() messages.success(request, _("The user was edited.")) + + if user.send_confirm_email_if_necessary(request): + messages.success(request, _("Sent a new confirmation email.")) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( {"userform": user_form, "action_name": _("Edit")}, @@ -221,8 +243,10 @@ def state(request, user, userid): state_form = StateForm(request.POST or None, instance=user) if state_form.is_valid(): if state_form.changed_data: - state_form.save() - messages.success(request, _("The state was edited.")) + user_instance = state_form.save() + messages.success(request, _("The states were edited.")) + if user_instance.trigger_email_changed_state(request): + messages.success(request, _("An email to confirm the address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( {"userform": state_form, "action_name": _("Edit")}, @@ -522,6 +546,10 @@ def edit_email_settings(request, user_instance, **_kwargs): if email_settings.changed_data: email_settings.save() messages.success(request, _("The email settings were edited.")) + + if user_instance.send_confirm_email_if_necessary(request): + messages.success(request, _("An email to confirm your address was sent.")) + return redirect( reverse("users:profil", kwargs={"userid": str(user_instance.id)}) ) @@ -974,12 +1002,15 @@ def reset_password(request): def process(request, token): - """Process, lien pour la reinitialisation du mot de passe""" + """Process, lien pour la reinitialisation du mot de passe + et la confirmation de l'email""" valid_reqs = Request.objects.filter(expires_at__gt=timezone.now()) req = get_object_or_404(valid_reqs, token=token) if req.type == Request.PASSWD: return process_passwd(request, req) + elif req.type == Request.EMAIL: + return process_email(request, req) else: messages.error(request, _("Error: please contact an admin.")) redirect(reverse("index")) @@ -992,9 +1023,12 @@ def process_passwd(request, req): u_form = PassForm(request.POST or None, instance=user, user=request.user) if u_form.is_valid(): with transaction.atomic(), reversion.create_revision(): + user.confirm_mail() u_form.save() reversion.set_comment("Password reset") - req.delete() + + # Delete all remaining requests + Request.objects.filter(user=user, type=Request.PASSWD).delete() messages.success(request, _("The password was changed.")) return redirect(reverse("index")) return form( @@ -1004,6 +1038,52 @@ def process_passwd(request, req): ) +def process_email(request, req): + """Process la confirmation de mail, renvoie le formulaire + de validation""" + user = req.user + if request.method == "POST": + with transaction.atomic(), reversion.create_revision(): + user.confirm_mail() + user.save() + reversion.set_comment("Email confirmation") + + # Delete all remaining requests + Request.objects.filter(user=user, type=Request.EMAIL).delete() + messages.success(request, _("The %s address was confirmed." % user.email)) + return redirect(reverse("index")) + + return form( + {"email": user.email, "name": user.get_full_name()}, + "users/confirm_email.html", + request + ) + + +@login_required +@can_edit(User) +def resend_confirmation_email(request, logged_user, userid): + """ Renvoi du mail de confirmation """ + try: + user = User.objects.get( + id=userid, + email_state__in=[User.EMAIL_STATE_PENDING, User.EMAIL_STATE_UNVERIFIED], + ) + except User.DoesNotExist: + messages.error(request, _("The user doesn't exist.")) + + if request.method == "POST": + user.confirm_email_address_mail(request) + messages.success(request, _("An email to confirm your address was sent.")) + return redirect(reverse("users:profil", kwargs={"userid": userid})) + + return form( + {"email": user.email}, + "users/resend_confirmation_email.html", + request + ) + + @login_required def initial_register(request): switch_ip = request.GET.get("switch_ip", None)