From bbca4c0a7fab26c963b71caafe035c832961b7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20PENHO=C3=8BT?= Date: Mon, 26 Oct 2020 17:23:25 +0100 Subject: [PATCH] =?UTF-8?q?Modification=20contr=C3=B4leur=20envoi=20mails?= =?UTF-8?q?=20aux=20abonn=C3=A9s=20(=C3=A0=20revoir)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/instance-example.js | 2 +- controllers/answer.js | 2 +- controllers/subscription.js | 144 ++++++++++++++++++++++++------------ front/src/tools/answers.js | 2 +- lang/fr/general.js | 1 + lang/fr/questionnaire.js | 2 +- lang/fr/subscription.js | 3 +- views/wikilerni/mail.pug | 3 +- 8 files changed, 106 insertions(+), 53 deletions(-) diff --git a/config/instance-example.js b/config/instance-example.js index 64187c3..d44233a 100644 --- a/config/instance-example.js +++ b/config/instance-example.js @@ -38,7 +38,7 @@ module.exports = nbNewQuestionnaires: 10, // for RSS, etc. hourGiveNewQuestionnaireBegin: 3, // in user local time hourGiveNewQuestionnaireEnd: 8, // idem - numberNewQuestionnaireAtSameTime: 50, // for mass mailing sending new quiz + maxQuestionnaireSendedAtSameTime: 50, // for subscription's e-mailing minSearchQuestionnaires: 3, fieldNewQuestionnaires : "publishingAt", // field to be used to create the list of the last questionnaires, can be "createdAt", "updatedAt" or "publishingAt" // Groups : diff --git a/controllers/answer.js b/controllers/answer.js index 55e9abe..894f1b2 100644 --- a/controllers/answer.js +++ b/controllers/answer.js @@ -126,7 +126,7 @@ const saveAnswerToGroup = async (req) => { const db = require("../models/index"); const answer = await db["Answer"].create({ ...req }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "GroupId", "UserId"] }); - const group = groupCtrl.searchGroupById(req.GroupId); + const group = await groupCtrl.searchGroupById(req.GroupId); for(let i in group.Questionnaires) { if(await subscriptionCtrl.checkQuestionnaireAccess(req.UserId, group.Questionnaires[i].id) === false) diff --git a/controllers/subscription.js b/controllers/subscription.js index 8dbacc7..2db28e7 100644 --- a/controllers/subscription.js +++ b/controllers/subscription.js @@ -17,6 +17,7 @@ const txtQuestionnaireAccess= require("../lang/"+config.adminLang+"/questionnair const txtGeneral= require("../lang/"+config.adminLang+"/general"); const answerCtrl = require("./answer"); +const groupCtrl = require("./group"); const questionnaireCtrl = require("./questionnaire"); const userCtrl = require("./user"); @@ -38,6 +39,7 @@ exports.unsubscribeLink = async (req, res, next) => } } + // CRONS // Envoi des notifications aux abonnés arrivés à quelques jours de la fin de leur période d'abonnement gratuit. @@ -177,80 +179,129 @@ exports.notifyExpirationAccount= async(req, res, next) => } } -// CRON donnant accès à l'utilisateur à nouveau quiz suivant son choix -// si il n'a plus de nouveaux quizs et que l'utilisateur souhaite recevoir un email, on lui passe un quiz au hasard à retester. +// Diffuse aux abonnés un nouvel élément de quiz, en parcourant ceux d'un même groupe, dans l'ordre où ils ont été enregistrés. +// Quand tous les éléments ont été envoyés, l'abonné reçoit en plus le quiz regroupant les questions des éléments du groupe. +// Si il n'y plus de nouveautés pour l'utilisateur, on lui envoie un quiz au hasard parmi ceux déjà envoyés pour le retester. exports.addNewQuestionnaireUsers = async(req, res, next) => { try - { /// revoir pour ne plus associer et envoyer de mails qu'aux utilisateurs ayant des jours d'envoi choisis + { const db = require("../models/index"); - // Utilisateurs dont l'abonnement est toujours actif et souhaitant recevoir un nouveau quiz le jour de l'appel de cette méthode. - // Le tout en heure locale et en ignorant ceux qui ont déjà été traités ce jour. - const subscriptionsOk = await db.sequelize.query("SELECT `Subscriptions`.`id` as SubscriptionId, `Subscriptions`.`lastProcessingAt`, `UserId`, `name`, `email`, `smtp`, `language`, `receiptDays`, ADDDATE(UTC_TIMESTAMP, INTERVAL `timeDifference` MINUTE) AS localDate FROM `Subscriptions` INNER JOIN `Users` ON `Subscriptions`.`UserId`=`Users`.`id` WHERE `status`='user' AND ADDDATE(`Subscriptions`.`createdAt`, `numberOfDays`) > UTC_TIMESTAMP HAVING HOUR(localDate) > "+config.hourGiveNewQuestionnaireBegin+" AND HOUR(localDate) < "+config.hourGiveNewQuestionnaireEnd+" AND LOCATE(DAYOFWEEK(localDate),receiptDays)!=0 AND SubscriptionId NOT IN (SELECT DISTINCT `SubscriptionId` FROM `Pauses` WHERE ADDDATE(`startingAt`, INTERVAL `timeDifference` MINUTE) <= localDate AND ADDDATE(`endingAT`, INTERVAL `timeDifference` MINUTE) > localDate) AND DATEDIFF(NOW(),`Subscriptions`.`lastProcessingAt`) >= 1 LIMIT "+config.numberNewQuestionnaireAtSameTime, { type: QueryTypes.SELECT }); - if(subscriptionsOk.length===0) + // Utilisateurs dont l'abonnement est toujours actif et souhaitant recevoir des quizs le jour de la semaine en cours. + // Le tout en heure locale et en ignorant ceux qui ont déjà été traités ces dernières 24H. + const subscriptionsOk = await db.sequelize.query("SELECT `Subscriptions`.`id` as SubscriptionId, `Subscriptions`.`lastProcessingAt`, `UserId`, `name`, `email`, `smtp`, `language`, `receiptDays`, ADDDATE(UTC_TIMESTAMP, INTERVAL `timeDifference` MINUTE) AS localDate FROM `Subscriptions` INNER JOIN `Users` ON `Subscriptions`.`UserId`=`Users`.`id` WHERE `status`='user' AND ADDDATE(`Subscriptions`.`createdAt`, `numberOfDays`) > UTC_TIMESTAMP HAVING HOUR(localDate) > "+config.hourGiveNewQuestionnaireBegin+" AND HOUR(localDate) < "+config.hourGiveNewQuestionnaireEnd+" AND LOCATE(DAYOFWEEK(localDate),receiptDays)!=0 AND SubscriptionId NOT IN (SELECT DISTINCT `SubscriptionId` FROM `Pauses` WHERE ADDDATE(`startingAt`, INTERVAL `timeDifference` MINUTE) <= localDate AND ADDDATE(`endingAT`, INTERVAL `timeDifference` MINUTE) > localDate) AND DATEDIFF(NOW(),`Subscriptions`.`lastProcessingAt`) >= 1 LIMIT "+config.maxQuestionnaireSendedAtSameTime, { type: QueryTypes.SELECT }); + if(subscriptionsOk.length === 0) res.status(200).json({ message: txt.allSubscriptionProcessed }); else { - let newQuestionnaire, access, questionnaire, token, now=new Date(); + let lastSended, lastSendedGroup, lastSendedGroupNbElements, elementToSend, getElementToSend, accessSave, token, textMail, now=new Date(); for (let i in subscriptionsOk) { - newQuestionnaire=await db.sequelize.query("SELECT `Questionnaires`.`id`, `title`, `slug`, `introduction`, `url`, `anchor`,`estimatedTime` FROM `Questionnaires` INNER JOIN `Links` ON `Links`.`QuestionnaireId`=`Questionnaires`.`id` WHERE `isPublished`=1 AND `language`='"+subscriptionsOk[i].language+"' AND `Questionnaires`.`id` NOT IN (SELECT `QuestionnaireId` FROM `QuestionnaireAccesses` WHERE `UserId`="+subscriptionsOk[i].UserId+") ORDER BY `id` LIMIT 1", { type: QueryTypes.SELECT }); - if(newQuestionnaire.length!==0) + // On commence à chercher le dernier élément auquel il a été donné accès à l'utilisateur pour comparer sa date à celle du dernier envoi + lastSended = await db.sequelize.query("SELECT DATE_FORMAT(`QuestionnaireAccesses`.`createdAt`, \"%Y-%m-%d\") as `dateSended`, `QuestionnaireId`, `GroupId`, `rankInGroup` FROM `QuestionnaireAccesses` INNER JOIN `Questionnaires` WHERE `Questionnaires`.`id`=`QuestionnaireAccesses`.`QuestionnaireId` AND `UserId`="+subscriptionsOk[i].UserId+" AND `selfCreatedOk`= 0 AND `GroupId` IS NOT NULL ORDER BY `QuestionnaireAccesses`.`CreatedAt` DESC LIMIT 1", { type: QueryTypes.SELECT }); + elementToSend="", lastSendedGroup=""; + // Il y a déjà eu au moins un envoi et le dernier envoi était l'élément d'un quiz groupé : + if(lastSended.length !==0 && lastSended[0].dateSended == subscriptionsOk[i].lastProcessingAt) { - access=await db["QuestionnaireAccess"].create({ QuestionnaireId: newQuestionnaire[0].id, UserId: subscriptionsOk[i].UserId, selfCreatedOk: false }); - if(access) + // Si l'élément envoyé était le dernier de son groupe, on va lui envoyer le lien du quiz du groupe : + lastSendedGroup = await groupCtrl.searchGroupById(lastSended[0].GroupId); + lastSendedGroupNbElements = lastSendedGroup.Questionnaires.length; + if(!tool.isEmpty(lastSendedGroupNbElements) && lastSendedGroup.Questionnaires[(lastSendedGroupNbElements-1)].id == lastSended[0].QuestionnaireId) + elementToSend = lastSendedGroup.Group; + else { - answerCtrl.creaUserQuestionnairesWithoutAnswerJson(subscriptionsOk[i].UserId); - token=jwt.sign({ userId: subscriptionsOk[i].UserId }, config.tokenPrivateKey, { expiresIn: config.tokenUnsubscribeLinkTimeInDays }); - const mapMail = - { - USER_NAME: subscriptionsOk[i].name, - QUESTIONNAIRE_URL: config.siteUrl+"/"+configQuestionnaires.dirWebQuestionnaire+"/"+newQuestionnaire[0].slug+".html", - UNSUBSCRIBE_URL: config.siteUrl+"/"+configTpl.stopMailPage+token - }; - const mailDatas= - { - mailSubject: newQuestionnaire[0].title, - mailPreheader: newQuestionnaire[0].title, - mailTitle: newQuestionnaire[0].title, - mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.stopMailPage+token, - mailHeaderLinkTxt: txt.mailStopMailLinkTxt, - mailMainContent: newQuestionnaire[0].introduction+"

"+txtQuestionnaire.estimatedTime+" "+txtQuestionnaire.estimatedTimeOption[newQuestionnaire[0].estimatedTime]+".

", - linksCTA: [{ url:newQuestionnaire[0].url, txt:newQuestionnaire[0].anchor }, { url:config.siteUrl+"/"+configQuestionnaires.dirWebQuestionnaire+"/"+newQuestionnaire[0].slug+".html#questionnaire", txt:txtQuestionnaire.btnShowQuestionnaire }], - mailRecipientAddress: subscriptionsOk[i].email - } - toolMail.sendMail(subscriptionsOk[i].smtp, subscriptionsOk[i].email, newQuestionnaire[0].title, tool.replaceAll(txt.mailNewQuestionnaireBodyTxt, mapMail), "", mailDatas); - + // dans ce cas, on va envoyer l'élément suivant du groupe : + for(let j in lastSendedGroup.Questionnaires) + if(lastSendedGroup.Questionnaires[j].id == lastSended[0].QuestionnaireId) + elementToSend = await questionnaireCtrl.searchQuestionnaireById(lastSendedGroup.Questionnaires[(parseInt(j)+1)].id, true); } - db["Subscription"].update({ lastProcessingAt: now }, { where: { id : subscriptionsOk[i].SubscriptionId }, limit:1 }); } else { - // L'utilisateur a déjà accès à tous les quizs enregistés. - // Si il est abonné par email, on en tire un au hasard pour le lui envoyer, sinon on ne fait rien - getRandomQuestionnaire=await db.sequelize.query("SELECT `Questionnaires`.`id`, `title`, `slug`, `introduction`, `url`, `anchor`, `estimatedTime` FROM `Questionnaires` INNER JOIN `Links` ON `Links`.`QuestionnaireId`=`Questionnaires`.`id` WHERE `isPublished`=1 AND `language`='"+subscriptionsOk[i].language+"' ORDER BY RAND() LIMIT 1", { type: QueryTypes.SELECT }); + // Soit il s'agit du premier envoi d'un abonnement, soit le dernier envoi était un quiz groupé. + // Dans ces deux cas, on va envoyer le premier élément non encore envoyé d'un groupe : + getElementToSend = await db.sequelize.query("SELECT `Questionnaires`.`id` FROM `Questionnaires` INNER JOIN `Groups` WHERE `Questionnaires`.`GroupId`=`Groups`.`id` AND `Questionnaires`.`isPublished`=1 AND `Groups`.`language`='"+subscriptionsOk[i].language+"' AND `Groups`.`publishingAt` < NOW() AND `Questionnaires`.`id` NOT IN (SELECT `QuestionnaireId` FROM `QuestionnaireAccesses` where `UserId`="+subscriptionsOk[i].UserId+") ORDER BY `Groups`.`publishingAt`,`rankInGroup` ASC", { type: QueryTypes.SELECT }); + if(getElementToSend.length !== 0) + elementToSend = await questionnaireCtrl.searchQuestionnaireById(getElementToSend[0].id, true); + } + // console.log(elementToSend); + if(!tool.isEmpty(elementToSend)) + { + token=jwt.sign({ userId: subscriptionsOk[i].UserId }, config.tokenPrivateKey, { expiresIn: config.tokenUnsubscribeLinkTimeInDays }); + if(elementToSend.Questionnaire !== undefined)// il s'agit de l'élément d'un group + { + const mapMail = + { + USER_NAME: subscriptionsOk[i].name, + QUESTIONNAIRE_URL: config.siteUrl+"/"+configQuestionnaires.dirWebQuestionnaires+"/"+elementToSend.Questionnaire.slug+".html", + UNSUBSCRIBE_URL: config.siteUrl+"/"+configTpl.stopMailPage+token + }; + const mailDatas = + { + mailSubject: elementToSend.Questionnaire.title, + mailPreheader: elementToSend.Questionnaire.title, + mailTitle: elementToSend.Questionnaire.title, + mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.stopMailPage+token, + mailHeaderLinkTxt: txt.mailStopMailLinkTxt, + mailMainContent: elementToSend.Questionnaire.introduction, + mailMainContentLinks : elementToSend.Questionnaire.Links, + linksCTA: [{ url:config.siteUrl+"/"+configQuestionnaires.dirWebQuestionnaires+"/"+elementToSend.Questionnaire.slug+".html", txt:txtGeneral.btnShowOnWebSite.replace("#SITE_NAME", config.siteName) }], + mailRecipientAddress: subscriptionsOk[i].email + } + toolMail.sendMail(subscriptionsOk[i].smtp, subscriptionsOk[i].email, mailDatas.mailSubject, tool.replaceAll(txt.mailNewElementForGroupTxt, mapMail), "", mailDatas); + // on enregistre le fait que cet élément a déjà été envoyé : + accessSave = await db["QuestionnaireAccess"].create({ QuestionnaireId: elementToSend.Questionnaire.id, UserId: subscriptionsOk[i].UserId, selfCreatedOk: false }); + /// à remplacer pour fonction listant les derniers éléments "lus" par l'utilisateur : + //answerCtrl.creaUserQuestionnairesWithoutAnswerJson(subscriptionsOk[i].UserId); + } + else // envoi du quiz du groupe + { + const mapMail = + { + USER_NAME: subscriptionsOk[i].name, + QUESTIONNAIRE_URL: config.siteUrl+"/"+configQuestionnaires.dirWebGroups+"/"+elementToSend.slug+".html", + UNSUBSCRIBE_URL: config.siteUrl+"/"+configTpl.stopMailPage+token + }; + const mailDatas = + { + mailSubject: elementToSend.title+" ("+txtQuestionnaire.questionnairesName+")", + mailPreheader: elementToSend.title, + mailTitle: elementToSend.title, + mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.stopMailPage+token, + mailHeaderLinkTxt: txt.mailStopMailLinkTxt, + mailMainContent: elementToSend.introduction, + linksCTA: [{ url:config.siteUrl+"/"+configQuestionnaires.dirWebGroups+"/"+elementToSend.slug+".html", txt:txtQuestionnaire.btnShowQuestionnaire }], + mailRecipientAddress: subscriptionsOk[i].email + } + toolMail.sendMail(subscriptionsOk[i].smtp, subscriptionsOk[i].email, mailDatas.mailSubject, tool.replaceAll(txt.mailNewQuestionnaireBodyTxt, mapMail), "", mailDatas); + } + } + else + { + // L'utilisateur a déjà reçu tous les élements et quizs des groupes publiés. Dans ces cas, on tire un au hasard un quiz groupé pour le lui renvoyer + getElementToSend = await db.sequelize.query("SELECT `id`, `title`, `slug`, `introduction` FROM `Groups` WHERE `publishingAt` < NOW() AND `language`='"+subscriptionsOk[i].language+"' ORDER BY RAND() LIMIT 1", { type: QueryTypes.SELECT }); token=jwt.sign({ userId: subscriptionsOk[i].UserId }, config.tokenPrivateKey, { expiresIn: config.tokenUnsubscribeLinkTimeInDays }); const mapMail = { USER_NAME: subscriptionsOk[i].name, - QUESTIONNAIRE_URL: config.siteUrl+"/"+configQuestionnaires.dirWebQuestionnaire+"/"+getRandomQuestionnaire[0].slug+".html", + QUESTIONNAIRE_URL: config.siteUrl+"/"+configQuestionnaires.dirWebQuestionnaires+"/"+getElementToSend[0].slug+".html", UNSUBSCRIBE_URL: config.siteUrl+"/"+configTpl.stopMailPage+token }; const mailDatas= { - mailSubject: getRandomQuestionnaire[0].title, - mailPreheader: getRandomQuestionnaire[0].title, - mailTitle: getRandomQuestionnaire[0].title, + mailSubject: getElementToSend[0].title+" ("+txtQuestionnaire.questionnairesName+")", + mailPreheader: getElementToSend[0].title, + mailTitle: getElementToSend[0].title, mailHeaderLinkUrl: config.siteUrl+"/"+configTpl.stopMailPage+token, mailHeaderLinkTxt: txt.mailStopMailLinkTxt, - mailMainContent: "

"+txtQuestionnaireAccess.questionnaireRetryInfo+"

"+getRandomQuestionnaire[0].introduction+"

"+txtQuestionnaire.estimatedTime+" "+txtQuestionnaire.estimatedTimeOption[getRandomQuestionnaire[0].estimatedTime]+".

", - linksCTA: [{ url:getRandomQuestionnaire[0].url, txt:getRandomQuestionnaire[0].anchor }, { url:config.siteUrl+"/"+configQuestionnaires.dirWebQuestionnaire+"/"+getRandomQuestionnaire[0].slug+".html#questionnaire", txt:txtQuestionnaire.btnShowQuestionnaire }], + mailMainContent: "

"+txtQuestionnaireAccess.questionnaireRetryInfo+"

"+getElementToSend[0].introduction, + linksCTA: [{ url:config.siteUrl+"/"+configQuestionnaires.dirWebGroups+"/"+getElementToSend[0].slug+".html", txt:txtQuestionnaire.btnShowQuestionnaire }], mailRecipientAddress: subscriptionsOk[i].email } - toolMail.sendMail(subscriptionsOk[i].smtp, subscriptionsOk[i].email, getRandomQuestionnaire[0].title, tool.replaceAll(txtQuestionnaireAccess.questionnaireRetryInfoTxt, mapMail), "", mailDatas); - db["Subscription"].update({ lastProcessingAt: now }, { where: { id : subscriptionsOk[i].SubscriptionId }, limit:1 }); - + toolMail.sendMail(subscriptionsOk[i].smtp, subscriptionsOk[i].email, mailDatas.mailSubject, tool.replaceAll(txtQuestionnaireAccess.questionnaireRetryInfoTxt, mapMail), "", mailDatas); } + // dans tout les cas, on enregistre l'envoi : + //db["Subscription"].update({ lastProcessingAt: now }, { where: { id : subscriptionsOk[i].SubscriptionId }, limit:1 }); } res.status(200).json(subscriptionsOk); } @@ -262,7 +313,6 @@ exports.addNewQuestionnaireUsers = async(req, res, next) => } } - // FONCTIONS UTILITAIRES // Retourne un booléen suivant si l'utilisateur a accès ou non à un questionnaire diff --git a/front/src/tools/answers.js b/front/src/tools/answers.js index c8defd5..2ad8466 100644 --- a/front/src/tools/answers.js +++ b/front/src/tools/answers.js @@ -17,7 +17,7 @@ export const saveAnswer = (answer) => return false; } -// Retourne le texte suivant le nombre de bonnes réponses +// Retourne un texte suivant le nombre de bonnes réponses export const checkAnswerOuput = (answer) => { if(!isEmpty(answer.duration) && !isEmpty(answer.nbCorrectAnswers) && !isEmpty(answer.nbQuestions)) diff --git a/lang/fr/general.js b/lang/fr/general.js index 799e55e..d9fb117 100644 --- a/lang/fr/general.js +++ b/lang/fr/general.js @@ -7,6 +7,7 @@ module.exports = btnLinkToQuestionnaire : "Aller au quiz !", btnProposeConnection: "Je me connecte.", btnProposeSubscribe: "Je crée mon compte.", + btnShowOnWebSite: "Voir sur #SITE_NAME.", deleteBtnTxt: "Supprimer", deleteFailMessage : "La suppression de l'enregistrement #ID a échoué.", deleteOkMessage : "La suppression a bien été enregistrée.", diff --git a/lang/fr/questionnaire.js b/lang/fr/questionnaire.js index 25a7540..5076bf0 100644 --- a/lang/fr/questionnaire.js +++ b/lang/fr/questionnaire.js @@ -4,7 +4,7 @@ module.exports = btnProposeSubscribe: "Je crée mon compte.",//idem btnSendResponse: "Testez vos réponses.", btnShareQuizTxt: "Partager ce quiz sur ", - btnShowQuestionnaire: "Afficher le quiz !", + btnShowQuestionnaire: "Répondre au quiz !", correctAnswerTxt: "Bonne réponse", estimatedTime: "Durée de lecture estimée : ", estimatedTimeOption : diff --git a/lang/fr/subscription.js b/lang/fr/subscription.js index e7330c8..5247b72 100644 --- a/lang/fr/subscription.js +++ b/lang/fr/subscription.js @@ -18,7 +18,8 @@ module.exports = mailExpirationMessage: "FIRST premières et SECOND deuxièmes relances envoyées pour des abonnements expirant d'ici peu.", mailExpirationRelaunchTxt: "[Rappel] ", mailExpirationSubject: "Votre abonnement va bientôt expirer", - mailNewQuestionnaireBodyTxt : "Bonjour USER_NAME,\n\nUn nouveau quiz vient de vous être proposé :\n\nQUESTIONNAIRE_URL\n\nBonne lecture !\n\nStopper les envois ?\nUNSUBSCRIBE_URL", + mailNewElementForGroupTxt : "Bonjour USER_NAME,\n\nVoici le lien vers le nouvel article à lire :\n\nQUESTIONNAIRE_URL\n\nBonne lecture !\n\nStopper les envois ?\nUNSUBSCRIBE_URL", + mailNewQuestionnaireBodyTxt : "Bonjour USER_NAME,\n\nVoici le lien vers le nouveau quiz :\n\nQUESTIONNAIRE_URL\n\nBonne lecture !\n\nStopper les envois ?\nUNSUBSCRIBE_URL", mailStopMailLinkTxt : "Stopper les envois.", needKnowIfNoticeOk : "Il manque l'information sur l'acceptation ou non de recevoir des notifications.", needIntegerNumberOfDays : "Le nombre de jours de l'abonnement doit être un nombre entier.", diff --git a/views/wikilerni/mail.pug b/views/wikilerni/mail.pug index 825f571..110d71a 100644 --- a/views/wikilerni/mail.pug +++ b/views/wikilerni/mail.pug @@ -8,7 +8,6 @@ style(type="text/css"). body{ margin: 0; padding: 0; } img{ border: 0px; display: block; } - .socialLinks{ font-size: 6px; } .socialLinks a{ display: inline-block; @@ -122,6 +121,8 @@ span(style="font-weight: normal;") #{mailTitle} td(class="long-text links-color" style="font-weight: normal; color: #ffffff; font-size: 15px; font-family: Arial, Helvetica, sans-serif; text-align: left; line-height: normal;" width="100%" valign="top" align="left") !{mailMainContent} + +