diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1ed7a2f216a0b33512e459221b3a7dfa8c10eec1..886993664132be60edd7d739379e5f8521e2ac1a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,9 +24,12 @@ metabuild: changes: - "pica-dokuwiki/*" - "pica-etherpad/*" + - "pica-tellform/*" + - "pica-wekan/*" refs: - master - dev-ci + - tx-services-p19-test # build the container that was modified build: @@ -45,9 +48,12 @@ build: changes: - "pica-dokuwiki/*" - "pica-etherpad/*" + - "pica-tellform/*" + - "pica-wekan/*" refs: - master - dev-ci + - tx-services-p19-test # run CoreOS' Clair and make the CI failed if a critical vulnerability isn't in the whitelist clair: @@ -75,9 +81,12 @@ clair: changes: - "pica-dokuwiki/*" - "pica-etherpad/*" + - "pica-tellform/*" + - "pica-wekan/*" refs: - master - dev-ci + - tx-services-p19-test # run docker-bench-security and upload the results docker-bench-security: @@ -110,9 +119,12 @@ docker-bench-security: changes: - "pica-dokuwiki/*" - "pica-etherpad/*" + - "pica-tellform/*" + - "pica-wekan/*" refs: - master - dev-ci + - tx-services-p19-test # automatically deploy the container on pica01-test deployment-test: @@ -147,9 +159,12 @@ deployment-test: changes: - "pica-dokuwiki/*" - "pica-etherpad/*" + - "pica-tellform/*" + - "pica-wekan/*" refs: - master - dev-ci + - tx-services-p19-test # automatically deploy the container on the production host associated with the modified image # this will only happen after manually triggering the deployment @@ -189,7 +204,10 @@ deployment-prod: changes: - "pica-dokuwiki/*" - "pica-etherpad/*" + - "pica-tellform/*" + - "pica-wekan/*" refs: - master - dev-ci + - tx-services-p19-test when: manual diff --git a/pica-tellform/Dockerfile b/pica-tellform/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4e591a5bd15cb206bfa13089916cad3e8e672c1a --- /dev/null +++ b/pica-tellform/Dockerfile @@ -0,0 +1,56 @@ +FROM node:8.12-slim as base +LABEL maintainer="liam.dhenin@etu.utc.fr" +ENV NODE_ENV="development" + +FROM base as downloader +RUN printf "deb http://archive.debian.org/debian/ jessie main\ndeb-src http://archive.debian.org/debian/ jessie main\ndeb http://security.debian.org jessie/updates main\ndeb-src http://security.debian.org jessie/updates main" > /etc/apt/sources.list && \ + apt-get update && \ + apt-get install -y \ + bzip2 \ + git \ + curl \ + apt-utils \ + build-essential \ + libkrb5-dev \ + gzip \ + unzip && \ + mkdir /opt/tellform && \ + chown -R node /opt/tellform && \ + npm install -g bower grunt-cli + +USER node +WORKDIR /opt/tellform + +# Trouver mieux que le git clone... +#COPY entrypoint.sh /opt/tellform/entrypoint.sh +RUN git clone https://github.com/tellform/tellform . && git checkout stable2.1 +COPY tellform-patch.patch /opt +RUN cd /opt; patch -p0 < tellform-patch.patch; exit 0 && \ + npm install && \ + bower install && \ + grunt build + +FROM base + +COPY entrypoint.sh /opt/tellform/entrypoint.sh +RUN chmod +x /opt/tellform/entrypoint.sh && \ + mkdir -p /opt/tellform && \ + chown -R node /opt/tellform && \ + usermod -d /opt/tellform node + +COPY --from=downloader --chown=node /opt/tellform /opt/tellform + +RUN printf "deb http://archive.debian.org/debian/ jessie main\ndeb-src http://archive.debian.org/debian/ jessie main\ndeb http://security.debian.org jessie/updates main\ndeb-src http://security.debian.org jessie/updates main" > /etc/apt/sources.list && \ + apt update && apt install -y git && \ + apt install -y vim && \ + npm install -g bower grunt-cli + +USER node +WORKDIR /opt/tellform + +# RUN node ./scripts/setup.js + ENTRYPOINT ["./entrypoint.sh"] + CMD ["node","server.js"] +# CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait" + +EXPOSE 3000 diff --git a/pica-tellform/docker-compose.yml b/pica-tellform/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..896f30d332ea27e365df74d43e9f01ae01f6a243 --- /dev/null +++ b/pica-tellform/docker-compose.yml @@ -0,0 +1,19 @@ +version : "2.4" +services: + mongo: + image: mongo + container_name: mongo + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=example + tellform: + image: tellform + container_name: tellform + links: + - mongo:mongo-db + env_file: ./env.secrets + environment: + - MONGO_DB_URI=mongodb://mongo-db/tellform + - MAILER_FROM=huetremy@picasoft.net + - MAILER_SMTP_HOST=mail.picasoft.net + - MAILER_SMTP_PORT=465 diff --git a/pica-tellform/entrypoint.sh b/pica-tellform/entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..8b1ee03afdd66dbc5fde15c998101c483f91fca9 --- /dev/null +++ b/pica-tellform/entrypoint.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e + +: ${MONGODB_URI:=localhost} +: ${BASE_URL:=tellform.test.picasoft.net} +: ${MAILER_EMAIL_ID:=form} +: ${MAILER_PASSWORD:=loremipsumdolorsitamet} +: ${MAILER_FROM:=form@picasoft.net} +: ${MAILER_SMTP_HOST:=mail.picasoft.net} +: ${MAILER_SMTP_PORT:=587} +: ${MAILER_SMTP_SECURE:=TRUE} + +#cat << EOF > /opt/tellform/.env +#MONGODB_URI=$MONGO_DB_URI +#BASE_URL=$BASE_URL +#MAILER_EMAIL_ID=$MAILER_EMAIL_ID +#MAILER_PASSWORD=$MAILER_PASSWORD +#MAILER_FROM=$MAILER_FROM +#MAILER_SMTP_HOST=$MAILER_SMTP_HOST +#MAILER_SMTP_PORT=$MAILER_SMTP_PORT +#MAILER_SMTP_SECURE=$MAILER_SMTP_SECURE +#EOF + +cat << EOF > /opt/tellform/.env +NODE_ENV=development +CREATE_ADMIN=TRUE +PORT=3000 +ADMIN_EMAIL=admin@admin.com +ADMIN_USERNAME=root +ADMIN_PASSWORD=password +MONGODB_URI=$MONGODB_URI +BASE_URL=$BASE_URL +MAILER_EMAIL_ID=$MAILER_EMAIL_ID +MAILER_PASSWORD=$MAILER_PASSWORD +MAILER_FROM=$MAILER_FROM +MAILER_SMTP_HOST=$MAILER_SMTP_HOST +MAILER_SMTP_PORT=$MAILER_SMTP_PORT +SIGNUP_DISABLED=FALSE +SUBDOMAINS_DISABLED=TRUE +ENABLE_CLUSTER_MODE=FALSE +APP_NAME=pica-form +APP_DESC=Une alternative OpenSource à TypeForm + +EOF + + +#cat << EOF > /opt/tellform/.env NODE_ENV=development CREATE_ADMIN_ACCOUNT=FALSE PORT=3000 ADMIN_EMAIL=admin@admin.com ADMIN_USERNAME=root ADMIN_PASSWORD=password MONGODB_URI=$MONGODB_URI BASE_URL=$BASE_URL MAILER_EMAIL_ID=$MAILER_EMAIL_ID MAILER_PASSWORD=$MAILER_PASSWORD MAILER_FROM=$MAILER_FROM MAILER_SMTP_HOST=$MAILER_SMTP_HOST MAILER_SMTP_PORT=$MAILER_SMTP_PORT MAILER_SMTP_SECURE=TRUE SIGNUP_DISABLED=FALSE SUBDOMAINS_DISABLED=TRUE + +exec "$@" diff --git a/pica-tellform/tellform-patch.patch b/pica-tellform/tellform-patch.patch new file mode 100644 index 0000000000000000000000000000000000000000..8b112493044b16c715b175fbc0fb32f0c4b630fb --- /dev/null +++ b/pica-tellform/tellform-patch.patch @@ -0,0 +1,1573 @@ +diff -Naur tellform/app/controllers/errors.server.controller.js tellform-patch/app/controllers/errors.server.controller.js +--- tellform/app/controllers/errors.server.controller.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/controllers/errors.server.controller.js 2019-06-03 17:31:23.675642088 +0200 +@@ -8,10 +8,10 @@ + + try { + var fieldName = err.err.substring(err.err.lastIndexOf('.$') + 2, err.err.lastIndexOf('_1')); +- output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'; ++ output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' existe déjà'; + + } catch (ex) { +- output = 'Unique field already exists'; ++ output = 'Un champ unique existe déjà'; + } + + return output; +@@ -34,7 +34,7 @@ + message = getUniqueErrorMessage(err); + break; + default: +- message = 'Something went wrong'; ++ message = 'Quelque chose s\'est mal passé'; + } + } else { + for (var errName in err.errors) { +diff -Naur tellform/app/controllers/forms.server.controller.js tellform-patch/app/controllers/forms.server.controller.js +--- tellform/app/controllers/forms.server.controller.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/controllers/forms.server.controller.js 2019-06-03 17:34:29.871899768 +0200 +@@ -36,7 +36,7 @@ + }); + return; + } +- res.status(200).send('Form submissions successfully deleted'); ++ res.status(200).send('La soumission des formulaires a bien été désactivé'); + + }); + }); +@@ -69,7 +69,7 @@ + message: errorHandler.getErrorMessage(err) + }); + } +- res.status(200).send('Form submission successfully saved'); ++ res.status(200).send('Votre réponse a bien été enregistré'); + }); + }; + +@@ -94,7 +94,7 @@ + * Create a new form + */ + exports.create = function(req, res) { +- ++ + if(!req.body.form){ + return res.status(400).send({ + message: 'Invalid Input' +@@ -130,7 +130,7 @@ + return res.json(newForm); + } + return res.status(404).send({ +- message: 'Form Does Not Exist' ++ message: 'Le formulaire n\'existe pas' + }); + } + return res.json(newForm); +@@ -144,7 +144,7 @@ + var newForm = req.form; + if (!newForm.isLive && !req.user) { + return res.status(401).send({ +- message: 'Form is Not Public' ++ message: 'Le formulaire n\'est pas public' + }); + } + +@@ -186,7 +186,7 @@ + } else { + + delete updatedForm.__v; +- delete updatedForm.created; ++ delete updatedForm.created; + //Unless we have 'admin' privileges, updating the form's admin is disabled + if(updatedForm && req.user.roles.indexOf('admin') === -1) { + delete updatedForm.admin; +@@ -272,7 +272,7 @@ + exports.formByID = function(req, res, next, id) { + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).send({ +- message: 'Form is invalid' ++ message: 'Le formulaire est invalide' + }); + } + Form.findById(id) +@@ -282,7 +282,7 @@ + return next(err); + } else if (!form || form === null) { + res.status(404).send({ +- message: 'Form not found' ++ message: 'Formulaire non trouvé' + }); + } + else { +@@ -304,7 +304,7 @@ + exports.formByIDFast = function(req, res, next, id) { + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).send({ +- message: 'Form is invalid' ++ message: 'Formulaire invalide' + }); + } + Form.findById(id) +@@ -315,7 +315,7 @@ + return next(err); + } else if (!form || form === null) { + res.status(404).send({ +- message: 'Form not found' ++ message: 'Formulaire non trouvé' + }); + } + else { +@@ -339,7 +339,7 @@ + var form = req.form; + if (req.form.admin.id !== req.user.id && req.user.roles.indexOf('admin') === -1) { + res.status(403).send({ +- message: 'User '+req.user.username+' is not authorized to edit Form: '+form.title ++ message: 'Utilisateur '+req.user.username+' n\'est pas authorisé à modifier le formulaire suivant : '+form.title + }); + } + return next(); +diff -Naur tellform/app/controllers/users/users.authentication.server.controller.js tellform-patch/app/controllers/users/users.authentication.server.controller.js +--- tellform/app/controllers/users/users.authentication.server.controller.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/controllers/users/users.authentication.server.controller.js 2019-06-03 17:22:57.150799235 +0200 +@@ -36,7 +36,7 @@ + + verificationURL: config.baseUrl+'/#!/verify/${URL}', + transportOptions: config.mailer.options, +- ++ + verifySendMailCallback: function(err, info) { + if (err) { + throw err; +@@ -73,12 +73,12 @@ + if(err) { + return res.status(500).send( {message: err } ); + } else if (user){ +- return res.status(200).send('User successfully verified'); ++ return res.status(200).send('Utilisateur vérifié'); + } + // redirect to resend verification email + else { +- return res.status(400).send( {message: 'Verification token is invalid or has expired'} ); +- } ++ return res.status(400).send( {message: 'Jeton de validation expiré ou invalide'} ); ++ } + }); + }; + +@@ -98,10 +98,10 @@ + } + + if (userFound){ +- res.status(200).send('Verification email successfully Re-Sent'); ++ res.status(200).send('Email de vérification renvoyé !'); + }else { + // user hasn't been found yet +- res.status(400).send( {message: 'Error: User has not been registered yet'} ); ++ res.status(400).send( {message: 'Erreur : l\'utilisateur n\'a pas encore été enregistré'} ); + } + }); + }; +@@ -149,10 +149,10 @@ + message: errorHandler.getErrorMessage(err) + }); + } +- return res.status(200).send('An email has been sent to you. Please check it to verify your account.'); ++ return res.status(200).send('Un email vous a été envoyé. Consultez votre boîte mail pour vérifier votre compte.'); + }); + } else { +- return res.status(400).send({message: 'User with username/email already exists!'}); ++ return res.status(400).send({message: 'Un utilisateur avec cet identifiant ou email existe déjà!'}); + } + }); + }; +@@ -161,7 +161,7 @@ + * Signin after passport authentication + */ + exports.signin = function(req, res, next) { +- ++ + passport.authenticate('local', function(err, user, info) { + if (err || !user) { + res.status(400).send(info); +@@ -193,14 +193,14 @@ + res.clearCookie('userLang'); + } + req.logout(); +- return res.status(200).send('You have successfully logged out.'); ++ return res.status(200).send('Vous êtes déconnectés.'); + }; + + /* Generate API Key for User */ + exports.generateAPIKey = function(req, res) { + if (!req.isAuthenticated()){ + return res.status(400).send({ +- message: 'User is not Authorized' ++ message: 'Utilisateur non autorisé' + }); + } + +@@ -212,7 +212,7 @@ + + if (!user) { + return res.status(400).send({ +- message: 'User does not Exist' ++ message: 'L\'utilisateur n\'existe pas' + }); + } + +diff -Naur tellform/app/controllers/users/users.authorization.server.controller.js tellform-patch/app/controllers/users/users.authorization.server.controller.js +--- tellform/app/controllers/users/users.authorization.server.controller.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/controllers/users/users.authorization.server.controller.js 2019-06-03 17:23:48.290918414 +0200 +@@ -13,7 +13,7 @@ + exports.userByID = function (req, res, next, id) { + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).send({ +- message: 'User is invalid' ++ message: 'Utilisateur invalide' + }); + } + +@@ -24,7 +24,7 @@ + return next(err); + } else if (!user) { + return res.status(404).send({ +- message: 'User does not exist' ++ message: 'L\'utilisateur n\'existe pas' + }); + } + +@@ -39,7 +39,7 @@ + exports.requiresLogin = function(req, res, next) { + if (!req.isAuthenticated()) { + return res.status(401).send({ +- message: 'User is not logged in' ++ message: 'Utilisateur non connecté' + }); + } else { + return next(); +@@ -58,7 +58,7 @@ + return next(); + } else { + return res.status(403).send({ +- message: 'User is not authorized' ++ message: 'Utilisateur non autorisé' + }); + } + }); +diff -Naur tellform/app/controllers/users/users.password.server.controller.js tellform-patch/app/controllers/users/users.password.server.controller.js +--- tellform/app/controllers/users/users.password.server.controller.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/controllers/users/users.password.server.controller.js 2019-06-03 17:30:27.007574740 +0200 +@@ -57,12 +57,12 @@ + } + if(!user){ + return res.status(400).send({ +- message: 'No account with that username or email has been found' +- }); ++ message: 'Pas d\'utilisateur n\'a été trouvé avec cet identifiant ou email' ++ }); + } + + return res.status(400).send({ +- message: 'The account associated with this email has not been activated yet' ++ message: 'Le compte associé avec cet identifiant ou email n\'a pas été vérifié' + }); + }); + } else { +@@ -76,14 +76,14 @@ + }); + } else { + return res.status(400).send({ +- message: 'Username field must not be blank' ++ message: 'Le champ utilisateur doit être renseigné' + }); + } + }, + function(token, user, done) { + const fn = pug.compileFile(__dirname + "/../../views/templates/reset-password-email.server.view.pug"); + res.locals['url'] = 'http://' + req.headers.host + '/auth/reset/' + token; +- ++ + console.log(res.locals); + var renderedHtml = fn(res.locals); + done(null, renderedHtml, user); +@@ -93,7 +93,7 @@ + var mailOptions = { + to: user.email, + from: config.mailer.from, +- subject: 'Password Reset', ++ subject: 'Réinitialisation de mot de passe', + html: emailHTML + }; + +@@ -116,11 +116,11 @@ + if (err) { + console.log(err); + return res.status(400).send({ +- message: 'Couldn\'t send reset password email due to internal server errors. Please contact support at team@tellform.com.' ++ message: 'Erreur interne lors de l\'envoi du mail de réinitialisation : pour plus d\information, contactez form@picasoft.net.' + }); + } else { + return res.send({ +- message: 'An email has been sent to ' + obfuscatedEmail + ' with further instructions.' ++ message: 'Un email a été envoyé à ' + obfuscatedEmail + ' avec la procédure à suivre.' + }); + } + }); +@@ -155,13 +155,13 @@ + exports.reset = function(req, res, next) { + if(req.body.newPassword.length < 4){ + return res.status(400).send({ +- message: 'Password must be at least 4 characters long' ++ message: 'Le mot de passe doit être supérieur à 4 caractères' + }); + } + + if(req.body.newPassword !== req.body.verifyPassword){ + return res.status(400).send({ +- message: 'Passwords do not match' ++ message: 'Les mots de passent ne correspondent pas' + }); + } + +@@ -187,7 +187,7 @@ + done(null, savedUser); + }); + } else { +- done('Password reset token is invalid or has expired.', null); ++ done('Jeton de réinitialisation de mot de passe expiré ou invalide.', null); + } + }); + }, +@@ -201,7 +201,7 @@ + var mailOptions = { + to: user.email, + from: config.mailer.from, +- subject: 'Your password has been changed', ++ subject: 'Votre mot de passe a été changé', + html: emailHTML + }; + +@@ -217,7 +217,7 @@ + } + + return res.json({ +- message: 'Successfully changed your password!' ++ message: 'Le changement de votre mot de passe a été effectué!' + }); + }); + }; +@@ -248,7 +248,7 @@ + res.status(400).send(err); + } else { + res.send({ +- message: 'Password changed successfully' ++ message: 'Votre mot de passe a été changé' + }); + } + }); +@@ -256,28 +256,28 @@ + }); + } else { + res.status(400).send({ +- message: 'Passwords do not match' ++ message: 'Les mots de passent ne correspondent pas' + }); + } + } else { + res.status(400).send({ +- message: 'Current password is incorrect' ++ message: 'Mot de passe courant incorrect' + }); + } + } else { + res.status(400).send({ +- message: 'User is not found' ++ message: 'Utilisateur non trouvé' + }); + } + }); + } else { + res.status(400).send({ +- message: 'Please provide a new password' ++ message: 'Merci de renseigner un nouveau mot de passe' + }); + } + } else { + res.status(400).send({ +- message: 'User is not signed in' ++ message: 'L\'utilisateur doit être enregistré' + }); + } + }; +diff -Naur tellform/app/controllers/users/users.profile.server.controller.js tellform-patch/app/controllers/users/users.profile.server.controller.js +--- tellform/app/controllers/users/users.profile.server.controller.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/controllers/users/users.profile.server.controller.js 2019-06-03 17:30:45.591596171 +0200 +@@ -27,7 +27,7 @@ + return res.status(500).send({ + message: errorHandler.getErrorMessage(err) + }); +- } ++ } + req.login(user, function(loginErr) { + if (err) { + res.status(500).send(loginErr); +@@ -35,11 +35,11 @@ + res.json(user); + } + }); +- ++ + }); + } else { + res.status(401).send({ +- message: 'User is not signed in' ++ message: 'L\'utilisateur doit être enregistré' + }); + } + }; +diff -Naur tellform/app/models/form_field.server.model.js tellform-patch/app/models/form_field.server.model.js +--- tellform/app/models/form_field.server.model.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/models/form_field.server.model.js 2019-06-03 17:37:37.616202537 +0200 +@@ -157,7 +157,7 @@ + if(this.fieldType !== 'rating'){ + + if(this.ratingOptions && this.ratingOptions.steps && this.ratingOptions.shape){ +- error.errors.ratingOptions = new mongoose.Error.ValidatorError({path: 'ratingOptions', message: 'ratingOptions is only allowed for type \'rating\' fields.', type: 'notvalid', value: this.ratingOptions}); ++ error.errors.ratingOptions = new mongoose.Error.ValidatorError({path: 'ratingOptions', message: 'ratingOptions sont uniquement authorisés pour les types \'rating\' ', type: 'notvalid', value: this.ratingOptions}); + console.error(error); + return(next(error)); + } +@@ -181,7 +181,7 @@ + //If field is multiple choice check that it has field + if(this.fieldType !== 'dropdown' && this.fieldType !== 'radio' && this.fieldType !== 'checkbox'){ + if(this.fieldOptions && this.fieldOptions.length > 0){ +- error.errors.ratingOptions = new mongoose.Error.ValidatorError({path:'fieldOptions', message: 'fieldOptions are only allowed for type dropdown, checkbox or radio fields.', type: 'notvalid', value: this.ratingOptions}); ++ error.errors.ratingOptions = new mongoose.Error.ValidatorError({path:'fieldOptions', message: 'fieldOptions sont uniquement authorisés pour les types menus déroualnts et cases à cocher.', type: 'notvalid', value: this.ratingOptions}); + console.error(error); + return(next(error)); + } +@@ -212,4 +212,3 @@ + var RatingOptions = mongoose.model('RatingOptions', RatingFieldSchema); + + module.exports = FormFieldSchema; +- +diff -Naur tellform/app/models/form.server.model.js tellform-patch/app/models/form.server.model.js +--- tellform/app/models/form.server.model.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/models/form.server.model.js 2019-06-03 20:20:28.450567139 +0200 +@@ -91,8 +91,8 @@ + language: { + type: String, + enum: ['en', 'fr', 'es', 'it', 'de'], +- default: 'en', +- required: 'Form must have a language' ++ default: 'fr', ++ required: 'Le formulaire doit avoir une langue' + }, + analytics:{ + gaCode: { +@@ -110,7 +110,7 @@ + admin: { + type: Schema.Types.ObjectId, + ref: 'User', +- required: 'Form must have an Admin' ++ required: 'Le formulaire doit avoir un administrateur' + }, + startPage: { + showStart:{ +@@ -119,7 +119,7 @@ + }, + introTitle:{ + type: String, +- default: 'Welcome to Form' ++ default: 'Bienvenue sur PicaForm' + }, + introParagraph:{ + type: String +@@ -137,14 +137,14 @@ + }, + title:{ + type: String, +- default: 'Thank you for filling out the form' ++ default: 'Merci d\'avoir rempli le formulaire' + }, + paragraph:{ + type: String + }, + buttonText:{ + type: String, +- default: 'Go back to Form' ++ default: 'Retour au formulaire' + }, + buttons:[ButtonSchema] + }, +@@ -261,12 +261,12 @@ + var totalViews = dropoffViews+continueViews; + var continueRate = 0; + var dropoffRate = 0; +- ++ + if(totalViews > 0){ + continueRate = (continueViews/totalViews*100).toFixed(0); + dropoffRate = (dropoffViews/totalViews*100).toFixed(0); + } +- ++ + fieldDropoffs[i] = { + dropoffViews: dropoffViews, + responses: continueViews, +@@ -444,4 +444,3 @@ + FormSchema.index({created: 1}); + + mongoose.model('Form', FormSchema); +- +diff -Naur tellform/app/models/plugins/languagePlugin.js tellform-patch/app/models/plugins/languagePlugin.js +--- tellform/app/models/plugins/languagePlugin.js 2019-06-03 21:41:43.768319367 +0200 ++++ tellform-patch/app/models/plugins/languagePlugin.js 2019-06-03 17:35:49.144023220 +0200 +@@ -9,18 +9,18 @@ + type: String, + enum: constants.languageTypes, + default: config.defaultLanguage, +- required: options.required || 'Must be a valid language' ++ required: options.required || 'Doit être une langue valide' + } + }); + + schema.pre('save', function (next) { + var currWord = this.language; + +- //English is the default backup language +- this.language = 'en'; ++ //French is the default backup language ++ this.language = 'fr'; + if(constants.wordToLangCode.has(currWord)){ + this.language = constants.wordToLangCode[currWord]; +- } ++ } + next(); + }); +-}; +\ Pas de fin de ligne à la fin du fichier ++}; +diff -Naur tellform/app/models/user.server.model.js tellform-patch/app/models/user.server.model.js +--- tellform/app/models/user.server.model.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/models/user.server.model.js 2019-06-03 17:41:53.060660640 +0200 +@@ -51,16 +51,16 @@ + type: String, + trim: true, + lowercase: true, +- unique: 'Account already exists with this email', +- match: [/.+\@.+\..+/, 'Please fill a valid email address'], +- required: [true, 'Email is required'] ++ unique: 'Un compte existe déjà avec cet email', ++ match: [/.+\@.+\..+/, 'Merci de renseigner une adresse mail valide'], ++ required: [true, 'Un email est requis'] + }, + username: { + type: String, + unique: true, + lowercase: true, +- match: [/^[a-zA-Z0-9\-]+$/, 'Username can only contain alphanumeric characters and \'-\''], +- required: [true, 'Username is required'] ++ match: [/^[a-zA-Z0-9\-]+$/, 'Les noms d\'utilisateur ne peuvent contenir que des caractères alphanumeriques et \'-\''], ++ required: [true, 'Un identifiant est requis'] + }, + passwordHash: { + type: String, +@@ -85,7 +85,7 @@ + language: { + type: String, + enum: ['en', 'fr', 'es', 'it', 'de'], +- default: 'en', ++ default: 'fr', + }, + lastModified: { + type: Date +diff -Naur tellform/app/views/form.server.view.pug tellform-patch/app/views/form.server.view.pug +--- tellform/app/views/form.server.view.pug 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/views/form.server.view.pug 2019-06-04 00:09:38.082736932 +0200 +@@ -2,24 +2,24 @@ + html(lang='en', xmlns='http://www.w3.org/1999/xhtml') + head + title=title +- // General META ++ // General META + meta(charset='utf-8') + meta(http-equiv='Content-type', content='text/html;charset=UTF-8') + meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1') + meta(name='viewport', content='width=device-width,initial-scale=1,maximum-scale=1') + meta(name='apple-mobile-web-app-capable', content='yes') + meta(name='apple-mobile-web-app-status-bar-style', content='black') +- // Semantic META ++ // Semantic META + meta(name='keywords', content='keywords') + meta(name='description', content='description') +- // Facebook META ++ // Facebook META + meta(property='og:site_name', content=title) + meta(property='og:title', content=title) + meta(property='og:description', content='description') + meta(property='og:url', content='url') + meta(property='og:image', content='/img/brand/logo.png') + meta(property='og:type', content='website') +- // Twitter META ++ // Twitter META + meta(name='twitter:title', content=title) + meta(name='twitter:description', content='description') + meta(name='twitter:url', content='url') +@@ -35,17 +35,17 @@ + background: url('/static/dist/page-loader.gif') 50% 35% no-repeat rgb(249,249,249); + background-size: 50px 50px; + } +- // Fav Icon ++ // Fav Icon + link(href='/static/modules/core/img/brand/favicon.ico', rel='shortcut icon', type='image/x-icon') + + body(ng-cloak='') + .loader + section.content + section(ui-view='') +- //Embedding The User Object signupDisabled, socketPort and socketUrl Boolean ++ //Embedding The User Object signupDisabled, socketPort and socketUrl Boolean + script(type='text/javascript'). + var signupDisabled = !{signupDisabled}; +- var socketPort = false; ++ var socketPort = false; + var socketUrl = false; + var subdomainsDisabled = !{subdomainsDisabled}; + +@@ -92,18 +92,3 @@ + each jsFile in formJSFiles + script(type='text/javascript', src=jsFile) + // end Application Javascript dependencies +- +- if process.env.NODE_ENV === 'development' +- //Livereload script rendered +- script(async='', type='text/javascript', src='http://#{request.hostname}:35729/livereload.js') +- +- script Raven.config('https://825fefd6b4ed4a4da199c1b832ca845c@sentry.tellform.com/2').install(); +- +- if google_analytics_id +- script window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;ga('create','{{google_analytics_id}}','auto');ga('send','pageview') +- +- script(src='https://www.google-analytics.com/analytics.js', async='') +- +- script(type="text/javascript"). +- window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c<p.length;c++)heap[p[c]]=o(p[c])}; +- heap.load("2213510609"); +diff -Naur tellform/app/views/index.server.view.pug tellform-patch/app/views/index.server.view.pug +--- tellform/app/views/index.server.view.pug 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/views/index.server.view.pug 2019-06-03 18:36:12.037450225 +0200 +@@ -12,7 +12,7 @@ + //Embedding The signupDisabled Boolean + script(type='text/javascript'). + var signupDisabled = !{signupDisabled}; +- var socketPort = false; ++ var socketPort = false; + var socketUrl = false; + var subdomainsDisabled = !{subdomainsDisabled}; + var locale = "en"; +@@ -47,9 +47,4 @@ + script(type='text/javascript', src=jsFile) + // end Application Javascript dependencies + +- if process.env.NODE_ENV === 'development' +- script(type='text/javascript', src='http://#{request.hostname}:35729/livereload.js') +- + script(src='https://cdn.ravenjs.com/2.3.0/angular/raven.min.js') +- +- script Raven.config('https://825fefd6b4ed4a4da199c1b832ca845c@sentry.tellform.com/2').install(); +\ Pas de fin de ligne à la fin du fichier +diff -Naur tellform/app/views/layout.server.view.pug tellform-patch/app/views/layout.server.view.pug +--- tellform/app/views/layout.server.view.pug 2019-06-03 21:41:43.768319367 +0200 ++++ tellform-patch/app/views/layout.server.view.pug 2019-06-04 00:11:34.854903019 +0200 +@@ -2,8 +2,8 @@ + html(lang='en', xmlns='http://www.w3.org/1999/xhtml') + head + title=title +- +- // General META ++ ++ // General META + meta(charset='utf-8') + meta(http-equiv='Content-type', content='text/html;charset=UTF-8') + meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1') +@@ -11,11 +11,11 @@ + meta(name='apple-mobile-web-app-capable', content='yes') + meta(name='apple-mobile-web-app-status-bar-style', content='black') + +- // Semantic META ++ // Semantic META + meta(name='keywords', content=keywords) + meta(name='description', content=description) + +- // Facebook META ++ // Facebook META + meta(property='og:site_name', content=title) + meta(property='og:title', content=title) + meta(property='og:description', content=description) +@@ -23,19 +23,19 @@ + meta(property='og:image', content='/img/brand/logo.png') + meta(property='og:type', content='website') + +- // Twitter META ++ // Twitter META + meta(name='twitter:title', content=title) + meta(name='twitter:description', content=description) + meta(name='twitter:url', content=url) + meta(name='twitter:image', content='/img/brand/logo.png') + +- // Fav Icon ++ // Fav Icon + link(href='/static/modules/core/img/brand/favicon.ico', rel='shortcut icon', type='image/x-icon') + link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css') + link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css', integrity='sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u', crossorigin='anonymous') + link(rel='stylesheet', href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,900') + +- //Bower CSS dependencies ++ //Bower CSS dependencies + each bowerCssFile in bowerCssFiles + link(rel='stylesheet', href=bowerCssFile) + link(rel='stylesheet', href='/static/lib/angular-input-stars/angular-input-stars.css') +@@ -51,11 +51,3 @@ + body + + block content +- +- script window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;ga('create','#{google_analytics_id}','auto');ga('send','pageview') +- +- script(src='https://www.google-analytics.com/analytics.js', async='', defer='') +- +- script(type="text/javascript"). +- window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c<p.length;c++)heap[p[c]]=o(p[c])}; +- heap.load("2213510609"); +diff -Naur tellform/app/views/redoc.server.view.pug tellform-patch/app/views/redoc.server.view.pug +--- tellform/app/views/redoc.server.view.pug 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/views/redoc.server.view.pug 2019-06-04 00:11:42.486913943 +0200 +@@ -3,7 +3,7 @@ + head + title #{title} + +- // General META ++ // General META + meta(charset='utf-8') + meta(http-equiv='Content-type', content='text/html;charset=UTF-8') + meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1') +@@ -11,11 +11,11 @@ + meta(name='apple-mobile-web-app-capable', content='yes') + meta(name='apple-mobile-web-app-status-bar-style', content='black') + +- // Semantic META ++ // Semantic META + meta(name='keywords', content='#{keywords}') + meta(name='description', content='#{description}') + +- // Facebook META ++ // Facebook META + meta(property='og:site_name', content='#{title}') + meta(property='og:title', content='#{title}') + meta(property='og:description', content='#{description}') +@@ -23,13 +23,13 @@ + meta(property='og:image', content='/img/brand/logo.png') + meta(property='og:type', content='website') + +- // Twitter META ++ // Twitter META + meta(name='twitter:title', content='#{title}') + meta(name='twitter:description', content='#{description}') + meta(name='twitter:url', content='{{url}}') + meta(name='twitter:image', content='/img/brand/logo.png') + +- // Fav Icon ++ // Fav Icon + link(href='/static/modules/core/img/brand/favicon.ico', rel='shortcut icon', type='image/x-icon') + link(rel='stylesheet', href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,900') + +@@ -40,23 +40,10 @@ + body + redoc(spec-url='/static/swagger.json') + script(src='https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js') +- ++ + //Bower JS dependencies + each bowerJSFile in bowerJSFiles + script(type='text/javascript', src='#{bowerJSFile}') + // end Bower JS dependencies +- ++ + script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/angular-strap/2.3.8/angular-strap.min.js') +- +- if process.env.NODE_ENV === 'development' +- script(src='https://cdn.ravenjs.com/2.3.0/angular/raven.min.js') +- +- script Raven.config('https://825fefd6b4ed4a4da199c1b832ca845c@sentry.tellform.com/2').install(); +- +- script window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;ga('create','{{google_analytics_id}}','auto');ga('send','pageview') +- +- script(src='https://www.google-analytics.com/analytics.js', async='', defer='') +- +- script(type="text/javascript"). +- window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c<p.length;c++)heap[p[c]]=o(p[c])}; +- heap.load("2213510609"); +diff -Naur tellform/app/views/templates/reset-password-confirm-email.server.view.html tellform-patch/app/views/templates/reset-password-confirm-email.server.view.html +--- tellform/app/views/templates/reset-password-confirm-email.server.view.html 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/views/templates/reset-password-confirm-email.server.view.html 2019-06-03 18:36:07.453459845 +0200 +@@ -27,10 +27,10 @@ + <tr> + <td width="36"></td> + <td width="454" align="left" style="color: #444444; border-collapse: collapse; font-size: 11pt; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; max-width: 454px;" valign="top"> +- <p>Hello there!</p> +- <p>This is a courtesy message to confirm that your password was just changed.</p> +- <p>Thanks so much for using our services! If you have any questions, or suggestions, please feel free to email us here at <a href="mailto:team@tellform.com">team@tellform.com</a>.</p> +- <p> - The #{appName} team</p> ++ <p>Bonjour!</p> ++ <p>Votre mot de passe a bien été changé !</p> ++ <p>Merci d'utiliser nos services ! Pour toute question ou recommandation, contactez <a href="mailto:form@picasoft.net">form@picasoft.net</a>.</p> ++ <p> - L'équipe #{appName}</p> + </td> + <td width="36"></td> + </tr> +@@ -61,4 +61,4 @@ + </table> + </center> + </body> +-</html> +\ Pas de fin de ligne à la fin du fichier ++</html> +diff -Naur tellform/app/views/templates/reset-password-confirm-email.server.view.pug tellform-patch/app/views/templates/reset-password-confirm-email.server.view.pug +--- tellform/app/views/templates/reset-password-confirm-email.server.view.pug 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/views/templates/reset-password-confirm-email.server.view.pug 2019-06-03 18:36:08.981456619 +0200 +@@ -23,7 +23,7 @@ + p=__('EMAIL_GREETING') + p=__('RESET_PASSWORD_CONFIRMATION_EMAIL_BODY_1') + p=__("VERIFICATION_EMAIL_PARAGRAPH_2") +- a(href='mailto:team@tellform.com') team@tellform.com ++ a(href='mailto:form@picasoft.net') form@picasoft.net + p=__('EMAIL_SIGNATURE') + td(width='36') + tr +diff -Naur tellform/app/views/templates/reset-password-email.server.view.html tellform-patch/app/views/templates/reset-password-email.server.view.html +--- tellform/app/views/templates/reset-password-email.server.view.html 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/views/templates/reset-password-email.server.view.html 2019-06-03 17:48:53.025481048 +0200 +@@ -27,12 +27,12 @@ + <tr> + <td width="36"></td> + <td width="454" align="left" style="color: #444444; border-collapse: collapse; font-size: 11pt; font-family: 'Open Sans', 'Lucida Grande', 'Segoe UI', Arial, Verdana, 'Lucida Sans Unicode', Tahoma, 'Sans Serif'; max-width: 454px;" valign="top"> +- <p>Hello there!</p> +- <p>Here is a special link that will allow you to reset your password. Please note it will expire in one hour for your protection:</p> +- <p><a href="{{url}}">Reset Your Password</a></p> +- <p>If you did not request this, please ignore this email and your password will remain unchanged.</p> +- <p>Thanks so much for using our services! If you have any questions, or suggestions, please feel free to email us here at <a href="mailto:team@tellform.com">team@tellform.com</a>.</p> +- <p> - The {{appName}} team</p> ++ <p>Bonjour!</p> ++ <p>Voilà un lien pour réinitialiser votre mot de passe. Il expirera dans une heure :</p> ++ <p><a href="{{url}}">Réinitialiser le mot de passe</a></p> ++ <p>Si vous n'êtes pas à l'origine de cette requête, ignorez ce message, votre mot de passe restera inchangé.</p> ++ <p>Merci d'utiliser nos services ! Pour toute question ou recommandation, contactez <a href="mailto:form@picasoft.net">form@picasoft.net</a>.</p> ++ <p> - L'équipe {{appName}}</p> + </td> + <td width="36"></td> + </tr> +@@ -63,4 +63,4 @@ + </table> + </center> + </body> +-</html> +\ Pas de fin de ligne à la fin du fichier ++</html> +diff -Naur tellform/app/views/templates/reset-password-email.server.view.pug tellform-patch/app/views/templates/reset-password-email.server.view.pug +--- tellform/app/views/templates/reset-password-email.server.view.pug 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/app/views/templates/reset-password-email.server.view.pug 2019-06-03 18:36:09.937454612 +0200 +@@ -26,7 +26,7 @@ + a(href=url)=__('RESET_PASSWORD_REQUEST_EMAIL_LINK_TEXT') + p=__('RESET_PASSWORD_REQUEST_EMAIL_PARAGRAPH_2') + p=__('VERIFICATION_EMAIL_PARAGRAPH_2') +- a(href='mailto:team@tellform.com') team@tellform.com ++ a(href='mailto:form@picasoft.net') form@picasoft.net + p=__('EMAIL_SIGNATURE') + td(width='36') + tr +diff -Naur tellform/config/env/all.js tellform-patch/config/env/all.js +--- tellform/config/env/all.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/config/env/all.js 2019-06-03 18:37:49.117283065 +0200 +@@ -14,14 +14,14 @@ + pass: '' + } + }, +- +- ++ ++ + admin:{ + email: process.env.ADMIN_EMAIL || 'admin@admin.com', + username: process.env.ADMIN_USERNAME || 'root', + password: process.env.ADMIN_PASSWORD || 'root', + }, +- ++ + redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379', + + port: process.env.PORT || 3000, +@@ -36,7 +36,7 @@ + tempUserCollection: 'temporary_users', + + mailer: { +- from: process.env.MAILER_FROM || 'testing@'+process.env.SPARKPOST_SANDBOX_DOMAIN || 'no-reply@tellform.com', ++ from: process.env.MAILER_FROM || 'testing@'+process.env.SPARKPOST_SANDBOX_DOMAIN || 'form@picasoft.net', + options: process.env.MAILER_SMTP_HOST ? { //Uses custom SMTP if MAILER_SMTP_HOST is set + host: process.env.MAILER_SMTP_HOST || '', + port: process.env.MAILER_SMTP_PORT || 465, +diff -Naur tellform/config/env/development.js tellform-patch/config/env/development.js +--- tellform/config/env/development.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/config/env/development.js 2019-06-03 18:37:50.065281754 +0200 +@@ -17,7 +17,7 @@ + // Uncomment to enable logging to a log on the file system + }, + mailer: { +- from: process.env.MAILER_FROM || 'no-reply@tellform.com', ++ from: process.env.MAILER_FROM || 'form@picasoft.net', + options: process.env.MAILER_SMTP_HOST ? { //Uses custom SMTP if MAILER_SMTP_HOST is set + host: process.env.MAILER_SMTP_HOST || '', + port: process.env.MAILER_SMTP_PORT || 465, +diff -Naur tellform/config/express.js tellform-patch/config/express.js +--- tellform/config/express.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/config/express.js 2019-06-03 20:56:17.832233360 +0200 +@@ -75,7 +75,7 @@ + + if(config.socketUrl){ + app.locals.socketUrl = config.socketUrl; +- } ++ } + + app.locals.bowerJSFiles = config.getBowerJSAssets(); + app.locals.bowerCssFiles = config.getBowerCSSAssets(); +@@ -91,7 +91,7 @@ + var User = mongoose.model('User'); + var subdomainPath = '/subdomain/'; + var subdomains = req.subdomains; +- ++ + if (subdomains.slice(0, 4).join('.') + '' === '1.0.0.127') { + subdomains = subdomains.slice(4); + } +@@ -100,7 +100,7 @@ + if (!subdomains.length) { + return next(); + } +- ++ + urlPath = url.parse(req.url).path.split('/'); + if (urlPath.indexOf('static') > -1) { + urlPath.splice(1, 1); +@@ -131,13 +131,13 @@ + req.subdomains = null; + // Error page + return res.status(404).render('404', { +- error: 'Page Does Not Exist' ++ error: 'La page n\'existe pas' + }); + } + if (user === null) { + // Error page + return res.status(404).render('404', { +- error: 'Page Does Not Exist' ++ error: 'La page n\'existe pas' + }); + } + +@@ -190,7 +190,7 @@ + i18n.configure({ + locales: supportedLanguages, + directory: __dirname + '/locales', +- defaultLocale: 'en', ++ defaultLocale: 'fr', + cookie: 'userLang' + }); + +@@ -340,10 +340,10 @@ + + // Assume 404 since no middleware responded + app.use(function(req, res) { +- client.captureError(new Error('Page Not Found')); ++ client.captureError(new Error('Pas non trouvée')); + res.status(404).render('404', { + url: req.originalUrl, +- error: 'Not Found', ++ error: 'Non trouvée', + __: i18n.__ + }); + }); +diff -Naur tellform/config/init.js tellform-patch/config/init.js +--- tellform/config/init.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/config/init.js 2019-06-03 19:33:23.324180800 +0200 +@@ -18,9 +18,9 @@ + + if (!environmentFiles.length) { + if (process.env.NODE_ENV) { +- console.error(chalk.red('No configuration file found for "' + process.env.NODE_ENV + '" environment using development instead')); ++ console.error(chalk.red('Pas de fichier de configuration trouvé pour l\'environnement "' + process.env.NODE_ENV + '"')); + } else { +- console.error(chalk.red('NODE_ENV is not defined! Using default development environment')); ++ console.error(chalk.red('NODE_ENV n\'est pas défini!')); + } + + process.env.NODE_ENV = 'development'; +diff -Naur tellform/config/locales/fr.json tellform-patch/config/locales/fr.json +--- tellform/config/locales/fr.json 2019-06-03 21:41:43.768319367 +0200 ++++ tellform-patch/config/locales/fr.json 2019-06-03 16:08:40.807954389 +0200 +@@ -4,14 +4,14 @@ + "404_BODY": "%s n'est pas un chemin valide.", + "500_BODY": "Une erreur inattendue semble s'être produite, pourquoi ne pas essayer d'actualiser votre page ? Ou vous pouvez nous contacter si le problème persiste.", + "EMAIL_GREETING": "Bonjour !", +- "VERIFICATION_EMAIL_PARAGRAPH_1": "Bienvenue sur TellForm ! Voici un lien spécial pour activer votre nouveau compte : ", ++ "VERIFICATION_EMAIL_PARAGRAPH_1": "Bienvenue sur PicaForm ! Voici un lien spécial pour activer votre nouveau compte : ", + "VERIFICATION_EMAIL_LINK_TEXT": "Activer mon compte", + "VERIFICATION_EMAIL_PARAGRAPH_2": "Merci infiniment d'utiliser nos services ! Si vous avez des questions ou des suggestions, n'hésitez pas à nous envoyer un courriel ici", +- "VERIFICATION_EMAIL_SUBJECT": "Activer votre nouveau compte TellForm !", ++ "VERIFICATION_EMAIL_SUBJECT": "Activer votre nouveau compte PicaForm !", + "VERIFICATION_EMAIL_TEXT": "Merci de vérifier votre compte en cliquant sur le lien suivant, ou en le copiant dans votre navigateur web : ${URL}", +- "EMAIL_SIGNATURE": "- L'équipe TellForm", ++ "EMAIL_SIGNATURE": "- L'équipe PicaForm", + "WELCOME_EMAIL_PARAGRAPH_1": "Nous aimerions vous accueillir en tant que nouveau membre !", +- "WELCOME_EMAIL_PARAGRAPH_2": "Nous espérons que vous apprécierez l'utilisation de TellForm ! Si vous avez des problèmes, n'hésitez pas à nous envoyer un e-mail ici", ++ "WELCOME_EMAIL_PARAGRAPH_2": "Nous espérons que vous apprécierez l'utilisation de PicaForm ! Si vous avez des problèmes, n'hésitez pas à nous envoyer un e-mail ici", + "WELCOME_EMAIL_SUBJECT": "Bienvenue dans %s!", + "WELCOME_EMAIL_TEXT": "Votre compte a été vérifié avec succès.", + "RESET_PASSWORD_CONFIRMATION_EMAIL_PARAGRAPH_1": "Ceci est un message de courtoisie pour confirmer que votre mot de passe a été modifié.", +diff -Naur tellform/config/logger.js tellform-patch/config/logger.js +--- tellform/config/logger.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/config/logger.js 2019-06-03 20:19:41.678306270 +0200 +@@ -56,7 +56,7 @@ + } catch (err) { + if (process.env.NODE_ENV !== 'test') { + console.log(); +- console.log(chalk.red('An error has occured during the creation of the File transport logger.')); ++ console.log(chalk.red('Une erreur s\'est produite pendant la création du fichier d\'inscription.')); + console.log(chalk.red(err)); + console.log(); + } +@@ -75,9 +75,9 @@ + + var _config = _.clone(config, true); + var configFileLogger = _config.log.fileLogger; +- ++ + if (!_.has(_config, 'log.fileLogger.directoryPath') || !_.has(_config, 'log.fileLogger.fileName')) { +- console.log('unable to find logging file configuration'); ++ console.log('Impossible de trouver le fichier de configuration de connexion'); + return false; + } + +@@ -122,13 +122,13 @@ + logger.getLogFormat = function getLogFormat() { + var format = config.log && config.log.format ? config.log.format.toString() : 'combined'; + +- if (!_.includes(validFormats, format)) { ++ if (!_.includes(validFormats, format)) { + if (process.env.NODE_ENV !== 'test') { + console.log(); +- console.log(chalk.yellow('Warning: An invalid format was provided. The logger will use the default format of "' + format + '"')); ++ console.log(chalk.yellow('Attetion : un format invalide a été fourni Le format par défaut sera : "' + format + '"')); + console.log(); + } +- } ++ } + return format; + }; + +diff -Naur tellform/config/passport_helpers.js tellform-patch/config/passport_helpers.js +--- tellform/config/passport_helpers.js 2019-06-03 15:54:01.650435222 +0200 ++++ tellform-patch/config/passport_helpers.js 2019-06-03 19:35:59.088482320 +0200 +@@ -31,7 +31,7 @@ + + module.exports.hasRole = function hasRole(roleRequired) { + if (!roleRequired) { +- throw new Error('Required role needs to be set'); ++ throw new Error('Des rôles doivent être définis'); + } + + return function(req, res, next) { +@@ -47,4 +47,3 @@ + module.exports.hasAdminRole = function hasAdminRole() { + return module.exports.hasRole('admin'); + }; +- +diff -Naur tellform/.git/index tellform-patch/.git/index +--- tellform/.git/index 2019-06-03 21:41:43.772319383 +0200 ++++ tellform-patch/.git/index 2019-06-03 15:58:01.042801090 +0200 +@@ -5,14 +5,14 @@ + ����=+�h"٫ ��m���.slugignore�������\�&y&�ѐ\�&y&�ѐ���}&���������������a:N�R�Ps}�������.travis.yml�������\�&y&�ږ\�&y&�ږ���}'�������������l��#�v1!��:��+��]d`��CODE_OF_CONDUCT.md��������\�'i��\�'i�����}"������������$k_�k��1��=��a � + Dockerfile��������\�&y&�ږ\�&y&�ږ���})����������������W*3t���U�g�� + �INSTALLATION_INSTRUCTIONS.md������\�&y&�ږ\�&y&�ږ���}*��������������kSp�ݴ�ü/���-���4�ISSUE_TEMPLATE.md�\�&y&�ږ\�&y&�ږ���},�������������f��x;7���|�a�`����� +-LICENSE.md��������\�&y&�ږ\�&y&�ږ���}5������������=*N�M��ʚP�����ۨ��PULL_REQUEST_TEMPLATE.md��\�&y&�ږ\�&y&�ږ���}X�������������03` zM�ٯ��Kp}�J��Procfile��\�'i��\�'i�����}(������������MI��5��o�@��8����$4� README.md�\�&y&�ږ\�&y&�ږ���}Z������������+�H��s�q���_C�k�^��Vagrantfile�������\�&y&�ږ\�&y&�ږ���}[�������������6�����2Gk{3���y��app.json��\�&y&�ږ\�&y&�ږ���}\������������ԑNe.��ш��%:x���4�)app/controllers/core.server.controller.js�\�&y&�ږ\�&y&�ږ���}]�������������x܇q��u`�Z��iC�[�+app/controllers/errors.server.controller.js�������\�&y&�ږ\�&y&�ږ���}^������������/���"]��!���_I8� �{�*app/controllers/forms.server.controller.js��������\�&y&�ږ\�&y&�ږ���}`������������n���1�x-!b��o^|��E��*app/controllers/users.server.controller.js��������\�&y&�ږ\�&y&�ږ���}a�������������[�lC*�?L��>���v ��+�?app/controllers/users/users.authentication.server.controller.js���\�&y&�ږ\�&y&�ږ���}b��������������%ϡ����v���_|sG�>app/controllers/users/users.authorization.server.controller.js����\�&y&�ږ\�&y&�ږ���}c���������������U��Ϊf-�_� `���9app/controllers/users/users.password.server.controller.js�\�&y&�ږ\�&y&�ږ���}e������������+q����ؕx�V��̗�i��8app/controllers/users/users.profile.server.controller.js��\�&y&�ږ\�&y&�ږ���}f�������������gjGz�\`�|ݞ1��܃B���app/libs/constants.js�����\�&y&�ږ\�&y&�ږ���}g�������������N, ��bwM%��S�Gl�'��#app/libs/timestamp.server.plugin.js�������\�&y&�ږ\�&y&�ږ���}h��������������+�C��y��'��+��app/libs/tokenGenerator.js��������\�&y&�ږ\�&y&�ږ���}i������������&#��&�X���l�<�{c�=�app/models/form.server.model.js���\�&y&�ږ\�&y&�ږ���}j������������1�nq5�bV�N�AF�&A�%app/models/form_field.server.model.js�����\�&y&�ږ\�&y&�ږ���}k������������ �P_:C�_T[t�X��/ܗ �*app/models/form_submission.server.model.js��������\�&y&�ږ\�&y&�ږ���}l�������������&��s���E��#���B��%app/models/logic_jump.server.model.js�����\�w�-˟�\�w�-˟����=�����������������@H_��{�3�D��f�$app/models/plugins/languagePlugin.js������\�&y&�ږ\�&y&�ږ���}n������������9�(J�T�ǫ{�� O��]�l�app/models/user.server.model.js���\�&y&�ږ\�&y&�ږ���}o������������D�T�y�Cu�`F�U�_"+��X� app/routes/core.server.routes.js��\�&y&�ږ\�&y&�ږ���}p������������g��%x�,�*J!$?P4��!app/routes/forms.server.routes.js�\�&y&�ږ\�&y&�ږ���}q������������9�qvΐ�'p(�Q��ʙBU\��!app/routes/users.server.routes.js�\�&y&�ږ\�&y&�ږ���}r�������������:h`N<�x�'�p����_i� app/sockets/analytics_service.js��\�&y&�ږ\�&y&�ږ���}s�������������W>c��`OZw��m�#l����#app/tests/form.server.model.test.js�������\�&y&�ږ\�&y&�ږ���}t������������'N����ޓԑ�_n�SY)O��$app/tests/form.server.routes.test.js������\�&y&�ږ\�&y&�ږ���}v������������F D,�Q��a#b�Z#�P,ZC�'app/tests/form_submission.model.test.js���\�&y&�ږ\�&y&�ږ���}w���������������@o2�)}Q� ��Y�(app/tests/form_submission.routes.test.js��\�&y&�ږ\�&y&�ږ���}x�������������)�r�V�$m������W��.app/tests/libs/timestamp.server.plugin.test.js����\�&y&�ږ\�&y&�ږ���}y������������Z|���$|I��� �d�� �D��#app/tests/user.server.model.test.js�������\�&y&�ږ\�&y&�ږ���}z������������ �����浇y�2 ++LICENSE.md��������\�&y&�ږ\�&y&�ږ���}5������������=*N�M��ʚP�����ۨ��PULL_REQUEST_TEMPLATE.md��\�&y&�ږ\�&y&�ږ���}X�������������03` zM�ٯ��Kp}�J��Procfile��\�'i��\�'i�����}(������������MI��5��o�@��8����$4� README.md�\�&y&�ږ\�&y&�ږ���}Z������������+�H��s�q���_C�k�^��Vagrantfile�������\�&y&�ږ\�&y&�ږ���}[�������������6�����2Gk{3���y��app.json��\�&y&�ږ\�&y&�ږ���}\������������ԑNe.��ш��%:x���4�)app/controllers/core.server.controller.js�\�&y&�ږ\�&y&�ږ���}]�������������x܇q��u`�Z��iC�[�+app/controllers/errors.server.controller.js�������\�&y&�ږ\�&y&�ږ���}^������������/���"]��!���_I8� �{�*app/controllers/forms.server.controller.js��������\�&y&�ږ\�&y&�ږ���}`������������n���1�x-!b��o^|��E��*app/controllers/users.server.controller.js��������\�&y&�ږ\�&y&�ږ���}a�������������[�lC*�?L��>���v ��+�?app/controllers/users/users.authentication.server.controller.js���\�&y&�ږ\�&y&�ږ���}b��������������%ϡ����v���_|sG�>app/controllers/users/users.authorization.server.controller.js����\�&y&�ږ\�&y&�ږ���}c���������������U��Ϊf-�_� `���9app/controllers/users/users.password.server.controller.js�\�&y&�ږ\�&y&�ږ���}e������������+q����ؕx�V��̗�i��8app/controllers/users/users.profile.server.controller.js��\�&y&�ږ\�&y&�ږ���}f�������������gjGz�\`�|ݞ1��܃B���app/libs/constants.js�����\�&y&�ږ\�&y&�ږ���}g�������������N, ��bwM%��S�Gl�'��#app/libs/timestamp.server.plugin.js�������\�&y&�ږ\�&y&�ږ���}h��������������+�C��y��'��+��app/libs/tokenGenerator.js��������\�&y&�ږ\�&y&�ږ���}i������������&#��&�X���l�<�{c�=�app/models/form.server.model.js���\�&y&�ږ\�&y&�ږ���}j������������1�nq5�bV�N�AF�&A�%app/models/form_field.server.model.js�����\�&y&�ږ\�&y&�ږ���}k������������ �P_:C�_T[t�X��/ܗ �*app/models/form_submission.server.model.js��������\�&y&�ږ\�&y&�ږ���}l�������������&��s���E��#���B��%app/models/logic_jump.server.model.js�����\�&y&�ږ\�&y&�ږ���}m����������������@H_��{�3�D��f�$app/models/plugins/languagePlugin.js������\�&y&�ږ\�&y&�ږ���}n������������9�(J�T�ǫ{�� O��]�l�app/models/user.server.model.js���\�&y&�ږ\�&y&�ږ���}o������������D�T�y�Cu�`F�U�_"+��X� app/routes/core.server.routes.js��\�&y&�ږ\�&y&�ږ���}p������������g��%x�,�*J!$?P4��!app/routes/forms.server.routes.js�\�&y&�ږ\�&y&�ږ���}q������������9�qvΐ�'p(�Q��ʙBU\��!app/routes/users.server.routes.js�\�&y&�ږ\�&y&�ږ���}r�������������:h`N<�x�'�p����_i� app/sockets/analytics_service.js��\�&y&�ږ\�&y&�ږ���}s�������������W>c��`OZw��m�#l����#app/tests/form.server.model.test.js�������\�&y&�ږ\�&y&�ږ���}t������������'N����ޓԑ�_n�SY)O��$app/tests/form.server.routes.test.js������\�&y&�ږ\�&y&�ږ���}v������������F D,�Q��a#b�Z#�P,ZC�'app/tests/form_submission.model.test.js���\�&y&�ږ\�&y&�ږ���}w���������������@o2�)}Q� ��Y�(app/tests/form_submission.routes.test.js��\�&y&�ږ\�&y&�ږ���}x�������������)�r�V�$m������W��.app/tests/libs/timestamp.server.plugin.test.js����\�&y&�ږ\�&y&�ږ���}y������������Z|���$|I��� �d�� �D��#app/tests/user.server.model.test.js�������\�&y&�ږ\�&y&�ږ���}z������������ �����浇y�2 + �0�g�$app/tests/user.server.routes.test.js������\�&y&�ږ\�&y&�ږ���}{������������7�7 Hk|?�W�7�e*�Ц� +-�app/views/404.server.view.pug�����\�&y&�ږ\�&y&�ږ���}|������������w9����:�b�'�o=/6�app/views/500.server.view.pug�����\�&y&�ږ\�&y&�ږ���}~������������a���/!�s7x��t����F���app/views/form.server.view.pug����\�&y&�ږ\�&y&�ږ���}������������-5Ű��!��sa�4Ak�!��app/views/index.server.view.pug���\�w�-˟�\�w�-˟����=��������������D��-���Z�r٥�T��w� app/views/layout.server.view.pug��\�&y&�ږ\�&y&�ږ���}���������������Pܖ�BS2���ఀ �Ne�app/views/redoc.server.view.pug���\�&y&�ږ\�&y&�ږ���}�������������<]��?o}UC�4H9��Y9c�Aapp/views/templates/reset-password-confirm-email.server.view.html�\�&y&�ږ\�&y&�ږ���}������������� ڕ�\�s3(\u �v��4���@app/views/templates/reset-password-confirm-email.server.view.pug��\�&y&�ږ\�&y&�ږ���}������������� 9�辰������vS�r���V�9app/views/templates/reset-password-email.server.view.html�\�&y&�ږ\�&y&�ږ���}������������� ++�app/views/404.server.view.pug�����\�&y&�ږ\�&y&�ږ���}|������������w9����:�b�'�o=/6�app/views/500.server.view.pug�����\�&y&�ږ\�&y&�ږ���}~������������a���/!�s7x��t����F���app/views/form.server.view.pug����\�&y&�ږ\�&y&�ږ���}������������-5Ű��!��sa�4Ak�!��app/views/index.server.view.pug���\�&y&�ږ\�&y&�ږ���}��������������D��-���Z�r٥�T��w� app/views/layout.server.view.pug��\�&y&�ږ\�&y&�ږ���}���������������Pܖ�BS2���ఀ �Ne�app/views/redoc.server.view.pug���\�&y&�ږ\�&y&�ږ���}�������������<]��?o}UC�4H9��Y9c�Aapp/views/templates/reset-password-confirm-email.server.view.html�\�&y&�ږ\�&y&�ږ���}������������� ڕ�\�s3(\u �v��4���@app/views/templates/reset-password-confirm-email.server.view.pug��\�&y&�ږ\�&y&�ږ���}������������� 9�辰������vS�r���V�9app/views/templates/reset-password-email.server.view.html�\�&y&�ږ\�&y&�ږ���}������������� + ��/2������DK@g�96��8app/views/templates/reset-password-email.server.view.pug��\�&y&�ږ\�&y&�ږ���}������������� + ��"U�,�D�V��sap��� + �%app/views/verification.email.view.pug�����\�&y&�ږ\�&y&�ږ���}������������� + {�V,�Fbܻ|�}FT'�'!D� app/views/welcome.email.view.pug��\�&y&�ږ\�&y&�ږ���}�������������h���q�-L�,|� ��� +-bower.json��������\�&y&�ږ\�&y&�ږ���}�������������I8�Q� �] Z�����h��͟� conf.json�\�&y&�ږ\�&y&�ږ���}�������������O��)�%�#P@:��r�h]�d��config/config.js��\�&y&�ږ\�&y&�ږ���}��������������5������bS?�d,"��config/env/all.js�\�&y&�ږ\�&y&�ږ���}�������������T�&s���+T�z?60�#�config/env/development.js�\�&y&�ږ\�&y&�ږ���}���������������r�O��Q�/+��f5��f��config/env/production.js��\�&y&�ږ\�&y&�ږ���}�������������ڼ�������dO��ew�config/env/secure.js������\�&y&�ږ\�&y&�ږ���}�������������DӠ/$��0?��h��zP�v�config/env/test.js��������\�&y&�ږ\�&y&�ږ���}�������������'��Dy�y�ζ�S1���o�!��config/express.js�\�&y&�ږ\�&y&�ږ���}��������������j�S7�\ϱ�C��Z����config/init.js����\�&y&�ږ\�&y&�ږ���}�������������{� V��a�D݈s!�p~t�config/locales/de.json����\�&y&�ږ\�&y&�ږ���}�������������f�"�[G��j���z`/��config/locales/en.json����\�&y&�ږ\�&y&�ږ���}�������������!Jn����GW�$��P�\�config/locales/es.json����\�w�-˟�\�w�-˟����=�������������{�Þ�n���i�s����U��config/locales/fr.json����\�&y&�ږ\�&y&�ږ���}���������������@���x�\��6L�config/locales/it.json����\�&y&�ږ\�&y&�ږ���}��������������c�}�7T��"�o /���*�config/locales/sv.json����\�&y&�ږ\�&y&�ږ���}������������� �3�/A���+��٬-�ԫ���config/logger.js��\�&y&�ږ\�&y&�ږ���}��������������Z��vn�5�ML_e(�����config/passport.js��������\�&y&�ږ\�&y&�ږ���}��������������ˆ���J^fL�ܬXQU�2~�config/passport_helpers.js��������\�&y&�ږ\�&y&�ږ���}�������������ɕ��8w0^�?��}hI�A�config/socket.io.js�������\�&y&�ږ\�&y&�ږ���}���������������r���L�/��o�cQ%��&�config/strategies/anonymous.js����\�&y&�ږ\�&y&�ږ���}���������������� �+n�Օ�"�&5�config/strategies/apikey.js�������\�&y&�ږ\�&y&�ږ���}�������������PJ��sqj�\�Us�&��9d�config/strategies/local.js��������\�&y'�\�&y'����}������������n_n���u(�� xȵ*�<�� design/screenshots/analytics.png��\�&y'�\�&y'����}������������r��ҏ�r`�j5 �����f@�?� design/screenshots/collapsed.png��\�&y'�\�&y'����}��������������$x��ΛKv�+��<���&design/screenshots/tellform_rating.png����\�&y'�\�&y'����}��������������Ʒ^l��ƫ1�;�R�+design/screenshots/tellform_screenshot1.png�������\�&y'�\�&y'����}�������������j=�W����+�V�[oYԭ�design/tellform_mascot.png��������\�&y'�\�&y'����}����������������4����Oz�� ��*�@a�dev_entrypoint.sh�\�&y'�\�&y'����}�������������� ��koV�'���b�g��D��dns_masq_setup_osx.md�����\�&y'�\�&y'����}������������� �}�+ � ++bower.json��������\�&y&�ږ\�&y&�ږ���}�������������I8�Q� �] Z�����h��͟� conf.json�\�&y&�ږ\�&y&�ږ���}�������������O��)�%�#P@:��r�h]�d��config/config.js��\�&y&�ږ\�&y&�ږ���}��������������5������bS?�d,"��config/env/all.js�\�&y&�ږ\�&y&�ږ���}�������������T�&s���+T�z?60�#�config/env/development.js�\�&y&�ږ\�&y&�ږ���}���������������r�O��Q�/+��f5��f��config/env/production.js��\�&y&�ږ\�&y&�ږ���}�������������ڼ�������dO��ew�config/env/secure.js������\�&y&�ږ\�&y&�ږ���}�������������DӠ/$��0?��h��zP�v�config/env/test.js��������\�&y&�ږ\�&y&�ږ���}�������������'��Dy�y�ζ�S1���o�!��config/express.js�\�&y&�ږ\�&y&�ږ���}��������������j�S7�\ϱ�C��Z����config/init.js����\�&y&�ږ\�&y&�ږ���}�������������{� V��a�D݈s!�p~t�config/locales/de.json����\�&y&�ږ\�&y&�ږ���}�������������f�"�[G��j���z`/��config/locales/en.json����\�&y&�ږ\�&y&�ږ���}�������������!Jn����GW�$��P�\�config/locales/es.json����\�&y&�ږ\�&y&�ږ���}�������������{�Þ�n���i�s����U��config/locales/fr.json����\�&y&�ږ\�&y&�ږ���}���������������@���x�\��6L�config/locales/it.json����\�&y&�ږ\�&y&�ږ���}��������������c�}�7T��"�o /���*�config/locales/sv.json����\�&y&�ږ\�&y&�ږ���}������������� �3�/A���+��٬-�ԫ���config/logger.js��\�&y&�ږ\�&y&�ږ���}��������������Z��vn�5�ML_e(�����config/passport.js��������\�&y&�ږ\�&y&�ږ���}��������������ˆ���J^fL�ܬXQU�2~�config/passport_helpers.js��������\�&y&�ږ\�&y&�ږ���}�������������ɕ��8w0^�?��}hI�A�config/socket.io.js�������\�&y&�ږ\�&y&�ږ���}���������������r���L�/��o�cQ%��&�config/strategies/anonymous.js����\�&y&�ږ\�&y&�ږ���}���������������� �+n�Օ�"�&5�config/strategies/apikey.js�������\�&y&�ږ\�&y&�ږ���}�������������PJ��sqj�\�Us�&��9d�config/strategies/local.js��������\�&y'�\�&y'����}������������n_n���u(�� xȵ*�<�� design/screenshots/analytics.png��\�&y'�\�&y'����}������������r��ҏ�r`�j5 �����f@�?� design/screenshots/collapsed.png��\�&y'�\�&y'����}��������������$x��ΛKv�+��<���&design/screenshots/tellform_rating.png����\�&y'�\�&y'����}��������������Ʒ^l��ƫ1�;�R�+design/screenshots/tellform_screenshot1.png�������\�&y'�\�&y'����}�������������j=�W����+�V�[oYԭ�design/tellform_mascot.png��������\�&y'�\�&y'����}����������������4����Oz�� ��*�@a�dev_entrypoint.sh�\�&y'�\�&y'����}�������������� ��koV�'���b�g��D��dns_masq_setup_osx.md�����\�&y'�\�&y'����}������������� �}�+ � + ��M%(�K��"docs/readme_logos/digitalOcean.png��������\�&y'�\�&y'����}������������� + xIǼWb�l0��� O鑿ͻe�docs/readme_logos/sentryIO.png����\�&y'�\�&y'����}���������������((P-�uf�1Pz8���Hإ�docs/readme_logos/sparkPost.png���\�&y'�\�&y'����}��������������;�lL �9$c�H@�uh�"docs/readme_logos/statusPageIO.png��������\�&y'�\�&y'����}�������������c�5N6��$I�5$f�W + ��/�!docs/readme_logos/stickerMule.png�\�&y'�\�&y'����}��������������n���4�L3��_�����n/�#docs/readme_logos/theRoostStand.png�������\�&y'�\�&y'����}�������������oo�/� +@@ -135,4 +135,4 @@ + (c�����:5���/ �q�edirectives�3 0 + ilnr�!�O�I�f�g�1scripts�5 1 + �B6ŷ�֬(h����g�#��build�1 0 +-He�5���C�]<������/�$����u?*��E��z0��� +\ Pas de fin de ligne à la fin du fichier ++He�5���C�]<������ͩ�����R>��kv���� +\ Pas de fin de ligne à la fin du fichier +diff -Naur tellform/public/form_modules/forms/base/config/i18n/english.js tellform-patch/public/form_modules/forms/base/config/i18n/english.js +--- tellform/public/form_modules/forms/base/config/i18n/english.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/form_modules/forms/base/config/i18n/english.js 2019-06-04 09:32:16.433539612 +0200 +@@ -42,8 +42,4 @@ + FORM_UNAUTHORIZED_BODY2: 'If you are the owner of the form, you can set it to "Public" in the "Configuration" panel in the form admin.', + }); + +- $translateProvider.preferredLanguage('en') +- .fallbackLanguage('en') +- .useSanitizeValueStrategy('escape'); +- + }]); +diff -Naur tellform/public/form_modules/forms/base/config/i18n/french.js tellform-patch/public/form_modules/forms/base/config/i18n/french.js +--- tellform/public/form_modules/forms/base/config/i18n/french.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/form_modules/forms/base/config/i18n/french.js 2019-06-04 09:33:50.377608073 +0200 +@@ -42,4 +42,8 @@ + FORM_UNAUTHORIZED_BODY2: 'Si vous êtes le propriétaire du formulaire, vous pouvez le définir en "Public" dans le panneau "Configuration" du formulaire admin.', + }); + ++ $translateProvider.preferredLanguage('fr') ++ .fallbackLanguage('fr') ++ .useSanitizeValueStrategy('escape'); ++ + }]); +diff -Naur tellform/public/form_modules/forms/base/directives/on-finish-render.client.directive.js tellform-patch/public/form_modules/forms/base/directives/on-finish-render.client.directive.js +--- tellform/public/form_modules/forms/base/directives/on-finish-render.client.directive.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/form_modules/forms/base/directives/on-finish-render.client.directive.js 2019-06-03 19:38:25.347772458 +0200 +@@ -4,7 +4,7 @@ + return { + restrict: 'A', + link: function (scope, element, attrs) { +- ++ + //Don't do anything if we don't have a ng-repeat on the current element + if(!element.attr('ng-repeat') && !element.attr('data-ng-repeat')){ + return; +@@ -14,11 +14,11 @@ + + if(scope.$first && !scope.$last) { + scope.$evalAsync(function () { +- $rootScope.$broadcast(broadcastMessage+' Started'); ++ $rootScope.$broadcast(broadcastMessage+' commencé'); + }); + }else if(scope.$last) { + scope.$evalAsync(function () { +- $rootScope.$broadcast(broadcastMessage+' Finished'); ++ $rootScope.$broadcast(broadcastMessage+' terminé'); + }); + } + } +diff -Naur tellform/public/form_modules/forms/base/directives/submit-form.client.directive.js tellform-patch/public/form_modules/forms/base/directives/submit-form.client.directive.js +--- tellform/public/form_modules/forms/base/directives/submit-form.client.directive.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/form_modules/forms/base/directives/submit-form.client.directive.js 2019-06-04 00:12:41.142998121 +0200 +@@ -1,13 +1,5 @@ + 'use strict'; + +-//FIXME: Should find an appropriate place for this +-//Setting up jsep +-jsep.addBinaryOp('contains', 10); +-jsep.addBinaryOp('!contains', 10); +-jsep.addBinaryOp('begins', 10); +-jsep.addBinaryOp('!begins', 10); +-jsep.addBinaryOp('ends', 10); +-jsep.addBinaryOp('!ends', 10); + + angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCounter', '$filter', '$rootScope', 'SendVisitorData', '$translate', '$timeout', + function ($http, TimeCounter, $filter, $rootScope, SendVisitorData, $translate, $timeout) { +@@ -22,7 +14,7 @@ + var NOSCROLL = false; + var FORM_ACTION_ID = 'submit_field'; + $scope.forms = {}; +- ++ + //Don't start timer if we are looking at a design preview + if($scope.ispreview){ + TimeCounter.restartClock(); +@@ -125,8 +117,8 @@ + + var getActiveField = function(){ + if($scope.selected === null){ +- console.error('current active field is null'); +- throw new Error('current active field is null'); ++ console.error('Le champ courant est null'); ++ throw new Error('Le champ courant est null'); + } + + if($scope.selected._id === FORM_ACTION_ID) { +@@ -146,7 +138,7 @@ + if($scope.selected === null || (!field_id && field_index === null) ) { + return; + } +- ++ + if(!field_id){ + field_id = $scope.myform.visible_form_fields[field_index]._id; + } else if(field_index === null){ +@@ -243,12 +235,12 @@ + field_index = $scope.selected.index+1; + $scope.setActiveField(FORM_ACTION_ID, field_index, false); + } +- ++ + //If we scrolled bellow the current field, move to next field + else if(fieldBottom < elemHeight * fractionToJump && $scope.selected.index < $scope.myform.visible_form_fields.length-1 ){ + field_index = $scope.selected.index+1; + $scope.setActiveField(null, field_index, false); +- } ++ } + //If we scrolled above the current field, move to prev field + else if ( $scope.selected.index !== 0 && fieldTop > elemHeight * fractionToJump) { + field_index = $scope.selected.index-1; +@@ -264,7 +256,7 @@ + + if($scope.selected._id !== FORM_ACTION_ID){ + var currField = $scope.myform.visible_form_fields[$scope.selected.index]; +- ++ + //Jump to logicJump's destination if it is true + if(currField.logicJump && currField.logicJump.jumpTo && evaluateLogicJump(currField)){ + $scope.setActiveField(currField.logicJump.jumpTo, null, true); +@@ -281,7 +273,7 @@ + //If selected is not defined go to the first field + $rootScope.setActiveField(null, 0, true); + } +- ++ + }; + + $rootScope.prevField = $scope.prevField = function(){ +@@ -380,7 +372,7 @@ + if($scope.myform.form_fields[i].fieldType === 'dropdown' && !$scope.myform.form_fields[i].deletePreserved){ + $scope.myform.form_fields[i].fieldValue = $scope.myform.form_fields[i].fieldValue.option_value; + } +- ++ + //Get rid of unnessecary attributes for each form field + delete form.form_fields[i].submissionId; + delete form.form_fields[i].disabled; +@@ -389,8 +381,8 @@ + delete form.form_fields[i].logicJump; + delete form.form_fields[i].description; + delete form.form_fields[i].validFieldTypes; +- delete form.form_fields[i].fieldType; +- ++ delete form.form_fields[i].fieldType; ++ + } + + setTimeout(function () { +diff -Naur tellform/public/form_modules/forms/base/services/time-counter.client.service.js tellform-patch/public/form_modules/forms/base/services/time-counter.client.service.js +--- tellform/public/form_modules/forms/base/services/time-counter.client.service.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/form_modules/forms/base/services/time-counter.client.service.js 2019-06-03 19:39:52.009289726 +0200 +@@ -25,7 +25,7 @@ + + return this.timeSpent; + } +- return new Error('Clock has not been started'); ++ return new Error('L\'horloge n\'as pas démarrée'); + }; + + this.clockStarted = function(){ +diff -Naur tellform/public/form_modules/forms/base/views/submit-form.client.view.html tellform-patch/public/form_modules/forms/base/views/submit-form.client.view.html +--- tellform/public/form_modules/forms/base/views/submit-form.client.view.html 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/form_modules/forms/base/views/submit-form.client.view.html 2019-06-03 20:19:26.070189906 +0200 +@@ -1,12 +1,3 @@ + <section class="public-form"> + <submit-form-directive myform="myform"></submit-form-directive> + </section> +- +-<!-- User's Google Analytics --> +-<script ng-if="myform.analytics.gaCode"> +- window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date; +- ga('create', '{{myform.analytics.gaCode}}', 'auto'); ga('send', 'pageview'); +-</script> +-<script ng-if="myform.analytics.gaCode" src="https://www.google-analytics.com/analytics.js" async defer></script> +- +-<!-- End Google Analytics --> +diff -Naur tellform/public/modules/core/config/i18n/english.js tellform-patch/public/modules/core/config/i18n/english.js +--- tellform/public/modules/core/config/i18n/english.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/modules/core/config/i18n/english.js 2019-06-04 09:33:06.745577915 +0200 +@@ -13,8 +13,4 @@ + TOGGLE_NAVIGATION: 'Toggle navigation' + }); + +- $translateProvider.preferredLanguage('en') +- .fallbackLanguage('en') +- .useSanitizeValueStrategy('escape'); +- + }]); +diff -Naur tellform/public/modules/core/config/i18n/french.js tellform-patch/public/modules/core/config/i18n/french.js +--- tellform/public/modules/core/config/i18n/french.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/modules/core/config/i18n/french.js 2019-06-04 09:33:02.525574857 +0200 +@@ -12,4 +12,8 @@ + CHANGE_PASSWORD: 'Changer mon mot de passe', + TOGGLE_NAVIGATION: 'Basculer la navigation', + }); ++ ++ $translateProvider.preferredLanguage('fr') ++ .fallbackLanguage('fr') ++ .useSanitizeValueStrategy('escape'); + }]); +diff -Naur tellform/public/modules/core/controllers/header.client.controller.js tellform-patch/public/modules/core/controllers/header.client.controller.js +--- tellform/public/modules/core/controllers/header.client.controller.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/modules/core/controllers/header.client.controller.js 2019-06-03 19:42:37.122929848 +0200 +@@ -31,7 +31,7 @@ + $state.reload(); + }, + function(reason) { +- console.error('Logout Failed: ' + reason); ++ console.error('Deconnexion échouée : ' + reason); + }); + }; + +diff -Naur tellform/public/modules/core/services/menus.client.service.js tellform-patch/public/modules/core/services/menus.client.service.js +--- tellform/public/modules/core/services/menus.client.service.js 2019-06-03 15:54:01.662435238 +0200 ++++ tellform-patch/public/modules/core/services/menus.client.service.js 2019-06-03 19:43:15.450441553 +0200 +@@ -15,7 +15,7 @@ + if (user) { + if (~this.roles.indexOf('*')) { + return true; +- } ++ } + for (var userRoleIndex in user.roles) { + for (var roleIndex in this.roles) { + if (this.roles[roleIndex] === user.roles[userRoleIndex]) { +@@ -25,7 +25,7 @@ + } + return false; + +- } ++ } + return this.isPublic; + }; + +@@ -35,10 +35,10 @@ + if (this.menus[menuId]) { + return true; + } else { +- throw new Error('Menu does not exists'); ++ throw new Error('Menu n\'existe pas '); + } + } else { +- throw new Error('MenuId was not provided'); ++ throw new Error('MenuId n\'est pas fourni'); + } + }; + +diff -Naur tellform/public/modules/forms/admin/controllers/admin-form.client.controller.js tellform-patch/public/modules/forms/admin/controllers/admin-form.client.controller.js +--- tellform/public/modules/forms/admin/controllers/admin-form.client.controller.js 2019-06-03 15:54:01.666435244 +0200 ++++ tellform-patch/public/modules/forms/admin/controllers/admin-form.client.controller.js 2019-06-03 19:44:25.647310706 +0200 +@@ -102,7 +102,7 @@ + $scope.deleteModal.close(); + + var form_id = $scope.myform._id; +- if(!form_id) throw new Error('Error - removeCurrentForm(): $scope.myform._id does not exist'); ++ if(!form_id) throw new Error('Erreur - removeCurrentForm(): $scope.myform._id n\'existe pas'); + + $http.delete('/forms/'+form_id) + .then(function(response){ +@@ -214,4 +214,4 @@ + + + } +-]); +\ Pas de fin de ligne à la fin du fichier ++]); +diff -Naur tellform/public/modules/forms/admin/controllers/list-forms.client.controller.js tellform-patch/public/modules/forms/admin/controllers/list-forms.client.controller.js +--- tellform/public/modules/forms/admin/controllers/list-forms.client.controller.js 2019-06-03 15:54:01.666435244 +0200 ++++ tellform-patch/public/modules/forms/admin/controllers/list-forms.client.controller.js 2019-06-03 19:44:51.522571004 +0200 +@@ -105,7 +105,7 @@ + + $scope.removeForm = function(form_index) { + if(form_index >= $scope.myforms.length || form_index < 0){ +- throw new Error('Error: form_index in removeForm() must be between 0 and '+$scope.myforms.length-1); ++ throw new Error('Erreur: form_index dans removeForm() doit être entre 0 et '+$scope.myforms.length-1); + } + + $http.delete('/forms/'+$scope.myforms[form_index]._id) +diff -Naur tellform/public/modules/users/config/i18n/english.js tellform-patch/public/modules/users/config/i18n/english.js +--- tellform/public/modules/users/config/i18n/english.js 2019-06-03 15:54:01.666435244 +0200 ++++ tellform-patch/public/modules/users/config/i18n/english.js 2019-06-03 21:51:06.101488598 +0200 +@@ -68,8 +68,4 @@ + ERROR: 'Error' + }); + +- $translateProvider.preferredLanguage('en') +- .fallbackLanguage('en') +- .useSanitizeValueStrategy('escape'); +- + }]); +diff -Naur tellform/public/modules/users/config/i18n/french.js tellform-patch/public/modules/users/config/i18n/french.js +--- tellform/public/modules/users/config/i18n/french.js 2019-06-03 15:54:01.666435244 +0200 ++++ tellform-patch/public/modules/users/config/i18n/french.js 2019-06-03 21:47:10.149216152 +0200 +@@ -49,4 +49,8 @@ + ERROR: 'Erreur' + }); + ++ $translateProvider.preferredLanguage('fr') ++ .fallbackLanguage('fr') ++ .useSanitizeValueStrategy('escape'); ++ + }]); +diff -Naur tellform/public/modules/users/controllers/authentication.client.controller.js tellform-patch/public/modules/users/controllers/authentication.client.controller.js +--- tellform/public/modules/users/controllers/authentication.client.controller.js 2019-06-03 15:54:01.666435244 +0200 ++++ tellform-patch/public/modules/users/controllers/authentication.client.controller.js 2019-06-03 19:47:40.038970866 +0200 +@@ -2,7 +2,7 @@ + + angular.module('users').controller('AuthenticationController', ['$scope', '$location', '$state', '$rootScope', 'User', 'Auth', '$translate', '$window', + function($scope, $location, $state, $rootScope, User, Auth, $translate, $window) { +- ++ + $scope = $rootScope; + $scope.credentials = {}; + $scope.error = ''; +@@ -36,7 +36,7 @@ + + $scope.signup = function() { + if($scope.credentials === 'admin'){ +- $scope.error = 'Username cannot be \'admin\'. Please pick another username.'; ++ $scope.error = 'L\'identifiant ne peut être \'admin\'. Merci d\'en choisir un autre.'; + return; + } + +@@ -51,7 +51,7 @@ + $scope.error = error; + console.error(error); + } else { +- console.error('No response received'); ++ console.error('Aucune réponse reçue'); + } + } + ); +diff -Naur tellform/public/modules/users/services/authorizer.client.service.js tellform-patch/public/modules/users/services/authorizer.client.service.js +--- tellform/public/modules/users/services/authorizer.client.service.js 2019-06-03 15:54:01.666435244 +0200 ++++ tellform-patch/public/modules/users/services/authorizer.client.service.js 2019-06-03 19:48:12.756023362 +0200 +@@ -11,7 +11,7 @@ + for (i = 0, len = permissions.length; i < len; i++) { + permission = permissions[i]; + if (APP_PERMISSIONS[permission] === null) { +- throw 'Bad permission value'; ++ throw 'Mauvaise valeur de permission'; + } + if (user && user.roles) { + switch (permission) { +@@ -31,4 +31,4 @@ + } + }; + }; +-}); +\ Pas de fin de ligne à la fin du fichier ++}); +diff -Naur tellform/public/modules/users/services/user.client.service.js tellform-patch/public/modules/users/services/user.client.service.js +--- tellform/public/modules/users/services/user.client.service.js 2019-06-03 15:54:01.666435244 +0200 ++++ tellform-patch/public/modules/users/services/user.client.service.js 2019-06-03 19:48:44.180484997 +0200 +@@ -12,7 +12,7 @@ + deferred.resolve(response); + }) + .error(function() { +- deferred.reject('User\'s session has expired'); ++ deferred.reject('La session utilisateur a expirée'); + }); + + return deferred.promise; +@@ -69,7 +69,7 @@ + //DAVID: TODO: The valid length of a token should somehow be linked to server config values + //DAVID: TODO: SEMI-URGENT: Should we even be doing this? + var validTokenRe = /^([A-Za-z0-9]{48})$/g; +- if( !validTokenRe.test(token) ) throw new Error('Error token: '+token+' is not a valid verification token'); ++ if( !validTokenRe.test(token) ) throw new Error('Erreur: '+token+' est un jeton invalide'); + + var deferred = $q.defer(); + $http.get('/auth/verify/'+token).then(function(response) { +diff -Naur tellform/scripts/create_admin.js tellform-patch/scripts/create_admin.js +--- tellform/scripts/create_admin.js 2019-06-03 15:54:01.702435295 +0200 ++++ tellform-patch/scripts/create_admin.js 2019-06-03 19:51:04.054570055 +0200 +@@ -6,7 +6,7 @@ + + var User = mongoose.model('User'); + var email = 'admin@admin.com' || config.admin.email; +- ++ + var newUser = new User({ + firstName: 'Admin', + lastName: 'Account', +@@ -27,12 +27,12 @@ + if (userErr) { + return cb(userErr); + } +- console.log(chalk.green('Successfully created Admin Account')); ++ console.log(chalk.green('Compte administrateur créé')); + + cb(); + }); + } else { +- cb('User already exists!'); ++ cb('L\'utilisateur existe déjà!'); + } + }); + } +diff -Naur tellform/scripts/git-remove-history.sh tellform-patch/scripts/git-remove-history.sh +--- tellform/scripts/git-remove-history.sh 2019-06-03 15:54:01.702435295 +0200 ++++ tellform-patch/scripts/git-remove-history.sh 2019-06-03 19:51:35.017628137 +0200 +@@ -1,24 +1,24 @@ + #!/bin/bash + set -o errexit +- ++ + # Author: David Underhill +-# Script to permanently delete files/folders from your git repository. To use ++# Script to permanently delete files/folders from your git repository. To use + # it, cd to your repository's root and then run the script with a list of paths + # you want to delete, e.g., git-delete-history path1 path2 +- ++ + if [ $# -eq 0 ]; then + exit 0 + fi +- ++ + # make sure we're at the root of git repo + if [ ! -d .git ]; then +- echo "Error: must run this script from the root of a git repository" ++ echo "Erreur: l\éxécution doit se faire depuis la racine du dossier git" + exit 1 + fi +- ++ + # remove all paths passed as arguments from the history of the repo + files=$@ + git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD +- ++ + # remove the temporary history git-filter-branch otherwise leaves behind for a long time + rm -rf .git/refs/original/ && git reflog expire --all && git gc --aggressive --prune +diff -Naur tellform/scripts/setup.js tellform-patch/scripts/setup.js +--- tellform/scripts/setup.js 2019-06-03 15:54:01.702435295 +0200 ++++ tellform-patch/scripts/setup.js 2019-06-03 19:52:24.523909961 +0200 +@@ -15,12 +15,12 @@ + // Bootstrap db connection + var db = mongoose.connect(config.db.uri, config.db.options, function(err) { + if (err) { +- console.error(chalk.red('Could not connect to MongoDB!')); ++ console.error(chalk.red('Impossible de se connecter à MongoDB!')); + console.log(chalk.red(err)); + } + }); + mongoose.connection.on('error', function(err) { +- console.error(chalk.red('MongoDB connection error: ' + err)); ++ console.error(chalk.red('Erreur de connexion MongoDB : ' + err)); + process.exit(-1); + }); + diff --git a/pica-wekan-scripts/Dockerfile b/pica-wekan-scripts/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..9101d6adfd1e24e6e8fc69ff6ab2fc367b4edf99 --- /dev/null +++ b/pica-wekan-scripts/Dockerfile @@ -0,0 +1,13 @@ +from alpine + +ENV DRYRUN=1 \ + url="https://wekan.test.picasoft.net" \ + CRON_TIME="0 0 1 * *" + +COPY *.sh ./ + +RUN apk add --no-cache --update curl jq + +ENTRYPOINT /bin/sh ./run.sh + + diff --git a/pica-wekan-scripts/README.md b/pica-wekan-scripts/README.md new file mode 100644 index 0000000000000000000000000000000000000000..73d3a16294691bef2548932282ee209cc1cbe45b --- /dev/null +++ b/pica-wekan-scripts/README.md @@ -0,0 +1,43 @@ +# Localement : script de suppression des boards Wekan trop anciens +## Introduction + +Ce README est obsolète mais peut-être testé en local, en dehors du serveur de picasoft, à condition de connaître les identifiants du compte Wekan dédié. + +## Avant le lancement +Le paquet jq permet de parser et manipuler du JSON en ligne de commande. Il est nécessaire pour filtrer les informations reçu de l'API. + +```bash +sudo apt install jq +``` + +Le Wekan doit contenir un compte dédié pour ce script permettant d'accéder à l'API. Ce compte doit disposer des droits administrateurs du Wekan. Les identifiants doivent être présents dans un fichier séparer du script `policy.sh`. + +```bash +touch ids.sh +``` + +## Contenu de `ids.sh` +2 variables sont nécessaires : username et password du compte dédié. +```bash +#!/bin/sh + +export username="username" +export password="password" +``` + +## Choix de la deadline et de l'URL de l'API +Les boards n'ayant pas été modifiés depuis un certain temps sont définitivement supprimés. Cette durée est la somme de trois variables : `boardExpirationDurationYears`, `boardExpirationDurationMonths` et `boardExpirationDurationDays`. L'URL de l'application est également nécessaire. + Ces informations sont à modifier directement dans les premières lignes du script `policy.sh`. + +```bash +boardExpirationDurationYears=0 # années +boardExpirationDurationMonths=12 # mois +boardExpirationDurationDays=0 # jours +url=https://wekan.test.picasoft.net +``` + + +## Lancement du script +```bash +./policy.sh +``` diff --git a/pica-wekan-scripts/docker-compose.yml b/pica-wekan-scripts/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..1af1c3070f38624719edd72e2926ae4afd50b858 --- /dev/null +++ b/pica-wekan-scripts/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3' + +services: + wekan-cleaner: + image: registry.picasoft.net/pica-wekan-cleaner + environment: + - DRYRUN=1 + container_name: wekan_clean diff --git a/pica-wekan-scripts/ids_example.sh b/pica-wekan-scripts/ids_example.sh new file mode 100644 index 0000000000000000000000000000000000000000..5fa2ccab1b3f8fe04ea8eef98014313881bff1dc --- /dev/null +++ b/pica-wekan-scripts/ids_example.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +username="username" +password="password" diff --git a/pica-wekan-scripts/policy.sh b/pica-wekan-scripts/policy.sh new file mode 100755 index 0000000000000000000000000000000000000000..f8415fb1e521df91d2bd6998d36ba2e759e05b4a --- /dev/null +++ b/pica-wekan-scripts/policy.sh @@ -0,0 +1,89 @@ +#!/bin/sh + + +. ./ids.sh # contient $username et $password pour authentification + +# Paramètres +boardExpirationDurationYears=${boardExpirationDurationYears-0} # années +boardExpirationDurationMonths=${boardExpirationDurationMonths-12} # mois +boardExpirationDurationDays=${boardExpirationDurationDays-0} # jours +url=${url-"https://wekan.test.picasoft.net"} +# Deadline = somme des durées du dessus + +boardExpirationDurationDays=$(($boardExpirationDurationDays+30*$boardExpirationDurationMonths)) +boardExpirationDurationDays=$(($boardExpirationDurationDays+365*$boardExpirationDurationYears)) +deadLineSeconds=$(($boardExpirationDurationDays*86400)) +now=$(date +%s) +deadlineTimeStamp=$(($now-$deadLineSeconds)) + +delete=false +while getopts "d" option +do + delete=true +done +boardsToDelete="" +totalBoardsToDelete=0 + +# Récupération du token de connexion +queryPica=$(curl -s $url/users/login -d 'username='"$username"'&password='"$password"'') +token=$(echo $queryPica | jq -r .token) + +# Récupération des utilisateurs +users=$(curl -s -X GET $url/api/users \ + -H 'Accept: application/json' \ + -H 'Authorization: Bearer '"$token"'') +usersIds=$(echo $users | jq -r '.[]._id') +usersUsernames=$(echo $users | jq -r '.[].username') + +# Boucle sur les utilisateurs +for usersId in $usersIds +do + # Récupération des boards de chaque utilisateur + userBoards=$(curl -s -X GET $url/api/users/$usersId/boards \ + -H 'Accept: application/json' \ + -H 'Authorization: Bearer '"$token"'' | jq -r '.[]._id') + + # Boucle sur chaque board de l'utilisateur + for userBoard in $userBoards + do + # Récupération de la date de dernière modification et du titre du board concerné + board=$(curl -s -X GET $url/api/boards/$userBoard \ + -H 'Accept: application/json' \ + -H 'Authorization: Bearer '"$token"'') + boardTitle=$(echo $board | jq -r '.title') + lastModificationDate=$(echo $board | jq -r '.modifiedAt' | sed 's/T.*Z//g') + # Si le board n'a jamais été modifié, on considère alors sa date de création + if [ $lastModificationDate = null ] + then + lastModificationDate=$(echo $board | jq -r '.createdAt' | sed 's/T.*Z//g') + fi + + if [ $(($deadlineTimeStamp-$(date -d $lastModificationDate +%s))) -gt 0 ] + then + if [ $delete = true ] + then + echo "Suppression du board "$boardTitle" (_id "$userBoard", modifié le "$lastModificationDate")" + + # Suppression du board si trop ancien + curl -X DELETE https://wekan.test.picasoft.net/api/boards/$userBoard \ + -H 'Authorization: Bearer '"$token"'' + totalBoardsToDelete=$(($totalBoardsToDelete+1)) + elif [ $(echo $boardsToDelete | grep -c $userBoard) -eq 0 ] + then + echo "Board "$boardTitle" trop ancien (modifié le "$lastModificationDate")" + boardsToDelete=$boardsToDelete" "$userBoard + totalBoardsToDelete=$(($totalBoardsToDelete+1)) + fi + fi + done +done + +if [ $delete = true ] +then + echo "Total de boards supprimés : "$totalBoardsToDelete +else + echo "Total de boards à supprimer : "$totalBoardsToDelete +fi + + +exit 1 diff --git a/pica-wekan-scripts/run.sh b/pica-wekan-scripts/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..64c53e15d66e6f851a00bb6db1411bc3b561a786 --- /dev/null +++ b/pica-wekan-scripts/run.sh @@ -0,0 +1,9 @@ +#!/bin/bash + + +if [ $DRYRUN -eq 0 ] +then + ./policy.sh -d +else + ./policy.sh +fi diff --git a/pica-wekan/Dockerfile b/pica-wekan/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b0d69e341ddbddc1639070394237727caa53238c --- /dev/null +++ b/pica-wekan/Dockerfile @@ -0,0 +1,261 @@ +FROM ubuntu:disco +LABEL maintainer="wekan" + +# Set the environment variables (defaults where required) +# DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303 +# ENV BUILD_DEPS="paxctl" +ENV BUILD_DEPS="apt-utils bsdtar gnupg gosu wget curl bzip2 build-essential python3 python3-pip git ca-certificates gcc-8" \ + DEBUG=false \ + NODE_VERSION=v8.16.0 \ + METEOR_RELEASE=1.6.0.1 \ + USE_EDGE=false \ + METEOR_EDGE=1.5-beta.17 \ + NPM_VERSION=latest \ + FIBERS_VERSION=2.0.0 \ + ARCHITECTURE=linux-x64 \ + SRC_PATH=./ \ + WITH_API=true \ + ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=3 \ + ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 \ + ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 \ + ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURES_BERORE=3 \ + ACCOUNTS_LOCKOUT_UNKNOWN_USERS_LOCKOUT_PERIOD=60 \ + ACCOUNTS_LOCKOUT_UNKNOWN_USERS_FAILURE_WINDOW=15 \ + EMAIL_NOTIFICATION_TIMEOUT=30000 \ + MATOMO_ADDRESS="" \ + MATOMO_SITE_ID="" \ + MATOMO_DO_NOT_TRACK=true \ + MATOMO_WITH_USERNAME=false \ + BROWSER_POLICY_ENABLED=true \ + TRUSTED_URL="" \ + WEBHOOKS_ATTRIBUTES="" \ + OAUTH2_ENABLED=false \ + OAUTH2_LOGIN_STYLE=redirect \ + OAUTH2_CLIENT_ID="" \ + OAUTH2_SECRET="" \ + OAUTH2_SERVER_URL="" \ + OAUTH2_AUTH_ENDPOINT="" \ + OAUTH2_USERINFO_ENDPOINT="" \ + OAUTH2_TOKEN_ENDPOINT="" \ + OAUTH2_ID_MAP="" \ + OAUTH2_USERNAME_MAP="" \ + OAUTH2_FULLNAME_MAP="" \ + OAUTH2_ID_TOKEN_WHITELIST_FIELDS=[] \ + OAUTH2_REQUEST_PERMISSIONS=['openid','profiles','email'] \ + OAUTH2_EMAIL_MAP="" \ + LDAP_ENABLE=false \ + LDAP_PORT=389 \ + LDAP_HOST="" \ + LDAP_BASEDN="" \ + LDAP_LOGIN_FALLBACK=false \ + LDAP_RECONNECT=true \ + LDAP_TIMEOUT=10000 \ + LDAP_IDLE_TIMEOUT=10000 \ + LDAP_CONNECT_TIMEOUT=10000 \ + LDAP_AUTHENTIFICATION=false \ + LDAP_AUTHENTIFICATION_USERDN="" \ + LDAP_AUTHENTIFICATION_PASSWORD="" \ + LDAP_LOG_ENABLED=false \ + LDAP_BACKGROUND_SYNC=false \ + LDAP_BACKGROUND_SYNC_INTERVAL=100 \ + LDAP_BACKGROUND_SYNC_KEEP_EXISTANT_USERS_UPDATED=false \ + LDAP_BACKGROUND_SYNC_IMPORT_NEW_USERS=false \ + LDAP_ENCRYPTION=false \ + LDAP_CA_CERT="" \ + LDAP_REJECT_UNAUTHORIZED=false \ + LDAP_USER_AUTHENTICATION=false \ + LDAP_USER_SEARCH_FILTER="" \ + LDAP_USER_SEARCH_SCOPE="" \ + LDAP_USER_SEARCH_FIELD="" \ + LDAP_SEARCH_PAGE_SIZE=0 \ + LDAP_SEARCH_SIZE_LIMIT=0 \ + LDAP_GROUP_FILTER_ENABLE=false \ + LDAP_GROUP_FILTER_OBJECTCLASS="" \ + LDAP_GROUP_FILTER_GROUP_ID_ATTRIBUTE="" \ + LDAP_GROUP_FILTER_GROUP_MEMBER_ATTRIBUTE="" \ + LDAP_GROUP_FILTER_GROUP_MEMBER_FORMAT="" \ + LDAP_GROUP_FILTER_GROUP_NAME="" \ + LDAP_UNIQUE_IDENTIFIER_FIELD="" \ + LDAP_UTF8_NAMES_SLUGIFY=true \ + LDAP_USERNAME_FIELD="" \ + LDAP_FULLNAME_FIELD="" \ + LDAP_MERGE_EXISTING_USERS=false \ + LDAP_EMAIL_FIELD="" \ + LDAP_EMAIL_MATCH_ENABLE=false \ + LDAP_EMAIL_MATCH_REQUIRE=false \ + LDAP_EMAIL_MATCH_VERIFIED=false \ + LDAP_SYNC_USER_DATA=false \ + LDAP_SYNC_USER_DATA_FIELDMAP="" \ + LDAP_SYNC_GROUP_ROLES="" \ + LDAP_DEFAULT_DOMAIN="" \ + LDAP_SYNC_ADMIN_STATUS="" \ + LDAP_SYNC_ADMIN_GROUPS="" \ + HEADER_LOGIN_ID="" \ + HEADER_LOGIN_FIRSTNAME="" \ + HEADER_LOGIN_LASTNAME="" \ + HEADER_LOGIN_EMAIL="" \ + LOGOUT_WITH_TIMER=false \ + LOGOUT_IN="" \ + LOGOUT_ON_HOURS="" \ + LOGOUT_ON_MINUTES="" \ + CORS="" \ + DEFAULT_AUTHENTICATION_METHOD="" + +RUN set -o xtrace && \ + apt-get update -y && apt-get install -y --no-install-recommends ${BUILD_DEPS} && \ + pip3 install -U pip setuptools wheel + +RUN git clone https://github.com/wekan/wekan && cd wekan && git checkout v2.75 && cd .. && mkdir /home/wekan && cp -r ./wekan /home/wekan/app && \ + useradd --user-group --system --home-dir /home/wekan wekan + + # Meteor installer doesn't work with the default tar binary, so using bsdtar while installing. + # https://github.com/coreos/bugs/issues/1095#issuecomment-350574389 +RUN cp $(which tar) $(which tar)~ && \ + ln -sf $(which bsdtar) $(which tar) && \ + \ + # Download nodejs + wget https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \ + wget https://nodejs.org/dist/${NODE_VERSION}/SHASUMS256.txt.asc && \ + #--------------------------------------------------------------------------------------------- + # Node Fibers 100% CPU usage issue: + # https://github.com/wekan/wekan-mongodb/issues/2#issuecomment-381453161 + # https://github.com/meteor/meteor/issues/9796#issuecomment-381676326 + # https://github.com/sandstorm-io/sandstorm/blob/0f1fec013fe7208ed0fd97eb88b31b77e3c61f42/shell/server/00-startup.js#L99-L129 + # Also see beginning of wekan/server/authentication.js + # import Fiber from "fibers"; + # Fiber.poolSize = 1e9; + # OLD: Download node version 8.12.0 prerelease that has fix included, => Official 8.12.0 has been released + # Description at https://releases.wekan.team/node.txt + #wget https://releases.wekan.team/node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \ + #echo "1ed54adb8497ad8967075a0b5d03dd5d0a502be43d4a4d84e5af489c613d7795 node-v8.12.0-linux-x64.tar.gz" >> SHASUMS256.txt.asc && \ + \ + # Verify nodejs authenticity + grep ${NODE_VERSION}-${ARCHITECTURE}.tar.gz SHASUMS256.txt.asc | shasum -a 256 -c - && \ + #export GNUPGHOME="$(mktemp -d)" && \ + #\ + # Try other key servers if ha.pool.sks-keyservers.net is unreachable + # Code from https://github.com/chorrell/docker-node/commit/2b673e17547c34f17f24553db02beefbac98d23c + # gpg keys listed at https://github.com/nodejs/node#release-team + # and keys listed here from previous version of this Dockerfile + #for key in \ + #9554F04D7259F04124DE6B476D5A82AC7E37093B \ + #94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ + #FD3A5288F042B6850C66B31F09FE44734EB7990E \ + #71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ + #DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ + #C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ + #B9AE9905FFD7803F25714661B63B535A4C206CA9 \ + #; do \ + #gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" || \ + #gpg --keyserver pgp.mit.edu --recv-keys "$key" || \ + #gpg --keyserver keyserver.pgp.com --recv-keys "$key" ; \ + #done && \ + #gpg --verify SHASUMS256.txt.asc && \ + # Ignore socket files then delete files then delete directories + #find "$GNUPGHOME" -type f | xargs rm -f && \ + #find "$GNUPGHOME" -type d | xargs rm -fR && \ + rm -f SHASUMS256.txt.asc && \ + \ + # Install Node + tar xvzf node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \ + rm node-${NODE_VERSION}-${ARCHITECTURE}.tar.gz && \ + mv node-${NODE_VERSION}-${ARCHITECTURE} /opt/nodejs && \ + ln -s /opt/nodejs/bin/node /usr/bin/node && \ + ln -s /opt/nodejs/bin/npm /usr/bin/npm && \ + \ + #DOES NOT WORK: paxctl fix for alpine linux: https://github.com/wekan/wekan/issues/1303 + #paxctl -mC `which node` && \ + \ + # Install Node dependencies + npm install -g npm@${NPM_VERSION} && \ + npm install -g node-gyp && \ + npm install -g fibers@${FIBERS_VERSION} && \ + \ + # Change user to wekan and install meteor + cd /home/wekan/ && \ + chown wekan:wekan --recursive /home/wekan && \ + curl "https://install.meteor.com" -o /home/wekan/install_meteor.sh && \ + #curl "https://install.meteor.com/?release=${METEOR_RELEASE}" -o /home/wekan/install_meteor.sh && \ + # OLD: sed -i "s|RELEASE=.*|RELEASE=${METEOR_RELEASE}\"\"|g" ./install_meteor.sh && \ + # Install Meteor forcing its progress + sed -i 's/VERBOSITY="--silent"/VERBOSITY="--progress-bar"/' ./install_meteor.sh && \ + echo "Starting meteor ${METEOR_RELEASE} installation... \n" && \ + chown wekan:wekan /home/wekan/install_meteor.sh && \ + \ + # Check if opting for a release candidate instead of major release + if [ "$USE_EDGE" = false ]; then \ + gosu wekan:wekan sh /home/wekan/install_meteor.sh; \ + else \ + gosu wekan:wekan git clone --recursive --depth 1 -b release/METEOR@${METEOR_EDGE} git://github.com/meteor/meteor.git /home/wekan/.meteor; \ + fi; \ + \ + # Get additional packages + #mkdir -p /home/wekan/app/packages && \ + #chown wekan:wekan --recursive /home/wekan && \ + # REPOS BELOW ARE INCLUDED TO WEKAN REPO + #cd /home/wekan/app/packages && \ + #gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/flow-router.git kadira-flow-router && \ + #gosu wekan:wekan git clone --depth 1 -b master https://github.com/meteor-useraccounts/core.git meteor-useraccounts-core && \ + #gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-cas.git && \ + #gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-ldap.git && \ + #gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/wekan-scrollbar.git && \ + #gosu wekan:wekan git clone --depth 1 -b master https://github.com/wekan/meteor-accounts-oidc.git && \ + #gosu wekan:wekan git clone --depth 1 -b master --recurse-submodules https://github.com/wekan/markdown.git && \ + #gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_accounts-oidc wekan-accounts-oidc && \ + #gosu wekan:wekan mv meteor-accounts-oidc/packages/switch_oidc wekan-oidc && \ + #gosu wekan:wekan rm -rf meteor-accounts-oidc && \ + sed -i 's/api\.versionsFrom/\/\/api.versionsFrom/' /home/wekan/app/packages/meteor-useraccounts-core/package.js && \ + cd /home/wekan/.meteor && \ + gosu wekan:wekan /home/wekan/.meteor/meteor -- help; \ + \ + # extract the OpenAPI specification + npm install -g api2html@0.3.3 && \ + mkdir -p /home/wekan/python && \ + chown wekan:wekan --recursive /home/wekan/python && \ + cd /home/wekan/python && \ + gosu wekan:wekan git clone --depth 1 -b master https://github.com/Kronuz/esprima-python && \ + cd /home/wekan/python/esprima-python && \ + python3 setup.py install --record files.txt && \ + cd /home/wekan/app &&\ + gosu wekan:wekan mkdir -p ./public/api && \ + gosu wekan:wekan python3 ./openapi/generate_openapi.py --release $(git describe --tags --abbrev=0) > ./public/api/wekan.yml && \ + gosu wekan:wekan /opt/nodejs/bin/api2html -c ./public/logo-header.png -o ./public/api/wekan.html ./public/api/wekan.yml; \ + # Build app + cd /home/wekan/app && \ + gosu wekan:wekan /home/wekan/.meteor/meteor add standard-minifier-js && \ + gosu wekan:wekan /home/wekan/.meteor/meteor npm install && \ + gosu wekan:wekan /home/wekan/.meteor/meteor build --directory /home/wekan/app_build && \ + cp /home/wekan/app/fix-download-unicode/cfs_access-point.txt /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \ + rm /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/rajit_bootstrap3-datepicker/lib/bootstrap-datepicker/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs && \ + chown wekan:wekan /home/wekan/app_build/bundle/programs/server/packages/cfs_access-point.js && \ + #Removed binary version of bcrypt because of security vulnerability that is not fixed yet. + #https://github.com/wekan/wekan/commit/4b2010213907c61b0e0482ab55abb06f6a668eac + #https://github.com/wekan/wekan/commit/7eeabf14be3c63fae2226e561ef8a0c1390c8d3c + #cd /home/wekan/app_build/bundle/programs/server/npm/node_modules/meteor/npm-bcrypt && \ + #gosu wekan:wekan rm -rf node_modules/bcrypt && \ + #gosu wekan:wekan npm install bcrypt && \ + cd /home/wekan/app_build/bundle/programs/server/ && \ + gosu wekan:wekan npm install && \ + #gosu wekan:wekan npm install bcrypt && \ + mv /home/wekan/app_build/bundle /build && \ + \ + # Put back the original tar + mv $(which tar)~ $(which tar) && \ + \ + # Cleanup + apt-get remove --purge -y ${BUILD_DEPS} && \ + apt-get autoremove -y && \ + npm uninstall -g api2html &&\ + rm -R /var/lib/apt/lists/* && \ + rm -R /home/wekan/.meteor && \ + rm -R /home/wekan/app && \ + rm -R /home/wekan/app_build && \ + cat /home/wekan/python/esprima-python/files.txt | xargs rm -R && \ + rm -R /home/wekan/python && \ + rm /home/wekan/install_meteor.sh + +ENV PORT=8080 +EXPOSE $PORT +USER wekan + +CMD ["node", "/build/main.js"] diff --git a/pica-wekan/clair-whitelist.yml b/pica-wekan/clair-whitelist.yml new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/pica-wekan/docker-compose.yml b/pica-wekan/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..e6b4b149d7cb88a858b0fbf91976b516584e6bfc --- /dev/null +++ b/pica-wekan/docker-compose.yml @@ -0,0 +1,66 @@ +version: '2.4' + +networks: + docker_default: + external: true + name: "docker_default" + + + +services: + + wekan-app: + image: pica-wekan:2.75 + container_name: wekan-app + restart: always + links: + - wekan-db:wekan-db + networks: + - docker_default + labels: + - "traefik.frontend.rule=Host:wekan2.test.picasoft.net" + - "traefik.port=8080" + - "traefik.enable=true" + environment: + - MONGO_URL=mongodb://wekan-db:27017/wekan + - ROOT_URL=http://wekan2.test.picasoft.net # <=== using only at same laptop/desktop where Wekan is installed + - WITH_API=true + #--------------------------------------------------------------- + # + # LOGOUT_ON_MINUTES : The number of minutes + # example : LOGOUT_ON_MINUTES=55 + #- LOGOUT_ON_MINUTES= + #------------------------------------------------------------------- + security_opt: + - no-new-privileges + mem_limit: "4096m" + cpus: "0.60" + pids_limit: 1024 + depends_on: + - wekan-db + + wekan-db: + image: mongo:3.2.21 + #------------------------------------------------------------------------------------- + container_name: wekan-db + restart: always + command: mongod --smallfiles --oplogSize 128 + networks: + - docker_default + expose: + - 27017 + #volumes: + #- wekan-db:/data/db + #- wekan-db-dump:/dump + #volumes: + #- /DATA/docker/wekan-db:/data/db + security_opt: + - no-new-privileges + mem_limit: "2048m" + cpus: "0.20" + pids_limit: 1024 +volumes: + wekan-db: + driver: local + wekan-db-dump: + driver: local