Login is possible

This commit is contained in:
Yohann D'ANELLO 2020-04-29 15:29:01 +02:00
parent 93735da1a4
commit eead385218
19 changed files with 904 additions and 97 deletions

39
apps/member/forms.py Normal file
View File

@ -0,0 +1,39 @@
from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import gettext_lazy as _
from member.models import TFJMUser
class SignUpForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["first_name"].required = True
self.fields["last_name"].required = True
print(self.fields["role"].choices)
self.fields["role"].choices = [
('', _("Choose a role...")),
('participant', _("Participant")),
('encadrant', _("Encadrant")),
]
class Meta:
model = TFJMUser
fields = (
'role',
'email',
'first_name',
'last_name',
'birth_date',
'gender',
'address',
'postal_code',
'city',
'country',
'phone_number',
'school',
'student_class',
'responsible_name',
'responsible_phone',
'responsible_email',
'description',
)

View File

@ -1,3 +1,6 @@
import os
from datetime import date
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -38,7 +41,7 @@ class TFJMUser(AbstractUser):
("female", _("Female")),
("non-binary", _("Non binary")),
],
verbose_name=_("address"),
verbose_name=_("gender"),
)
address = models.CharField(
@ -132,6 +135,7 @@ class TFJMUser(AbstractUser):
)
year = models.PositiveIntegerField(
default=os.getenv("TFJM_YEAR", date.today().year),
verbose_name=_("year"),
)

10
apps/member/urls.py Normal file
View File

@ -0,0 +1,10 @@
from django.urls import path
from .views import CreateUserView
app_name = "member"
urlpatterns = [
path('signup/', CreateUserView.as_view(), name="signup"),
]

View File

@ -1,3 +1,10 @@
from django.shortcuts import render
from django.views.generic import CreateView
# Create your views here.
from .forms import SignUpForm
from .models import TFJMUser
class CreateUserView(CreateView):
model = TFJMUser
form_class = SignUpForm
template_name = "registration/signup.html"

View File

@ -1,4 +1,5 @@
import os
from datetime import date
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -58,7 +59,8 @@ class Tournament(models.Model):
)
year = models.PositiveIntegerField(
verbose_name=_("year")
default=os.getenv("TFJM_YEAR", date.today().year),
verbose_name=_("year"),
)
@classmethod
@ -117,6 +119,7 @@ class Team(models.Model):
)
year = models.PositiveIntegerField(
default=os.getenv("TFJM_YEAR", date.today().year),
verbose_name=_("year"),
)

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: TFJM2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-29 04:43+0000\n"
"POT-Creation-Date: 2020-04-29 06:24+0000\n"
"PO-Revision-Date: 2020-04-29 02:30+0000\n"
"Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n"
"Language-Team: fr <LL@li.org>\n"
@ -22,328 +22,514 @@ msgstr ""
msgid "member"
msgstr "membre"
#: apps/member/models.py:15
#: apps/member/forms.py:14
msgid "Choose a role..."
msgstr ""
#: apps/member/forms.py:15
msgid "Participant"
msgstr "Participant"
#: apps/member/forms.py:16
msgid "Encadrant"
msgstr "Encadrant"
#: apps/member/models.py:18
msgid "email"
msgstr "Adresse électronique"
#: apps/member/models.py:23 apps/member/models.py:196 apps/member/models.py:209
#: apps/member/models.py:230 apps/tournament/models.py:132
#: apps/tournament/models.py:152
#: apps/member/models.py:26 apps/member/models.py:214 apps/member/models.py:230
#: apps/member/models.py:255 apps/tournament/models.py:147
#: apps/tournament/models.py:167
msgid "team"
msgstr "équipe"
#: apps/member/models.py:29
#: apps/member/models.py:32
msgid "birth date"
msgstr "date de naissance"
#: apps/member/models.py:37
#: apps/member/models.py:40
msgid "Male"
msgstr "Homme"
#: apps/member/models.py:38
#: apps/member/models.py:41
msgid "Female"
msgstr "Femme"
#: apps/member/models.py:39
#: apps/member/models.py:42
msgid "Non binary"
msgstr "Non binaire"
#: apps/member/models.py:41 apps/member/models.py:48
#: apps/member/models.py:44
msgid "gender"
msgstr "genre"
#: apps/member/models.py:51
msgid "address"
msgstr "adresse"
#: apps/member/models.py:54
#: apps/member/models.py:57
msgid "postal code"
msgstr "code postal"
#: apps/member/models.py:61
#: apps/member/models.py:64
msgid "city"
msgstr "ville"
#: apps/member/models.py:68
#: apps/member/models.py:71
msgid "country"
msgstr "pays"
#: apps/member/models.py:76
#: apps/member/models.py:79
msgid "phone number"
msgstr "numéro de téléphone"
#: apps/member/models.py:83
#: apps/member/models.py:86
msgid "school"
msgstr "école"
#: apps/member/models.py:89
#: apps/member/models.py:92
msgid "Seconde or less"
msgstr "Seconde ou inférieur"
#: apps/member/models.py:90
#: apps/member/models.py:93
msgid "Première"
msgstr "Première"
#: apps/member/models.py:91
#: apps/member/models.py:94
msgid "Terminale"
msgstr "Terminale"
#: apps/member/models.py:102
#: apps/member/models.py:105
msgid "responsible name"
msgstr "nom du responsable"
#: apps/member/models.py:109
#: apps/member/models.py:112
msgid "responsible phone"
msgstr "téléphone du responsable"
#: apps/member/models.py:115
#: apps/member/models.py:118
msgid "responsible email"
msgstr "email du responsable"
#: apps/member/models.py:121 apps/tournament/models.py:33
#: apps/member/models.py:124 apps/tournament/models.py:34
msgid "description"
msgstr "description"
#: apps/member/models.py:127
#: apps/member/models.py:130
msgid "admin"
msgstr "administrateur"
#: apps/member/models.py:128
#: apps/member/models.py:131
msgid "organizer"
msgstr "oragnisateur"
#: apps/member/models.py:129
#: apps/member/models.py:132
msgid "encadrant"
msgstr "encadrant"
#: apps/member/models.py:130
#: apps/member/models.py:133
msgid "participant"
msgstr "participant"
#: apps/member/models.py:135 apps/tournament/models.py:61
#: apps/tournament/models.py:120
#: apps/member/models.py:139 apps/tournament/models.py:63
#: apps/tournament/models.py:123
msgid "year"
msgstr "année"
#: apps/member/models.py:139 apps/member/models.py:172
#: apps/tournament/models.py:145
#: apps/member/models.py:155 apps/member/models.py:187
#: apps/tournament/models.py:160
msgid "user"
msgstr "utilisateur"
#: apps/member/models.py:140
#: apps/member/models.py:156
msgid "users"
msgstr "utilisateurs"
#: apps/member/models.py:154
#: apps/member/models.py:169
msgid "file"
msgstr "fichier"
#: apps/member/models.py:159
#: apps/member/models.py:174
msgid "uploaded at"
msgstr "téléversé le"
#: apps/member/models.py:163
#: apps/member/models.py:178
msgid "document"
msgstr "document"
#: apps/member/models.py:164
#: apps/member/models.py:179
msgid "documents"
msgstr "documents"
#: apps/member/models.py:178
#: apps/member/models.py:193
msgid "Parental consent"
msgstr "Autorisation parentale"
#: apps/member/models.py:179
#: apps/member/models.py:194
msgid "Photo consent"
msgstr "Autorisation de droit à l'image"
#: apps/member/models.py:180
#: apps/member/models.py:195
msgid "Sanitary plug"
msgstr "Fiche sanitaire"
#: apps/member/models.py:181 apps/tournament/models.py:163
#: apps/member/models.py:196 apps/tournament/models.py:178
msgid "Scholarship"
msgstr "Bourse"
#: apps/member/models.py:183
#: apps/member/models.py:198
msgid "type"
msgstr "type"
#: apps/member/models.py:187
#: apps/member/models.py:202
msgid "authorization"
msgstr "autorisation"
#: apps/member/models.py:188
#: apps/member/models.py:203
msgid "authorizations"
msgstr "autorisations"
#: apps/member/models.py:200
msgid "{authorization} for user {user}"
msgstr "{authorization} pour l'utilisateur {user}"
#: apps/member/models.py:218
msgid "motivation letter"
msgstr "lettre de motivation"
#: apps/member/models.py:201
#: apps/member/models.py:219
msgid "motivation letters"
msgstr "lettres de motivation"
#: apps/member/models.py:213
#: apps/member/models.py:222
#, python-brace-format
msgid "Motivation letter of team {team} ({trigram})"
msgstr "Lettre de motivation de l'équipe {team} ({trigram})"
#: apps/member/models.py:234
msgid "problem"
msgstr "problème"
#: apps/member/models.py:221
#: apps/member/models.py:242
msgid "solution"
msgstr "solution"
#: apps/member/models.py:222
#: apps/member/models.py:243
msgid "solutions"
msgstr "solutions"
#: apps/member/models.py:236
#: apps/member/models.py:246
#, python-brace-format
msgid "Solution of team {trigram} for problem {problem}"
msgstr ""
#: apps/member/models.py:261
msgid "Defender"
msgstr "Défenseur"
#: apps/member/models.py:237
#: apps/member/models.py:262
msgid "Opponent"
msgstr "Opposant"
#: apps/member/models.py:238
#: apps/member/models.py:263
msgid "Rapporteur"
msgstr "Rapporteur"
#: apps/member/models.py:240
#: apps/member/models.py:265
msgid "dest"
msgstr "destinataire"
#: apps/member/models.py:248
#: apps/member/models.py:273
msgid "synthesis"
msgstr "synthèse"
#: apps/member/models.py:249
#: apps/member/models.py:274
msgid "syntheses"
msgstr "synthèses"
#: apps/tournament/apps.py:7 apps/tournament/models.py:69
#: apps/tournament/models.py:90
#: apps/member/models.py:277
#, python-brace-format
msgid "Synthesis of team {trigram} that is {dest} for problem {problem}"
msgstr ""
#: apps/member/models.py:285
msgid "key"
msgstr "clé"
#: apps/member/models.py:290
msgid "value"
msgstr "valeur"
#: apps/member/models.py:294
msgid "configuration"
msgstr "configuration"
#: apps/member/models.py:295
msgid "configurations"
msgstr "configurations"
#: apps/tournament/apps.py:7 apps/tournament/models.py:71
#: apps/tournament/models.py:92
msgid "tournament"
msgstr "tournoi"
#: apps/tournament/models.py:10 apps/tournament/models.py:79
#: apps/tournament/models.py:11 apps/tournament/models.py:81
msgid "name"
msgstr "nom"
#: apps/tournament/models.py:16
#: apps/tournament/models.py:17
msgid "organizers"
msgstr "organisateurs"
#: apps/tournament/models.py:20
#: apps/tournament/models.py:21
msgid "size"
msgstr "taille"
#: apps/tournament/models.py:25
#: apps/tournament/models.py:26
msgid "place"
msgstr "lieu"
#: apps/tournament/models.py:29
#: apps/tournament/models.py:30
msgid "price"
msgstr "prix"
#: apps/tournament/models.py:37 apps/tournament/models.py:41
#: apps/tournament/models.py:38 apps/tournament/models.py:42
msgid "date start"
msgstr "date de début"
#: apps/tournament/models.py:45
#: apps/tournament/models.py:46
msgid "date of registration closing"
msgstr "date de clôture des inscriptions"
#: apps/tournament/models.py:49
#: apps/tournament/models.py:50
msgid "date of maximal solution submission"
msgstr "date d'envoi maximal des solutions"
#: apps/tournament/models.py:53
#: apps/tournament/models.py:54
msgid "date of maximal syntheses submission"
msgstr "date d'envoi maximal des notes de synthèses"
#: apps/tournament/models.py:57
#: apps/tournament/models.py:58
msgid "final tournament"
msgstr "finale"
#: apps/tournament/models.py:70
#: apps/tournament/models.py:72
msgid "tournaments"
msgstr "tournois"
#: apps/tournament/models.py:84
#: apps/tournament/models.py:86
msgid "trigram"
msgstr "trigramme"
#: apps/tournament/models.py:95
#: apps/tournament/models.py:97
msgid "inscription date"
msgstr "date d'inscription"
#: apps/tournament/models.py:101 apps/tournament/models.py:172
#: apps/tournament/models.py:103 apps/tournament/models.py:187
msgid "Registration not validated"
msgstr "Inscription non validée"
#: apps/tournament/models.py:102 apps/tournament/models.py:173
#: apps/tournament/models.py:104 apps/tournament/models.py:188
msgid "Waiting for validation"
msgstr "En attente de validation"
#: apps/tournament/models.py:103 apps/tournament/models.py:174
#: apps/tournament/models.py:105 apps/tournament/models.py:189
msgid "Registration validated"
msgstr "Inscription validée"
#: apps/tournament/models.py:105 apps/tournament/models.py:176
#: apps/tournament/models.py:107 apps/tournament/models.py:191
msgid "validation status"
msgstr "statut de validation"
#: apps/tournament/models.py:110
#: apps/tournament/models.py:112
msgid "selected for final"
msgstr "sélectionnée pour la finale"
#: apps/tournament/models.py:116
#: apps/tournament/models.py:118
msgid "access code"
msgstr "code d'accès"
#: apps/tournament/models.py:133
#: apps/tournament/models.py:148
msgid "teams"
msgstr "équipes"
#: apps/tournament/models.py:158
#: apps/tournament/models.py:173
msgid "Not paid"
msgstr "Non payé"
#: apps/tournament/models.py:159
#: apps/tournament/models.py:174
msgid "Credit card"
msgstr "Carte bancaire"
#: apps/tournament/models.py:160
#: apps/tournament/models.py:175
msgid "Bank check"
msgstr "Chèque bancaire"
#: apps/tournament/models.py:161
#: apps/tournament/models.py:176
msgid "Bank transfer"
msgstr "Virement bancaire"
#: apps/tournament/models.py:162
#: apps/tournament/models.py:177
msgid "Cash"
msgstr "Espèces"
#: apps/tournament/models.py:166
#: apps/tournament/models.py:181
msgid "payment method"
msgstr "moyen de paiement"
#: apps/tournament/models.py:180
#: apps/tournament/models.py:195
msgid "payment"
msgstr "paiement"
#: apps/tournament/models.py:181
#: apps/tournament/models.py:196
msgid "payments"
msgstr "paiements"
#: apps/tournament/models.py:184
#: apps/tournament/models.py:199
#, python-brace-format
msgid "Payment of {user}"
msgstr "Paiement de {user}"
#: tfjm/settings.py:128
#: templates/base.html:11
msgid "The inscription site of the TFJM²."
msgstr "Le site d'inscription au TFJM²."
#: templates/registration/email_validation_complete.html:6
msgid "Your email have successfully been validated."
msgstr "Votre adresse e-mail a bien été validée."
#: templates/registration/email_validation_complete.html:8
#, python-format
msgid "You can now <a href=\"%(login_url)s\">log in</a>."
msgstr "Vous pouvez désormais <a href=\"%(login_url)s\">vous connecter</a>"
#: templates/registration/email_validation_complete.html:13
msgid ""
"The link was invalid. The token may have expired. Please send us an email to "
"activate your account."
msgstr ""
"Le lien est invalide. Le jeton a du expirer. Merci de nous envoyer un mail "
"afin d'activer votre compte."
#: templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today."
msgstr "Merci d'avoir utilisé la plateforme du TFJM²."
#: templates/registration/logged_out.html:9
msgid "Log in again"
msgstr "Se connecter à nouveau"
#: templates/registration/login.html:7 templates/registration/login.html:8
#: templates/registration/login.html:22
#: templates/registration/password_reset_complete.html:10
msgid "Log in"
msgstr "Connexion"
#: templates/registration/login.html:13
#, python-format
msgid ""
"You are authenticated as %(user)s, but are not authorized to access this "
"page. Would you like to login to a different account?"
msgstr ""
"Vous êtes déjà connecté sous le nom %(user)s, mais vous n'êtes pas autorisés "
"à accéder à cette page. Souhaitez-vous vous connecter sous un compte différent ?"
#: templates/registration/login.html:23
msgid "Forgotten your password or username?"
msgstr "Mot de passe oublié ?"
#: templates/registration/mails/email_validation_email.html:3
msgid "Hi"
msgstr "Bonjour"
#: templates/registration/mails/email_validation_email.html:5
msgid ""
"You recently registered on the Note Kfet. Please click on the link below to "
"confirm your registration."
msgstr ""
#: templates/registration/mails/email_validation_email.html:9
msgid ""
"This link is only valid for a couple of days, after that you will need to "
"contact us to validate your email."
msgstr ""
#: templates/registration/mails/email_validation_email.html:11
msgid ""
"After that, you'll have to wait that someone validates your account before "
"you can log in. You will need to pay your membership in the Kfet."
msgstr ""
#: templates/registration/mails/email_validation_email.html:13
msgid "Thanks"
msgstr "Merci"
#: templates/registration/mails/email_validation_email.html:15
msgid "The Note Kfet team."
msgstr ""
#: templates/registration/password_change_done.html:8
msgid "Your password was changed."
msgstr "Votre mot de passe a été changé"
#: templates/registration/password_change_form.html:9
msgid ""
"Please enter your old password, for security's sake, and then enter your new "
"password twice so we can verify you typed it in correctly."
msgstr ""
#: templates/registration/password_change_form.html:11
#: templates/registration/password_reset_confirm.html:12
msgid "Change my password"
msgstr ""
#: templates/registration/password_reset_complete.html:8
msgid "Your password has been set. You may go ahead and log in now."
msgstr ""
#: templates/registration/password_reset_confirm.html:9
msgid ""
"Please enter your new password twice so we can verify you typed it in "
"correctly."
msgstr ""
#: templates/registration/password_reset_confirm.html:15
msgid ""
"The password reset link was invalid, possibly because it has already been "
"used. Please request a new password reset."
msgstr ""
#: templates/registration/password_reset_done.html:8
msgid ""
"We've emailed you instructions for setting your password, if an account "
"exists with the email you entered. You should receive them shortly."
msgstr ""
#: templates/registration/password_reset_done.html:9
msgid ""
"If you don't receive an email, please make sure you've entered the address "
"you registered with, and check your spam folder."
msgstr ""
#: templates/registration/password_reset_form.html:8
msgid ""
"Forgotten your password? Enter your email address below, and we'll email "
"instructions for setting a new one."
msgstr ""
#: templates/registration/password_reset_form.html:11
msgid "Reset my password"
msgstr "Réinitialiser mon mot de passe"
#: templates/registration/signup.html:5 templates/registration/signup.html:8
#: templates/registration/signup.html:14
msgid "Sign up"
msgstr "S'inscrire"
#: tfjm/settings.py:135
msgid "English"
msgstr "Anglais"
#: tfjm/settings.py:129
#: tfjm/settings.py:136
msgid "French"
msgstr "Français"

View File

@ -0,0 +1,121 @@
@font-face {
font-family: 'Glyphicons Halflings';
src: url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot');
src: url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff2') format('woff2'),
url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.woff') format('woff'),
url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
url('//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
}
.glyphicon {
position: relative;
top: 1px;
display: inline-block;
font-family: 'Glyphicons Halflings';
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.glyphicon-time:before {
content: "\e023";
}
.glyphicon-chevron-left:before {
content: "\e079";
}
.glyphicon-chevron-right:before {
content: "\e080";
}
.glyphicon-chevron-up:before {
content: "\e113";
}
.glyphicon-chevron-down:before {
content: "\e114";
}
.glyphicon-calendar:before {
content: "\e109";
}
.glyphicon-screenshot:before {
content: "\e087";
}
.glyphicon-trash:before {
content: "\e020";
}
.glyphicon-remove:before {
content: "\e014";
}
.bootstrap-datetimepicker-widget .btn {
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: normal;
line-height: 1.42857143;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-image: none;
border: 1px solid transparent;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget.dropdown-menu {
position: absolute;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
font-size: 14px;
text-align: left;
list-style: none;
background-color: #fff;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, .15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
}
.bootstrap-datetimepicker-widget .list-unstyled {
padding-left: 0;
list-style: none;
}
.bootstrap-datetimepicker-widget .collapse {
display: none;
}
.bootstrap-datetimepicker-widget .collapse.in {
display: block;
}
/* fix for bootstrap4 */
.bootstrap-datetimepicker-widget .table-condensed > thead > tr > th,
.bootstrap-datetimepicker-widget .table-condensed > tbody > tr > td,
.bootstrap-datetimepicker-widget .table-condensed > tfoot > tr > td {
padding: 5px;
}

View File

@ -0,0 +1,55 @@
jQuery(function ($) {
var datepickerDict = {};
var isBootstrap4 = $.fn.collapse.Constructor.VERSION.split('.').shift() == "4";
function fixMonthEndDate(e, picker) {
e.date && picker.val().length && picker.val(e.date.endOf('month').format('YYYY-MM-DD'));
}
$("[dp_config]:not([disabled])").each(function (i, element) {
var $element = $(element), data = {};
try {
data = JSON.parse($element.attr('dp_config'));
}
catch (x) { }
if (data.id && data.options) {
data.$element = $element.datetimepicker(data.options);
data.datepickerdata = $element.data("DateTimePicker");
datepickerDict[data.id] = data;
data.$element.next('.input-group-addon').on('click', function(){
data.datepickerdata.show();
});
if(isBootstrap4){
data.$element.on("dp.show", function (e) {
$('.collapse.in').addClass('show');
});
}
}
});
$.each(datepickerDict, function (id, to_picker) {
if (to_picker.linked_to) {
var from_picker = datepickerDict[to_picker.linked_to];
from_picker.datepickerdata.maxDate(to_picker.datepickerdata.date() || false);
to_picker.datepickerdata.minDate(from_picker.datepickerdata.date() || false);
from_picker.$element.on("dp.change", function (e) {
to_picker.datepickerdata.minDate(e.date || false);
});
to_picker.$element.on("dp.change", function (e) {
if (to_picker.picker_type == 'MONTH') fixMonthEndDate(e, to_picker.$element);
from_picker.datepickerdata.maxDate(e.date || false);
});
if (to_picker.picker_type == 'MONTH') {
to_picker.$element.on("dp.hide", function (e) {
fixMonthEndDate(e, to_picker.$element);
});
fixMonthEndDate({ date: to_picker.datepickerdata.date() }, to_picker.$element);
}
}
});
if(isBootstrap4) {
$('body').on('show.bs.collapse','.bootstrap-datetimepicker-widget .collapse',function(e){
$(e.target).addClass('in');
});
$('body').on('hidden.bs.collapse','.bootstrap-datetimepicker-widget .collapse',function(e){
$(e.target).removeClass('in');
});
}
});

View File

@ -0,0 +1,11 @@
<div class="input-group">
<input class="form-control mx-auto d-block" type="number" min="0" step="0.01"
{% if widget.value != None and widget.value != "" %}value="{{ widget.value }}"{% endif %}
name="{{ widget.name }}"
{% for name, value in widget.attrs.items %}
{% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %}
{% endfor %}>
<div class="input-group-append">
<span class="input-group-text"></span>
</div>
</div>

View File

@ -0,0 +1,9 @@
<input type="hidden" name="{{ widget.name }}" {% if widget.attrs.model_pk %}value="{{ widget.attrs.model_pk }}"{% endif %} id="{{ widget.attrs.id }}_pk">
<input type="text"
{% if widget.value != None and widget.value != "" %}value="{{ widget.value }}"{% endif %}
name="{{ widget.name }}_name" autocomplete="off"
{% for name, value in widget.attrs.items %}
{% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %}
{% endfor %}>
<ul class="list-group list-group-flush" id="{{ widget.attrs.id }}_list">
</ul>

View File

@ -155,7 +155,7 @@
<a class="nav-link" href="{% url "login" %}"><i class="fas fa-sign-in-alt"></i> Connexion</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/inscription"><i class="fas fa-user-plus"></i> Inscription</a>
<a class="nav-link" href="{% url "member:signup" %}"><i class="fas fa-user-plus"></i> Inscription</a>
</li>
{% else %}
<li class="nav-item active">

View File

@ -0,0 +1,6 @@
<div class="input-group date">
{% include "bootstrap_datepicker_plus/input.html" %}
<div class="input-group-addon input-group-append" data-target="#datetimepicker1" data-toggle="datetimepickerv">
<div class="input-group-text"><i class="glyphicon glyphicon-calendar"></i></div>
</div>
</div>

View File

@ -0,0 +1,4 @@
<input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None and widget.value != "" %}
value="{{ widget.value }}"{% endif %}{% for name, value in widget.attrs.items %}{% ifnotequal value False %}
{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}
{% endifnotequal %}{% endfor %}/>

View File

@ -0,0 +1,6 @@
<div class="input-group date">
{% include "bootstrap_datepicker_plus/input.html" %}
<div class="input-group-addon input-group-append" data-target="#datetimepicker1" data-toggle="datetimepickerv">
<div class="input-group-text"><i class="glyphicon glyphicon-time"></i></div>
</div>
</div>

View File

@ -0,0 +1,8 @@
<input type="text"
id="{{ widget.attrs.id }}"
class="form-control colorfield_field jscolor"
name="{{ widget.name }}"
value="{% firstof widget.value widget.attrs.default '' %}"
placeholder="{% firstof widget.value widget.attrs.default '' %}"
data-jscolor="{hash:true,width:225,height:150,required:{% if widget.attrs.required %}true{% else %}false{% endif %}}"
{% if widget.attrs.required %}required{% endif %} />

View File

@ -0,0 +1,15 @@
{% load i18n %}
{% trans "Hi" %} {{ user.username }},
{% trans "You recently registered on the Note Kfet. Please click on the link below to confirm your registration." %}
https://{{ domain }}{% url 'registration:email_validation' uidb64=uid token=token %}
{% trans "This link is only valid for a couple of days, after that you will need to contact us to validate your email." %}
{% trans "After that, you'll have to wait that someone validates your account before you can log in. You will need to pay your membership in the Kfet." %}
{% trans "Thanks" %},
{% trans "The Note Kfet team." %}

View File

@ -1,20 +1,15 @@
<!-- templates/signup.html -->
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load crispy_forms_filters %}
{% load i18n %}
{% block title %}{% trans "Sign up" %}{% endblock %}
{% block content %}
<h2>{% trans "Sign up" %}</h2>
<div class="alert alert-warning">
{% blocktrans %}If you already signed up, your registration is taken into account. The BDE must validate your account before your can log in. You have to go to the Kfet and pay the registration fee. You must also validate your email address by following the link you received.{% endblocktrans %}
</div>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
{{ profile_form|crispy }}
<button class="btn btn-success" type="submit">
{% trans "Sign up" %}
</button>

325
tfjm/inputs.py Normal file
View File

@ -0,0 +1,325 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from json import dumps as json_dumps
from django.forms.widgets import DateTimeBaseInput, NumberInput, TextInput, Widget
class AmountInput(NumberInput):
"""
This input type lets the user type amounts in euros, but forms receive data in cents
"""
template_name = "amount_input.html"
def format_value(self, value):
return None if value is None or value == "" else "{:.02f}".format(int(value) / 100, )
def value_from_datadict(self, data, files, name):
val = super().value_from_datadict(data, files, name)
return str(int(100 * float(val))) if val else val
class Autocomplete(TextInput):
template_name = "autocomplete_model.html"
def __init__(self, model, attrs=None):
super().__init__(attrs)
self.model = model
self.model_pk = None
class Media:
"""JS/CSS resources needed to render the date-picker calendar."""
js = ('js/autocomplete_model.js', )
def format_value(self, value):
if value:
self.attrs["model_pk"] = int(value)
return str(self.model.objects.get(pk=int(value)))
return ""
class ColorWidget(Widget):
"""
Pulled from django-colorfield.
Select a color.
"""
template_name = 'colorfield/color.html'
class Media:
js = [
'colorfield/jscolor/jscolor.min.js',
'colorfield/colorfield.js',
]
def format_value(self, value):
if value is None:
value = 0xFFFFFF
return "#{:06X}".format(value)
def value_from_datadict(self, data, files, name):
val = super().value_from_datadict(data, files, name)
return int(val[1:], 16)
"""
The remaining of this file comes from the project `django-bootstrap-datepicker-plus` available on Github:
https://github.com/monim67/django-bootstrap-datepicker-plus
This is distributed under Apache License 2.0.
This adds datetime pickers with bootstrap.
"""
"""Contains Base Date-Picker input class for widgets of this package."""
class DatePickerDictionary:
"""Keeps track of all date-picker input classes."""
_i = 0
items = dict()
@classmethod
def generate_id(cls):
"""Return a unique ID for each date-picker input class."""
cls._i += 1
return 'dp_%s' % cls._i
class BasePickerInput(DateTimeBaseInput):
"""Base Date-Picker input class for widgets of this package."""
template_name = 'bootstrap_datepicker_plus/date_picker.html'
picker_type = 'DATE'
format = '%Y-%m-%d'
config = {}
_default_config = {
'id': None,
'picker_type': None,
'linked_to': None,
'options': {} # final merged options
}
options = {} # options extended by user
options_param = {} # options passed as parameter
_default_options = {
'showClose': True,
'showClear': True,
'showTodayButton': True,
"locale": "fr",
}
# source: https://github.com/tutorcruncher/django-bootstrap3-datetimepicker
# file: /blob/31fbb09/bootstrap3_datetime/widgets.py#L33
format_map = (
('DDD', r'%j'),
('DD', r'%d'),
('MMMM', r'%B'),
('MMM', r'%b'),
('MM', r'%m'),
('YYYY', r'%Y'),
('YY', r'%y'),
('HH', r'%H'),
('hh', r'%I'),
('mm', r'%M'),
('ss', r'%S'),
('a', r'%p'),
('ZZ', r'%z'),
)
class Media:
"""JS/CSS resources needed to render the date-picker calendar."""
js = (
'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/'
'moment-with-locales.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/'
'4.17.47/js/bootstrap-datetimepicker.min.js',
'bootstrap_datepicker_plus/js/datepicker-widget.js'
)
css = {'all': (
'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/'
'4.17.47/css/bootstrap-datetimepicker.css',
'bootstrap_datepicker_plus/css/datepicker-widget.css'
), }
@classmethod
def format_py2js(cls, datetime_format):
"""Convert python datetime format to moment datetime format."""
for js_format, py_format in cls.format_map:
datetime_format = datetime_format.replace(py_format, js_format)
return datetime_format
@classmethod
def format_js2py(cls, datetime_format):
"""Convert moment datetime format to python datetime format."""
for js_format, py_format in cls.format_map:
datetime_format = datetime_format.replace(js_format, py_format)
return datetime_format
def __init__(self, attrs=None, format=None, options=None):
"""Initialize the Date-picker widget."""
self.format_param = format
self.options_param = options if options else {}
self.config = self._default_config.copy()
self.config['id'] = DatePickerDictionary.generate_id()
self.config['picker_type'] = self.picker_type
self.config['options'] = self._calculate_options()
attrs = attrs if attrs else {}
if 'class' not in attrs:
attrs['class'] = 'form-control'
super().__init__(attrs, self._calculate_format())
def _calculate_options(self):
"""Calculate and Return the options."""
_options = self._default_options.copy()
_options.update(self.options)
if self.options_param:
_options.update(self.options_param)
return _options
def _calculate_format(self):
"""Calculate and Return the datetime format."""
_format = self.format_param if self.format_param else self.format
if self.config['options'].get('format'):
_format = self.format_js2py(self.config['options'].get('format'))
else:
self.config['options']['format'] = self.format_py2js(_format)
return _format
def get_context(self, name, value, attrs):
"""Return widget context dictionary."""
context = super().get_context(
name, value, attrs)
context['widget']['attrs']['dp_config'] = json_dumps(self.config)
return context
def start_of(self, event_id):
"""
Set Date-Picker as the start-date of a date-range.
Args:
- event_id (string): User-defined unique id for linking two fields
"""
DatePickerDictionary.items[str(event_id)] = self
return self
def end_of(self, event_id, import_options=True):
"""
Set Date-Picker as the end-date of a date-range.
Args:
- event_id (string): User-defined unique id for linking two fields
- import_options (bool): inherit options from start-date input,
default: TRUE
"""
event_id = str(event_id)
if event_id in DatePickerDictionary.items:
linked_picker = DatePickerDictionary.items[event_id]
self.config['linked_to'] = linked_picker.config['id']
if import_options:
backup_moment_format = self.config['options']['format']
self.config['options'].update(linked_picker.config['options'])
self.config['options'].update(self.options_param)
if self.format_param or 'format' in self.options_param:
self.config['options']['format'] = backup_moment_format
else:
self.format = linked_picker.format
# Setting useCurrent is necessary, see following issue
# https://github.com/Eonasdan/bootstrap-datetimepicker/issues/1075
self.config['options']['useCurrent'] = False
self._link_to(linked_picker)
else:
raise KeyError(
'start-date not specified for event_id "%s"' % event_id)
return self
def _link_to(self, linked_picker):
"""
Executed when two date-inputs are linked together.
This method for sub-classes to override to customize the linking.
"""
pass
class DatePickerInput(BasePickerInput):
"""
Widget to display a Date-Picker Calendar on a DateField property.
Args:
- attrs (dict): HTML attributes of rendered HTML input
- format (string): Python DateTime format eg. "%Y-%m-%d"
- options (dict): Options to customize the widget, see README
"""
picker_type = 'DATE'
format = '%Y-%m-%d'
format_key = 'DATE_INPUT_FORMATS'
class TimePickerInput(BasePickerInput):
"""
Widget to display a Time-Picker Calendar on a TimeField property.
Args:
- attrs (dict): HTML attributes of rendered HTML input
- format (string): Python DateTime format eg. "%Y-%m-%d"
- options (dict): Options to customize the widget, see README
"""
picker_type = 'TIME'
format = '%H:%M'
format_key = 'TIME_INPUT_FORMATS'
template_name = 'bootstrap_datepicker_plus/time_picker.html'
class DateTimePickerInput(BasePickerInput):
"""
Widget to display a DateTime-Picker Calendar on a DateTimeField property.
Args:
- attrs (dict): HTML attributes of rendered HTML input
- format (string): Python DateTime format eg. "%Y-%m-%d"
- options (dict): Options to customize the widget, see README
"""
picker_type = 'DATETIME'
format = '%Y-%m-%d %H:%M'
format_key = 'DATETIME_INPUT_FORMATS'
class MonthPickerInput(BasePickerInput):
"""
Widget to display a Month-Picker Calendar on a DateField property.
Args:
- attrs (dict): HTML attributes of rendered HTML input
- format (string): Python DateTime format eg. "%Y-%m-%d"
- options (dict): Options to customize the widget, see README
"""
picker_type = 'MONTH'
format = '01/%m/%Y'
format_key = 'DATE_INPUT_FORMATS'
class YearPickerInput(BasePickerInput):
"""
Widget to display a Year-Picker Calendar on a DateField property.
Args:
- attrs (dict): HTML attributes of rendered HTML input
- format (string): Python DateTime format eg. "%Y-%m-%d"
- options (dict): Options to customize the widget, see README
"""
picker_type = 'YEAR'
format = '01/01/%Y'
format_key = 'DATE_INPUT_FORMATS'
def _link_to(self, linked_picker):
"""Customize the options when linked with other date-time input"""
yformat = self.config['options']['format'].replace('-01-01', '-12-31')
self.config['options']['format'] = yformat

View File

@ -25,6 +25,9 @@ urlpatterns = [
path('admin/doc/', include('django.contrib.admindocs.urls')),
path('admin/', admin.site.urls, name="admin"),
path('accounts/', include('django.contrib.auth.urls')),
path('member/', include('member.urls')),
path('tournament/', include('tournament.urls')),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)