2020-10-12 17:51:35 +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 tool = require ( "../tools/main" ) ;
const toolError = require ( "../tools/error" ) ;
const toolFile = require ( "../tools/file" ) ;
const questionnaireCtrl = require ( "./questionnaire" ) ;
const userCtrl = require ( "./user" ) ;
const txtGeneral = require ( "../lang/" + config . adminLang + "/general" ) ;
const txtGroups = require ( "../lang/" + config . adminLang + "/group" ) ;
exports . create = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
req . body . CreatorId = req . connectedUser . User . id ;
const group = await db [ "Group" ] . create ( { ... req . body } , { fields : [ "title" , "slug" , "introduction" , "publishingAt" , "language" , "CreatorId" ] } ) ;
creaGroupJson ( group . id ) ;
res . status ( 201 ) . json ( { message : txtGeneral . addedOkMessage , id : group . 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 . modify = async ( req , res , next ) =>
{
try
{
const db = require ( "../models/index" ) ;
const group = await searchGroupById ( req . params . id ) ;
if ( ! group )
{
const Err = new Error ;
error . status = 404 ;
error . message = txtGroups . notFound . replace ( "#SEARCH" , req . params . id ) ;
throw Err ;
}
else if ( req . connectedUser . User . status === "creator" && req . connectedUser . User . id !== group . CreatorId )
res . status ( 401 ) . json ( { errors : txtGeneral . notAllowed } ) ;
else
await db [ "Group" ] . update ( { ... req . body } , { where : { id : req . params . id } , fields : [ "title" , "slug" , "introduction" , "publishingAt" , "language" ] , limit : 1 } ) ;
await creaGroupJson ( req . params . id ) ;
res . status ( 200 ) . json ( { message : txtGeneral . updateOkMessage } ) ;
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
{
const db = require ( "../models/index" ) ;
const group = await searchGroupById ( req . params . id ) ;
if ( ! group )
{
const Err = new Error ;
error . status = 404 ;
error . message = txtGroups . notFound . replace ( "#SEARCH" , req . params . id ) ;
throw Err ;
}
else if ( req . connectedUser . User . status === "creator" && req . connectedUser . User . id !== group . CreatorId )
res . status ( 401 ) . json ( { errors : txtGeneral . notAllowed } ) ;
else
{
// La suppression sera bloquée par SQL si des quizs dépendent de ce groupe
// Donc il faut d'abord supprimer tous les quizs du groupe :
for ( let i in group . Questionnaires )
await questionnaireCtrl . deleteQuestionnaireById ( group . Questionnaires [ i ] . id ) ;
const nb = await db [ "Group" ] . destroy ( { where : { id : req . params . id } , limit : 1 } ) ;
if ( nb === 1 )
{
toolFile . deleteJSON ( configQuestionnaires . dirCacheGroups , req . params . id ) ;
toolFile . deleteFile ( configQuestionnaires . dirHTMLGroups , group . Group . slug + ".html" ) ;
creaStatsGroupsJson ( ) ;
res . status ( 200 ) . json ( { message : txtGeneral . deleteOkMessage } ) ;
}
else
{
const Err = new Error ;
error . status = 404 ;
error . message = txtGroups . deleteFailMessage . replace ( "#ID" , req . params . id ) ;
throw Err ;
}
}
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// Recherche par mots-clés parmis tous les groupes (y compris ceux non publiés).
2020-10-27 11:30:33 +01:00
// Le rank le + élevé des éléments déjà enregistrés dans le groupe permet de placer le nouveau.
2020-10-12 17:51:35 +02:00
exports . searchGroups = async ( req , res , next ) =>
{
try
{
let search = tool . trimIfNotNull ( req . body . searchGroups ) ;
if ( search === null || search === "" || search . length < configQuestionnaires . searchGroups . minlength )
res . status ( 400 ) . json ( txtGroups . searchIsNotLongEnough . replace ( "#MIN" , configQuestionnaires . searchGroups . minlength ) ) ;
else
{
const db = require ( "../models/index" ) ;
2020-10-27 11:30:33 +01:00
const getGroups = await db . sequelize . query ( "SELECT `Groups`.`id`,`Groups`.`title`, MAX(`Questionnaires`.`rankInGroup`) as maxRank FROM `Groups` LEFT JOIN `Questionnaires` ON `Questionnaires`.`GroupId` = `Groups`.`id` WHERE `Groups`.`title` LIKE :search OR `Groups`.`introduction` LIKE :search GROUP BY `Groups`.`id` ORDER BY `Groups`.`title` ASC" , { replacements : { search : "%" + search + "%" } , type : QueryTypes . SELECT } ) ;
2020-10-12 17:51:35 +02:00
res . status ( 200 ) . json ( getGroups ) ;
}
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
exports . getOneById = async ( req , res , next ) =>
{
try
{
2020-10-26 18:31:39 +01:00
const datas = await searchGroupById ( req . params . id , true ) ;
2020-10-12 17:51:35 +02:00
if ( datas )
res . status ( 200 ) . json ( datas ) ;
else
res . status ( 404 ) . json ( { message : txtGroups . notFound . replace ( "#SEARCH" , req . params . id ) } ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-20 11:01:52 +02:00
exports . showOneGroupById = async ( req , res , next ) =>
{
try
{
// Seuls certains utilisateurs peuvent avoir accès à cette page
const connectedUser = await userCtrl . checkTokenUser ( req . params . token ) ;
if ( connectedUser === false )
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 creaGroupHTML ( req . params . id , true ) ;
if ( HTML )
{
res . setHeader ( "Content-Type" , "text/html" ) ;
res . send ( HTML ) ;
}
else
res . status ( 404 ) . json ( { errors : txtGeneral . notFound . replace ( "#SEARCH" , req . params . id ) } ) ;
}
}
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-12 17:51:35 +02:00
// Retourne les statistiques concernant les groupes de questionnaires
exports . getStats = async ( req , res , next ) =>
{
try
{
const stats = await getStatsGroups ( ) ;
res . status ( 200 ) . json ( stats ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-11-02 11:32:17 +01:00
// (Re)génère tous les fichiers HTML des groupes
// La requête est ensuite passé aux tags qui font la même chose
exports . HTMLRegenerate = async ( req , res , next ) =>
{
try
{
const nb = await checkGroupsNeedToBePublished ( true ) ;
res . messageToNext = txtGroups . haveBeenPublished . replace ( "#NB" , nb ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-12 17:51:35 +02:00
// CRONS
// Supprime fichiers json de groupes n'existant plus.
2020-11-02 12:16:31 +01:00
exports . cronDeleteJsonFiles = async ( req , res , next ) =>
2020-10-12 17:51:35 +02:00
{
try
{
const db = require ( "../models/index" ) ;
const groups = await db [ "Group" ] . findAll ( { attributes : [ "id" ] } ) ;
2020-11-02 12:16:31 +01:00
let saveFiles = [ "stats.json" ] ; // dans le même répertoire et à garder.
2020-10-12 17:51:35 +02:00
for ( let i in groups )
saveFiles . push ( groups [ i ] . id + ".json" ) ;
const deleteFiles = await toolFile . deleteFilesInDirectory ( configQuestionnaires . dirCacheGroups , saveFiles ) ;
res . status ( 200 ) . json ( deleteFiles ) ;
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// Teste si des groupes doivent être publiés
2020-11-02 12:16:31 +01:00
exports . cronCheckGroupsNeedToBePublished = async ( req , res , next ) =>
2020-10-12 17:51:35 +02:00
{
try
{
2020-11-16 11:37:04 +01:00
console . log ( "Je teste si des groupes doivent être publiés" ) ;
2020-10-12 17:51:35 +02:00
const nb = await checkGroupsNeedToBePublished ( ) ;
res . status ( 200 ) . json ( txtGroups . haveBeenPublished . replace ( "#NB" , nb ) ) ;
2020-11-16 11:37:04 +01:00
console . log ( txtGroups . haveBeenPublished . replace ( "#NB" , nb ) ) ;
2020-10-12 17:51:35 +02:00
next ( ) ;
}
catch ( e )
{
next ( e ) ;
}
}
// FONCTIONS PARTAGÉES
const creaGroupJson = async ( id ) =>
{
const db = require ( "../models/index" ) ;
const Group = await db [ "Group" ] . findByPk ( id ) ;
if ( Group )
{
let datas = { Group } ;
2020-10-20 11:01:52 +02:00
const Questionnaires = await db [ "Questionnaire" ] . findAll ( { where : { GroupId : Group . id , isPublished : true } , order : [ [ "rankInGroup" , "ASC" ] , [ "createdAt" , "ASC" ] ] , attributes : [ "id" ] } ) ;
2020-10-12 17:51:35 +02:00
if ( Questionnaires )
datas . Questionnaires = Questionnaires ;
await toolFile . createJSON ( configQuestionnaires . dirCacheGroups , id , datas ) ;
2020-10-20 11:01:52 +02:00
// Si le groupe est publiable on génère/actualise la page HTML :
2020-10-12 17:51:35 +02:00
if ( checkGroupIsPublishable ( datas ) )
2020-10-20 11:01:52 +02:00
await creaGroupHTML ( id ) ; // !!! await important car sinon bug, plusieurs fonctions accédant au même fichier (à revoir car pas clair !)
2020-10-12 17:51:35 +02:00
else // dans le cas contraire, on supprime l'éventuel fichier préexistant
2020-11-16 11:37:04 +01:00
toolFile . deleteFile ( configQuestionnaires . dirHTMLGroups , Group . slug + ".html" ) ;
2020-10-20 11:01:52 +02:00
// Dans certains cas les fichiers HTML des quizs du groupe peuvent aussi être impactés (notamment le dernier)
for ( let i in Questionnaires )
await questionnaireCtrl . creaQuestionnaireHTML ( Questionnaires [ i ] . id , false ) ;
2020-10-12 17:51:35 +02:00
// + mise à jour des statistiques :
creaStatsGroupsJson ( ) ;
return datas ;
}
else
return false ;
}
exports . creaGroupJson = creaGroupJson ;
const checkGroupIsPublishable = ( datas , checkDate = true ) =>
{
if ( checkDate )
{
if ( datas . Group . publishingAt === null )
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 . Group . publishingAt ) ;
if ( publishingAt . getTime ( ) > today . getTime ( ) )
return false ;
}
}
if ( datas . Questionnaires === undefined || datas . Questionnaires . length < config . nbQuestionnairesByGroupMin )
return false ;
return true ;
}
const creaGroupHTML = async ( id , preview = false ) =>
{
const group = await searchGroupById ( id , true ) ;
if ( ! group )
return false ;
2020-10-20 11:01:52 +02:00
if ( group . isPublishable === false && preview === false )
2020-10-12 17:51:35 +02:00
return false ;
2020-11-04 10:02:04 +01:00
const txtIllustration = require ( "../lang/" + config . adminLang + "/illustration" ) ;
const txtQuestionnaire = require ( "../lang/" + config . adminLang + "/questionnaire" ) ;
2020-10-12 17:51:35 +02:00
const compiledFunction = pug . compileFile ( "./views/" + config . theme + "/quiz-group.pug" ) ;
const configTpl = require ( "../views/" + config . theme + "/config/" + config . availableLangs [ 0 ] + ".js" ) ;
2020-10-20 11:01:52 +02:00
const pageDatas =
2020-10-12 17:51:35 +02:00
{
config : config ,
configQuestionnaires : configQuestionnaires ,
configTpl : configTpl ,
tool : tool ,
txtGeneral : txtGeneral ,
txtGroups : txtGroups ,
2020-10-20 11:01:52 +02:00
txtIllustration : txtIllustration ,
2020-11-04 10:02:04 +01:00
txtQuestionnaire : txtQuestionnaire ,
2020-10-20 11:01:52 +02:00
pageLang : group . Group . language ,
metaDescription : tool . shortenIfLongerThan ( config . siteName + " : " + striptags ( group . Group . introduction . replace ( "<br>" , " " ) . replace ( "</p>" , " " ) ) , 200 ) ,
author : group . Group . CreatorName ,
pageTitle : txtGroups . groupsName + " " + group . Group . title ,
contentTitle : group . Group . title + "(" + txtGroups . groupsName + ")" ,
2020-10-12 17:51:35 +02:00
group : group ,
2022-01-28 17:37:19 +01:00
linkCanonical : config . siteUrlProd + "/" + configQuestionnaires . dirWebGroups + "/" + group . Group . slug + ".html"
2020-10-12 17:51:35 +02:00
}
const html = await compiledFunction ( pageDatas ) ;
if ( preview === false )
{
2020-10-20 11:01:52 +02:00
await toolFile . createHTML ( configQuestionnaires . dirHTMLGroups , group . Group . slug , html ) ;
2020-10-12 17:51:35 +02:00
return true ;
}
else
return html ;
}
// Remonte toutes les données du groupe + les données des questionnaires y étant classés si reassemble=true
const searchGroupById = async ( id , reassemble = false ) =>
{
let group = await toolFile . readJSON ( configQuestionnaires . dirCacheGroups , id ) ;
if ( ! group )
group = await creaGroupJson ( id ) ;
if ( ! group )
return false ;
2020-10-20 11:01:52 +02:00
group . Group . isPublishable = checkGroupIsPublishable ( group ) ;
2020-10-12 17:51:35 +02:00
if ( reassemble )
{
let questionnaire ; Questionnaires = [ ] ;
const author = await userCtrl . searchUserById ( group . Group . CreatorId ) ;
if ( author )
group . Group . CreatorName = author . User . name ;
for ( let i in group . Questionnaires )
{
2020-10-20 11:01:52 +02:00
questionnaire = await questionnaireCtrl . searchQuestionnaireById ( group . Questionnaires [ i ] . id , true ) ;
2020-10-12 17:51:35 +02:00
if ( questionnaire )
Questionnaires . push ( questionnaire ) ;
}
group . Questionnaires = Questionnaires ;
}
return group ;
}
exports . searchGroupById = searchGroupById ;
2020-11-16 11:37:04 +01:00
// Cherche si il y a des groupes de questionnaires dont la date de publication est passée, mais qui ne sont pas publiés
2020-10-12 17:51:35 +02:00
// Vérifie si ils sont publiables et si oui génère le HTML
// Si regenerate=true, tous les fichiers sont (ré)générés, même s'ils existent déjà (évolution template...)
// Retourne le nombre de fichiers ayant été (ré)générés
const checkGroupsNeedToBePublished = async ( regenerate = false ) =>
{
const db = require ( "../models/index" ) ;
2020-11-02 11:32:17 +01:00
const groups = await db . sequelize . query ( "SELECT `id`,`slug` FROM `Groups` WHERE `publishingAt` < NOW()" , { type : QueryTypes . SELECT } ) ;
let nb = 0 ;
2020-10-12 17:51:35 +02:00
for ( let i in groups )
{
2020-11-02 11:32:17 +01:00
if ( regenerate === false )
2020-10-12 17:51:35 +02:00
{
2020-11-16 11:37:04 +01:00
if ( await toolFile . checkIfFileExist ( configQuestionnaires . dirHTMLGroups , groups [ i ] . slug + ".html" ) === false )
2020-10-12 17:51:35 +02:00
{
2020-11-16 11:37:04 +01:00
if ( await creaGroupJson ( groups [ i ] . id ) ) // permet aussi de mettre à jour les fichiers des quizs du groupe avec lien vers le groupe
2020-10-12 17:51:35 +02:00
nb ++ ;
}
}
else
{
2020-11-16 11:37:04 +01:00
const publishedOk = await creaGroupHTML ( groups [ i ] . id ) ; // creaGroupHTLM contrôle que le groupe est publiable.
2020-10-12 17:51:35 +02:00
if ( publishedOk )
nb ++ ;
}
}
return nb ;
}
2020-11-02 11:32:17 +01:00
exports . checkGroupsNeedToBePublished = checkGroupsNeedToBePublished ;
2020-10-12 17:51:35 +02:00
// Compte le nombre total de groupes et le stocke
const creaStatsGroupsJson = async ( ) =>
{
const db = require ( "../models/index" ) ;
const Groups = await db [ "Group" ] . findAll ( { attributes : [ "id" ] } ) ;
const GroupsPublished = await db . sequelize . query ( "SELECT `id` FROM `Groups` WHERE `publishingAt` < NOW()" , { type : QueryTypes . SELECT } ) ;
const QuestionnairesInGroups = await db . sequelize . query ( "SELECT DISTINCT `id` FROM `Questionnaires` WHERE `GroupId` IS NOT NULL" , { type : QueryTypes . SELECT } ) ;
if ( Groups && GroupsPublished && QuestionnairesInGroups )
{
const stats =
{
nbTot : Groups . length ,
2020-10-28 18:06:20 +01:00
nbPublished : GroupsPublished . length , // ! en fait, peuvent être passé de date et non publié
2020-10-12 17:51:35 +02:00
nbQuestionnaires : QuestionnairesInGroups . length
}
await toolFile . createJSON ( configQuestionnaires . dirCacheGroups , "stats" , stats ) ;
return stats ;
}
else
return false ;
}
exports . creaStatsGroupsJson = creaStatsGroupsJson ;
// Retourne les données créées par la fonction précédente
const getStatsGroups = async ( ) =>
{
let stats = await toolFile . readJSON ( configQuestionnaires . dirCacheGroups , "stats" ) ;
if ( ! stats )
stats = await creaStatsGroupsJson ( ) ;
if ( ! stats )
return false ;
else
return stats ;
}
exports . getStatsGroups = getStatsGroups ;