From 51208cad8d07f36a45317bcf5db9ea5728b1975c Mon Sep 17 00:00:00 2001
From: root
Date: Wed, 15 Jul 2020 20:32:42 +0000
Subject: [PATCH] update
---
nodemon.json | 4 +
presentation.md | 1 +
src/app.ts | 13 +-
.../AdminOrganizationController.ts | 90 ++++--
src/controllers/DelegateController.ts | 239 +++++++++++++--
src/controllers/MediaController.ts | 5 +-
src/controllers/PublicController.ts | 66 +++-
src/models/Organization.ts | 13 +-
static/about.html | 39 ---
static/assets/home.css | 15 +-
static/assets/img/favicon.png | Bin 0 -> 4687 bytes
static/assets/js/home.js | 31 +-
static/assets/main.css | 17 +-
static/assets/organization.css | 112 ++++++-
static/fa_icons | 1 -
static/home.html | 248 ---------------
static/organization.html | 288 ------------------
views/about.html | 0
views/about.twig | 15 +
views/base.twig | 50 ++-
views/emails/rejected.twig | 4 +
views/emails/token.twig | 4 +-
views/home.twig | 104 ++++++-
views/legals.twig | 43 +++
views/not-found.twig | 7 +
views/organization.html | 0
views/organization.twig | 272 +++++++++++++++++
27 files changed, 1003 insertions(+), 678 deletions(-)
create mode 100644 nodemon.json
create mode 100644 presentation.md
delete mode 100644 static/about.html
create mode 100644 static/assets/img/favicon.png
delete mode 120000 static/fa_icons
delete mode 100644 static/home.html
delete mode 100644 static/organization.html
delete mode 100644 views/about.html
create mode 100644 views/about.twig
create mode 100644 views/legals.twig
create mode 100644 views/not-found.twig
delete mode 100644 views/organization.html
create mode 100644 views/organization.twig
diff --git a/nodemon.json b/nodemon.json
new file mode 100644
index 0000000..f44c2de
--- /dev/null
+++ b/nodemon.json
@@ -0,0 +1,4 @@
+{
+ "verbose": false,
+ "ignore": ["views", "static"]
+}
\ No newline at end of file
diff --git a/presentation.md b/presentation.md
new file mode 100644
index 0000000..2a434f2
--- /dev/null
+++ b/presentation.md
@@ -0,0 +1 @@
+Potremmo impermutabile e lui carissime ignoranza, della nome noi sempre a prieghi in beato per. Noia quale lodato nella raccontare novella che. Nel dare modo in liberalita piaceri, alla impetrata il di e la novella manifestamente siamo, che che quali.
\ No newline at end of file
diff --git a/src/app.ts b/src/app.ts
index 87619ed..35d17ac 100644
--- a/src/app.ts
+++ b/src/app.ts
@@ -12,6 +12,7 @@ import AdminAuthMiddleware from './middlewares/AdminAuthMiddleware'
import DelegateAuthMiddleware from './middlewares/DelegateAuthMiddleware'
import PublicController from './controllers/PublicController'
import cors from 'cors'
+import twig from 'twig'
dotenv.config({
path: __dirname + '/../.env'
@@ -21,6 +22,8 @@ const app: express.Application = express()
const host: string = "0.0.0.0"
const port: number = 8001
+twig.cache(false)
+
let main = async () => {
mongoose.connection.on('error', err => {
console.error(err)
@@ -36,6 +39,10 @@ let main = async () => {
console.log('> Connected to mongodb')
})
+ app.set("twig options", {
+ allow_async: true,
+ strict_variables: false
+ })
app.use(cors())
app.use(bodyParser.json())
@@ -46,6 +53,8 @@ let main = async () => {
app.get('/', PublicController.home)
app.get('/association/:slug', PublicController.organization)
+ app.get('/a-propos', PublicController.about)
+ app.get('/mentions-legales', PublicController.legals)
app.get('/icon/:id', DefaultController.viewIcon)
app.get('/email', DefaultController.sendEmail)
@@ -79,6 +88,8 @@ let main = async () => {
.put('/', DelegateController.update)
.post('/submit', DelegateController.submit)
.post('/thumbnail', DelegateController.uploadThumbnail())
+ .post('/cover', DelegateController.uploadCover())
+ .post('/medias', DelegateController.uploadMedias())
.delete('/', DelegateController.destroy)
)
@@ -92,7 +103,7 @@ let main = async () => {
*/
app.post('/api/media', MediaController.uploadRoute())
- app.delete('/api/media/:key', MediaController.delete)
+ //app.delete('/api/media/:key', MediaController.delete)
app.use(express.static('/apps/forum_server/static'))
diff --git a/src/controllers/AdminOrganizationController.ts b/src/controllers/AdminOrganizationController.ts
index 5d0d0b4..8ad2edc 100644
--- a/src/controllers/AdminOrganizationController.ts
+++ b/src/controllers/AdminOrganizationController.ts
@@ -4,6 +4,7 @@ import cryptoRandomString from 'crypto-random-string'
import EmailService from '../EmailService'
import slugify from 'slugify'
import { Document } from 'mongoose'
+import MediaService from '../MediaService'
export default class AdminOrganizationController {
static getMany(req: express.Request, res: express.Response) {
@@ -22,22 +23,22 @@ export default class AdminOrganizationController {
let body: any = {
token: AdminOrganizationController.generateToken(),
createdAt: new Date(),
- slug: req.body.adminName === undefined ? undefined : slugify(req.body.adminName).toLowerCase(),
- ...req.body
- // ...{
- // proposedVersion: {
- // name: '',
- // descriptionShort: '',
- // descriptionLong: '',
- // contacts: [],
- // schedule: [],
- // pricing: [],
- // tag: null,
- // cover: null,
- // gallery: [],
- // thumbnail: null
- // }
- // }
+ slug: slugify(req.body.adminName).toLowerCase(),
+ ...req.body,
+ ...{
+ proposedVersion: {
+ name: req.body.adminName
+ // descriptionShort: '',
+ // descriptionLong: '',
+ // contacts: [],
+ // schedule: [],
+ // pricing: [],
+ // tag: null,
+ // cover: null,
+ // gallery: [],
+ // thumbnail: null
+ }
+ }
}
Organization.create(body).then(data => {
AdminOrganizationController.sendEmailTokenUniversal(data)
@@ -75,16 +76,46 @@ export default class AdminOrganizationController {
}
static destroy(req: express.Request, res: express.Response) {
- Organization.deleteOne({ _id: req.params.id }).then(data => {
- let isSuccess = data.deletedCount !== undefined && data.deletedCount > 0
- res.status(isSuccess ? 200 : 400).json({
- success: isSuccess,
- data
- })
- }).catch(err => res.status(400).json({
- success: false,
- errors: err.errors
- }))
+ Organization.findById(req.params.id).then(organization => {
+ Organization.deleteOne({ _id: req.params.id }).then(data => {
+ if (organization === null) {
+ return
+ }
+ // delete all media from this organization
+ let keys: string[] = []
+ const proposedVersion: any = organization.get('proposedVersion')
+ if (proposedVersion.thumbnail !== undefined && proposedVersion.thumbnail !== null) {
+ keys.push(proposedVersion.thumbnail.key)
+ }
+ if (proposedVersion.cover !== undefined && proposedVersion.cover !== null) {
+ keys.push(proposedVersion.cover.key)
+ }
+ console.log(proposedVersion.gallery)
+ if (Array.isArray(proposedVersion.gallery)) {
+ keys = keys.concat(proposedVersion.gallery.map((m: any) => m.key))
+ }
+
+ keys.forEach((key: string) => {
+ if (key === undefined || key === null || key.length <= 2) { return }
+ console.log('> OrganizationDestroyMediaCleanup: Deleted ' + key)
+ MediaService.getS3().deleteObject({
+ Bucket: MediaService.getBucket(),
+ Key: key
+ }, (err, _) => {
+ if (err !== null) {
+ console.error('> OrganizationDestroyMediaCleanup: Cannot delete a media from a organization which will be deleted')
+ console.log(err, err.stack)
+ }
+ })
+ })
+
+ let isSuccess = data.deletedCount !== undefined && data.deletedCount > 0
+ res.status(isSuccess ? 200 : 400).json({
+ success: isSuccess,
+ data
+ })
+ }).catch(err => res.status(400).json({ success: false, errors: err.errors }))
+ }).catch(err => res.status(400).json({ success: false, errors: err }))
}
static sendEmailToken(req: express.Request, res: express.Response) {
@@ -150,11 +181,16 @@ export default class AdminOrganizationController {
}
static sendEmailTokenUniversal(data: Document) {
+ const baseUrl = process.env.WEB_UI_URL === undefined ? "URL_NOT_FOUND" : process.env.WEB_UI_URL
EmailService.send(
data.get('email'),
"Votre lien secret pour modifier votre association",
"token",
- { adminName: data.get('adminName'), token: data.get('token') }
+ {
+ adminName: data.get('adminName'),
+ token: data.get('token'),
+ link: baseUrl + '/delegate?delegateToken=' + data.get('token')
+ }
).then(() => {
console.log('> A token email was sent')
}).catch(() => {
diff --git a/src/controllers/DelegateController.ts b/src/controllers/DelegateController.ts
index 475aea3..b2a54cd 100644
--- a/src/controllers/DelegateController.ts
+++ b/src/controllers/DelegateController.ts
@@ -1,3 +1,4 @@
+import Tag from '../models/Tag'
import Organization from '../models/Organization'
import * as express from 'express'
import EmailService from '../EmailService'
@@ -9,7 +10,15 @@ import slugify from 'slugify'
export default class DelegateController {
static get(req: express.Request, res: express.Response) {
- res.json({ success: true, data: res.locals.organization })
+ Tag.find().then(tags => {
+ res.json({
+ success: true,
+ data: {
+ organization: res.locals.organization,
+ tags
+ }
+ })
+ })
}
static test(req: express.Request, res: express.Response) {
@@ -17,28 +26,92 @@ export default class DelegateController {
}
static update(req: express.Request, res: express.Response) {
- if (res.locals.organization.validationState === 'pending') {
+ const organization: any = res.locals.organization
+ if (organization.validationState === 'pending') {
return res.json({
success: false,
errors: [{ code: 'update-forbidden', message: 'Organization cannot be updated while the validationState is set to "pending"' }]
})
}
- // only update proposedVersion
- Organization.updateOne({ _id: res.locals.organization._id }, {
- proposedVersion: req.body,
- updatedAt: new Date()
- }).then(data => {
- res.json({
- success: true,
- data,
- body: req.body
+
+ const next = (tag: any) => {
+ // only update proposedVersion
+ let proposedVersion: any = req.body
+ proposedVersion.tag = tag
+ proposedVersion.descriptionLong = proposedVersion.descriptionLong.replace(/\n/g, '')
+
+ // validate contact.address
+ // validate all fields to not overflow
+ // validate the size of all the json, all the data recorded
+
+ // manage medias
+ // delete media that are not used
+ if (!Array.isArray(proposedVersion.gallery)) {
+ proposedVersion.gallery = []
+ }
+ if (Array.isArray(organization.proposedVersion.gallery)) {
+ organization.proposedVersion.gallery.forEach((media: any) => {
+ // if a existing media is not in the new version we delete it
+ if (proposedVersion.gallery.filter((m: any) => m.key === media.key).length === 0) {
+ console.log('> Old media cleanup: we must delete ', media)
+ MediaService.getS3().deleteObject({
+ Bucket: MediaService.getBucket(),
+ Key: media.key
+ }, (err, _) => {
+ if (err !== null) {
+ console.error('> Cannot delete a old media element')
+ console.log(err, err.stack)
+ }
+ })
+ }
+ })
+ }
+
+ // format schedule, pricing
+ if (!Array.isArray(proposedVersion.schedule)) {
+ proposedVersion.schedule = []
+ }
+ if (!Array.isArray(proposedVersion.pricing)) {
+ proposedVersion.pricing = []
+ }
+
+ Organization.updateOne({ _id: organization._id }, {
+ proposedVersion,
+ updatedAt: new Date()
+ }).then(data => {
+ res.json({
+ success: true,
+ data,
+ body: req.body
+ })
+ }).catch(err => {
+ res.status(400).json({
+ success: false,
+ errors: err.errors !== undefined ? err.errors : err
+ })
})
- }).catch(err => {
- res.status(400).json({
- success: false,
- errors: err.errors !== undefined ? err.errors : err
- })
- })
+ }
+
+ if (req.body.tag !== undefined && req.body.tag !== null && req.body.tag.length > 2) {
+ // skip the tag part if the tag didn't changed
+ if (
+ organization.proposedVersion.tag === undefined ||
+ organization.proposedVersion.tag === null ||
+ req.body.tag !== organization.proposedVersion.tag._id
+ ) {
+ // if the tag is defined, search the tag id
+ Tag.findById(req.body.tag).then(tag => {
+ next(tag)
+ }).catch(err => {
+ console.log(err)
+ res.status(400).json({ success: false, errors: err, _note: 'The tag id provided is invalid' })
+ })
+ } else {
+ next(organization.tag)
+ }
+ } else {
+ next(null)
+ }
}
static submit(req: express.Request, res: express.Response) {
@@ -78,7 +151,6 @@ export default class DelegateController {
}
static uploadThumbnail(): express.RequestHandler[] {
- // we upload the thumbnail
return [
multer({
storage: multerS3({
@@ -88,14 +160,14 @@ export default class DelegateController {
contentType: multerS3.AUTO_CONTENT_TYPE,
key: (_: any, file: any, cb: any) => {
console.log(file)
- cb(null, Date.now().toString() + '_' + slugify(file.originalname))
+ cb(null, Date.now().toString() + '_thumbnail')
}
})
}).single('file'),
(req: express.Request, res: express.Response) => {
// if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one
// THEN we delete the current thumbnail
- const proposedVersion: any = res.locals.organization.proposedVersion
+ let proposedVersion: any = res.locals.organization.proposedVersion
const publishedVersion: any = res.locals.organization.publishedVersion
if (
proposedVersion !== undefined && proposedVersion !== null &&
@@ -109,7 +181,7 @@ export default class DelegateController {
Bucket: MediaService.getBucket(),
Key: proposedVersion.thumbnail.key
}, (err, _) => {
- if (err !== null) {
+ if (err !== null) {
console.error('> Cannot delete a old thumbnail')
console.log(err, err.stack)
}
@@ -122,13 +194,134 @@ export default class DelegateController {
contentType: req.file.contentType,
// @ts-ignore
location: req.file.location,
+ // @ts-ignore
+ size: req.file.size,
+ // @ts-ignore
+ originalFileName: req.file.originalname,
type: 'thumbnail'
}
+ proposedVersion = { ...res.locals.organization.proposedVersion, thumbnail }
Organization.updateOne({ _id: res.locals.organization._id }, {
- proposedVersion: { thumbnail },
+ proposedVersion,
updatedAt: new Date()
}).then(data => {
- res.json({ success: true, data, thumbnail })
+ res.json({ success: true, data, thumbnail, proposedVersion })
+ }).catch(err => {
+ res.status(400).json({
+ success: false,
+ errors: err.errors !== undefined ? err.errors : err
+ })
+ })
+ }
+ ]
+ }
+
+ static uploadCover(): express.RequestHandler[] {
+ return [
+ multer({
+ storage: multerS3({
+ s3: MediaService.getS3(),
+ bucket: 'development-bucket',
+ acl: 'public-read',
+ contentType: multerS3.AUTO_CONTENT_TYPE,
+ key: (_: any, file: any, cb: any) => {
+ console.log(file)
+ cb(null, Date.now().toString() + '_cover')
+ }
+ })
+ }).single('file'),
+ (req: express.Request, res: express.Response) => {
+ // if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one
+ // THEN we delete the current thumbnail
+ let proposedVersion: any = res.locals.organization.proposedVersion
+ const publishedVersion: any = res.locals.organization.publishedVersion
+ if (
+ proposedVersion !== undefined && proposedVersion !== null &&
+ proposedVersion.cover !== undefined && proposedVersion.cover !== null &&
+ publishedVersion !== undefined && publishedVersion !== null &&
+ publishedVersion.cover !== undefined && publishedVersion.cover !== null &&
+ publishedVersion.cover.location !== proposedVersion.cover.location
+ ) {
+ console.log(' we must delete ', proposedVersion.cover)
+ MediaService.getS3().deleteObject({
+ Bucket: MediaService.getBucket(),
+ Key: proposedVersion.cover.key
+ }, (err, _) => {
+ if (err !== null) {
+ console.error('> Cannot delete a old cover')
+ console.log(err, err.stack)
+ }
+ })
+ }
+ const cover: any = {
+ // @ts-ignore
+ key: req.file.key,
+ // @ts-ignore
+ contentType: req.file.contentType,
+ // @ts-ignore
+ location: req.file.location,
+ // @ts-ignore
+ size: req.file.size,
+ // @ts-ignore
+ originalFileName: req.file.originalname,
+ type: 'cover'
+ }
+ proposedVersion = { ...res.locals.organization.proposedVersion, cover }
+ Organization.updateOne({ _id: res.locals.organization._id }, {
+ proposedVersion,
+ updatedAt: new Date()
+ }).then(data => {
+ res.json({ success: true, data, cover, proposedVersion })
+ }).catch(err => {
+ res.status(400).json({
+ success: false,
+ errors: err.errors !== undefined ? err.errors : err
+ })
+ })
+ }
+ ]
+ }
+
+ static uploadMedias(): express.RequestHandler[] {
+ return [
+ multer({
+ storage: multerS3({
+ s3: MediaService.getS3(),
+ bucket: 'development-bucket',
+ acl: 'public-read',
+ contentType: multerS3.AUTO_CONTENT_TYPE,
+ key: (_: any, file: any, cb: any) => {
+ console.log(file)
+ cb(null, Date.now().toString() + '_media')
+ }
+ })
+ }).array('file'),
+ (req: express.Request, res: express.Response) => {
+ let proposedVersion: any = res.locals.organization.proposedVersion
+
+ // @ts-ignore
+ req.files.forEach((file: any) => {
+ proposedVersion.gallery.push({
+ key: file.key,
+ contentType: file.contentType,
+ location: file.location,
+ size: file.size,
+ type: 'image',
+ originalFileName: file.originalname
+ })
+ })
+
+ Organization.updateOne({ _id: res.locals.organization._id }, {
+ proposedVersion,
+ updatedAt: new Date()
+ }).then(result => {
+ res.json({
+ success: true,
+ data: {
+ result,
+ gallery: proposedVersion.gallery
+ }
+ })
}).catch(err => {
res.status(400).json({
success: false,
diff --git a/src/controllers/MediaController.ts b/src/controllers/MediaController.ts
index c651baa..8eff1c2 100644
--- a/src/controllers/MediaController.ts
+++ b/src/controllers/MediaController.ts
@@ -16,7 +16,7 @@ export default class MediaController {
contentType: multerS3.AUTO_CONTENT_TYPE,
key: (_: any, file: any, cb: any) => {
console.log(file)
- cb(null, Date.now().toString() + '_' + slugify(file.originalname)) //use Date.now() for unique file keys
+ cb(null, Date.now().toString()) //use Date.now() for unique file keys
}
})
}).single('file'),
@@ -25,9 +25,6 @@ export default class MediaController {
}
static upload(req: express.Request, res: express.Response) {
-
-
-
res.json({
success: true,
data: { file: req.file }
diff --git a/src/controllers/PublicController.ts b/src/controllers/PublicController.ts
index 273f712..64b5a9b 100644
--- a/src/controllers/PublicController.ts
+++ b/src/controllers/PublicController.ts
@@ -7,24 +7,78 @@ import Mustache from 'mustache'
import fs from 'fs'
import { IconService, IconInterface } from '../IconService'
import IORedis from 'ioredis'
+import Tag from '../models/Tag'
export default class PublicController {
-
static async home(req: express.Request, res: express.Response) {
// let client: IORedis.Redis = RedisService.getClient()
// await client.set('hello', 'world')
// res.json({
// data: await client.get('hello')
// })
- res.render('home.twig', {
- message : "Hello World"
+ Tag.find().then(tags => {
+ Organization.find().then(organizations => {
+ res.render('home.twig', {
+ tags,
+ tagsJSON: JSON.stringify(tags),
+ organizationsJSON: JSON.stringify(organizations.map(o => {
+ const version = o.get('proposedVersion')
+ return {
+ name: version.name,
+ description: version.descriptionShort,
+ thumbnail: version.thumbnail.location,
+ tag: version.tag._id,
+ slug: o.get('slug')
+ }
+ }))
+ })
+ })
})
}
static async organization(req: express.Request, res: express.Response) {
- res.render('index.twig', {
- message : "Hello World"
- })
+ Organization.find({ slug: req.params.slug }).then(data => {
+ if (data.length === 0) {
+ res.status(404).render('not-found.twig')
+ } else {
+ const version = data[0].get('proposedVersion')
+ if (version.contacts !== null && version.contacts !== undefined) {
+ if (typeof version.contacts.address === 'string') {
+ version.contacts.address = version.contacts.address.split('\n')
+ }
+ if (typeof version.contacts.phone === 'string') {
+ let phone = version.contacts.phone
+ if (phone.indexOf('+33') === 0) {
+ phone = '0' + phone.substr(3)
+ }
+ let phoneSplit = ''
+ let partEnd = false
+ for (var i = 0; i < phone.length; i++) {
+ phoneSplit += phone.charAt(i)
+ if (partEnd === true) {
+ phoneSplit += ' '
+ }
+ partEnd = !partEnd
+ }
+ version.contacts.phoneInt = "+33" + phone.substr(1)
+ version.contacts.phoneSplit = phoneSplit
+ }
+ }
+ console.log(version)
+ res.render('organization.twig', {
+ layout: 'standalone',
+ data: version
+ })
+ }
+ }).catch(_ => res.status(404).render('not-found.twig'))
+ }
+
+ static async about(req: express.Request, res: express.Response) {
+ res.render('about.twig')
+ }
+
+ static async legals(req: express.Request, res: express.Response) {
+ res.render('legals.twig')
}
}
\ No newline at end of file
diff --git a/src/models/Organization.ts b/src/models/Organization.ts
index 0cd41e5..4e99abb 100644
--- a/src/models/Organization.ts
+++ b/src/models/Organization.ts
@@ -29,11 +29,6 @@ class AllowedString extends mongoose.SchemaType {
// @ts-ignore
mongoose.Schema.Types['AllowedString'] = AllowedString
-const ScheduleIntervalBoundary = {
- hour: { type: Number },
- minute: { type: Number }
-}
-
const Media = {
location: { type: String },
type: {
@@ -41,8 +36,10 @@ const Media = {
name: 'MediaType',
allowedValues: ['cover', 'thumbnail', 'video', 'image']
},
+ originalFileName: { type: String },
key: { type: String },
- contentType: { type: String }
+ contentType: { type: String },
+ size: { type: Number } // size of the file in bytes
}
const OrganizationVersion = {
@@ -68,8 +65,8 @@ const OrganizationVersion = {
description: { type: String },
when: [{
day: { type: String }, // (Lundi,Mardi,Mercredi,Jeudi,Vendredi,Samedi,Dimanche)
- from: ScheduleIntervalBoundary,
- to: ScheduleIntervalBoundary
+ from: { type: String },
+ to: { type: String }
}]
}],
pricing: [{
diff --git a/static/about.html b/static/about.html
deleted file mode 100644
index 81b2855..0000000
--- a/static/about.html
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
- Home
-
-
-
-
-
-
-
-
-
-
-
-
-
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Autem alias sapiente neque? Dignissimos harum blanditiis fugiat eius alias nam repudiandae, et explicabo nihil eos, quos reprehenderit nobis aperiam quibusdam ad?
-
-
-
-
-
\ No newline at end of file
diff --git a/static/assets/home.css b/static/assets/home.css
index 56e9499..235167c 100644
--- a/static/assets/home.css
+++ b/static/assets/home.css
@@ -17,7 +17,6 @@
.header-menu {
display: flex;
align-items: flex-end;
-
}
.header-menu a {
@@ -94,6 +93,11 @@
height: 1.2em;
}
+.nav-item svg {
+ width: .75em;
+ height: .75em;
+}
+
.nav-icon {
width: 3em;
padding-left: .5em;
@@ -114,7 +118,7 @@
.nav-title {
display: flex;
align-items: center;
- opacity: 0.85;
+ opacity: 0.7;
}
.nav-access {
@@ -194,6 +198,7 @@
}
.card-content {
+ /* width: 100%; */
padding: 1.5em;
display: flex;
flex-direction: column;
@@ -213,6 +218,11 @@
font-size: 1.4em;
}
+.card-icon svg {
+ width: 1em;
+ height: 1em;
+}
+
.card-title {
font-size: 1.6em;
color: #B12008;
@@ -316,4 +326,3 @@
margin: 0;
}
}
-
diff --git a/static/assets/img/favicon.png b/static/assets/img/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f77dc215a7d90281077f3d1eb033bd437726cfb
GIT binary patch
literal 4687
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
RFfIT9002ovPDHLkV1nIA*bM*x
literal 0
HcmV?d00001
diff --git a/static/assets/js/home.js b/static/assets/js/home.js
index 5c2422d..b862ff7 100644
--- a/static/assets/js/home.js
+++ b/static/assets/js/home.js
@@ -10,6 +10,11 @@ let navContent = document.getElementById('nav-content')
let mosaic = document.getElementById('mosaic')
let mosaicHeader = document.getElementById('mosaic-header')
+organizations = organizations.map(org => {
+ org.tag = tags.filter(t => t._id === org.tag)[0]
+ return org
+})
+
navEnabler.onclick = async () => {
if (!navOpened) {
// open the menu
@@ -73,6 +78,14 @@ function renderNavItem(tag) {
return navItem
}
+function setAttributes(node, attrs) {
+ for (var key in attrs) {
+ attr = document.createAttribute(key)
+ attr.value = attrs[key]
+ node.attributes.setNamedItem(attr)
+ }
+}
+
function renderCard(organization) {
let card = createEl('card')
@@ -92,13 +105,19 @@ function renderCard(organization) {
titleContainer.appendChild(title)
let icon = createEl('card-icon')
- let iconTag = createEl(organization.tag.icon, 'i')
- icon.appendChild(iconTag)
+ icon.innerHTML = `
+
+ `
titleContainer.appendChild(icon)
upperContent.appendChild(titleContainer)
let description = createEl('card-description')
- description.textContent = organization.descriptionShort
+ description.textContent = organization.description
upperContent.appendChild(description)
let link = createEl('card-link')
@@ -109,6 +128,10 @@ function renderCard(organization) {
content.appendChild(upperContent)
content.appendChild(link)
card.appendChild(content)
+
+ card.onclick = () => {
+ window.location = aTag.href
+ }
return card
}
@@ -129,7 +152,7 @@ function enableTag(node) {
if (!all) {
tagId = node.attributes['data-tag-id'].value
}
- let data = organizations.filter(orga => orga.tag.id === tagId || all)
+ let data = organizations.filter(orga => orga.tag._id === tagId || all)
let cards = renderMosaic(data)
if (currentCardContainer !== null) {
mosaic.removeChild(currentCardContainer)
diff --git a/static/assets/main.css b/static/assets/main.css
index 8038f15..6312487 100644
--- a/static/assets/main.css
+++ b/static/assets/main.css
@@ -8,7 +8,7 @@ body {
}
.container {
- width: 80%;
+ width: 60%;
margin: 0 auto;
}
@@ -23,8 +23,21 @@ a:hover {
color: #2980b9;
}
+
+@media (max-width: 1600px) {
+ .container {
+ width: 70%;
+ }
+}
+
+@media (max-width: 1500px) {
+ .container {
+ width: 80%;
+ }
+}
+
@media (max-width: 900px) {
.container {
width: 92%;
}
-}
\ No newline at end of file
+}
diff --git a/static/assets/organization.css b/static/assets/organization.css
index 8420c56..982469a 100644
--- a/static/assets/organization.css
+++ b/static/assets/organization.css
@@ -23,13 +23,19 @@
color: #34495e;
cursor: pointer;
transition: all .2s;
+ text-decoration: none;
}
.return-icon {
- font-size: 1.5em;
+ display: flex;
+ align-items: center;
margin-right: .5em;
}
+.return-icon svg {
+ width: 1.3em;
+}
+
.return:hover {
opacity: 0.88;
text-decoration: underline;
@@ -140,7 +146,7 @@
transform: scale(1.2);
}
-.media-main {
+.media-container:first-of-type {
grid-row: 1 / span 2;
}
@@ -207,10 +213,29 @@ section {
margin-bottom: .5em;
}
+.description p {
+ margin-top: .75em;
+}
.description p:last-child {
margin-bottom: 0;
}
+.description h3 {
+ font-size: 1.4em;
+ margin-top: 1em;
+ margin-bottom: 0;
+}
+
+.description h4 {
+ font-size: 1.2em;
+ margin-top: 1em;
+ margin-bottom: 0;
+}
+
+.description h3, .description h4 {
+ opacity: 0.8;
+}
+
/* *****************************************************************************
* SCHEDULE
@@ -237,7 +262,7 @@ section {
.schedule-category-collapse-icon {
color: #B12008;
- font-size: 1.5em;
+ width: 1.5em;
transition: all 0.1s ease-out;
}
@@ -251,7 +276,6 @@ section {
.schedule-category-days-container {
border-radius: 4px;
background-color: #ECF0F1;
- width: 40;
margin-left: 2em;
margin-top: 1em;
@@ -283,14 +307,14 @@ section {
.pricing {
display: flex;
- justify-content: space-between;
+ justify-content: center;
}
.pricing-card {
+ max-width: 14em;
height: 10em;
width: 100%;
- padding-top: 1.5em;
- padding-bottom: 1.5em;
+ padding: 1.5em 1em;
text-align: center;
border-radius: 4px;
display: flex;
@@ -360,6 +384,10 @@ section {
font-size: 1.5em;
}
+.contact-icon svg {
+ width: .7em;
+}
+
.contact-content {
text-align: right;
}
@@ -369,21 +397,45 @@ section {
font-weight: bold;
}
+.external-link {
+ width: .8em;
+}
+
+.email .contact-icon svg {
+ width: .9em;
+}
+
+.website .contact-icon svg {
+ width: .9em;
+}
+
.facebook .contact-icon {
background-color: #3B5999;
color: white;
}
+.facebook .contact-icon svg {
+ width: .5em;
+}
+
.instagram .contact-icon {
background:linear-gradient(45deg, #405de6, #5851db, #833ab4, #c13584, #e1306c, #fd1d1d);
color: white;
}
+.instagram .contact-icon svg {
+ width: .75em;
+}
+
.twitter .contact-icon {
background-color: #1DA1F2;
color: white;
}
+.twitter .contact-icon svg {
+ width: .75em;
+}
+
/* *****************************************************************************
* FOOTER
@@ -412,6 +464,13 @@ section {
}
+@media (max-width: 1200px) {
+ .schedule-category-days-container {
+ margin-right: 25em;
+ }
+}
+
+
@media (max-width: 900px) {
.cover-background {
@@ -446,23 +505,40 @@ section {
grid-template-columns: .5fr .5fr;
grid-template-rows: 1fr .5fr .5fr;
}
- .media-main {
+
+ .media-container:first-of-type {
grid-row: 1 / span 1;
grid-column: 1 / span 2;
}
+ .schedule-category-header {
+ padding-left: 1em;
+ padding-right: 1em;
+ }
+
.schedule-category-days-container {
margin-right: 5em;
}
- .pricing-card {
- margin-right: .5em;
+ .pricing {
+ flex-direction: column;
+ align-items: center;
}
+ .pricing-card {
+ margin-right: 0;
+ margin-bottom: 1em;
+ }
+
.contact {
width: 100%;
}
+ .contact-icon {
+ width: 1.5em;
+ height: 1.5em;
+ }
+
.contact-item {
padding-left: 1em;
padding-right: 1em;
@@ -470,5 +546,21 @@ section {
.contact-content {
word-break: break-all;
+ font-size: .9em;
+ }
+
+ .facebook .contact-content, .twitter .contact-content, .website .contact-content, .instagram .contact-content {
+ font-size: .8em;
+ }
+}
+
+@media (max-width: 600px) {
+ .schedule-category-days-container {
+ margin-right: 1em;
+ padding-left: 1em;
+ padding-right: 1em;
+ }
+ .return-title {
+ display: none;
}
}
\ No newline at end of file
diff --git a/static/fa_icons b/static/fa_icons
deleted file mode 120000
index ccdb5bc..0000000
--- a/static/fa_icons
+++ /dev/null
@@ -1 +0,0 @@
-/home/mbess/font_awesome_icons/
\ No newline at end of file
diff --git a/static/home.html b/static/home.html
deleted file mode 100644
index cd25905..0000000
--- a/static/home.html
+++ /dev/null
@@ -1,248 +0,0 @@
-
-
-
- Home
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Afficher le menu
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Culturelles
-
-
-
-
-
-
-
-
-
-
-
-
- Danse et musique
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/static/organization.html b/static/organization.html
deleted file mode 100644
index 8db9c12..0000000
--- a/static/organization.html
+++ /dev/null
@@ -1,288 +0,0 @@
-
-
-
- Association
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- We Robot
-
-
- Association de robotique
-
-
-
-
-
-
-
-
-
-
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia minima aliquam corporis fugit repellat obcaecati consequatur cumque, dolore omnis et porro, sit iusto similique blanditiis vel, alias quam ducimus voluptates.
-
-
- Unde neque adipisci et. Consequatur labore similique quia. Rerum nihil eius assumenda quae. Non vel sapiente omnis. Eum explicabo neque maxime sapiente et perspiciatis et.
-
-
- Delectus unde inventore similique ut quo. Consequatur assumenda quaerat aliquid velit et corrupti. Laboriosam qui magnam culpa est amet nobis tenetur. Ducimus a sint ea. Expedita omnis libero ipsum dolor ipsam dolor beatae.
-
-
- Qui vel sit expedita eum recusandae nemo. Facere quas dolor eum ut. Aut omnis et qui repellat nihil accusantium. Et vitae beatae ratione. Tenetur sit omnis sa
-
-
-
-
-
-
-
-
-
-
-
-
- Lundi
-
-
- 17:00 - 18:00
-
-
-
-
- Mardi
-
-
- 14:00 - 15:00
-
-
-
-
- Samedi
-
-
- 16:00 - 17:00
-
-
-
-
-
-
-
-
-
-
-
- Lundi
-
-
- 17:00 - 18:00
-
-
-
-
- Mardi
-
-
- 14:00 - 15:00
-
-
-
-
- Samedi
-
-
- 16:00 - 17:00
-
-
-
-
-
-
-
-
-
-
-
-
- 12 €
-
-
- Enfants
-
-
- Facere quas dolor eum ut.
-
-
-
-
- 12 €
-
-
- Enfants
-
-
- Facere quas dolor eum ut.
-
-
-
-
- 12 €
-
-
- Enfants
-
-
- Facere quas dolor eum ut.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/views/about.html b/views/about.html
deleted file mode 100644
index e69de29..0000000
diff --git a/views/about.twig b/views/about.twig
new file mode 100644
index 0000000..50ae9a0
--- /dev/null
+++ b/views/about.twig
@@ -0,0 +1,15 @@
+{% extends "./base.twig" %}
+{% block title %}A propos{% endblock %}
+{% block content %}
+
+
+ Lorem, ipsum dolor sit amet consectetur adipisicing elit. Autem alias sapiente neque? Dignissimos harum blanditiis fugiat eius alias nam repudiandae, et explicabo nihil eos, quos reprehenderit nobis aperiam quibusdam ad?
+
+
+ Takimata dolores sanctus lorem dolor labore dolores lorem voluptua diam ipsum, at accusam sed tempor accusam ea clita et tempor. Duo kasd eirmod at amet sed sed sanctus sit, sed kasd eos dolore amet diam nonumy est ipsum diam, lorem tempor dolore sed tempor sed eos justo no amet. Est.Lorem, ipsum dolor sit amet consectetur adipisicing elit. Autem alias sapiente neque? Dignissimos harum blanditiis fugiat eius alias nam repudiandae, et explicabo nihil eos, quos reprehenderit nobis aperiam quibusdam ad?
+
+
+ Lorem, ipsum dolor sit amet consectetur adipisicing elit. Autem alias sapiente neque? Dignissimos harum blanditiis fugiat eius alias nam repudiandae, et explicabo nihil eos, quos reprehenderit nobis aperiam quibusdam ad?
+
+
+{% endblock %}
diff --git a/views/base.twig b/views/base.twig
index cea3613..93828a4 100644
--- a/views/base.twig
+++ b/views/base.twig
@@ -1,11 +1,55 @@
- {% block title %}{% endblock %} - Forum des associations 2020
+
+
+
+
+ {% block title %}{% endblock %} | Forum des associations 2020
+
+
+
+ {% if layout is not defined %}
+
+ {% endif %}
+
{% block head %}{% endblock %}
- {% block content %}{% endblock %}
- {% block script %}{% endblock %}
+ {% if layout is not defined %}
+
+
+
+ {% block content %}{% endblock %}
+
+
+ {% else %}
+ {% block content %}{% endblock %}
+ {% endif %}
+
+ {% block scripts %}{% endblock %}
\ No newline at end of file
diff --git a/views/emails/rejected.twig b/views/emails/rejected.twig
index 4e8f159..7abc809 100644
--- a/views/emails/rejected.twig
+++ b/views/emails/rejected.twig
@@ -20,5 +20,9 @@ La personne vérifiant ces modifications a laissé un message indiquant la raiso
N'hésitez pas à corriger les informations et à resoumettre pour une nouvelle verification et peut être cette fois ci vous serez publiés...
+
+Pour rappel, vous pouvez utiliser ce lien pour vous connecter sur votre interface : {{ link }}
+
+
Coordialement
diff --git a/views/emails/token.twig b/views/emails/token.twig
index 4072109..0b5453c 100644
--- a/views/emails/token.twig
+++ b/views/emails/token.twig
@@ -4,7 +4,9 @@
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.
-{{ link }}
+
+Vous pouvez utilisez ce lien pour vous connecter sur l'interface web permettant de modifier votre association sur la platforme : {{ link }}
+
Voici votre clée: {{ token }}
diff --git a/views/home.twig b/views/home.twig
index 31cd237..319ee80 100644
--- a/views/home.twig
+++ b/views/home.twig
@@ -1,13 +1,97 @@
{% extends "./base.twig" %}
-{% block title %}Index{% endblock %}
-{% block head %}
-
-{% endblock %}
+{% block title %}Accueil{% endblock %}
+{% block head %}{% endblock %}
{% block content %}
- Index
-
- Welcome on my awesome homepage.
-
+
+
+
+
+
+
+
+ Afficher le menu
+
+
+
+
+
+
+
+
+
+ {% for tag in tags %}
+
+ {% endfor %}
+
+
+
+{% endblock %}
+
+{% block scripts %}
+
+
{% endblock %}
\ No newline at end of file
diff --git a/views/legals.twig b/views/legals.twig
new file mode 100644
index 0000000..261d35e
--- /dev/null
+++ b/views/legals.twig
@@ -0,0 +1,43 @@
+{% extends "./base.twig" %}
+{% block title %}Informations légales{% endblock %}
+{% block content %}
+
+
Informations légales
+
+
Siège social
+
+
+ Espace Condorcet Centre Social
+ 12 rue Jean Moulin,
+ 27600 GAILLON
+
+
+
Contact
+
+
+ Téléphone : 02 32 77 50 80
+ Fax : 02 32 77 50 99
+
+
+
Représentant légal
+
+
Liliane COQUET
+
+
+
Immatriculation
+
+
+ Numéro SIREN : 338 248 206 000 25
+
+ Code APE 88 99 B
+
+
+
Hébergement
+
+
+ Scaleway
+
+ ONLINE SAS BP 438 75366 PARIS CEDEX 08 FRANCE
+
+
+{% endblock %}
diff --git a/views/not-found.twig b/views/not-found.twig
new file mode 100644
index 0000000..aa6857e
--- /dev/null
+++ b/views/not-found.twig
@@ -0,0 +1,7 @@
+{% extends "./base.twig" %}
+{% block title %}Page introuvable{% endblock %}
+{% block content %}
+
+
Page introuvable
+
+{% endblock %}
diff --git a/views/organization.html b/views/organization.html
deleted file mode 100644
index e69de29..0000000
diff --git a/views/organization.twig b/views/organization.twig
new file mode 100644
index 0000000..35e2ed4
--- /dev/null
+++ b/views/organization.twig
@@ -0,0 +1,272 @@
+{% extends "./base.twig" %}
+{% block title %}Association{% endblock %}
+{% block head %}
+
+{% endblock %}
+{% block content %}
+
+
+
+
+
+
+
+
+ {{ data.name }}
+
+
+
+
+
+
+
+
+
+
+ {% if data.descriptionLong|length > 0 %}
+
+
+
+ {{ data.descriptionLong|raw }}
+
+
+ {% endif %}
+
+ {% if data.schedule|length > 0 %}
+
+
+
+ {% for item in data.schedule %}
+
+
+
+
+ {% for when in item.when %}
+
+
+ {{ when.day }}
+
+
+ {{ when.from }} - {{ when.to }}
+
+
+ {% endfor %}
+
+
+
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if data.pricing|length > 0 %}
+
+
+
+ {% for item in data.pricing %}
+
+
+ {{ item.priceLabel }}
+
+
+ {{ item.name }}
+
+
+ {{ item.description }}
+
+
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if data.contacts is defined %}
+
+ {% endif %}
+
+
+{% endblock %}
+
+{% block scripts %}
+
+{% endblock %}
\ No newline at end of file