1
0
forked from ti-nuage/site

Refonte de la page de services (fix #6).

This commit is contained in:
David Soulayrol
2022-10-15 19:10:31 +02:00
parent fb10373252
commit 0f85790dbc
21 changed files with 533 additions and 67 deletions

View File

@@ -0,0 +1,176 @@
const { Buffer } = require('node:buffer')
const debug = require('debug')('metalsmith-tinuage-services')
const config = require('./config.js')
const fs = require('node:fs')
const path = require('path')
module.exports = plugin
/**
* Un plugin pour Metalsmith qui lit une collection de fichiers JSON décrivant
* les services de l'association pour composer la page services.html du site.
*
* @return {Function}
*/
function plugin () {
const Sections = Object.freeze({
OPEN: Symbol('open'),
MEMBERS: Symbol('members')
})
const PageFiller = {
setup: function (page, services) {
this.page = page
this.registry = services.reduce((registry, service) => {
registry[service.section].push(service)
return registry
}, {
[Sections.OPEN]: [],
[Sections.MEMBERS]: []
})
return this
},
fill: function () {
let html = ''
html += this.createSection('Services réservés aux adhérents', Sections.MEMBERS)
html += this.createSection('Services en libre accès, sans adhésion', Sections.OPEN)
this.page.contents = Buffer.concat([
this.page.contents,
Buffer.from(html)
])
},
createSection: function (title, section) {
let html = '<section class="Services">\n' +
'<h2 class="tac mt128 mb64">' + title + '</h2>\n' +
'<div class="Two-cols-One-row">\n'
this.registry[section].forEach((service) => { html += service.html })
html += '</section>'
return html
}
}
const Parser = {
/**
* Traduit la description JSON du service donné en paramètre en un extrait
* HTML.
*/
parseService: function (service) {
let html = '<div class="services">\n' +
'<h3 class="services-title services-icon-' + service.icon + '">' + service.title + '</h3>\n' +
'<p>' + service.description + '</p>\n'
if (service.buttons !== undefined) {
html += '<p class="tac">\n'
service.buttons.forEach((b) => { html += this.createButton(b) })
html += '</p>'
}
if (service.softwares !== undefined) {
html += '<div class="services-software">\n'
service.softwares.forEach((s) => { html += this.createSoftwareDescription(s) })
html += '</div>\n'
}
if (service.links !== undefined) {
html += '<div><p>Autres liens :</p>\n<ul>'
service.links.forEach((l) => { html += this.createListLink(l) })
html += '</ul></div>\n'
}
html += '</div>'
debug('Parsed ' + service.title + '(' + service.order + ')')
service.html = html
service.section = service.open ? Sections.OPEN : Sections.MEMBERS
return service
},
createButton: function (button) {
const cls = button.class || ''
return '<a class="button ' + cls + '" href="' + button.url + '">' + button.label + '</a>\n'
},
createListLink: function (link) {
return '<li><a class="external" href="' + link.url + '">' + link.label + '</a></li>\n'
},
createSoftwareDescription: function (soft) {
let html = '<p>' + soft.description + '</p>\n' +
'<div>'
if (soft.url !== undefined) {
html += this.createSoftwareLink('home.svg', 'Page Web', soft.url)
}
if (soft.sources !== undefined) {
html += this.createSoftwareLink('files.svg', 'Sources du logiciel', soft.sources)
}
soft.licenses.forEach((l) => { html += this.createSoftwareLink('copyright.svg', 'Licence', l.url, l.name) })
html += '</div>\n'
return html
},
createSoftwareLink: function (icon, alt, url, title) {
let element = '<div class="services-link">\n' +
'<a href="' + url + '"><img src="/images/uicons/' + icon + '" alt="' + alt + '"/></a>\n'
if (title !== undefined) {
element += '<br><p>' + title + '</p>'
}
element += '</div>'
return element
}
}
/**
* Lit de manière asynchrone l'ensemble des fichiers JSON décrivant les services.
*
* Seuls les fichiers ayant .json comme extension sont chargés depuis le répertoire fourni.
*/
function loadServices (root, filenames) {
const promises = filenames
.filter(f => { return f.indexOf('.json') !== -1 })
.map(function (f) {
debug('Loading ' + f)
return fs.promises
.readFile(path.join(root, f), 'utf8')
.then((data) => { return JSON.parse(data) })
})
return Promise.all(promises)
}
return function (files, metalsmith, done) {
const root = path.join(config.paths.projectRoot, 'services')
debug('Looking for services in ' + root)
fs.readdir(root, (err, filenames) => {
if (err) {
console.error('%s: %s', root, err)
} else {
loadServices(root, filenames)
.then(function (services) {
const p = Object.create(Parser)
const parsed = services
.sort((a, b) => { return a.order - b.order })
.map((s) => p.parseService(s))
Object.create(PageFiller).setup(files['services.html'], parsed).fill()
done()
})
.catch(function (err) {
console.log(err)
})
}
})
}
}

View File

@@ -12,6 +12,7 @@ const marked = require('marked')
const moment = require('moment')
const permalinks = require('@metalsmith/permalinks')
const rename = require('metalsmith-rename')
const services = require('./metalsmith-tinuage-services')
const sitemap = require('metalsmith-sitemap')
const slug = require('slug')
const statistics = require('./metalsmith-statistics-plugin')
@@ -70,6 +71,7 @@ module.exports = new Metalsmith(config.paths.projectRoot)
smartypants: true,
xhtml: true
}))
.use(services())
.use(excerpts())
.use(permalinks({
pattern: 'blog/:date/:title',