diff --git a/controllers/user.js b/controllers/user.js index 8c8a7ae..8c63529 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -61,6 +61,8 @@ exports.getGodfatherId = async (req, res, next) => } } +// Contrôleur traitant les données envoyées pour une inscription +// Il peut n'y avoir qu'une adresse e-mail ou des données plus complètes (pseudo, parrain, etc.) dès l'inscription exports.signup = async (req, res, next) => { try @@ -68,10 +70,24 @@ exports.signup = async (req, res, next) => const db = require("../models/index"); if(req.body.cguOk !== "true") res.status(400).json({ errors: [txt.needUGCOk] }); - else if(req.body.password.length < config.passwordMinLength) // si le champ est supprimé du formulaire d'inscription, générer un mot de passe ok ici + else if((req.body.password !== undefined) && (req.body.password.length < config.passwordMinLength)) res.status(400).json({ errors: [txt.needLongPassWord.replace("MIN_LENGTH", config.passwordMinLength)] }); + else if(tool.isEmpty(req.body.email)) + res.status(400).json({ errors: [txt.needEmail] }); else { + // Dans le cas d'une inscription simplifiée, seule l'adresse e-mail est demandée : + if(tool.isEmpty(req.body.password)) + req.body.password=tool.getPassword(8, 12); + req.body.password=await bcrypt.hash(req.body.password, config.bcryptSaltRounds); + // Si pas de pseudo envoyé, on utilise la partie de l'e-mail précédant le "@" + if(tool.isEmpty(req.body.name)) + { + const lastIndex=req.body.email.indexOf("@"); + if(lastIndex === -1) + lastIndex=1; + req.body.name=req.body.email.substring(0,lastIndex); + } req.body.GodfatherId=null; if(req.body.codeGodfather!=="") { @@ -79,7 +95,6 @@ exports.signup = async (req, res, next) => if(godfather) req.body.GodfatherId=godfather.id; } - req.body.password=await bcrypt.hash(req.body.password, config.bcryptSaltRounds); const user=await db["User"].create({ ...req.body }, { fields: ["name", "email", "password", "newsletterOk", "GodfatherId", "timeDifference"] }); req.body.UserId=user.id; // si l'utilisateur a répondu à un quiz avant de créer son compte, on enregistre son résultats. diff --git a/front/src/groupElement.js b/front/src/groupElement.js index 1201d90..015688c 100644 --- a/front/src/groupElement.js +++ b/front/src/groupElement.js @@ -1,51 +1,42 @@ -// -- GESTION DU FORMULAIRE PERMETTANT DE CRÉER SON COMPTE +// -- PAGE AFFICHANT L'ÉLÉMENT D'UN GROUPE DE QUIZ ET PROPOSANT DE CRÉER SON COMPTE DE MANIÈRE SIMPLIFIÉE -/// L'utilisateur peut avoir répondu à un quiz avant d'arriver sur la page d'inscription -/// Des ce cas il faut enregistrer son résultat en même temps que les informations de son compte +/// L'utilisateur peut avoir répondu à un quiz avant de lancer la création de son compte +/// Dans ce cas il faut enregistrer son résultat en même temps que les informations de son compte // Fichier de configuration tirés du backend : import { apiUrl, availableLangs, theme } from "../../config/instance.js"; const lang=availableLangs[0]; - const configTemplate = require("../../views/"+theme+"/config/"+lang+".js");// besoin de toutes les déclarations, car appel dynamique : configTemplate[homePage] const configUsers = require("../../config/users");// idem pour configurer formulaire // Importation des fonctions utiles au script : -import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js"; +import { getLocaly, removeLocaly } from "./tools/clientstorage.js"; import { addElement } from "./tools/dom.js"; -import { helloDev } from "./tools/everywhere.js"; +import { helloDev, updateAccountLink } from "./tools/everywhere.js"; import { getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js"; import { loadMatomo } from "./tools/matomo.js"; -import { checkAnswerDatas, checkSession, getPassword, getTimeDifference } from "./tools/users.js"; +import { checkAnswerDatas, checkSession, getTimeDifference } from "./tools/users.js"; // Dictionnaires : -const { notRequired, serverError } = require("../../lang/"+lang+"/general"); -const { alreadyConnected, godfatherFound, godfatherNotFound, needUniqueEmail, passwordCopied } = require("../../lang/"+lang+"/user"); +const { serverError } = require("../../lang/"+lang+"/general"); +const { needUniqueEmail } = require("../../lang/"+lang+"/user"); // Principaux éléments du DOM manipulés : -const myForm=document.getElementById("subscription"); -const divResponse=document.getElementById("response"); -const passwordInput=document.getElementById("password"); -const passwordLink=document.getElementById("getPassword"); -const passwordHelp=document.getElementById("passwordMessage"); -const emailInput=document.getElementById("email"); const btnSubmit=document.getElementById("submitDatas"); -const codeGodfatherInput=document.getElementById("codeGodfather"); +const divResponse=document.getElementById("response"); +const emailInput=document.getElementById("email"); +const myForm=document.getElementById("subscription"); -helloDev(); - -// Test de connexion de l'utilisateur + affichage formulaire d'inscription. +// Test de connexion de l'utilisateur + affichage formulaire d'inscription : const initialise = async () => { try { - const isConnected=await checkSession(); + let isConnected=await checkSession(), user; if(isConnected) { - //saveLocaly("message", { message: alreadyConnected, color:"info" });// pour l'afficher sur la page suivante - //const user=getLocaly("user", true); - //const homePage=user.status+"HomePage"; - //window.location.assign("/"+configTemplate[homePage]); + user=getLocaly("user", true); + updateAccountLink(user.status, configTemplate);// lien vers le compte adapté pour les utilisateurs connectés } else { @@ -61,18 +52,7 @@ const initialise = async () => } } initialise(); - -// Générateur de mot de passe "aléatoire" -passwordLink.addEventListener("click", function(e) -{ - e.preventDefault(); - passwordInput.type="text"; - passwordInput.value=getPassword(8, 12); - // Copie du mot de passe généré dans le "presse-papier" de l'ordinateur : - passwordInput.select(); - document.execCommand("copy"); - addElement(passwordHelp, "div", passwordCopied, "", ["success"]); -}); +helloDev(); // Test si l'e-mail saisi est déjà utilisé par un autre compte. // Si c'est le cas, la validation du formulaire est bloquée. @@ -92,7 +72,7 @@ emailInput.addEventListener("blur", function(e) if (this.readyState == XMLHttpRequest.DONE) { let response=JSON.parse(this.responseText); - if (this.status === 200 && response.free!==undefined && response.free === false) + if (this.status === 200 && response.free !== undefined && response.free === false) { addElement(document.getElementById("emailMessage"), "div", needUniqueEmail.replace("#URL", configTemplate.connectionPage), "", ["error"]); btnSubmit.setAttribute("disabled", true); @@ -107,34 +87,6 @@ emailInput.addEventListener("blur", function(e) } }); -// Vérification que le code/e-mail de parrainage saisi est valide. -codeGodfatherInput.addEventListener("focus", function(e) -{ // on efface l'éventuel message d'erreur si on revient sur le champ pour tester un autre code. - addElement(document.getElementById("codeGodfatherMessage"), "i", notRequired); -}); -codeGodfatherInput.addEventListener("blur", function(e) -{ - const codeValue=codeGodfatherInput.value.trim(); - if(codeValue!=="") - { - const xhr = new XMLHttpRequest(); - xhr.open("POST", apiUrl+configUsers.userRoutes+configUsers.getGodfatherRoute); - xhr.onreadystatechange = function() - { - if (this.readyState == XMLHttpRequest.DONE) - { - if (this.status === 204) - addElement(document.getElementById("codeGodfatherMessage"), "div", godfatherNotFound, "", ["error"]); - else - addElement(document.getElementById("codeGodfatherMessage"), "div", godfatherFound, "", ["success"]); - } - } - xhr.setRequestHeader("Content-Type", "application/json"); - const datas={ codeTest:codeValue }; - xhr.send(JSON.stringify(datas)); - } -}); - // Traitement de l'envoi des données d'inscription : myForm.addEventListener("submit", function(e) { @@ -152,11 +104,14 @@ myForm.addEventListener("submit", function(e) { myForm.style.display="none"; addElement(divResponse, "p", response.message, "", ["success"]); - removeLocaly("lastAnswer");// ! important pour ne pas enregister plusieurs fois le résultat. + removeLocaly("lastAnswer");// !! important, pour ne pas enregister plusieurs fois le résultat. } else if (response.errors) { - response.errors = response.errors.join("
"); + if(Array.isArray(response.errors)) + response.errors = response.errors.join("
"); + else + response.errors = serverError; addElement(divResponse, "p", response.errors, "", ["error"]); } else @@ -168,7 +123,7 @@ myForm.addEventListener("submit", function(e) if(datas) { datas.timeDifference=getTimeDifference(configUsers); - // si l'utilisateur a précédement répondu à un quiz, j'ajoute les infos de son résultat : + // si l'utilisateur a précédement répondu à un quiz, on ajoute les données de son résultat : datas=checkAnswerDatas(datas); xhr.send(JSON.stringify(datas)); } diff --git a/front/src/manageUsers.js b/front/src/manageUsers.js index 96d8432..075c0c3 100644 --- a/front/src/manageUsers.js +++ b/front/src/manageUsers.js @@ -20,9 +20,9 @@ import { getLocaly, removeLocaly } from "./tools/clientstorage.js"; import { addElement } from "./tools/dom.js"; import { helloDev, updateAccountLink } from "./tools/everywhere.js"; import { empyForm, getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js"; -import { dateFormat, isEmpty, replaceAll } from "../../tools/main"; +import { dateFormat, getPassword, isEmpty, replaceAll } from "../../tools/main"; import { getUrlParams } from "./tools/url.js"; -import { checkSession, getPassword } from "./tools/users.js"; +import { checkSession } from "./tools/users.js"; // Dictionnaires : const { addOkMessage, serverError } = require("../../lang/"+lang+"/general"); diff --git a/front/src/subscribe.js b/front/src/subscribe.js index 27c5a07..c4b4eef 100644 --- a/front/src/subscribe.js +++ b/front/src/subscribe.js @@ -15,8 +15,9 @@ import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js"; import { addElement } from "./tools/dom.js"; import { helloDev } from "./tools/everywhere.js"; import { getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js"; +import { getPassword } from "../../tools/main"; import { loadMatomo } from "./tools/matomo.js"; -import { checkAnswerDatas, checkSession, getPassword, getTimeDifference } from "./tools/users.js"; +import { checkAnswerDatas, checkSession, getTimeDifference } from "./tools/users.js"; // Dictionnaires : const { notRequired, serverError } = require("../../lang/"+lang+"/general"); diff --git a/front/src/tools/users.js b/front/src/tools/users.js index ea7a3f4..812da29 100644 --- a/front/src/tools/users.js +++ b/front/src/tools/users.js @@ -18,25 +18,6 @@ export const getTimeDifference = () => return timeLocal; } -// On enlève volontairement les 0/O pour éviter les confusions ! -// Et mieux vaut aussi débuter et finir par une lettre simple. -export const getPassword = (nbCarMin, nbCarMax) => -{ - const nbCar=nbCarMin+Math.floor(Math.random()*(nbCarMax-nbCarMin)); - const letters="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz"; - const others="123456789!?.*-_%@&ÉÀÈÙ€$ÂÊÛÎ"; - let password=letters[Math.floor(Math.random()*letters.length)]; - for(let i=1;i<(nbCar-1);i++) - { - if((i % 2) ===1) - password+=others[Math.floor(Math.random()*others.length)]; - else - password+=letters[Math.floor(Math.random()*letters.length)]; - } - password+=letters[Math.floor(Math.random()*letters.length)]; - return password; -} - // J'utilise le stockage local du navigateur pour enregistrer les données permettant de reconnaître l'utilisateur par la suite // Seul le serveur pourra vérifier que les identifiants sont (toujours) valides. export const setSession = (userId, token, durationTS) => diff --git a/tools/main.js b/tools/main.js index 054df57..e684989 100644 --- a/tools/main.js +++ b/tools/main.js @@ -76,6 +76,25 @@ class Tool else return myMounth+"/"+myDay+"/"+myYear; } + + // On enlève volontairement les 0/O pour éviter les confusions ! + // Et mieux vaut aussi débuter et finir par une lettre simple. + static getPassword (nbCarMin, nbCarMax) + { + const nbCar=nbCarMin+Math.floor(Math.random()*(nbCarMax-nbCarMin)); + const letters="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz"; + const others="123456789!?.*-_%@&ÉÀÈÙ€$ÂÊÛÎ"; + let password=letters[Math.floor(Math.random()*letters.length)]; + for(let i=1;i<(nbCar-1);i++) + { + if((i % 2) ===1) + password+=others[Math.floor(Math.random()*others.length)]; + else + password+=letters[Math.floor(Math.random()*letters.length)]; + } + password+=letters[Math.floor(Math.random()*letters.length)]; + return password; + } } module.exports = Tool; \ No newline at end of file diff --git a/views/wikilerni/quiz-element.pug b/views/wikilerni/quiz-element.pug index 5f1d06d..a9a3bfb 100644 --- a/views/wikilerni/quiz-element.pug +++ b/views/wikilerni/quiz-element.pug @@ -85,7 +85,7 @@ block content strong #{configTpl.noJSNotification} - const cguOkLabel = txtUser.formsCGUOkLabel.replace("#link", "/"+configTpl.cguPage); /// remettre class="needJS" au formulaire ci-dessous div#quizElementSignupForm - form(id="subscription" method="POST") + form(id="subscription" method="POST" class="needJS") h3 #{configTpl.quizElementSubcriptionFormTitle} fieldset label(for="email") #{txtUser.formsEmailLabel} diff --git a/views/wikilerni/quiz-group.pug b/views/wikilerni/quiz-group.pug index 41ee537..302d979 100644 --- a/views/wikilerni/quiz-group.pug +++ b/views/wikilerni/quiz-group.pug @@ -58,7 +58,7 @@ block content div strong #{configTpl.noJSNotification} // à cacher si pas de JS ! - form(id="group" method="POST") + form(id="group" method="POST" class="needJS") h2 #{group.Group.title} div#response div(class="subscribeBtns")