This commit is contained in:
root 2020-07-17 22:12:11 +00:00
parent 76fd9fb811
commit fa6bc5f3f3
14 changed files with 224 additions and 47 deletions

View file

@ -14,6 +14,7 @@
"@types/multer-s3": "^2.7.7", "@types/multer-s3": "^2.7.7",
"@types/mustache": "^4.0.1", "@types/mustache": "^4.0.1",
"@types/nodemailer": "^6.4.0", "@types/nodemailer": "^6.4.0",
"@types/papaparse": "^5.0.4",
"@types/sanitize-html": "^1.23.3", "@types/sanitize-html": "^1.23.3",
"@types/twig": "^1.12.3", "@types/twig": "^1.12.3",
"aws-sdk": "^2.706.0", "aws-sdk": "^2.706.0",
@ -32,6 +33,7 @@
"mustache": "^4.0.1", "mustache": "^4.0.1",
"nanoid": "^3.1.10", "nanoid": "^3.1.10",
"nodemailer": "^6.4.10", "nodemailer": "^6.4.10",
"papaparse": "^5.2.0",
"sanitize-html": "^1.27.1", "sanitize-html": "^1.27.1",
"slugify": "^1.4.4", "slugify": "^1.4.4",
"ts-jest": "^26.1.2", "ts-jest": "^26.1.2",

View file

@ -14,15 +14,18 @@ export default class EmailService {
if (process.env.SMTP_HOST == undefined || process.env.SMTP_PORT == undefined) { if (process.env.SMTP_HOST == undefined || process.env.SMTP_PORT == undefined) {
throw new Error("Invalid SMTP config") throw new Error("Invalid SMTP config")
} }
const config: any = { let config: any = {
host: process.env.SMTP_HOST, host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT), port: parseInt(process.env.SMTP_PORT),
secure: process.env.SMTP_SECURE == 'true', secure: process.env.SMTP_SECURE == 'true'
auth: { }
if (process.env.SMTP_USERNAME !== undefined && process.env.SMTP_PASSWORD !== undefined) {
config.auth = {
user: process.env.SMTP_USERNAME, user: process.env.SMTP_USERNAME,
pass: process.env.SMTP_PASSWORD, pass: process.env.SMTP_PASSWORD,
} }
} }
console.log(config)
return nodemailer.createTransport(config) return nodemailer.createTransport(config)
} }
@ -54,8 +57,8 @@ export default class EmailService {
const text: string = htmlToText.fromString(html, { wordwrap: 130 }) const text: string = htmlToText.fromString(html, { wordwrap: 130 })
// for now replace every email by a predefined one // for now replace every email by a predefined one
console.info(to + ' replaced') // console.info(to + ' replaced')
to = "spamfree@matthieubessat.fr" // to = "spamfree@matthieubessat.fr"
subject += " - Forum des associations 2020" subject += " - Forum des associations 2020"
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -81,11 +84,11 @@ export default class EmailService {
} }
static getWebUiBaseUrl() { static getWebUiBaseUrl() {
return process.env.WEB_UI_URL === undefined ? "URL_NOT_FOUND" : process.env.WEB_UI_URL return process.env.WEB_UI_URL === undefined ? "WEB_UI_URL_NOT_FOUND" : process.env.WEB_UI_URL
} }
static getBaseUrl() { static getBaseUrl() {
return process.env.BASE_URL === undefined ? "URL_NOT_FOUND" : process.env.BASE_URL return process.env.BASE_URL === undefined ? "BASE_URL_NOT_FOUND" : process.env.BASE_URL
} }
static isMocked() { static isMocked() {
@ -93,4 +96,8 @@ export default class EmailService {
// CUSTOM DESACTIVATION PLEASE ENABLE IN PRODUCTION // CUSTOM DESACTIVATION PLEASE ENABLE IN PRODUCTION
return process.env.MOCK_EMAIL === "true" return process.env.MOCK_EMAIL === "true"
} }
static getAdminAddress() {
return process.env.ADMIN_ADDRESS === undefined ? "ADMIN_ADDRESS_NOT_FOUND" : process.env.ADMIN_ADDRESS
}
} }

View file

@ -72,6 +72,7 @@ let main = async () => {
.delete('/:id', AdminTagController.destroy) .delete('/:id', AdminTagController.destroy)
) )
.use('/organizations', express.Router() .use('/organizations', express.Router()
.post('/import', AdminOrganizationController.import)
.get('/', AdminOrganizationController.getMany) .get('/', AdminOrganizationController.getMany)
.get('/:id', AdminOrganizationController.getOne) .get('/:id', AdminOrganizationController.getOne)
.post('', AdminOrganizationController.store) .post('', AdminOrganizationController.store)

View file

@ -18,6 +18,14 @@ export default class AdminOrganizationController {
.catch(err => res.status(400).json({ success: false, errors: err })) .catch(err => res.status(400).json({ success: false, errors: err }))
} }
static import(req: express.Request, res: express.Response) {
const data = req.body.csv
res.json({
success: true,
data
})
}
static store(req: express.Request, res: express.Response) { static store(req: express.Request, res: express.Response) {
let body: any = { let body: any = {
token: AdminOrganizationController.generateToken(), token: AdminOrganizationController.generateToken(),
@ -51,8 +59,8 @@ export default class AdminOrganizationController {
extra.slug = slugify(req.body.name) extra.slug = slugify(req.body.name)
} }
Organization.updateOne({ _id: req.params.id }, { Organization.updateOne({ _id: req.params.id }, {
...req.body,
...extra, ...extra,
...req.body,
updatedAt: new Date() updatedAt: new Date()
}) })
.then(data => res.json({ success: true, data })) .then(data => res.json({ success: true, data }))
@ -179,7 +187,7 @@ export default class AdminOrganizationController {
}).then(updateData => { }).then(updateData => {
EmailService.send( EmailService.send(
data.get('email'), data.get('email'),
"Félicitation, vos changements ont été approvés et publiés !", "Félicitations, vos changements ont été approuvés et publiés !",
"published", "published",
{ {
adminName: data.get('adminName'), adminName: data.get('adminName'),

View file

@ -54,9 +54,9 @@ export default class DelegateController {
proposedVersion.gallery = [] proposedVersion.gallery = []
} }
if (Array.isArray(organization.proposedVersion.gallery)) { if (Array.isArray(organization.proposedVersion.gallery)) {
let toDeleteMedias = organization.proposedVersion.gallery.filter((media: any) => let toDeleteMedias = organization.proposedVersion.gallery
// if a existing media is not in the new version we delete it AND in the published version .filter((media: any) => proposedVersion.gallery.filter((m: any) => m.key === media.key).length === 0)
proposedVersion.gallery.filter((m: any) => m.key === media.key).length === 0) .map((media: any) => media.key)
MediaService.deleteMany(toDeleteMedias, 'galleryUpdated') MediaService.deleteMany(toDeleteMedias, 'galleryUpdated')
} }
@ -129,8 +129,8 @@ export default class DelegateController {
// send email to the manager // send email to the manager
const organization: any = res.locals.organization const organization: any = res.locals.organization
EmailService.send( EmailService.send(
organization.email, EmailService.getAdminAddress(),
"L'association \"" + organization.adminName + "\" demande à être vérifé", "L'association \"" + organization.adminName + "\" demande à être vérifié",
"approval", "approval",
{ {
adminName: organization.adminName, adminName: organization.adminName,

View file

@ -30,7 +30,7 @@ export default class PublicController {
name: version.name, name: version.name,
description: version.descriptionShort, description: version.descriptionShort,
thumbnail: version.thumbnail.location, thumbnail: version.thumbnail.location,
tag: version.tag._id, tag: version.tag === null ? 'tag_not_found' : version.tag._id,
slug: o.get('slug') slug: o.get('slug')
} }
}) })

View file

@ -1,3 +1,6 @@
/**
* Schedule collapsable section animation
*/
document.querySelectorAll('.schedule-category').forEach(node => { document.querySelectorAll('.schedule-category').forEach(node => {
let opened = false let opened = false
let icon = node.querySelector('.schedule-category-collapse-icon') let icon = node.querySelector('.schedule-category-collapse-icon')
@ -17,3 +20,25 @@ document.querySelectorAll('.schedule-category').forEach(node => {
} }
}) })
/**
* Gallery modal to view media in large
*/
let mediaModal = document.querySelector('#media-modal')
let mediaModalImage = document.querySelector('#media-modal-image')
let openModal = url => {
mediaModal.style.visibility = 'visible'
mediaModal.style.opacity = 1
let attr = document.createAttribute('src')
attr.value = url
mediaModalImage.attributes.setNamedItem(attr)
document.body.style.height = "100vh"
document.body.style.overflow = "hidden"
}
let closeModal = () => {
mediaModal.style.visibility = 'hidden'
mediaModal.style.opacity = 0
document.body.style.overflow = "initial"
document.body.style.height = "initial"
}

View file

@ -106,16 +106,10 @@
margin-top: .75em; margin-top: .75em;
width: 100%; width: 100%;
height: 25em; height: 25em;
display: grid; display: grid;
grid-template-columns: 1fr .5fr .5fr;
grid-template-rows: 1fr 1fr;
grid-column-gap: .75em; grid-column-gap: .75em;
grid-row-gap: .75em; grid-row-gap: .75em;
/* Negative to cancel the grid gap */
/* margin-bottom: -.75em; */
} }
.media-overlay { .media-overlay {
@ -146,7 +140,45 @@
transform: scale(1.2); transform: scale(1.2);
} }
.media-container:first-of-type { /**
* MOSAIC CASES
**/
/* Mosaic level 1 */
.mosaic-1 {
display: flex;
justify-content: center;
}
.mosaic-1 .media-container {
width: 70%;
}
/* Mosaic level 2 */
.mosaic-2 {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
}
/* Mosaic level 3 */
.mosaic-3 {
grid-template-columns: 1fr .5fr;
grid-template-rows: 1fr 1fr;
}
.mosaic-3 .media-container:first-of-type {
grid-row: 1 / span 2;
}
/* Mosaic level 4 */
.mosaic-4 {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
/* With five medias */
.mosaic-5 {
grid-template-columns: 1fr .5fr .5fr;
grid-template-rows: 1fr 1fr;
}
.mosaic-5 .media-container:first-of-type {
grid-row: 1 / span 2; grid-row: 1 / span 2;
} }
@ -168,6 +200,54 @@
background-position: center; background-position: center;
} }
/**
* Media modal
*/
.media-modal-container {
position: absolute;
width: 100%;
height: 100vh;
z-index: 99;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, .80);
display: flex;
justify-content: center;
align-items: center;
visibility: hidden;
opacity: 0;
transition: visibility 0.1s linear,opacity 0.1s linear;
}
.media-modal {
position: relative;
width: 50%;
}
.media-modal img {
border-radius: 4px;
width: 100%;
}
.media-close {
position: absolute;
width: 1.5em;
height: 1.5em;
right: -.75em;
top: -.75em;
color: white;
cursor: pointer;
opacity: 0.7;
transition: all 0.2s;
}
.media-close:hover {
transform: scale(1.2);
opacity: 1;
}
/* ***************************************************************************** /* *****************************************************************************
* SECTION * SECTION
@ -513,11 +593,46 @@ section {
.media-mosaic { .media-mosaic {
height: 33em; height: 33em;
}
/**
* MOSAIC CASES
*/
/* Mosaic level 1 */
.mosaic-1 .media-container {
width: 100%;
}
/* Mosaic level 2 */
.mosaic-2, .mosaic-3 {
grid-template-columns: 1fr;
}
/* Mosaic level 3 */
.mosaic-3 {
grid-template-rows: 1fr 1fr 1fr;
}
.mosaic-3 .media-container:first-of-type {
grid-row: 1 / span 1;
}
/* Mosaic level 4 */
.mosaic-4 {
grid-template-rows: 1fr 1fr 1fr;
}
.mosaic-4 .media-container:first-of-type, .mosaic-4 .media-container:nth-of-type(2) {
grid-column: 1 / span 2;
}
.mosaic-4 .media-container:first-of-type {
grid-row: 1 / span 1;
}
/* Mosaic level 5 */
.mosaic-5 {
grid-template-columns: .5fr .5fr; grid-template-columns: .5fr .5fr;
grid-template-rows: 1fr .5fr .5fr; grid-template-rows: 1fr .5fr .5fr;
} }
.mosaic-5 .media-container:first-of-type {
.media-container:first-of-type {
grid-row: 1 / span 1; grid-row: 1 / span 1;
grid-column: 1 / span 2; grid-column: 1 / span 2;
} }

View file

@ -1,6 +1,6 @@
<p>Bonjour,</p> <p>Bonjour,</p>
<p>L'association "{{ adminName }}" vient de proposer de nouveau changements à propos de son association sur la platforme.</p> <p>L'association "{{ adminName }}" vient de proposer de nouveaux changements à propos de son association sur la plateforme.</p>
<p>L'email associé à cette association est {{ email }}</p> <p>L'email associé à cette association est {{ email }}</p>
@ -8,4 +8,4 @@
<p><a href="{{ link }}">Lien pour consulter les changements</a></p> <p><a href="{{ link }}">Lien pour consulter les changements</a></p>
<p>Coordialement</p> <p>Cordialement</p>

View file

@ -1,13 +1,13 @@
<p>Bonjour,</p> <p>Bonjour,</p>
<p> <p>
Nous vous informons que les changements qui ont été soumis à verification concernant l'association "{{ adminName }}" viennent d'être publiés. Félicitations ! Nous vous informons que les changements qui ont été soumis à vérification concernant l'association "{{ adminName }}" viennent d'être publiés. Félicitations !
</p> </p>
<p> <p>
Nous vous invitons à consulter la page web générée à partir des informations que vous avez soumises : <a href="{{ link }}">{{ link }}</a> Nous vous invitons à consulter la page web générée à partir des informations que vous avez soumises : <a href="{{ link }}">{{ link }}</a>
</p> </p>
<p>Coordialement</p> <p>Cordialement</p>
{% include 'views/emails/delegateFooter.twig' %} {% include 'views/emails/delegateFooter.twig' %}

View file

@ -1,11 +1,11 @@
<p>Bonjour,</p> <p>Bonjour,</p>
<p> <p>
Nous vous informons que les changements qui ont été soumis à verification concernant l'association "{{ adminName }}" ne peuvent pas en l'état être publiées. Nous vous informons que les changements qui ont été soumis à vérification concernant l'association "{{ adminName }}" ne peuvent pas en l'état être publiées.
</p> </p>
<p> <p>
La personne vérifiant ces modifications a laissé un message indiquant la raison pour laquelle ces modifications ne peuvent pas être publiés : La personne vérifiant ces modifications a laissé un message indiquant la raison pour laquelle ces modifications ne peuvent pas être publiées :
</p> </p>
<p> <p>
@ -15,13 +15,13 @@ La personne vérifiant ces modifications a laissé un message indiquant la raiso
</p> </p>
<p> <p>
N'hésitez pas à corriger les informations et à resoumettre pour une nouvelle verification et peut être cette fois ci vous serez publié... N'hésitez pas à corriger les informations et à resoumettre pour une nouvelle vérification et peut-être cette fois-ci vous serez publié....
</p> </p>
<p> <p>
Pour rappel, vous pouvez utiliser ce lien pour vous connecter sur votre interface : <a href="{{ link }}">{{ link }}</a> Pour rappel, vous pouvez utiliser ce lien pour vous connecter sur votre interface : <a href="{{ link }}">{{ link }}</a>
</p> </p>
<p>Coordialement</p> <p>Cordialement</p>
{% include 'views/emails/delegateFooter.twig' %} {% include 'views/emails/delegateFooter.twig' %}

View file

@ -1,15 +1,15 @@
<p>Bonjour,</p> <p>Bonjour,</p>
<p> <p>
Ceci est un email automatique afin de communiquer le lien au gérant de l'association nommée "{{ adminName }}" dans le but de modifier les informations concernant cette association sur la platforme de espace condorcet. Bienvenue ! Ceci est juste un petit email pour communiquer le lien de gestion de l'association "{{ adminName }}" dans le but de modifier les informations concernant cette association sur la platforme de espace condorcet.
</p> </p>
<p> <p>
Vous pouvez utilisez ce lien pour vous connecter sur l'interface web permettant de modifier votre association sur la platforme : <a href="{{ link }}">{{ link }}</a> Vous pouvez utiliser ce lien pour vous connecter sur l'interface web permettant de modifier votre association sur la platforme : <a href="{{ link }}">{{ link }}</a>
</p> </p>
<p>Voici votre clée: {{ token }}</p> <p>Et voici votre clé: {{ token }}</p>
<p>Coordialement</p> <p>Cordialement</p>
{% include 'views/emails/delegateFooter.twig' %} {% include 'views/emails/delegateFooter.twig' %}

View file

@ -8,12 +8,7 @@
<div class="container header-container"> <div class="container header-container">
<a href="/" class="return"> <a href="/" class="return">
<div class="return-icon"> <div class="return-icon">
<svg <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path fill="currentColor" d="M256 504C119 504 8 393 8 256S119 8 256 8s248 111 248 248-111 248-248 248zM142.1 273l135.5 135.5c9.4 9.4 24.6 9.4 33.9 0l17-17c9.4-9.4 9.4-24.6 0-33.9L226.9 256l101.6-101.6c9.4-9.4 9.4-24.6 0-33.9l-17-17c-9.4-9.4-24.6-9.4-33.9 0L142.1 239c-9.4 9.4-9.4 24.6 0 34z"></path> <path fill="currentColor" d="M256 504C119 504 8 393 8 256S119 8 256 8s248 111 248 248-111 248-248 248zM142.1 273l135.5 135.5c9.4 9.4 24.6 9.4 33.9 0l17-17c9.4-9.4 9.4-24.6 0-33.9L226.9 256l101.6-101.6c9.4-9.4 9.4-24.6 0-33.9l-17-17c-9.4-9.4-24.6-9.4-33.9 0L142.1 239c-9.4 9.4-9.4 24.6 0 34z"></path>
</svg> </svg>
</div> </div>
@ -33,16 +28,17 @@
<h1 class="cover-title"> <h1 class="cover-title">
{{ data.name }} {{ data.name }}
</h1> </h1>
<h4 class="cover-sub-title"> {# <h4 class="cover-sub-title">
Sous titre
</h4> </h4> #}
</div> </div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
<div class="media-mosaic"> {% if data.gallery|length > 0 %}
<div class="media-mosaic mosaic-{{ data.gallery|length }}">
{% for media in data.gallery %} {% for media in data.gallery %}
<div class="media-container"> <div class="media-container" onclick="openModal('{{ media.location }}')">
<div class="media" style="background-image: url({{ media.location }})"></div> <div class="media" style="background-image: url({{ media.location }})"></div>
</div> </div>
{% endfor %} {% endfor %}
@ -51,6 +47,17 @@
<div class="media-overlay"><i class="fas fa-play-circle"></i></div> <div class="media-overlay"><i class="fas fa-play-circle"></i></div>
</div> #} </div> #}
</div> </div>
<div class="media-modal-container" id="media-modal">
<div class="media-modal">
<img src="" id="media-modal-image" />
<div class="media-close" onclick="closeModal()">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path>
</svg>
</div>
</div>
</div>
{% endif %}
{% if data.descriptionLong|length > 0 %} {% if data.descriptionLong|length > 0 %}
<section> <section>

View file

@ -720,6 +720,13 @@
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
"@types/papaparse@^5.0.4":
version "5.0.4"
resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.0.4.tgz#70792c74d9932bcc0bfa945ae7dacfef67f4ee57"
integrity sha512-jFv9NcRddMiW4+thmntwZ1AhvMDAX4+tAUDkWWbNcIzgqyjjkuSHOEUPoVh1/gqJTWfDOD1tvl+hSp88W3UtqA==
dependencies:
"@types/node" "*"
"@types/prettier@^2.0.0": "@types/prettier@^2.0.0":
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.2.tgz#5bb52ee68d0f8efa9cc0099920e56be6cc4e37f3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.2.tgz#5bb52ee68d0f8efa9cc0099920e56be6cc4e37f3"
@ -3810,6 +3817,11 @@ package-json@^6.3.0:
registry-url "^5.0.0" registry-url "^5.0.0"
semver "^6.2.0" semver "^6.2.0"
papaparse@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.2.0.tgz#97976a1b135c46612773029153dc64995caa3b7b"
integrity sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA==
parse-json@^5.0.0: parse-json@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f"