Compare commits

..

3 commits

Author SHA1 Message Date
Cédrix
ddf3f79e7a Rendre cliquables les adresses écrites dans l'agenda
« infos et inscriptions sur https://installparty.alpinux.org » s'affichait
en texte mort dans la fiche d'un événement : à recopier à la main. Les
adresses du descriptif et du lieu deviennent des liens, la ponctuation
qui termine la phrase restant en dehors.

Le texte vient du calendrier, donc de quelqu'un qui écrit du texte et
non du HTML : il est échappé avant d'être lié, et les titres et lieux
des cartes le sont aussi, ce qui n'était pas le cas jusqu'ici.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JYfYUZRcJmmEWJwAxZb4o2
2026-09-23 12:42:22 +02:00
Cédrix
3baa10db65 Griser « Jeux & quiz », qui n'est pas encore ouvert
La carte menait à dynamic.alpinux.org comme si le service était prêt.
Elle reste visible — le projet existe et s'annonce — mais en pointillés,
l'icône en gris, un « Bientôt » à la place du lien : on ne promet plus
une porte qui ne s'ouvre pas.

Le journal des nouveautés perd du même coup son entrée : on n'annonce
pas comme nouveau ce qu'on ne peut pas encore visiter. Elle reviendra
le jour de l'ouverture, avec sa vraie date.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JYfYUZRcJmmEWJwAxZb4o2
2026-09-23 12:42:12 +02:00
Cédrix
d740c2c900 Publier toutes les pages du wiki, et une vraie page d'erreur
L'accueil n'annonçait que six pages du wiki, et rien ne menait au reste :
qui voulait savoir ce qui avait bougé depuis sa dernière visite n'avait
nulle part où aller. modifications.html liste tout ce que le wiki publie,
du plus récemment retouché au plus ancien, avec un filtre par titre ou
par rubrique. Elle lit toutes-les-pages.json, que le build du wiki écrit
à côté de derniers-articles.json ; tant que ce fichier n'existe pas, elle
se rabat sur celui de l'accueil et le dit plutôt que de rester vide.

404.html reprend la charte et propose l'accueil, le wiki, les mises à
jour et les listes. Elle attend encore sa directive Apache : le site
répond aujourd'hui 200 et l'accueil à toute adresse inconnue, ce qui
trompe le visiteur comme le moteur de recherche. Le README dit quoi
poser dans le .htaccess du serveur.

Les deux pages partagent commun.css plutôt que de recopier le style de
l'accueil, qui garde le sien en ligne — une page, une requête. Et le
garde-fou du déploiement, qui ne regardait que l'accueil, refuse
désormais de mettre en ligne n'importe quelle page tronquée : une 404
servie à moitié serait aussi laide qu'une erreur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JYfYUZRcJmmEWJwAxZb4o2
2026-09-23 12:42:03 +02:00
8 changed files with 753 additions and 28 deletions

View file

@ -1,7 +1,8 @@
# www.alpinux.org # www.alpinux.org
La page d'accueil de l'association — du HTML statique, une seule page, servie sur Le site public de l'association — du HTML statique, servi sur `https://alpinux.org` et
`https://alpinux.org` et `https://www.alpinux.org`. `https://www.alpinux.org` : la page d'accueil, la page des mises à jour du wiki, et la
page d'erreur 404.
Elle affiche deux choses qu'elle ne contient pas : l'**agenda**, lu dans le calendrier Elle affiche deux choses qu'elle ne contient pas : l'**agenda**, lu dans le calendrier
public Nextcloud, et les **dernières pages du wiki**, lues dans public Nextcloud, et les **dernières pages du wiki**, lues dans
@ -16,12 +17,13 @@ nouveautés**, lui, vit dans le dépôt : `site/nouveautes.json`.
| Ce que vous voulez faire | Ce que vous faites | | Ce que vous voulez faire | Ce que vous faites |
|---|---| |---|---|
| Modifier la page | Éditer `site/index.html`, pousser sur `main` — la mise en ligne suit | | Modifier l'accueil | Éditer `site/index.html`, pousser sur `main` — la mise en ligne suit |
| Annoncer une nouveauté | Ajouter une entrée en tête de `site/nouveautes.json` (voir plus bas) | | Annoncer une nouveauté | Ajouter une entrée en tête de `site/nouveautes.json` (voir plus bas) |
| Voir le rendu avant de pousser | `python3 -m http.server -d site` puis http://localhost:8000 | | Voir le rendu avant de pousser | `python3 -m http.server -d site` puis http://localhost:8000 |
| Comprendre pourquoi ça n'est pas en ligne | `tail -20 /var/log/www-deploy.log` sur le serveur | | Comprendre pourquoi ça n'est pas en ligne | `tail -20 /var/log/www-deploy.log` sur le serveur |
| Republier sans nouveau commit | `sudo -u abonnelc /opt/www-alpinux/deploy-www.sh` | | Republier sans nouveau commit | `sudo -u abonnelc /opt/www-alpinux/deploy-www.sh` |
| Réinstaller la chaîne de déploiement | `sudo bash deploiement/installer.sh` | | Réinstaller la chaîne de déploiement | `sudo bash deploiement/installer.sh` |
| Reprendre une modification de `deploiement/` | Relancer `installer.sh` : le serveur exécute sa copie dans `/opt/www-alpinux`, pas celle du dépôt |
En local, l'agenda reste vide : `/public-calendars/…` est relayé vers Nextcloud par le En local, l'agenda reste vide : `/public-calendars/…` est relayé vers Nextcloud par le
serveur, pas par `http.server`. Les articles du wiki, eux, s'affichent normalement. serveur, pas par `http.server`. Les articles du wiki, eux, s'affichent normalement.
@ -33,8 +35,11 @@ serveur, pas par `http.server`. Les articles du wiki, eux, s'affichent normaleme
``` ```
alpinux-www/ alpinux-www/
├── site/ ce qui part en ligne, tel quel ├── site/ ce qui part en ligne, tel quel
│ ├── index.html │ ├── index.html l'accueil, son style et ses scripts en ligne
│ ├── nouveautes.json le journal des nouveautés, lu par la page │ ├── modifications.html toutes les pages du wiki, datées
│ ├── 404.html la page d'erreur
│ ├── commun.css la charte des deux pages secondaires
│ ├── nouveautes.json le journal des nouveautés, lu par l'accueil
│ ├── robots.txt │ ├── robots.txt
│ └── sitemap.xml │ └── sitemap.xml
└── deploiement/ la chaîne, versionnée avec le site └── deploiement/ la chaîne, versionnée avec le site
@ -68,6 +73,48 @@ si le fichier disparaît ou devient illisible, la section se masque comme les de
--- ---
## Les mises à jour du wiki
`site/modifications.html` liste **toutes** les pages de wiki.alpinux.org, de la plus
récemment retouchée à la plus ancienne, avec un filtre par titre ou rubrique. Elle lit
`https://wiki.alpinux.org/toutes-les-pages.json`, que le build du wiki publie à chaque
fois — comme il publie déjà `derniers-articles.json` pour l'accueil.
Si ce fichier manque, la page se rabat sur `derniers-articles.json` et le dit : on
obtient les vingt dernières pages au lieu de toutes, plutôt qu'une page vide. Les deux
sources injoignables, elle affiche un message et renvoie vers le wiki.
Le contenu se change donc **dans le dépôt du wiki**, pas ici : ajouter une page à la
navigation du wiki suffit à la faire apparaître.
---
## La page 404
`site/404.html` reprend la charte du site, annonce clairement l'erreur et propose
l'accueil, le wiki, les mises à jour et les listes de diffusion. Elle porte
`noindex, follow` : une page d'erreur n'a rien à faire dans un moteur de recherche,
mais ses liens restent bons à suivre.
**Elle ne sert à rien sans la directive Apache qui va avec.** Le `.htaccess` du
DocumentRoot — qui vit sur le serveur, pas dans ce dépôt public — doit porter :
```apache
ErrorDocument 404 /404.html
```
et **ne pas** renvoyer les adresses inconnues vers l'accueil. Une règle du type
`RewriteRule . /index.html [L]` produit un « soft 404 » : le visiteur voit l'accueil,
le moteur de recherche enregistre une page valide, et personne ne sait que l'adresse
était fausse.
```bash
# ce qu'on doit lire une fois la directive en place
curl -o /dev/null -w '%{http_code}\n' https://alpinux.org/adresse-qui-nexiste-pas # 404
```
---
## Ce qui se passe après un push ## Ce qui se passe après un push
``` ```

View file

@ -13,12 +13,15 @@ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deploy started" >> "$LOG"
cd "$REPO_DIR" cd "$REPO_DIR"
git pull origin main >> "$LOG" 2>&1 git pull origin main >> "$LOG" 2>&1
# Garde-fou : un index.html tronqué ne part pas en ligne. C'est l'équivalent, pour un # Garde-fou : une page tronquée ne part pas en ligne. C'est l'équivalent, pour un
# site sans build, du « mkdocs build --strict » du wiki. # site sans build, du « mkdocs build --strict » du wiki. Toutes les pages y passent,
if ! grep -q "</html>" "$SOURCE/index.html"; then # pas seulement l'accueil : la 404 servie à moitié serait aussi laide qu'une erreur.
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ABANDON : index.html incomplet" >> "$LOG" for page in "$SOURCE"/*.html; do
exit 1 if ! grep -q "</html>" "$page"; then
fi echo "[$(date '+%Y-%m-%d %H:%M:%S')] ABANDON : $(basename "$page") incomplet" >> "$LOG"
exit 1
fi
done
# Pas de --delete, volontairement : le DocumentRoot contient aussi des fichiers qui ne # Pas de --delete, volontairement : le DocumentRoot contient aussi des fichiers qui ne
# sont pas dans ce dépôt — le .htaccess qui relaie /public-calendars vers Nextcloud, les # sont pas dans ce dépôt — le .htaccess qui relaie /public-calendars vers Nextcloud, les

135
site/404.html Normal file
View file

@ -0,0 +1,135 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, follow">
<title>Page introuvable — Alpinux</title>
<link rel="icon" type="image/x-icon" href="https://static.alpinux.org/logo/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="https://static.alpinux.org/logo/favicon-32.png">
<link rel="apple-touch-icon" sizes="192x192" href="https://static.alpinux.org/logo/favicon-192.png">
<link rel="stylesheet" href="/commun.css">
<style>
.code-erreur {
font-size: 4.5rem;
font-weight: 800;
line-height: 1;
color: #9fc5e8;
letter-spacing: -.03em;
margin-bottom: .6rem;
}
.actions { margin-top: 2rem; }
.pistes {
margin-top: 2.8rem;
border-top: 2px solid #e8f1fb;
padding-top: 1.6rem;
}
.pistes h2 {
font-size: 1rem;
color: #0f4e8f;
margin-bottom: 1rem;
}
.pistes-liste {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
gap: .9rem;
list-style: none;
}
.pistes-liste a {
display: block;
background: #fff;
border-radius: 10px;
padding: .9rem 1.1rem;
box-shadow: 0 2px 10px rgba(26,107,191,.08);
text-decoration: none;
color: inherit;
transition: transform .15s, box-shadow .15s;
}
.pistes-liste a:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(26,107,191,.18);
}
.pistes-liste a:hover strong { color: #1a6bbf; }
.pistes-liste strong { display: block; font-size: .95rem; margin-bottom: .15rem; }
.pistes-liste span { font-size: .84rem; color: #7a8ba0; }
</style>
</head>
<body>
<header>
<a href="/" class="logo-wrap">
<img src="https://static.alpinux.org/logo/alpinux-logo.png"
alt="Logo Alpinux — LUG de Savoie"
class="logo-img" width="66" height="66">
<span class="logo-text">
<span class="brand">
<span class="light">A</span><span class="bold">l</span><span class="light">p</span><span class="bold">inux</span>
</span>
<span class="tagline">le LUG de Savoie</span>
</span>
</a>
</header>
<nav aria-label="Navigation principale">
<a href="/">Accueil</a>
<a href="https://wiki.alpinux.org">Wiki</a>
<a href="https://portail.alpinux.org">Portail membres</a>
<a href="https://installparty.alpinux.org">Install Party</a>
<a href="https://gitea.alpinux.org">Gitea</a>
<a href="https://messagerie.alpinux.org">Listes de diffusion</a>
</nav>
<main>
<p class="code-erreur" aria-hidden="true">404</p>
<h1>Cette page n'existe pas… ou plus</h1>
<p class="chapeau">
L'adresse demandée ne correspond à rien sur ce site. Un lien qui a vieilli, une
coquille dans l'adresse, ou une page que nous avons déplacée — dans tous les cas,
l'accueil vous remettra sur les rails.
</p>
<p class="actions">
<a href="/" class="bouton">Retour à l'accueil</a>
</p>
<section class="pistes">
<h2>Ou bien, directement&nbsp;:</h2>
<ul class="pistes-liste">
<li><a href="https://wiki.alpinux.org">
<strong>📖 Le wiki</strong>
<span>Guides, tutoriels et présentations</span></a></li>
<li><a href="/modifications.html">
<strong>🕒 Les mises à jour du wiki</strong>
<span>Toutes les pages, de la plus récente à la plus ancienne</span></a></li>
<li><a href="https://messagerie.alpinux.org">
<strong>✉️ Les listes de diffusion</strong>
<span>S'abonner aux annonces, relire les archives</span></a></li>
<li><a href="https://installparty.alpinux.org">
<strong>🐧 Les Install Party</strong>
<span>Événements &amp; inscriptions</span></a></li>
</ul>
</section>
</main>
<footer>
<p>© Alpinux — Association loi 1901 &nbsp;·&nbsp; Chambéry, Savoie</p>
<p style="margin-top:.4rem;">
<a href="https://wiki.alpinux.org/alpinux/">À propos</a> &nbsp;·&nbsp;
<a href="https://wiki.alpinux.org/alpinux/faq/">FAQ</a> &nbsp;·&nbsp;
<a href="https://wiki.alpinux.org/alpinux/donnees-personnelles/">Vos données</a> &nbsp;·&nbsp;
<a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a>
</p>
</footer>
</body>
</html>

128
site/commun.css Normal file
View file

@ -0,0 +1,128 @@
/* Charte partagée par les pages secondaires — 404, mises à jour du wiki.
L'accueil garde son style en ligne : une page, une requête, rien à attendre. */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: #f3f6fb;
color: #1a1a2e;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ── En-tête ──────────────────────────────────────────────────────── */
header {
background: #1a6bbf;
color: #fff;
padding: 2.2rem 1.5rem 1.8rem;
text-align: center;
}
.logo-wrap {
display: flex;
align-items: center;
justify-content: center;
gap: 1.1rem;
flex-wrap: wrap;
text-decoration: none;
}
.logo-img { width: 66px; height: 66px; border-radius: 10px; }
.logo-text { text-align: left; }
.brand {
display: block;
font-size: 2.1rem;
line-height: 1;
color: #fff;
letter-spacing: -.02em;
}
.brand .light { font-weight: 300; }
.brand .bold { font-weight: 800; }
.tagline {
display: block;
font-size: .82rem;
font-weight: 300;
letter-spacing: .18em;
color: rgba(255,255,255,.8);
margin-top: .25rem;
text-transform: lowercase;
}
/* ── Navigation ───────────────────────────────────────────────────── */
nav {
background: #0f4e8f;
display: flex;
justify-content: center;
gap: .2rem;
flex-wrap: wrap;
padding: .4rem 1rem;
}
nav a {
color: rgba(255,255,255,.85);
text-decoration: none;
font-size: .88rem;
padding: .45rem .9rem;
border-radius: 4px;
transition: background .15s;
}
nav a:hover { background: rgba(255,255,255,.15); color: #fff; }
nav a[aria-current="page"] { background: rgba(255,255,255,.18); color: #fff; }
/* ── Corps ────────────────────────────────────────────────────────── */
main {
max-width: 860px;
width: 100%;
margin: 0 auto;
padding: 3rem 1.5rem;
flex: 1;
}
h1 {
font-size: 1.6rem;
color: #0f4e8f;
line-height: 1.25;
margin-bottom: .8rem;
}
.chapeau {
font-size: 1rem;
line-height: 1.7;
color: #4a5a6a;
max-width: 62ch;
}
.chapeau a { color: #1a6bbf; }
.bouton {
display: inline-block;
background: #1a6bbf;
color: #fff;
text-decoration: none;
font-weight: 600;
font-size: .95rem;
padding: .7rem 1.4rem;
border-radius: 8px;
transition: background .15s, transform .15s;
}
.bouton:hover { background: #0f4e8f; transform: translateY(-2px); }
/* ── Pied de page ─────────────────────────────────────────────────── */
footer {
background: #0f4e8f;
color: rgba(255,255,255,.75);
text-align: center;
padding: 1.6rem 1.5rem;
font-size: .85rem;
}
footer a { color: rgba(255,255,255,.9); text-decoration: none; }
footer a:hover { text-decoration: underline; }

View file

@ -437,6 +437,8 @@
} }
.ev-modal-body { font-size: .92rem; line-height: 1.6; color: #444; } .ev-modal-body { font-size: .92rem; line-height: 1.6; color: #444; }
.ev-modal-body a { color: #1a6bbf; word-break: break-word; }
.ev-modal-body a:hover { text-decoration: none; }
.event-date { .event-date {
font-size: .82rem; font-size: .82rem;
@ -636,6 +638,36 @@
color: #1a6bbf; color: #1a6bbf;
} }
/* Un service annoncé mais pas encore ouvert : visible, pas cliquable. */
.service-card.a-venir {
background: #fbfcfe;
border: 1px dashed #c9d8e8;
box-shadow: none;
color: #8a99aa;
cursor: default;
}
.service-card.a-venir:hover {
transform: none;
box-shadow: none;
color: #8a99aa;
}
.service-card.a-venir .service-icon { filter: grayscale(1); opacity: .6; }
.service-bientot {
display: inline-block;
margin-top: .5rem;
font-size: .68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .06em;
color: #7a8ba0;
background: #eef3f9;
border-radius: 3px;
padding: .15rem .5rem;
}
.service-icon { .service-icon {
font-size: 2rem; font-size: 2rem;
margin-bottom: .6rem; margin-bottom: .6rem;
@ -702,6 +734,7 @@
<a href="https://portail.alpinux.org">Portail membres</a> <a href="https://portail.alpinux.org">Portail membres</a>
<a href="https://installparty.alpinux.org">Install Party</a> <a href="https://installparty.alpinux.org">Install Party</a>
<a href="https://gitea.alpinux.org">Gitea</a> <a href="https://gitea.alpinux.org">Gitea</a>
<a href="/modifications.html">Mises à jour du wiki</a>
<a href="https://messagerie.alpinux.org">Listes de diffusion</a> <a href="https://messagerie.alpinux.org">Listes de diffusion</a>
<a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a> <a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a>
</nav> </nav>
@ -755,6 +788,8 @@
<div class="wiki-list" id="wiki-list"></div> <div class="wiki-list" id="wiki-list"></div>
<p class="wiki-more"> <p class="wiki-more">
<a href="https://wiki.alpinux.org">Parcourir tout le wiki →</a> <a href="https://wiki.alpinux.org">Parcourir tout le wiki →</a>
&nbsp;·&nbsp;
<a href="/modifications.html">Toutes les mises à jour →</a>
</p> </p>
</section> </section>
@ -785,11 +820,12 @@
<div class="service-name">Listes de diffusion</div> <div class="service-name">Listes de diffusion</div>
<div class="service-desc">S'abonner aux annonces, relire les archives</div> <div class="service-desc">S'abonner aux annonces, relire les archives</div>
</a> </a>
<a href="https://dynamic.alpinux.org" class="service-card"> <div class="service-card a-venir" aria-disabled="true">
<div class="service-icon">🎮</div> <div class="service-icon">🎮</div>
<div class="service-name">Jeux &amp; quiz</div> <div class="service-name">Jeux &amp; quiz</div>
<div class="service-desc">Mini-jeux et questionnaires interactifs</div> <div class="service-desc">Mini-jeux et questionnaires interactifs</div>
</a> <span class="service-bientot">Bientôt</span>
</div>
</div> </div>
<section id="news-section" hidden> <section id="news-section" hidden>
@ -1054,6 +1090,30 @@
} }
} }
/* Le calendrier est saisi par des humains : son texte est du texte, jamais du HTML. */
function echapper(texte) {
return String(texte == null ? '' : texte)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
/* Rend cliquables les adresses écrites dans un descriptif. Le texte reçu est déjà
échappé — le motif s'arrête donc avant une balise — et la ponctuation qui termine
une phrase reste en dehors du lien. */
function avecLiens(texteEchappe) {
return texteEchappe.replace(/https?:\/\/[^\s<]+/g, function(url) {
var queue = '';
var fin = /[.,;:!?»)\]]+$/.exec(url);
if (fin) {
queue = fin[0];
url = url.slice(0, url.length - queue.length);
}
return '<a href="' + url + '" target="_blank" rel="noopener">' + url + '</a>' + queue;
});
}
function showDetail(date, evs, el) { function showDetail(date, evs, el) {
el.hidden = false; el.hidden = false;
el.innerHTML = '<strong>' + el.innerHTML = '<strong>' +
@ -1063,9 +1123,9 @@
var time = e.allDay ? '' : var time = e.allDay ? '' :
' <span class="ev-loc">' + ' <span class="ev-loc">' +
e.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) + '</span>'; e.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) + '</span>';
return '<li>' + e.title + time + return '<li>' + echapper(e.title) + time +
(e.desc ? ' <span class="ev-has-desc" title="Descriptif disponible">ℹ</span>' : '') + (e.desc ? ' <span class="ev-has-desc" title="Descriptif disponible">ℹ</span>' : '') +
(e.location ? '<span class="ev-loc"> — ' + e.location + '</span>' : '') + (e.location ? '<span class="ev-loc"> — ' + echapper(e.location) + '</span>' : '') +
'</li>'; '</li>';
}).join('') + '</ul>'; }).join('') + '</ul>';
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
@ -1086,11 +1146,11 @@
var descHtml = ''; var descHtml = '';
if (ev.desc) { if (ev.desc) {
descHtml = '<p style="margin-top:.8rem;color:#555;font-size:.88rem;white-space:pre-line">' + descHtml = '<p style="margin-top:.8rem;color:#555;font-size:.88rem;white-space:pre-line">' +
ev.desc.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') + '</p>'; avecLiens(echapper(ev.desc)) + '</p>';
} }
body.innerHTML = body.innerHTML =
'<p>📅 ' + dateStr + '</p>' + '<p>📅 ' + dateStr + '</p>' +
(ev.location ? '<p style="margin-top:.4rem">📍 ' + ev.location + '</p>' : '') + (ev.location ? '<p style="margin-top:.4rem">📍 ' + avecLiens(echapper(ev.location)) + '</p>' : '') +
descHtml; descHtml;
modal.hidden = false; modal.hidden = false;
document.getElementById('ev-modal').querySelector('.ev-modal-close').focus(); document.getElementById('ev-modal').querySelector('.ev-modal-close').focus();
@ -1122,8 +1182,8 @@
: ev.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) + : ev.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) +
(ev.end ? ' – ' + ev.end.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) : '')) + (ev.end ? ' – ' + ev.end.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) : '')) +
'</div>' + '</div>' +
'<div class="event-title">' + ev.title + '</div>' + '<div class="event-title">' + echapper(ev.title) + '</div>' +
(ev.location ? '<div class="event-where">📍 ' + ev.location + '</div>' : ''); (ev.location ? '<div class="event-where">📍 ' + echapper(ev.location) + '</div>' : '');
card.addEventListener('click', function() { openModal(ev); }); card.addEventListener('click', function() { openModal(ev); });
container.appendChild(card); container.appendChild(card);
}); });
@ -1145,8 +1205,8 @@
card.innerHTML = card.innerHTML =
(ev.desc ? '<span class="ev-has-desc" title="Descriptif disponible">ℹ</span>' : '') + (ev.desc ? '<span class="ev-has-desc" title="Descriptif disponible">ℹ</span>' : '') +
'<div class="event-date">' + fmtDate(ev.start, ev.allDay) + '</div>' + '<div class="event-date">' + fmtDate(ev.start, ev.allDay) + '</div>' +
'<div class="event-title">' + ev.title + '</div>' + '<div class="event-title">' + echapper(ev.title) + '</div>' +
(ev.location ? '<div class="event-where">📍 ' + ev.location + '</div>' : ''); (ev.location ? '<div class="event-where">📍 ' + echapper(ev.location) + '</div>' : '');
card.addEventListener('click', function() { openModal(ev); }); card.addEventListener('click', function() { openModal(ev); });
container.appendChild(card); container.appendChild(card);
}); });

352
site/modifications.html Normal file
View file

@ -0,0 +1,352 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Toutes les pages du wiki d'Alpinux, de la plus récemment mise à jour à la plus ancienne.">
<meta name="robots" content="index, follow">
<title>Les mises à jour du wiki — Alpinux</title>
<link rel="canonical" href="https://alpinux.org/modifications.html">
<link rel="icon" type="image/x-icon" href="https://static.alpinux.org/logo/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="https://static.alpinux.org/logo/favicon-32.png">
<link rel="apple-touch-icon" sizes="192x192" href="https://static.alpinux.org/logo/favicon-192.png">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Alpinux">
<meta property="og:title" content="Les mises à jour du wiki — Alpinux">
<meta property="og:description" content="Toutes les pages du wiki d'Alpinux, de la plus récemment mise à jour à la plus ancienne.">
<meta property="og:url" content="https://alpinux.org/modifications.html">
<meta property="og:image" content="https://static.alpinux.org/logo/alpinux-logo-512.png">
<meta property="og:locale" content="fr_FR">
<link rel="stylesheet" href="/commun.css">
<style>
.barre {
display: flex;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
margin: 1.8rem 0 1.2rem;
}
.barre input {
flex: 1 1 260px;
font: inherit;
font-size: .95rem;
padding: .6rem .9rem;
border: 2px solid #dce8f5;
border-radius: 8px;
background: #fff;
color: inherit;
min-width: 0;
}
.barre input:focus {
outline: none;
border-color: #1a6bbf;
}
.compte {
font-size: .85rem;
color: #7a8ba0;
white-space: nowrap;
}
.mois {
font-size: .78rem;
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
color: #0f4e8f;
background: #e8f1fb;
border-radius: 4px;
padding: .3rem .6rem;
display: inline-block;
margin: 1.8rem 0 .8rem;
}
.pages { list-style: none; }
.page-ligne {
display: grid;
grid-template-columns: 5.5rem 1fr;
gap: 1rem;
align-items: baseline;
background: #fff;
border-radius: 10px;
border-left: 4px solid #9fc5e8;
box-shadow: 0 2px 10px rgba(26,107,191,.08);
padding: .85rem 1.1rem;
margin-bottom: .6rem;
text-decoration: none;
color: inherit;
transition: transform .15s, box-shadow .15s;
}
.page-ligne:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(26,107,191,.18);
}
.page-ligne:hover .page-titre { color: #1a6bbf; }
.page-date {
font-size: .8rem;
font-weight: 700;
color: #7a8ba0;
letter-spacing: .04em;
text-transform: uppercase;
white-space: nowrap;
}
.page-titre {
display: block;
font-size: 1rem;
font-weight: 600;
line-height: 1.35;
}
.page-rubrique {
display: block;
font-size: .8rem;
color: #7a8ba0;
margin-top: .15rem;
}
.badge {
background: #f5a623;
color: #fff;
font-size: .66rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .06em;
padding: .1rem .45rem;
border-radius: 3px;
margin-left: .5rem;
vertical-align: .1em;
}
.etat {
background: #fff;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(26,107,191,.08);
padding:1.4rem;
font-size: .95rem;
color: #4a5a6a;
line-height: 1.6;
}
.etat a { color: #1a6bbf; }
.genere {
margin-top: 2rem;
font-size: .8rem;
color: #93a2b3;
}
@media (max-width: 560px) {
.page-ligne { grid-template-columns: 1fr; gap: .25rem; }
}
</style>
</head>
<body>
<header>
<a href="/" class="logo-wrap">
<img src="https://static.alpinux.org/logo/alpinux-logo.png"
alt="Logo Alpinux — LUG de Savoie"
class="logo-img" width="66" height="66">
<span class="logo-text">
<span class="brand">
<span class="light">A</span><span class="bold">l</span><span class="light">p</span><span class="bold">inux</span>
</span>
<span class="tagline">le LUG de Savoie</span>
</span>
</a>
</header>
<nav aria-label="Navigation principale">
<a href="/">Accueil</a>
<a href="https://wiki.alpinux.org">Wiki</a>
<a href="/modifications.html" aria-current="page">Mises à jour</a>
<a href="https://portail.alpinux.org">Portail membres</a>
<a href="https://installparty.alpinux.org">Install Party</a>
<a href="https://messagerie.alpinux.org">Listes de diffusion</a>
</nav>
<main>
<h1>Les mises à jour du wiki</h1>
<p class="chapeau">
Toutes les pages de <a href="https://wiki.alpinux.org">wiki.alpinux.org</a>, de la
plus récemment retouchée à la plus ancienne. Les dates viennent de l'historique du
dépôt : c'est le jour où le texte a changé, pas celui où le fichier a été recopié.
Déplacer une page ne la fait donc pas remonter ici.
</p>
<div class="barre">
<label for="filtre" class="sr-only" hidden>Filtrer les pages</label>
<input id="filtre" type="search" placeholder="Filtrer par titre ou rubrique…"
autocomplete="off" hidden>
<span class="compte" id="compte"></span>
</div>
<div id="etat" class="etat">Chargement de la liste…</div>
<ol class="pages" id="pages"></ol>
<p class="genere" id="genere" hidden></p>
</main>
<footer>
<p>© Alpinux — Association loi 1901 &nbsp;·&nbsp; Chambéry, Savoie</p>
<p style="margin-top:.4rem;">
<a href="https://wiki.alpinux.org/alpinux/">À propos</a> &nbsp;·&nbsp;
<a href="https://wiki.alpinux.org/alpinux/faq/">FAQ</a> &nbsp;·&nbsp;
<a href="https://wiki.alpinux.org/alpinux/donnees-personnelles/">Vos données</a> &nbsp;·&nbsp;
<a href="https://messagerie.alpinux.org/archives">Archives des listes</a> &nbsp;·&nbsp;
<a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a>
</p>
</footer>
<script>
(function () {
'use strict';
/* Le wiki publie l'inventaire complet à chaque build. Le second fichier est celui
que lit l'accueil : il ne porte que vingt entrées, mais il existe depuis plus
longtemps — s'en contenter vaut mieux qu'une page vide. */
var SOURCES = [
{ url: 'https://wiki.alpinux.org/toutes-les-pages.json', clef: 'pages', partiel: false },
{ url: 'https://wiki.alpinux.org/derniers-articles.json', clef: 'articles', partiel: true }
];
var etat = document.getElementById('etat');
var liste = document.getElementById('pages');
var compte = document.getElementById('compte');
var filtre = document.getElementById('filtre');
var genere = document.getElementById('genere');
var MOIS = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'];
var toutes = [];
function echappe(texte) {
var d = document.createElement('div');
d.textContent = texte;
return d.innerHTML;
}
function sansAccent(texte) {
return texte.normalize ? texte.normalize('NFD').replace(/[̀-ͯ]/g, '') : texte;
}
function ligne(page) {
var a = document.createElement('a');
a.className = 'page-ligne';
a.href = page.url;
a.innerHTML =
'<time class="page-date" datetime="' + echappe(page.date) + '">' +
page.d.getDate() + ' ' + MOIS[page.d.getMonth()].slice(0, 4) + '.' +
'</time>' +
'<span>' +
'<span class="page-titre">' + echappe(page.titre) +
(page.nouveau ? '<span class="badge">Nouveau</span>' : '') +
'</span>' +
(page.rubrique ? '<span class="page-rubrique">' + echappe(page.rubrique) + '</span>' : '') +
'</span>';
return a;
}
function dessiner(pages) {
liste.textContent = '';
var moisCourant = '';
pages.forEach(function (page) {
var cle = page.d.getFullYear() + '-' + page.d.getMonth();
if (cle !== moisCourant) {
moisCourant = cle;
var titre = document.createElement('li');
titre.className = 'mois';
titre.textContent = MOIS[page.d.getMonth()] + ' ' + page.d.getFullYear();
liste.appendChild(titre);
}
var li = document.createElement('li');
li.appendChild(ligne(page));
liste.appendChild(li);
});
compte.textContent = pages.length + (pages.length > 1 ? ' pages' : ' page') +
(pages.length !== toutes.length ? ' sur ' + toutes.length : '');
}
function appliquerLeFiltre() {
var q = sansAccent(filtre.value.trim().toLowerCase());
if (!q) return dessiner(toutes);
dessiner(toutes.filter(function (page) {
return sansAccent((page.titre + ' ' + (page.rubrique || '')).toLowerCase()).indexOf(q) !== -1;
}));
}
async function lire(source) {
var r = await fetch(source.url, { cache: 'no-cache' });
if (!r.ok) throw new Error(r.status);
var data = await r.json();
var brut = data[source.clef];
if (!Array.isArray(brut) || !brut.length) throw new Error('vide');
return { data: data, brut: brut };
}
async function charger() {
var lu = null, source = null;
for (var i = 0; i < SOURCES.length && !lu; i++) {
try {
lu = await lire(SOURCES[i]);
source = SOURCES[i];
} catch (_) { /* on essaie la source suivante */ }
}
if (!lu) {
etat.innerHTML = 'La liste n\'a pas pu être chargée. ' +
'Le wiki reste consultable sur <a href="https://wiki.alpinux.org">wiki.alpinux.org</a>.';
return;
}
toutes = lu.brut
.map(function (page) {
var d = new Date(page.date);
return (page && page.titre && page.url && !isNaN(d)) ?
{ titre: page.titre, url: page.url, date: page.date,
nouveau: !!page.nouveau, rubrique: page.rubrique || '', d: d } : null;
})
.filter(Boolean)
.sort(function (a, b) { return b.d - a.d; });
if (!toutes.length) {
etat.textContent = 'Le wiki n\'annonce aucune page pour le moment.';
return;
}
etat.hidden = true;
filtre.hidden = false;
filtre.addEventListener('input', appliquerLeFiltre);
dessiner(toutes);
var quand = new Date(lu.data.genere);
genere.textContent =
(source.partiel
? 'Le wiki ne publie pour l\'instant que ses vingt dernières pages. '
: '') +
(isNaN(quand) ? '' : 'Liste publiée par le wiki le ' +
quand.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric' }) +
' à ' + quand.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) + '.');
genere.hidden = !genere.textContent;
}
document.addEventListener('DOMContentLoaded', charger);
})();
</script>
</body>
</html>

View file

@ -22,12 +22,6 @@
"texte": "Une page explique quelles données l'association conserve, pourquoi, et comment en demander la suppression. Son lien vit en pied de page.", "texte": "Une page explique quelles données l'association conserve, pourquoi, et comment en demander la suppression. Son lien vit en pied de page.",
"url": "https://wiki.alpinux.org/alpinux/donnees-personnelles/" "url": "https://wiki.alpinux.org/alpinux/donnees-personnelles/"
}, },
{
"date": "2026-05-03",
"titre": "Jeux & quiz",
"texte": "dynamic.alpinux.org rassemble les mini-jeux et les questionnaires interactifs préparés pour nos rencontres.",
"url": "https://dynamic.alpinux.org"
},
{ {
"date": "2026-05-03", "date": "2026-05-03",
"titre": "Un agenda qui se tient à jour tout seul", "titre": "Un agenda qui se tient à jour tout seul",

View file

@ -2,8 +2,14 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url> <url>
<loc>https://alpinux.org/</loc> <loc>https://alpinux.org/</loc>
<lastmod>2026-05-03</lastmod> <lastmod>2026-09-23</lastmod>
<changefreq>monthly</changefreq> <changefreq>monthly</changefreq>
<priority>1.0</priority> <priority>1.0</priority>
</url> </url>
<url>
<loc>https://alpinux.org/modifications.html</loc>
<lastmod>2026-09-23</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
</urlset> </urlset>