Refonte de la page de services (fix #6).
This commit is contained in:
176
scripts/metalsmith-tinuage-services.js
Normal file
176
scripts/metalsmith-tinuage-services.js
Normal 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)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user