2020-08-07 12:23:59 +02:00
const { Op , QueryTypes } = require ( "sequelize" ) ;
const pug = require ( "pug" ) ;
const striptags = require ( "striptags" ) ;
const config = require ( "../config/main.js" ) ;
const configQuestionnaires = require ( "../config/questionnaires.js" ) ;
const configTags = require ( "../config/tags.js" ) ;
const configLinks = require ( "../config/links.js" ) ;
const configIllustrations = require ( "../config/illustrations.js" ) ;
const configTpl = require ( "../views/" + config . theme + "/config/" + config . availableLangs [ 0 ] + ".js" ) ;
const tool = require ( "../tools/main" ) ;
const toolError = require ( "../tools/error" ) ;
const toolFile = require ( "../tools/file" ) ;
const toolMail = require ( "../tools/mail" ) ;
2020-10-20 11:01:52 +02:00
const groupCtrl = require ( "./group" ) ;
2020-08-07 12:23:59 +02:00
const questionCtrl = require ( "./question" ) ;
const illustrationCtrl = require ( "./illustration" ) ;
const tagCtrl = require ( "./tag" ) ;
const userCtrl = require ( "./user" ) ;
const txtGeneral = require ( "../lang/" + config . adminLang + "/general" ) ;
const txtQuestionnaire = require ( "../lang/" + config . adminLang + "/questionnaire" ) ;
const txtQuestionnaireAccess = require ( "../lang/" + config . adminLang + "/questionnaireaccess" ) ;
const txtIllustration = require ( "../lang/" + config . adminLang + "/illustration" ) ;
exports . create = async ( req , res , next ) =>
{
try
{
2020-10-20 11:01:52 +02:00
if ( tool . isEmpty ( req . body . GroupId ) && ! tool . isEmpty ( req . body . rankInGroup ) )
res . status ( 400 ) . json ( { errors : [ txtQuestionnaire . needGroupIfRank ] } ) ;
else
{
const db = require ( "../models/index" ) ;
req . body . CreatorId = req . connectedUser . User . id ;
if ( ! tool . isEmpty ( req . body . GroupId ) && tool . isEmpty ( req . body . rankInGroup ) )
req . body . rankInGroup = 1 ;
const questionnaire = await db [ "Questionnaire" ] . create ( { ... req . body } , { fields : [ "title" , "slug" , "introduction" , "keywords" , "publishingAt" , "language" , "estimatedTime" , "CreatorId" , "GroupId" , "rankInGroup" ] } ) ;
creaStatsQuestionnairesJson ( ) ;
// Si un groupe a été sélectionné, il faut mettre à jour les fichiers du groupe :
if ( ! tool . isEmpty ( req . body . GroupId ) )
{
const groupInfos = await groupCtrl . creaGroupJson ( req . body . GroupId ) ;
// Si le nouveau quiz est publiable, le fichier HTML de l'éventuel quiz/élément précédent du groupe doit être actualisé
// Si le nouveau n'est pas publiable, il ne sera pas listé dans groupInfos.Questionnaires donc pas d'incidence
if ( groupInfos !== false )
{
for ( let i in groupInfos . Questionnaires )
{
if ( groupInfos . Questionnaires [ i ] . id === questionnaire . id && i != 0 )
{
let j = parseInt ( i ) - 1 ; // !! "i" n'est pas un nombre !
if ( groupInfos . Questionnaires [ j ] !== undefined )
await creaQuestionnaireHTML ( groupInfos . Questionnaires [ j ] . id ) ;
}
}
}
}
// id utile au middleware suivant (classement tags) qui s'occupe aussi de retourner une réponse si ok :
req . body . QuestionnaireId = questionnaire . id ;
next ( ) ;
}
2020-08-07 12:23:59 +02:00
}
catch ( e )
{
const returnAPI = toolError . returnSequelize ( e ) ;
if ( returnAPI . messages )
{
res . status ( returnAPI . status ) . json ( { errors : returnAPI . messages } ) ;
next ( ) ;
}
else
next ( e ) ;
}
}
exports . modify = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
const questionnaire = await searchQuestionnaireById ( req . params . id ) ;
2022-01-28 17:37:19 +01:00
2020-08-07 12:23:59 +02:00
if ( ! questionnaire )
{
const Err = new Error ;
error . status = 404 ;
error . message = txtQuestionnaire . notFound + " (" + req . params . id + ")" ;
throw Err ;
}
else if ( req . connectedUser . User . status === "creator" && req . connectedUser . User . id !== questionnaire . CreatorId )
res . status ( 401 ) . json ( { errors : txtGeneral . notAllowed } ) ;
2020-10-20 11:01:52 +02:00
else if ( tool . isEmpty ( req . body . GroupId ) && ! tool . isEmpty ( req . body . rankInGroup ) )
res . status ( 400 ) . json ( { errors : [ txtQuestionnaire . needGroupIfRank ] } ) ;
2020-08-07 12:23:59 +02:00
else
{
2020-10-20 11:01:52 +02:00
if ( ! tool . isEmpty ( req . body . GroupId ) && tool . isEmpty ( req . body . rankInGroup ) )
req . body . rankInGroup = 1 ;
await db [ "Questionnaire" ] . update ( { ... req . body } , { where : { id : req . params . id } , fields : [ "title" , "slug" , "introduction" , "keywords" , "publishingAt" , "language" , "estimatedTime" , "GroupId" , "rankInGroup" ] , limit : 1 } ) ;
2020-08-07 12:23:59 +02:00
creaStatsQuestionnairesJson ( ) ; // le nombre de quizs publiés peut avoir changé
2020-10-20 11:01:52 +02:00
// Si le questionnaire a été déplacé d'un groupe, il faut actualiser les fichiers de l'ancien groupe :
if ( req . body . GroupId != questionnaire . Questionnaire . GroupId && ! tool . isEmpty ( questionnaire . Questionnaire . GroupId ) )
{ //(ne pas utiliser "!==" car types différents)
const groupInfos = await groupCtrl . creaGroupJson ( questionnaire . Questionnaire . GroupId ) ;
// Peut aussi avoir un impact sur les autres élement dans l'ancien groupe, notamment celui qui le précédait
if ( groupInfos !== false )
{
for ( let i in groupInfos . Questionnaires )
await creaQuestionnaireHTML ( groupInfos . Questionnaires [ i ] . id ) ;
}
}
2020-08-07 12:23:59 +02:00
}
2020-10-20 11:01:52 +02:00
// id utile au middleware suivant (classement tags) qui s'occupe aussi de retourner une réponse si ok :
2020-08-07 12:23:59 +02:00
req . body . QuestionnaireId = req . params . id ;
next ( ) ;
}
catch ( e )
{
const returnAPI = toolError . returnSequelize ( e ) ;
if ( returnAPI . messages )
{
res . status ( returnAPI . status ) . json ( { errors : returnAPI . messages } ) ;
next ( ) ;
}
else
next ( e ) ;
}
}
exports . delete = async ( req , res , next ) =>
{
try
{
2020-10-12 17:51:35 +02:00
const del = deleteQuestionnaire ( req . params . id , req ) ;
if ( typeof del === Error )
2020-08-07 12:23:59 +02:00
throw Err ;
else
2020-10-12 17:51:35 +02:00
res . status ( 200 ) . json ( { message : txtGeneral . deleteOkMessage } ) ;
2020-08-07 12:23:59 +02:00
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
exports . getOneQuestionnaireById = async ( req , res , next ) =>
{
try
2020-10-20 11:01:52 +02:00
{
2020-08-07 12:23:59 +02:00
const datas = await searchQuestionnaireById ( req . params . id , true ) ;
if ( datas )
res . status ( 200 ) . json ( datas ) ;
else
res . status ( 404 ) . json ( { errors : txtQuestionnaire . notFound } ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
exports . showOneQuestionnaireById = async ( req , res , next ) =>
{
try
{
2020-10-20 11:01:52 +02:00
// Seuls certains utilisateurs peuvent avoir accès à cette route :
2020-08-07 12:23:59 +02:00
const connectedUser = await userCtrl . checkTokenUser ( req . params . token ) ;
2020-10-20 11:01:52 +02:00
if ( connectedUser === false )
2020-08-07 12:23:59 +02:00
res . status ( 403 ) . json ( { errors : txtGeneral . failAuthToken } ) ;
else
{
if ( [ "admin" , "manager" , "creator" ] . indexOf ( connectedUser . User . status ) === - 1 )
res . status ( 403 ) . json ( { errors : txtGeneral . notAllowed } ) ;
else
{
const HTML = await creaQuestionnaireHTML ( req . params . id , true ) ;
if ( HTML )
{
res . setHeader ( "Content-Type" , "text/html" ) ;
res . send ( HTML ) ;
}
else
res . status ( 404 ) . json ( { errors : txtQuestionnaire . notFound } ) ;
}
}
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-28 18:06:20 +01:00
// Recherche par mots-clés parmis tous les questionnaires publiés associés à un groupe
2020-08-07 12:23:59 +02:00
// Seul un certain nombre de résultats est renvoyé (pagination)
exports . searchQuestionnaires = async ( req , res , next ) =>
{
try
{
let search = tool . trimIfNotNull ( req . body . searchQuestionnaires ) ;
if ( search === null || search === "" || search . length < config . minSearchQuestionnaires )
res . status ( 400 ) . json ( txtQuestionnaireAccess . searchIsNotLongEnough ) ;
else
{
const db = require ( "../models/index" ) ;
2020-10-28 18:06:20 +01:00
const getQuestionnaires = await db . sequelize . query ( "SELECT `id` FROM `Questionnaires` WHERE (`title` LIKE :search OR `keywords` LIKE :search) AND `isPublished` = 1 AND GroupId IS NOT NULL" , { replacements : { search : "%" + search + "%" } , type : QueryTypes . SELECT } ) ;
2020-08-07 12:23:59 +02:00
let begin = 0 , end , output = "" ;
if ( ! tool . isEmpty ( req . body . begin ) )
begin = parseInt ( req . body . begin , 10 ) ;
end = begin + configTpl . nbQuestionnairesUserHomePage - 1 ; // tableau commence à 0 !
if ( req . body . output !== undefined )
output = req . body . output ;
datas = await getListingsQuestionnairesOuput ( getQuestionnaires , begin , end , output ) ;
res . status ( 200 ) . json ( datas ) ;
}
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-28 18:06:20 +01:00
// Recherche aléatoire parmi tous les questionnaires publiés associés à un groupe
// Ici pas de pagination car on maîtrise le nombre de résultats envoyés
2020-08-07 12:23:59 +02:00
exports . getRandomQuestionnaires = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
2020-10-28 18:06:20 +01:00
const getQuestionnaires = await db . sequelize . query ( "SELECT `id` FROM `Questionnaires` WHERE `isPublished` = 1 AND GroupId IS NOT NULL ORDER BY RAND() LIMIT " + configQuestionnaires . nbRandomResults , { type : QueryTypes . SELECT } ) ;
2020-08-07 12:23:59 +02:00
let begin = 0 , end ;
end = begin + configTpl . nbQuestionnairesUserHomePage - 1 ;
datas = await getListingsQuestionnairesOuput ( getQuestionnaires , begin , end , "html" ) ;
res . status ( 200 ) . json ( datas ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// Recherche par mots-clés parmis tous les questionnaires, y compris ceux non publiés
exports . searchAdminQuestionnaires = async ( req , res , next ) =>
{
try
{
let search = tool . trimIfNotNull ( req . body . searchQuestionnaires ) ;
if ( search === null || search === "" || search . length < config . minSearchQuestionnaires )
res . status ( 400 ) . json ( txtQuestionnaireAccess . searchIsNotLongEnough ) ;
else
{
const db = require ( "../models/index" ) ;
const getQuestionnaires = await db . sequelize . query ( "SELECT `id`,`title` FROM `Questionnaires` WHERE (`title` LIKE :search OR `introduction` LIKE :search OR `keywords` LIKE :search) ORDER BY `title` ASC" , { replacements : { search : "%" + search + "%" } , type : QueryTypes . SELECT } ) ;
res . status ( 200 ) . json ( getQuestionnaires ) ;
}
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// Retourne les statistiques concernant les questionnaires
exports . getStats = async ( req , res , next ) =>
{
try
{
const stats = await getStatsQuestionnaires ( ) ;
res . status ( 200 ) . json ( stats ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// Liste des prochains questionnaires devant être publiés.
// On vérifie à chaque fois si ils ont ce qu'il faut pour être publiables
// On en profite pour chercher la prochaine date sans questionnaire programmé
exports . getListNextQuestionnaires = async ( req , res , next ) =>
{
try
{
let questionnaires = await getNextQuestionnaires ( ) ;
2020-08-30 18:23:21 +02:00
const dayWithoutPublication = [ 0 , 6 ] ; // à déclarer dans config + gérer cas où aucun jour n'est obligatoire
let dateNeeded = "" , questionnairesDatas , dateQuestionnaireTS , previousDayNeededTS , previousDayTS ;
2020-08-07 12:23:59 +02:00
for ( let i in questionnaires )
{
questionnairesDatas = await searchQuestionnaireById ( questionnaires [ i ] . id ) ;
questionnaires [ i ] . isPublishable = checkQuestionnaireIsPublishable ( questionnairesDatas , false ) ; // le questionnaire est-il complet ?
2020-08-30 18:23:21 +02:00
dateQuestionnaireTS = new Date ( questionnaires [ i ] . datePublishing ) . getTime ( ) ;
if ( dateNeeded === "" )
2020-08-07 12:23:59 +02:00
{
2020-08-30 18:23:21 +02:00
// je commence par chercher le jour précédent pour lequel je dois avoir publié quelque chose :
previousDayTS = new Date ( new Date ( dateQuestionnaireTS - 3600 * 1000 * 24 ) ) ;
previousDayNeededTS = 0 ;
while ( previousDayNeededTS === 0 )
{
if ( dayWithoutPublication . indexOf ( previousDayTS . getDay ( ) ) === - 1 )
previousDayNeededTS = previousDayTS ;
else
previousDayTS = new Date ( previousDayTS . getTime ( ) - 3600 * 1000 * 24 ) ;
}
// si il n'y a pas de quiz précédent, cette date est celle que je cherche
if ( ! questionnaires [ i - 1 ] )
{
if ( previousDayNeededTS >= Date . now ( ) )
dateNeeded = previousDayNeededTS ;
}
2020-08-07 12:23:59 +02:00
else
2020-08-30 18:23:21 +02:00
{ // sinon je compare la date du précédent quiz à celle pour laquelle j'ai besoin d'un quiz
if ( new Date ( questionnaires [ i - 1 ] . datePublishing ) . getTime ( ) < previousDayNeededTS )
dateNeeded = previousDayNeededTS ;
}
2020-08-07 12:23:59 +02:00
}
}
if ( questionnaires . length > 0 && dateNeeded === "" )
2020-08-30 18:23:21 +02:00
dateNeeded = new Date ( dateQuestionnaireTS + 3600 * 1000 * 24 ) ; // le jour suivant celui du dernier questionnaire
2020-08-07 12:23:59 +02:00
else
2020-08-30 18:23:21 +02:00
dateNeeded = new Date ( Date . now ( ) + 3600 * 1000 * 24 ) ; // mais il est possible que rien n'ai été publié ce jour, le quiz du jour étant absent de la liste traitée
2020-08-07 12:23:59 +02:00
res . status ( 200 ) . json ( { questionnaires : questionnaires , dateNeeded : dateNeeded } ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-20 11:01:52 +02:00
// Test si des questionnaires doivent être publiés puis (re)génère tous les fichiers HTML des questionnaires + les pages accueil + news
2020-11-02 11:32:17 +01:00
// La requête est ensuite passé aux groupes, puis aux tags qui font la même chose
2020-08-07 12:23:59 +02:00
exports . HTMLRegenerate = async ( req , res , next ) =>
{
try
{
await checkQuestionnairesNeedToBePublished ( ) ;
const nb = await checkQuestionnairesPublishedHaveHTML ( true ) ;
2020-11-02 11:32:17 +01:00
let messageToNext = txtQuestionnaire . haveBeenRegenerated . replace ( "#NB1" , nb ) ;
creaNewQuestionnairesJson ( ) ; // Provoque mise à jour du HTLM, etc.
// Les groupes sont aussi à regénérés :
const nbGroups = await groupCtrl . checkGroupsNeedToBePublished ( true ) ;
messageToNext = messageToNext . replace ( "#NB2" , nbGroups ) ;
res . messageToNext = messageToNext ; // Adapter par le middleware suivant.
2020-08-07 12:23:59 +02:00
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-09-22 18:18:12 +02:00
2020-08-07 12:23:59 +02:00
// CRONS
2020-09-22 18:18:12 +02:00
// Supprime fichiers json de questionnaires n'existant plus.
2020-08-07 12:23:59 +02:00
exports . deleteJsonFiles = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
const questionnaires = await db [ "Questionnaire" ] . findAll ( { attributes : [ "id" ] } ) ;
2020-11-02 12:16:31 +01:00
let saveFiles = [ "last.json" , "stats.json" ] ; // dans le même répertoire et à garder.
2020-08-07 12:23:59 +02:00
for ( let i in questionnaires )
saveFiles . push ( questionnaires [ i ] . id + ".json" ) ;
2020-09-22 18:18:12 +02:00
const deleteFiles = await toolFile . deleteFilesInDirectory ( configQuestionnaires . dirCacheQuestionnaires , saveFiles ) ;
2020-08-07 12:23:59 +02:00
res . status ( 200 ) . json ( deleteFiles ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-11-02 11:32:17 +01:00
// Teste si des questionnaires doivent être publiés + si des questionnaires publiés n'ont pas de fichier html
// Si au moins un fichier html est créé, il faut aussi actualiser la page d'accueil & co
2020-11-16 11:37:04 +01:00
exports . checkQuestionnairesNeedToBePublished = async ( req , res , next ) =>
2020-08-07 12:23:59 +02:00
{
try
{
2020-11-16 11:37:04 +01:00
console . log ( "Je teste si des questionnaires doivent être publiés." ) ;
const nb = await checkQuestionnairesNeedToBePublished ( ) ; // actualise bd, json, html, listes d'articles... si l'élement devient publiable.
//const nb=await checkQuestionnairesPublishedHaveHTML();
//if(nb > 0)
//creaNewQuestionnairesJson();// Provoque mise à jour du HTLM, etc.
2020-08-07 12:23:59 +02:00
res . status ( 200 ) . json ( txtQuestionnaire . haveBeenPublished . replace ( ":NB" , nb ) ) ;
2020-11-16 11:37:04 +01:00
console . log ( txtQuestionnaire . haveBeenPublished . replace ( ":NB" , nb ) ) ;
2020-08-07 12:23:59 +02:00
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-12 17:51:35 +02:00
// FONCTIONS PARTAGÉES
2020-08-07 12:23:59 +02:00
const creaQuestionnaireJson = async ( id ) =>
{
2020-10-20 11:01:52 +02:00
const db = require ( "../models/index" ) ;
2022-01-28 17:37:19 +01:00
console . log ( "j'arrive ici avec " + id ) ;
2020-08-07 12:23:59 +02:00
const Questionnaire = await db [ "Questionnaire" ] . findByPk ( id ) ;
if ( Questionnaire )
2022-01-28 17:37:19 +01:00
{
2020-08-07 12:23:59 +02:00
let datas = { Questionnaire } ;
const Tags = await db [ "QuestionnaireClassification" ] . findAll ( { where : { QuestionnaireId : Questionnaire . id } , attributes : [ "TagId" ] } ) ;
if ( Tags )
datas . Tags = Tags ;
const Illustrations = await db [ "Illustration" ] . findAll ( { where : { QuestionnaireId : Questionnaire . id } } ) ;
if ( Illustrations )
datas . Illustrations = Illustrations ;
const Links = await db [ "Link" ] . findAll ( { where : { QuestionnaireId : Questionnaire . id } } ) ;
if ( Links )
datas . Links = Links ;
const Questions = await db [ "Question" ] . findAll ( { where : { QuestionnaireId : Questionnaire . id } , order : [ [ "rank" , "ASC" ] , [ "createdAt" , "ASC" ] ] , attributes : [ "id" ] } ) ;
if ( Questions )
datas . Questions = Questions ;
const wasPublished = datas . Questionnaire . isPublished ;
datas . Questionnaire . isPublished = checkQuestionnaireIsPublishable ( datas ) ;
2020-10-20 11:01:52 +02:00
// Important d'écrire le fichier ici, car il est nécessaire aux fonctions appelées par la suite
2020-08-07 12:23:59 +02:00
await toolFile . createJSON ( config . dirCacheQuestionnaires , id , datas ) ;
2020-10-20 11:01:52 +02:00
if ( datas . Questionnaire . isPublished !== wasPublished )
2020-08-07 12:23:59 +02:00
{
await db [ "Questionnaire" ] . update ( { isPublished : datas . Questionnaire . isPublished } , { where : { id : id } , fields : [ "isPublished" ] , limit : 1 } ) ;
2020-10-20 11:01:52 +02:00
// + Peut impacter la liste des tags utilisés :
2020-08-07 12:23:59 +02:00
tagCtrl . creaUsedTagsJson ( ) ;
2020-10-20 11:01:52 +02:00
// Et si le quiz était publié jusqu'ici, il me faut supprimer son fichier HTML.
2020-08-08 17:17:02 +02:00
if ( wasPublished )
2020-10-12 17:51:35 +02:00
toolFile . deleteFile ( config . dirHTMLQuestionnaires , Questionnaire . slug + ".html" ) ;
2020-08-07 12:23:59 +02:00
}
2020-10-20 11:01:52 +02:00
// Peut impacter la liste des derniers quizs si des informations affichées ont changé
creaNewQuestionnairesJson ( ) ;
2020-08-07 12:23:59 +02:00
// + les listes de quizs / tags :
for ( let i in Tags )
tagCtrl . creaQuestionnairesTagJson ( Tags [ i ] . TagId ) // ! Json + HTML, donc potentiellement long.
if ( datas . Questionnaire . isPublished )
await creaQuestionnaireHTML ( id ) ;
2020-10-20 11:01:52 +02:00
// Si le questionnaire est l'élément d'un groupe, il faut actualiser les fichiers du groupe
// Il faut le faire ici et non dans le contrôleur de mise à jour, car des chgts des éléments annexes (liens, questions...) peuvent aussi impacter les fichiers du groupe ou avoir rendu le questionnaire publiable, etc.
if ( ! tool . isEmpty ( Questionnaire . GroupId ) )
2020-11-16 11:37:04 +01:00
await groupCtrl . creaGroupJson ( Questionnaire . GroupId ) ; // actualise aussi le HTML du quiz + les fichiers html des autres éléments du groupe
2020-08-07 12:23:59 +02:00
return datas ;
}
else
return false ;
}
exports . creaQuestionnaireJson = creaQuestionnaireJson ;
2020-10-12 17:51:35 +02:00
// Supprime un questionnaire et toutes ses dépendances
const deleteQuestionnaire = async ( id , req ) =>
{
const db = require ( "../models/index" ) ;
const questionnaire = await searchQuestionnaireById ( id ) ;
if ( ! questionnaire )
{
const Err = new Error ;
error . status = 404 ;
error . message = txtQuestionnaire . notFound + " (" + req . params . id + ")" ;
return Err ;
}
else if ( req . connectedUser . User . status === "creator" && req . connectedUser . User . id !== questionnaire . CreatorId )
{
const Err = new Error ;
error . status = 401 ;
error . message = txtGeneral . notAllowed ;
return Err ;
}
else
{
// Suppression des fichiers associés en plus du sql. Inutile pour link qui n'a pas de fichier.
// À faire avant la suppression SQL du questionnaire qui entraîne des suppressions en cascade dans la base de données.
for ( let i in questionnaire . Questions )
await questionCtrl . deleteQuestionById ( questionnaire . Questions [ i ] . id ) ;
for ( let i in questionnaire . Illustrations )
await illustrationCtrl . deleteIllustrationById ( questionnaire . Illustrations [ i ] . id ) ;
2020-10-13 17:28:48 +02:00
const nb = await db [ "Questionnaire" ] . destroy ( { where : { id : id } , limit : 1 } ) ;
2020-10-20 11:01:52 +02:00
if ( nb === 1 ) // = json existant, bien que sql déjà supprimé ?
2020-10-12 17:51:35 +02:00
{
2020-10-13 17:28:48 +02:00
toolFile . deleteJSON ( configQuestionnaires . dirCacheQuestionnaires , id ) ;
2020-10-12 17:51:35 +02:00
// + HTML :
toolFile . deleteFile ( configQuestionnaires . dirHTMLQuestionnaires , questionnaire . Questionnaire . slug + ".html" ) ;
2020-10-20 11:01:52 +02:00
// Si ce questionnaire était l'élément d'un groupe, il faut actualiser ses fichiers :
if ( ! tool . isEmpty ( questionnaire . Questionnaire . GroupId ) )
{
const groupInfos = await groupCtrl . creaGroupJson ( questionnaire . Questionnaire . GroupId ) ;
// Idem pour le HTML des éventuels autres quizs du groupe, notamment si un d'entre eux le précédait
if ( groupInfos !== false )
{
for ( let i in groupInfos . Questionnaires )
await creaQuestionnaireHTML ( groupInfos . Questionnaires [ i ] . id ) ;
}
}
2020-10-12 17:51:35 +02:00
// Actualisation de liste des questionnaires pour les tags concernés.
// Ici au contraire, les enregistrements doivent être supprimés avant.
for ( let i in questionnaire . Tags )
tagCtrl . creaQuestionnairesTagJson ( questionnaire . Tags [ i ] . TagId ) ;
// La suppression peut éventuellement concerner un des derniers questionnaires, donc :
2020-10-20 11:01:52 +02:00
await creaNewQuestionnairesJson ( ) ;
await creaStatsQuestionnairesJson ( ) ;
2020-10-12 17:51:35 +02:00
return true ;
}
}
}
exports . deleteQuestionnaire = deleteQuestionnaire ;
2020-08-07 12:23:59 +02:00
const checkQuestionnaireIsPublishable = ( datas , checkDate = true ) =>
{
if ( checkDate )
{
2020-11-02 11:32:17 +01:00
if ( datas . Questionnaire . publishingAt === null )
2020-08-07 12:23:59 +02:00
return false ;
else
{
const today = new Date ( ) ;
today . setHours ( 0 , 0 , 0 , 0 ) ; // !! attention au décalage horaire du fait de l'enregistrement en UTC dans mysql
const publishingAt = new Date ( datas . Questionnaire . publishingAt ) ;
if ( publishingAt . getTime ( ) > today . getTime ( ) )
return false ;
}
}
2020-10-20 11:01:52 +02:00
if ( datas . Questions === undefined || datas . Questions . length < config . nbQuestionsMin )
2020-10-06 11:38:07 +02:00
return false ; // le nombre de réponses mini étant contrôlé au moment de l'enregistrement de la question
2020-10-20 11:01:52 +02:00
if ( datas . Tags === undefined || datas . Tags . length < config . nbTagsMin )
2020-08-07 12:23:59 +02:00
return false ;
2020-10-20 11:01:52 +02:00
if ( datas . Links === undefined || datas . Links . length < config . nbLinksMin )
2020-08-07 12:23:59 +02:00
return false ;
2020-10-20 11:01:52 +02:00
if ( datas . Illustrations === undefined || datas . Illustrations . length < config . nbIllustrationsMin )
2020-08-07 12:23:59 +02:00
return false ;
return true ;
}
const creaQuestionnaireHTML = async ( id , preview = false ) =>
{
2020-10-20 11:01:52 +02:00
// deux possibilités :
// -- si élément d'un groupe de quiz : juste le texte sans les questions
// -- si quiz automone : toutes les infos
2020-08-07 12:23:59 +02:00
const questionnaire = await searchQuestionnaireById ( id , true ) ;
if ( ! questionnaire )
return false ;
if ( questionnaire . Questionnaire . isPublished === false && preview === false )
return false ;
2020-10-20 11:01:52 +02:00
if ( ! tool . isEmpty ( questionnaire . Questionnaire . GroupId ) )
return creaQuestionnaireInGroupHTML ( questionnaire , preview ) ;
else
{
const compiledFunction = pug . compileFile ( "./views/" + config . theme + "/quiz.pug" ) ;
const configTpl = require ( "../views/" + config . theme + "/config/" + config . availableLangs [ 0 ] + ".js" ) ;
const pageDatas =
{
config : config ,
configTpl : configTpl ,
tool : tool ,
txtGeneral : txtGeneral ,
txtQuestionnaire : txtQuestionnaire ,
txtIllustration : txtIllustration ,
pageLang : questionnaire . Questionnaire . language ,
metaDescription : tool . shortenIfLongerThan ( config . siteName + " : " + striptags ( questionnaire . Questionnaire . introduction . replace ( "<br>" , " " ) . replace ( "</p>" , " " ) ) , 200 ) ,
author : questionnaire . Questionnaire . CreatorName ,
pageTitle : questionnaire . Questionnaire . title + " (" + txtQuestionnaire . questionnairesName + ")" ,
contentTitle : questionnaire . Questionnaire . title ,
questionnaire : questionnaire ,
2022-01-28 17:37:19 +01:00
linkCanonical : config . siteUrlProd + "/" + config . dirWebQuestionnaires + "/" + questionnaire . Questionnaire . slug + ".html"
2020-10-20 11:01:52 +02:00
}
const html = await compiledFunction ( pageDatas ) ;
if ( preview === false )
{
await toolFile . createHTML ( config . dirHTMLQuestionnaires , questionnaire . Questionnaire . slug , html ) ;
return true ;
}
else
return html ;
}
}
exports . creaQuestionnaireHTML = creaQuestionnaireHTML ;
const creaQuestionnaireInGroupHTML = async ( questionnaire , preview = false ) =>
{
if ( questionnaire === undefined )
return false ;
if ( questionnaire . Questionnaire . isPublished === false && preview === false )
return false ;
const compiledFunction = pug . compileFile ( "./views/" + config . theme + "/quiz-element.pug" ) ;
2020-08-07 12:23:59 +02:00
const configTpl = require ( "../views/" + config . theme + "/config/" + config . availableLangs [ 0 ] + ".js" ) ;
2020-10-20 11:01:52 +02:00
const txtUser = require ( "../lang/" + config . adminLang + "/user" ) ;
// J'ai aussi besoin de certaines informations du groupe :
const groupInfos = await groupCtrl . searchGroupById ( questionnaire . Questionnaire . GroupId , false ) ;
if ( ! groupInfos )
return false ;
// + Certaines infos de l'élément suivant du groupe, s'il existe :
let nextQuestionnaire = null ;
for ( let i in groupInfos . Questionnaires )
if ( groupInfos . Questionnaires [ i ] . id === questionnaire . Questionnaire . id )
{
let j = parseInt ( i ) + 1 ; // !! "i" n'est pas un nombre !
if ( groupInfos . Questionnaires [ j ] !== undefined )
nextQuestionnaire = await searchQuestionnaireById ( groupInfos . Questionnaires [ j ] . id , false ) ;
}
2020-08-07 12:23:59 +02:00
const pageDatas =
{
config : config ,
2020-10-20 11:01:52 +02:00
configQuestionnaires : configQuestionnaires ,
2020-08-07 12:23:59 +02:00
configTpl : configTpl ,
tool : tool ,
txtGeneral : txtGeneral ,
txtQuestionnaire : txtQuestionnaire ,
txtIllustration : txtIllustration ,
2020-10-20 11:01:52 +02:00
txtUser : txtUser ,
2020-08-07 12:23:59 +02:00
pageLang : questionnaire . Questionnaire . language ,
metaDescription : tool . shortenIfLongerThan ( config . siteName + " : " + striptags ( questionnaire . Questionnaire . introduction . replace ( "<br>" , " " ) . replace ( "</p>" , " " ) ) , 200 ) ,
author : questionnaire . Questionnaire . CreatorName ,
2020-11-04 10:02:04 +01:00
pageTitle : questionnaire . Questionnaire . title ,
2020-08-07 12:23:59 +02:00
contentTitle : questionnaire . Questionnaire . title ,
questionnaire : questionnaire ,
2020-10-20 11:01:52 +02:00
group : groupInfos ,
nextQuestionnaire : nextQuestionnaire ,
2022-01-28 17:37:19 +01:00
linkCanonical : config . siteUrlProd + "/" + config . dirWebQuestionnaires + "/" + questionnaire . Questionnaire . slug + ".html"
2020-08-18 12:14:20 +02:00
}
2020-08-07 12:23:59 +02:00
const html = await compiledFunction ( pageDatas ) ;
if ( preview === false )
{
2020-10-12 17:51:35 +02:00
await toolFile . createHTML ( config . dirHTMLQuestionnaires , questionnaire . Questionnaire . slug , html ) ;
2020-08-07 12:23:59 +02:00
return true ;
}
else
return html ;
}
const searchQuestionnaireById = async ( id , reassemble = false ) =>
{
let questionnaire = await toolFile . readJSON ( config . dirCacheQuestionnaires , id ) ;
if ( ! questionnaire )
questionnaire = await creaQuestionnaireJson ( id ) ;
if ( ! questionnaire )
return false ;
if ( reassemble )
{
const author = await userCtrl . searchUserById ( questionnaire . Questionnaire . CreatorId ) ;
if ( author )
questionnaire . Questionnaire . CreatorName = author . User . name ;
2020-10-20 11:01:52 +02:00
if ( ! tool . isEmpty ( questionnaire . Questionnaire . GroupId ) )
questionnaire . Group = await groupCtrl . searchGroupById ( questionnaire . Questionnaire . GroupId , false ) ; // !! false, sinon on risque de tourner en rond !
let questionDatas ; questionsDatas = [ ] ;
for ( let i in questionnaire . Questions )
2020-08-07 12:23:59 +02:00
{
2020-10-20 11:01:52 +02:00
questionDatas = await questionCtrl . searchQuestionById ( questionnaire . Questions [ i ] . id ) ;
if ( questionDatas )
questionsDatas . push ( questionDatas ) ;
2020-08-07 12:23:59 +02:00
}
2020-10-20 11:01:52 +02:00
questionnaire . Questions = questionsDatas ;
2020-08-07 12:23:59 +02:00
const tags = await tagCtrl . getTagsQuestionnaire ( id ) ;
if ( tags )
questionnaire . Tags = tags ;
}
2020-10-20 11:01:52 +02:00
return questionnaire ;
2020-08-07 12:23:59 +02:00
}
exports . searchQuestionnaireById = searchQuestionnaireById ;
2020-11-02 11:32:17 +01:00
// Cherche si il y a des questionnaires dont la date de publication est passée mais qui ne sont pas notés comme publiés.
// Vérifie si ils sont publiables et si oui change leur statut et réactualise le cache json.
2020-08-07 12:23:59 +02:00
const checkQuestionnairesNeedToBePublished = async ( ) =>
{
const db = require ( "../models/index" ) ;
2020-11-16 11:37:04 +01:00
let nb = 0 ;
2020-11-02 11:32:17 +01:00
const questionnaires = await db . sequelize . query ( "SELECT `id` FROM `Questionnaires` WHERE `publishingAt` < NOW() AND `isPublished` = false" , { type : QueryTypes . SELECT } ) ;
2020-08-07 12:23:59 +02:00
for ( let i in questionnaires )
{
2020-11-02 11:32:17 +01:00
const questionnaireDatas = await searchQuestionnaireById ( questionnaires [ i ] . id , true ) ;
2020-08-07 12:23:59 +02:00
if ( checkQuestionnaireIsPublishable ( questionnaireDatas ) )
{
await db [ "Questionnaire" ] . update ( { isPublished : true } , { where : { id : questionnaires [ i ] . id } , fields : [ "isPublished" ] , limit : 1 } ) ;
2020-11-16 11:37:04 +01:00
creaQuestionnaireJson ( questionnaires [ i ] . id ) ; // Provoque la création du HTML en impactant celui du groupe, etc.
nb ++ ;
2020-08-07 12:23:59 +02:00
}
}
2020-11-16 11:37:04 +01:00
return nb ;
2020-08-07 12:23:59 +02:00
}
// Contrôle si tous les fichiers devant être publiés ont bien leur fichier HTML, sinon le génère
// Si regenerate true, tous les fichiers sont générés, même si ils existent déjà (évolution template...)
// Retourne le nombre de fichiers ayant été régénérés
const checkQuestionnairesPublishedHaveHTML = async ( regenerate = false ) =>
{
const db = require ( "../models/index" ) ;
const questionnaires = await db . sequelize . query ( "SELECT `id`,`slug` FROM `Questionnaires` WHERE `isPublished` = true" , { type : QueryTypes . SELECT } ) ;
let nb = 0 ;
for ( let i in questionnaires )
{
if ( regenerate )
{
await creaQuestionnaireHTML ( questionnaires [ i ] . id ) ;
nb ++ ;
}
2020-11-02 11:32:17 +01:00
else if ( await toolFile . checkIfFileExist ( config . dirHTMLQuestionnaires , questionnaires [ i ] . slug + ".html" ) === false )
2020-08-07 12:23:59 +02:00
{
await creaQuestionnaireHTML ( questionnaires [ i ] . id ) ;
nb ++ ;
}
}
return nb ;
}
2020-11-02 11:32:17 +01:00
// Liste des derniers questionnaires publiés (utile pour page d'accueil, etc.)
2020-08-07 12:23:59 +02:00
const creaNewQuestionnairesJson = async ( ) =>
{
const db = require ( "../models/index" ) ;
2020-09-07 18:38:13 +02:00
const Questionnaires = await db [ "Questionnaire" ] . findAll ( { where : { isPublished : true } , order : [ [ config . fieldNewQuestionnaires , "DESC" ] , [ "id" , "DESC" ] ] , attributes : [ "id" ] , limit : config . nbNewQuestionnaires } ) ;
2020-08-07 12:23:59 +02:00
if ( Questionnaires )
{
await toolFile . createJSON ( config . dirCacheQuestionnaires , "last" , Questionnaires ) ;
creaNewQuestionnairesHTML ( Questionnaires ) ;
return Questionnaires ;
}
else
return false ;
}
exports . creaNewQuestionnairesJson = creaNewQuestionnairesJson ;
2020-10-28 18:06:20 +01:00
// Se limite à compter le nombre total de questionnaires non groupés et à le stocker pour éviter de lancer une requête sql à chaque fois
2020-08-07 12:23:59 +02:00
const creaStatsQuestionnairesJson = async ( ) =>
{
const db = require ( "../models/index" ) ;
2020-10-28 18:06:20 +01:00
const Questionnaires = await db . sequelize . query ( "SELECT DISTINCT `id` FROM `Questionnaires`" , { type : QueryTypes . SELECT } ) ;
const QuestionnairesPublished = await db . sequelize . query ( "SELECT DISTINCT `id` FROM `Questionnaires` WHERE `isPublished`=1" , { type : QueryTypes . SELECT } ) ;
const QuestionnairesWithoutGroup = await db . sequelize . query ( "SELECT DISTINCT `id` FROM `Questionnaires` WHERE `GroupId` IS NULL" , { type : QueryTypes . SELECT } ) ;
const QuestionnairesWithoutGroupPublished = await db . sequelize . query ( "SELECT DISTINCT `id` FROM `Questionnaires` WHERE `GroupId` IS NULL AND `isPublished`=1" , { type : QueryTypes . SELECT } ) ;
2020-08-07 12:23:59 +02:00
if ( Questionnaires && QuestionnairesPublished )
{
const stats =
{
nbTot : Questionnaires . length ,
2020-10-28 18:06:20 +01:00
nbPublished : QuestionnairesPublished . length ,
nbTotWithoutGroup : QuestionnairesWithoutGroup . length ,
nbWithoutGroupPublished : QuestionnairesWithoutGroupPublished . length
2020-08-07 12:23:59 +02:00
}
await toolFile . createJSON ( config . dirCacheQuestionnaires , "stats" , stats ) ;
return stats ;
}
else
return false ;
}
exports . creaStatsQuestionnairesJson = creaStatsQuestionnairesJson ;
// Retourne les données créées par la fonction précédente
const getStatsQuestionnaires = async ( ) =>
{
let stats = await toolFile . readJSON ( config . dirCacheQuestionnaires , "stats" ) ;
if ( ! stats )
stats = await creaStatsQuestionnairesJson ( ) ;
if ( ! stats )
return false ;
else
return stats ;
}
exports . getStatsQuestionnaires = getStatsQuestionnaires ;
// Quelle est la prochaine date pour laquelle aucun questionnaire n'a été publié ?
const getDateNewQuestionnaireNeeded = async ( maxDays ) =>
{
const db = require ( "../models/index" ) ;
let dateNeed = "" , checkDate , addMin = 0 , addMax = 1 ;
while ( dateNeed === "" )
{
checkDate = await db . sequelize . query ( "SELECT ADDDATE(CURDATE(), :addMin) as `dateNeeded` FROM `Questionnaires` WHERE `isPublished`=1 AND `publishingAt` > ADDDATE(CURDATE(), :addMin) AND `publishingAt`< ADDDATE(CURDATE(), :addMax) ORDER BY `publishingAt` LIMIT 1" , { replacements : { addMin : addMin , addMax : addMax } , type : QueryTypes . SELECT } ) ;
if ( checkDate && getSubscriptions24H . length > 0 )
dateNeed = checkDate . dateNeeded ;
else
{
addMin ++ ;
addMax ++ ;
}
if ( addMin > maxDays )
return false ;
}
return dateNeed ;
}
exports . getDateNewQuestionnaireNeeded = getDateNewQuestionnaireNeeded ;
// Les prochains questionnaires devant être publiés (permet aux gestionnaires de voir les dates vides)
const getNextQuestionnaires = async ( ) =>
{
const db = require ( "../models/index" ) ;
const questionnaires = await db . sequelize . query ( "SELECT `id`,`title`, `publishingAt` as `datePublishing` FROM `Questionnaires` WHERE `publishingAt` > NOW() order by `publishingAt`" , { type : QueryTypes . SELECT } ) ;
return questionnaires ;
}
exports . getNextQuestionnaires = getNextQuestionnaires ;
const creaNewQuestionnairesHTML = async ( Questionnaires ) =>
{
// On regénère la page d'accueil avec le(s) dernier(s) questionnaire(s) mis en avant :
let compiledFunction = pug . compileFile ( "./views/" + config . theme + "/home.pug" ) ;
const configTpl = require ( "../views/" + config . theme + "/config/" + config . availableLangs [ 0 ] + ".js" ) ;
2020-11-30 16:10:53 +01:00
const txtUser = require ( "../lang/" + config . adminLang + "/user" ) ;
2020-08-07 12:23:59 +02:00
let questionnaires = [ ] ;
for ( let i in Questionnaires )
questionnaires . push ( await searchQuestionnaireById ( Questionnaires [ i ] . id ) ) ;
2020-10-29 12:57:00 +01:00
let pageDatas =
2020-08-07 12:23:59 +02:00
{
config : config ,
configTpl : configTpl ,
tool : tool ,
striptags : striptags ,
txtGeneral : txtGeneral ,
txtQuestionnaire : txtQuestionnaire ,
txtIllustration : txtIllustration ,
2020-11-30 16:10:53 +01:00
txtUser : txtUser ,
2020-08-07 12:23:59 +02:00
pageLang : config . adminLang ,
metaDescription : txtGeneral . siteMetaDescription ,
pageTitle : txtGeneral . siteHTMLTitle ,
contentTitle : config . siteName ,
questionnaires : questionnaires ,
2022-01-28 17:37:19 +01:00
linkCanonical : config . siteUrlProd
2020-08-07 12:23:59 +02:00
}
let html = await compiledFunction ( pageDatas ) ;
await toolFile . createHTML ( config . dirHTML , "index" , html ) ;
// + la page listant les X derniers quizs + liste des tags
compiledFunction = pug . compileFile ( "./views/" + config . theme + "/newQuestionnaires.pug" ) ;
pageDatas . metaDescription = configTpl . newQuestionnairesIntro ;
pageDatas . pageTitle = configTpl . newQuestionnairesTitle ;
2022-01-28 17:37:19 +01:00
pageDatas . linkCanonical = config . siteUrlProd + "/" + configQuestionnaires . dirWebNews ;
2020-08-07 12:23:59 +02:00
html = await compiledFunction ( pageDatas ) ;
2020-10-29 12:57:00 +01:00
await toolFile . createHTML ( configQuestionnaires . dirHTMLNews , "index" , html ) ;
2022-01-28 17:37:19 +01:00
// + le flux ATOM
compiledFunction = pug . compileFile ( "./views/" + config . theme + "/atom.pug" ) ;
//console.log(pageDatas.questionnaires);
const xml = await compiledFunction ( pageDatas ) ;
await toolFile . createXML ( config . dirHTML , "atom" , xml ) ;
2020-08-07 12:23:59 +02:00
return true ;
}
// À partir d'une liste d'id questionnaires fournis, retourne la liste complète ou partielle (pagination) avec les infos de chaque questionnaire
// Retour en html ou json suivant les cas
const getListingsQuestionnairesOuput = async ( Questionnaires , begin = 0 , end = null , output = "" ) =>
{
const datas = { nbTot : Questionnaires . length , questionnaires : [ ] , html : "" } ;
if ( datas . nbTot !== 0 )
{
if ( end === null )
end = datas . nbTot ;
for ( let i = begin ; i <= end ; i ++ )
{
if ( Questionnaires [ i ] === undefined )
break ;
else
datas . questionnaires . push ( await searchQuestionnaireById ( Questionnaires [ i ] . id ) ) ;
}
// Utiles pour savoir où j'en suis rendu dans le front :
datas . begin = begin ;
datas . end = end ;
if ( output === "html" )
{
const compiledFunction = pug . compileFile ( "./views/" + config . theme + "/includes/listing-questionnaires.pug" ) ;
const pageDatas =
{
tool : tool ,
striptags : striptags ,
txtGeneral : txtGeneral ,
txtIllustration : txtIllustration ,
questionnaires : datas . questionnaires ,
nbQuestionnairesList : configTpl . nbQuestionnairesUserHomePage
}
datas . html = await compiledFunction ( pageDatas ) ;
datas . questionnaires = null ; // allège un peu le contenu retourné...
}
}
return datas ;
}
exports . getListingsQuestionnairesOuput = getListingsQuestionnairesOuput ;