Revue processus connexion compte avec mot de passe ou envoi de lien.
This commit is contained in:
parent
1c056db3bd
commit
7bafca1dc9
@ -336,17 +336,20 @@ exports.checkToken = async (req, res, next) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reçoit les données du formulaire de connexion avec mot de passe.
|
||||||
exports.login = async (req, res, next) =>
|
exports.login = async (req, res, next) =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const db = require("../models/index");
|
const db = require("../models/index");
|
||||||
|
// Est-ce qu'un compte existe pour l'adresse e-mail envoyée ?
|
||||||
const emailSend=tool.trimIfNotNull(req.body.email);
|
const emailSend=tool.trimIfNotNull(req.body.email);
|
||||||
const user=await db["User"].findOne({ attributes: ["id", "password", "email", "name", "status"], where: { email: emailSend } });
|
const user=await db["User"].findOne({ attributes: ["id", "password", "email", "name", "status"], where: { email: emailSend } });
|
||||||
if(!user)
|
if(!user)
|
||||||
res.status(404).json({ errors: [txt.emailNotFound] });
|
res.status(404).json({ errors: [txt.emailNotFound] });
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Est-ce ce compte a déjà été validé par l'utilisateur ? Si non, on lui envoie un nouveau lien de validation.
|
||||||
const subscription=await db["Subscription"].findOne({ attributes: ["id"], where: { UserId: user.id } });
|
const subscription=await db["Subscription"].findOne({ attributes: ["id"], where: { UserId: user.id } });
|
||||||
if(!subscription)
|
if(!subscription)
|
||||||
{
|
{
|
||||||
@ -356,30 +359,35 @@ exports.login = async (req, res, next) =>
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
const nowTS=new Date().getTime();
|
const nowTS=new Date().getTime();
|
||||||
|
// L'utilisateur n'a-t-il pas testé de se connecter de trop nombreuses fois sans succès ?
|
||||||
const countLogin=await toolFile.readJSON(config.dirTmpLogin, slugify(emailSend));
|
const countLogin=await toolFile.readJSON(config.dirTmpLogin, slugify(emailSend));
|
||||||
if(countLogin && countLogin.nb >= config.maxLoginFail && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*3600*1000))
|
if(countLogin && countLogin.nb >= config.maxLoginFail && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*60*1000))
|
||||||
res.status(401).json({ errors: [txt.tooManyLoginFails.replace("MINUTES", config.loginFailTimeInMinutes)] });
|
res.status(401).json({ errors: [txt.tooManyLoginFails.replace("MINUTES", config.loginFailTimeInMinutes)] });
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Le mot du passe envoyé est-il cohérent avec celui de la base de données après chiffrement ?
|
||||||
const valid = await bcrypt.compare(req.body.password, user.password);
|
const valid = await bcrypt.compare(req.body.password, user.password);
|
||||||
if (!valid)
|
if (!valid)
|
||||||
{
|
{
|
||||||
res.status(401).json({ errors: [txt.badPassword] });
|
res.status(401).json({ errors: [txt.badPassword] });
|
||||||
|
// On comptabilise l'erreur :
|
||||||
let newCountLogin={ nb:1, lastTime:nowTS };
|
let newCountLogin={ nb:1, lastTime:nowTS };
|
||||||
if(countLogin.nb && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*3600*1000))
|
if(countLogin.nb && countLogin.lastTime > (nowTS-config.loginFailTimeInMinutes*60*1000))
|
||||||
newCountLogin.nb=countLogin.nb+1;
|
newCountLogin.nb=countLogin.nb+1;
|
||||||
await toolFile.createJSON(config.dirTmpLogin, slugify(emailSend), newCountLogin);
|
await toolFile.createJSON(config.dirTmpLogin, slugify(emailSend), newCountLogin);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Si tout est ok, on enregistre la date de connexion + retourne un token de connexion.
|
||||||
const now=new Date();
|
const now=new Date();
|
||||||
const timeDifference=req.body.timeDifference;// permet d'actualiser en cas de déplacements/heures d'été, etc.
|
const timeDifference=req.body.timeDifference;
|
||||||
db["User"].update({ connectedAt: now, timeDifference: timeDifference }, { where: { id : user.id }, limit:1 });
|
db["User"].update({ connectedAt: now, timeDifference: timeDifference }, { where: { id : user.id }, limit:1 });
|
||||||
creaUserJson(user.id);
|
creaUserJson(user.id);
|
||||||
|
// Connexion à rallonge uniquement possible pour utilisateur de base :
|
||||||
let loginTime=config.tokenConnexionMinTimeInHours;
|
let loginTime=config.tokenConnexionMinTimeInHours;
|
||||||
if((req.body.keepConnected==="true") && (user.status==="user"))
|
if((req.body.keepConnected==="true") && (user.status==="user"))
|
||||||
loginTime=config.tokenConnexionMaxTimeInDays;
|
loginTime=config.tokenConnexionMaxTimeInDays;
|
||||||
// si des données concernant un quiz ont été transmises, je les enregistre ici :
|
// Si des données concernant un quiz ont été transmises, on les enregistre ici :
|
||||||
req.body.UserId=user.id;
|
req.body.UserId=user.id;
|
||||||
if(req.body.QuestionnaireId)
|
if(req.body.QuestionnaireId)
|
||||||
{
|
{
|
||||||
@ -410,18 +418,21 @@ exports.login = async (req, res, next) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reçoit les données du formulaire de connexion avec demande de recevoir un lien de connexion.
|
||||||
exports.getLoginLink = async (req, res, next) =>
|
exports.getLoginLink = async (req, res, next) =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Est-ce qu'un compte existe pour l'adresse e-mail envoyée ?
|
||||||
const emailSend=tool.trimIfNotNull(req.body.email);
|
const emailSend=tool.trimIfNotNull(req.body.email);
|
||||||
const userDatas=await searchUserByEmail(emailSend);
|
const userDatas=await searchUserByEmail(emailSend);
|
||||||
if(!userDatas)
|
if(!userDatas)
|
||||||
res.status(404).json({ errors: [txt.emailNotFound] });
|
res.status(404).json({ errors: [txt.emailNotFound] });
|
||||||
else if(userDatas.User.status!=="user")
|
else if(userDatas.User.status!=="user") // seuls les utilisateurs de base peuvent se connecter de cette façon.
|
||||||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Est-ce ce compte a déjà été validé par l'utilisateur ? Si non, on lui envoie un nouveau lien de validation.
|
||||||
if(!userDatas.Subscription)
|
if(!userDatas.Subscription)
|
||||||
{
|
{
|
||||||
await sendValidationLink(userDatas.User);
|
await sendValidationLink(userDatas.User);
|
||||||
@ -447,7 +458,7 @@ exports.getLoginLink = async (req, res, next) =>
|
|||||||
mailRecipientAddress: userDatas.User.email
|
mailRecipientAddress: userDatas.User.email
|
||||||
}
|
}
|
||||||
await toolMail.sendMail(userDatas.User.smtp, userDatas.User.email, txt.mailLoginLinkSubject, tool.replaceAll(txt.mailLoginLinkBodyTxt, mapMail), "", mailDatas);
|
await toolMail.sendMail(userDatas.User.smtp, userDatas.User.email, txt.mailLoginLinkSubject, tool.replaceAll(txt.mailLoginLinkBodyTxt, mapMail), "", mailDatas);
|
||||||
res.status(200).json({ message: txt.mailLoginLinkMessage+config.tokenLoginLinkTimeInHours+"." });
|
res.status(200).json({ message: txt.mailLoginLinkMessage.replace("*TIMING*", config.tokenLoginLinkTimeInHours) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
<div id="prompt" class="cardboard">
|
<div id="prompt" class="cardboard">
|
||||||
<a href="/" title="Page d'accueil WikLerni"><img src="/themes/wikilerni/img/wikilerni-purple-2-512.png" alt="Logo WikiLerni" title="W I K I L E R N I" /></a>
|
<a href="/" title="Page d'accueil WikLerni"><img src="/themes/wikilerni/img/wikilerni-purple-2-512.png" alt="Logo WikiLerni" title="W I K I L E R N I" /></a>
|
||||||
<p class="cardboard">Cultivons notre jardin !</p>
|
<p class="cardboard">Cultivons notre jardin !</p>
|
||||||
<div id="response" class="error">Si vous voyez ce message, c'est que votre lien de connexion n'est pas valide. Vous pouvez <a href="/connexion.html">en demander un nouveau en cliquant ici</a>.</div>
|
<div id="response">Si vous voyez ce message, c'est que votre lien de connexion n'est pas valide ou a expiré. Vous pouvez <a href="/connexion.html">en demander un nouveau en cliquant ici</a>.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="cardboard">
|
<footer class="cardboard">
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
// -- GESTION DU FORMULAIRE PERMETTANT DE SE CONNECTER
|
// -- GESTION DU FORMULAIRE PERMETTANT DE SE CONNECTER
|
||||||
|
|
||||||
/// L'utilisateur peut avoir répondu à un quiz avant d'arriver sur la page de connexion
|
/// L'utilisateur peut avoir répondu à un quiz avant d'arriver sur la page de connexion.
|
||||||
/// Dans ce cas il faut enregistrer son résultat en même temps, une fois la connexion validée
|
/// Dans ce cas il faut enregistrer son résultat en même temps, une fois la connexion validée.
|
||||||
|
|
||||||
/// Le connexion peut se faire directement ici via la saisie d'un mot de passe
|
/// Le connexion peut se faire directement ici via la saisie d'un mot de passe ou via l'envoi d'un token par e-mail.
|
||||||
/// Ou via l'envoi d'un token par e-mail
|
|
||||||
|
|
||||||
// Fichier de configuration tirés du backend :
|
// Fichier de configuration tirés du backend :
|
||||||
import { apiUrl, availableLangs, siteUrl, theme } from "../../config/instance.js";
|
import { apiUrl, availableLangs, siteUrl, theme } from "../../config/instance.js";
|
||||||
@ -13,17 +12,18 @@ const lang=availableLangs[0];
|
|||||||
import { connectionRoute, getLoginLinkRoute, userRoutes } from "../../config/users.js";
|
import { connectionRoute, getLoginLinkRoute, userRoutes } from "../../config/users.js";
|
||||||
const configTemplate = require("../../views/"+theme+"/config/"+lang+".js");
|
const configTemplate = require("../../views/"+theme+"/config/"+lang+".js");
|
||||||
|
|
||||||
// Importation des fonctions utile au script :
|
// Importation des fonctions utiles au script :
|
||||||
import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js";
|
import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js";
|
||||||
import { addElement } from "./tools/dom.js";
|
import { addElement } from "./tools/dom.js";
|
||||||
import { helloDev } from "./tools/everywhere.js";
|
import { helloDev } from "./tools/everywhere.js";
|
||||||
import { getDatasFromInputs } from "./tools/forms.js";
|
import { getDatasFromInputs } from "./tools/forms.js";
|
||||||
import { isEmpty } from "../../tools/main";
|
import { isEmpty } from "../../tools/main";
|
||||||
import { checkAnswerDatas, checkSession, getConfig, getTimeDifference, setSession } from "./tools/users.js";
|
import { checkAnswerDatas, checkSession, getTimeDifference, setSession } from "./tools/users.js";
|
||||||
|
|
||||||
// Dictionnaires :
|
// Dictionnaires :
|
||||||
const txt = require("../../lang/"+lang+"/general");
|
const txtServerError = require("../../lang/"+lang+"/general").serverError;
|
||||||
const txtUsers = require("../../lang/"+lang+"/user");
|
const txtAlreadyConnected = require("../../lang/"+lang+"/user").alreadyConnected;
|
||||||
|
const txtNeedChooseLoginWay = require("../../lang/"+lang+"/user").needChooseLoginWay;
|
||||||
|
|
||||||
// Principaux éléments du DOM manipulés :
|
// Principaux éléments du DOM manipulés :
|
||||||
const myForm = document.getElementById("connection");
|
const myForm = document.getElementById("connection");
|
||||||
@ -40,11 +40,10 @@ const initialise = async () =>
|
|||||||
const isConnected=await checkSession();
|
const isConnected=await checkSession();
|
||||||
if(isConnected)
|
if(isConnected)
|
||||||
{
|
{
|
||||||
saveLocaly("message", { message: txtUsers.alreadyConnected, color:"information" });// pour l'afficher sur la page suivante
|
saveLocaly("message", { message: txtAlreadyConnected, color:"info" });// pour l'afficher sur la page suivante
|
||||||
const user=getLocaly("user", true);
|
const user=getLocaly("user", true);
|
||||||
const homePage=user.status+"HomePage";
|
const homePage=user.status+"HomePage";
|
||||||
window.location.assign("/"+configTemplate[homePage]);
|
window.location.assign("/"+configTemplate[homePage]);
|
||||||
addElement(divResponse, "p", txtUsers.alreadyConnected, "", ["information"]);// au cas où blocage redirection
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -58,7 +57,7 @@ const initialise = async () =>
|
|||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +72,7 @@ myForm.addEventListener("submit", function(e)
|
|||||||
divResponse.innerHTML="";// efface d'éventuels messages déjà affichés
|
divResponse.innerHTML="";// efface d'éventuels messages déjà affichés
|
||||||
let datas=getDatasFromInputs(myForm);
|
let datas=getDatasFromInputs(myForm);
|
||||||
if(isEmpty(datas.password) && isEmpty(datas.getLoginLink))
|
if(isEmpty(datas.password) && isEmpty(datas.getLoginLink))
|
||||||
addElement(divResponse, "div", txtUsers.needChooseLoginWay, "", ["error"]);
|
addElement(divResponse, "div", txtNeedChooseLoginWay, "", ["error"]);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
@ -89,21 +88,20 @@ myForm.addEventListener("submit", function(e)
|
|||||||
if (this.status === 200)
|
if (this.status === 200)
|
||||||
{
|
{
|
||||||
if(!isEmpty(response.message))
|
if(!isEmpty(response.message))
|
||||||
{ // cas d'une demande de lien de connexion
|
{ // cas d'une demande de lien de connexion avec succès.
|
||||||
myForm.style.display="none";
|
myForm.style.display="none";
|
||||||
addElement(divResponse, "p", response.message, "", ["success"]);
|
addElement(divResponse, "p", response.message, "", ["success"]);
|
||||||
}
|
}
|
||||||
else if(!isEmpty(response.userId) && !isEmpty(response.connexionTime) && !isEmpty(response.token))
|
else if(!isEmpty(response.userId) && !isEmpty(response.connexionTime) && !isEmpty(response.token))
|
||||||
{ // cas d'une connexion directe, on créé une session de connexion et redirige l'utilisateur
|
{ // cas d'une connexion via mot de passe avec succès : on crée une session de connexion et redirige l'utilisateur.
|
||||||
let connexionMaxTime=Date.now();
|
let connexionMaxTime=Date.now();
|
||||||
if(response.connexionTime.endsWith("days"))
|
if(response.connexionTime.endsWith("days"))// l'utilisateur a demandé à rester connecté sur la durée.
|
||||||
connexionMaxTime+=parseInt(response.connexionTime,10)*24*3600*1000;
|
connexionMaxTime+=parseInt(response.connexionTime,10)*24*3600*1000;
|
||||||
else
|
else
|
||||||
connexionMaxTime+=parseInt(response.connexionTime,10)*3600*1000;
|
connexionMaxTime+=parseInt(response.connexionTime,10)*3600*1000;
|
||||||
setSession(response.userId, response.token, connexionMaxTime);
|
setSession(response.userId, response.token, connexionMaxTime);
|
||||||
removeLocaly("lastAnswer");// ! important pour ne pas enregister plusieurs fois le résultat
|
removeLocaly("lastAnswer");// ! important pour ne pas enregister plusieurs fois son éventuel résultat au quiz.
|
||||||
myForm.style.display="none";
|
myForm.style.display="none";
|
||||||
//addElement(divResponse, "p", txtUsers.connectionOk, "", ["success"]);// au cas où blocage redirection
|
|
||||||
// l'utilisateur peut avoir tenté d'accéder à une autre page que sa page d'accueil :
|
// l'utilisateur peut avoir tenté d'accéder à une autre page que sa page d'accueil :
|
||||||
let url=getLocaly("url", true);
|
let url=getLocaly("url", true);
|
||||||
if(!isEmpty(url) && url.href.indexOf(siteUrl)!==-1)
|
if(!isEmpty(url) && url.href.indexOf(siteUrl)!==-1)
|
||||||
@ -112,29 +110,26 @@ myForm.addEventListener("submit", function(e)
|
|||||||
removeLocaly("url");
|
removeLocaly("url");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
url=configTemplate[response.status+"HomePage"]
|
url=configTemplate[response.status+"HomePage"];
|
||||||
window.location.assign(url);
|
window.location.assign(url);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||||
}
|
}
|
||||||
else if (response.errors)
|
else if (response.errors)
|
||||||
{
|
{
|
||||||
if(Array.isArray(response.errors))
|
response.errors = response.errors.join("<br>");
|
||||||
response.errors = response.errors.join("<br>");
|
|
||||||
else
|
|
||||||
response.errors = txt.serverError;
|
|
||||||
addElement(divResponse, "p", response.errors, "", ["error"]);
|
addElement(divResponse, "p", response.errors, "", ["error"]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
xhr.setRequestHeader("Content-Type", "application/json");
|
||||||
if(datas)
|
if(datas)
|
||||||
{
|
{
|
||||||
datas.timeDifference=getTimeDifference();
|
datas.timeDifference=getTimeDifference();
|
||||||
// si l'utilisateur a précédement répondu à un quiz, j'ajoute les infos de son résultat :
|
// Si l'utilisateur a répondu à un quiz, j'ajoute les infos de son résultat aux données envoyées :
|
||||||
datas=checkAnswerDatas(datas);
|
datas=checkAnswerDatas(datas);
|
||||||
xhr.send(JSON.stringify(datas));
|
xhr.send(JSON.stringify(datas));
|
||||||
}
|
}
|
||||||
@ -142,7 +137,7 @@ myForm.addEventListener("submit", function(e)
|
|||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
addElement(divResponse, "p", txt.serverError, "", ["error"]);
|
addElement(divResponse, "p", txtServerError, "", ["error"]);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -15,7 +15,7 @@ import { getLocaly, removeLocaly, saveLocaly } from "./tools/clientstorage.js";
|
|||||||
import { addElement } from "./tools/dom.js";
|
import { addElement } from "./tools/dom.js";
|
||||||
import { helloDev } from "./tools/everywhere.js";
|
import { helloDev } from "./tools/everywhere.js";
|
||||||
import { getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js";
|
import { getDatasFromInputs, setAttributesToInputs } from "./tools/forms.js";
|
||||||
import { checkAnswerDatas, checkSession, getConfig, getPassword, getTimeDifference } from "./tools/users.js";
|
import { checkAnswerDatas, checkSession, getPassword, getTimeDifference } from "./tools/users.js";
|
||||||
|
|
||||||
// Dictionnaires :
|
// Dictionnaires :
|
||||||
const txtServerError = require("../../lang/"+lang+"/general").serverError;
|
const txtServerError = require("../../lang/"+lang+"/general").serverError;
|
||||||
|
@ -44,14 +44,14 @@ module.exports =
|
|||||||
needBeConnected: "Vous devez être connecté pour accéder à cette page.",
|
needBeConnected: "Vous devez être connecté pour accéder à cette page.",
|
||||||
connectionOk: "Connexion réussie.",
|
connectionOk: "Connexion réussie.",
|
||||||
needChooseLoginWay: "Vous devez soit saisir votre mot de passe, soit cocher la case vous permettant de recevoir un lien de connexion par e-mail.",
|
needChooseLoginWay: "Vous devez soit saisir votre mot de passe, soit cocher la case vous permettant de recevoir un lien de connexion par e-mail.",
|
||||||
needValidationToLogin : "Vous devez d'abord valider votre compte avant de vous connecter. Pour ce faire, un lien vient de vous être envoyé par e-mail.",
|
needValidationToLogin : "Vous devez d'abord valider votre compte avant de pouvoir vous connecter. Pour ce faire, un nouveau lien vient de vous être envoyé par e-mail.",
|
||||||
tooManyLoginFails : "Désolé mais il y a eu trop de tentatives de connexion infructueuses pour cette adresse e-mail. Vous devez attendre MINUTES minutes pour essayer de nouveau.",
|
tooManyLoginFails : "Désolé mais il y a eu trop de tentatives de connexion infructueuses pour cette adresse e-mail. Vous devez attendre MINUTES minutes pour essayer de nouveau.",
|
||||||
badPassword: "Aucun compte utilisateur ne correspond aux informations saisies.",
|
badPassword: "Aucun compte utilisateur ne correspond aux informations saisies.",
|
||||||
mailLoginLinkSubject : "Votre lien de connexion.",
|
mailLoginLinkSubject : "Votre lien de connexion.",
|
||||||
mailLoginLinkTxt : "Me connecter.",
|
mailLoginLinkTxt : "Me connecter.",
|
||||||
mailLoginLinkBodyTxt : "Bonjour USER_NAME,\n\nPour vous connecter à votre compte, cliquez sur le lien suivant sans tarder :\nLINK_URL",
|
mailLoginLinkBodyTxt : "Bonjour USER_NAME,\n\nPour vous connecter à votre compte, cliquez sur le lien suivant sans tarder :\nLINK_URL",
|
||||||
mailLoginLinkBodyHTML : "<h3>Bonjour USER_NAME,</h3><p>Pour vous connecter à votre compte, cliquez sur le lien suivant sans tarder :</p>",
|
mailLoginLinkBodyHTML : "<h3>Bonjour USER_NAME,</h3><p>Pour vous connecter à votre compte, cliquez sur le lien suivant sans tarder :</p>",
|
||||||
mailLoginLinkMessage : "Un lien de connexion vient de vous être envoyé sur votre adresse e-mail. Ne tardez pas à l'utiliser, car il n'est valable que durant ",
|
mailLoginLinkMessage : "Un lien de connexion vient de vous être envoyé sur votre adresse e-mail. Ne tardez pas à l'utiliser, car il n'est valable que durant *TIMING* !",
|
||||||
updatedOkMessage: "Vos informations ont bien été mises à jour.",
|
updatedOkMessage: "Vos informations ont bien été mises à jour.",
|
||||||
updatedNeedGoodEmail : "Mais la nouvelle adresse e-mail n'a pu être enregistrée, car elle n'a pas un format correct.",
|
updatedNeedGoodEmail : "Mais la nouvelle adresse e-mail n'a pu être enregistrée, car elle n'a pas un format correct.",
|
||||||
updatedNeedUniqueEmail : "Mais la nouvelle adresse e-mail saisie (NEW_EMAIL) n'a pu être enregistrée, car elle est déjà utilisée pour un autre compte.",
|
updatedNeedUniqueEmail : "Mais la nouvelle adresse e-mail saisie (NEW_EMAIL) n'a pu être enregistrée, car elle est déjà utilisée pour un autre compte.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user