diff --git a/AGENTS.md b/AGENTS.md index bf6f401..76a1a5e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ **Dépôt Gitea officiel :** `https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme.git` **Chemin de déploiement VPS :** `/var/www/fastuser/data/www/cgt-fha.fr/wp-content/themes/cgt-fha/` **Ressource Skills locale :** `../skills/` (`../skills/wp-performance/SKILL.md` et `../skills/wp-block-themes/SKILL.md`) -**Ressource graphique locale :** `../ressources/banniere union locale noir.png` (à copier dans `assets/img/banniere-ul-noir.png` et utiliser comme logo par défaut de l'en-tête) +**Ressources graphiques locales :** `../ressources/banniere union locale noir.png` (`assets/img/banniere-ul-noir.png` pour l'en-tête), `../ressources/banniere union locale blanc.png` (`assets/img/banniere-ul-blanc.png` pour le pied de page), logos officiels du réseau CGT (`logo-cgt-09.jpg`, `logo-cgt.png`, `logo-nvo.png`, `logo-ihs.jpg`, `logo-indecosa.png`) et `screenshot.png` (`1200x900`). **Maquette de référence visuelle :** L'utilisateur fournit l'image de maquette validée en pièce jointe. Respecte fidèlement sa hiérarchie visuelle, ses proportions, ses espacements et son style graphique. --- @@ -22,9 +22,11 @@ Tu es l'unique développeur de ce thème WordPress sur mesure. Ce thème remplac - CSS moderne natif (`CSS Custom Properties`, `CSS Grid`, `Flexbox`, mobile-first). Aucune bibliothèque CSS externe (pas de Bootstrap ni Tailwind CDN)[cite: 6]. - JavaScript *Vanilla* ES6 minimaliste uniquement pour le menu mobile accessible et les onglets de l'admin (pas de jQuery)[cite: 6]. - Aucune police Google Fonts chargée via CDN tiers : utilise des polices système modernes (`system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`) ou des fichiers hébergés localement[cite: 6]. -- **100 % Modifiable sans toucher au code :** +- **100 % Modifiable sans toucher au code & Préservation stricte des réglages :** - En dehors des articles locaux (publiés dans WordPress) et des actualités confédérales extraites automatiquement depuis `cgt.fr`, **tous les textes, images, icônes, liens, boutons et coordonnées du thème doivent être modifiables depuis l'administration WordPress** (*Apparence > Réglages UL CGT* + *Apparence > Menus* + *Médiathèque*). - - **Pré-remplissage obligatoire par défaut :** Toutes les options doivent posséder des valeurs par défaut complètes (définies via `wp_parse_args()`) afin que le site soit immédiatement fonctionnel et complet dès l'activation du thème, sans aucune zone vide. + - **Pré-remplissage par défaut & Non-écrasement strict :** Toutes les options possèdent des valeurs par défaut complètes lues dynamiquement via `wp_parse_args( get_option( 'cgt_fha_options', array() ), $defaults )`. Aucun hook (`after_switch_theme`, `upgrader_process_complete`, `init`) ne doit écraser les options enregistrées en base (`wp_options`), les menus ou les médias lors des mises à jour ; d'éventuelles évolutions de structure passent uniquement par la fonction versionnée non destructive `cgt_fha_maybe_migrate_options()`. +- **Identité territoriale Foix & Haute-Ariège + Syndicat Multipros :** + - L'Union Locale CGT de Foix et de la Haute-Ariège fédère l'ensemble des syndicats CGT de son périmètre et assure le rattachement direct des salarié·e·s isolé·e·s (TPE/PME sans section syndicale) via son syndicat multiprofessionnel (*Multipros*). - **Intégration propre avec les extensions sœurs (Gitea) :** - Le thème gère le visuel, le SEO/OpenGraph de base, le bandeau de mobilisation et l'encart d'actualités confédérales[cite: 6]. - Il prévoit des hooks/emplacements propres (`do_action(...)` / support de shortcodes) pour accueillir les sorties des extensions `cgt-wp-calendar` (Agenda) et `cgt-wp-forms` (Formulaires Défense, Adhésion, Formation), tout en affichant un rendu cohérent même si ces extensions ne sont pas encore activées[cite: 6]. @@ -88,9 +90,9 @@ Placé **immédiatement sous le Hero et avant les actualités** (fond blanc `#ff 3. **Carte 3 :** Icône bulles d'échange / livre (SVG propre) • Titre `S'INFORMER & SE FORMER` • Texte par défaut : *« Connaître les actualités des luttes en Haute-Ariège et bénéficier de vos droits à la formation syndicale (CFESES). »* - *Note :* Les icônes (sélecteur parmi une bibliothèque SVG intégrée), titres et textes de ces 3 cartes sont modifiables dans l'admin WordPress. -### Section 3 : Vitrine 1 — « SUR LE TERRAIN EN HAUTE-ARIÈGE » (Actualités Locales) +### Section 3 : Vitrine 1 — « SUR LE TERRAIN À FOIX ET EN HAUTE-ARIÈGE » (Actualités Locales) - **Priorité éditoriale locale :** Affiche automatiquement les **3 derniers articles publiés** dans le WordPress de l'UL (`WP_Query` sur `post_type => 'post'`, `posts_per_page => 3`)[cite: 6]. -- **Titre de section :** `SUR LE TERRAIN EN HAUTE-ARIÈGE` centré en majuscules (`

`). +- **Titre de section :** `SUR LE TERRAIN À FOIX ET EN HAUTE-ARIÈGE` centré en majuscules (`

`), personnalisable dans l'admin. - **Mise en page des 3 cartes** (fidèle à la maquette) : - Grille 3 colonnes (`gap: 1.5rem`). - Image à la une en haut avec coins arrondis (`border-radius: 12px; height: 190px; object-fit: cover; width: 100%;`), avec fallback SVG propre si un article n'a pas d'image. @@ -110,43 +112,43 @@ Placé **immédiatement sous le Hero et avant les actualités** (fond blanc `#ff - **Cache & WP-Cron :** Stockage dans le transient `cgt_fha_confederal_feed` (expiration 3h) + sauvegarde de secours `cgt_fha_confederal_feed_backup` conservée en cas d'indisponibilité réseau. Événement `WP-Cron` toutes les 2 heures + bouton *"Actualiser les actus confédérales"* dans la barre d'admin WordPress (`admin_bar_menu`). - **Rendu HTML/CSS fidèle à la maquette :** - **Bloc supérieur « À LA UNE » :** - - Conteneur centré (`max-width: 880px; margin: 0 auto 2.5rem auto;`), disposition horizontale Flexbox (`align-items: center`). - - À gauche (`flex: 0 0 58%`) : Image de l'article (`border-radius: 14px; height: 290px; object-fit: cover;`) avec un badge overlay discret en bas à droite `À LA UNE`. + - Conteneur centré aligné sur la largeur de la grille des 3 cartes ci-dessous (`max-width: 1040px; margin: 0 auto 2.5rem auto;`), disposition horizontale Flexbox (`align-items: center`). + - À gauche (`flex: 0 0 58%`) : Image de l'article (`border-radius: 14px; height: 290px; object-fit: cover;`) avec un badge overlay discret en bas à droite `À LA UNE` (`right: 56px;` pour ne jamais être masqué par le chevauchement). - À droite (`flex: 1; margin-left: -40px; position: relative; z-index: 2;`) : **Boîte blanche arrondie chevauchant le bord droit de l'image** (`background: #fff; border: 1px solid #cbd5e1; border-radius: 14px; padding: 1.75rem; box-shadow: 0 10px 25px rgba(0,0,0,0.06); text-align: center;`) contenant : - Le titre de l'article (ou surtitre `À LA UNE` + titre). - La description / chapô nettoyé. - Le bouton pilule encadré (`border: 1.5px solid #1a1a1a; border-radius: 999px; padding: 6px 28px; font-weight: 700;`) : **« LIRE SUR CGT.FR ↗ »** (`target="_blank" rel="noopener noreferrer"`). - *Responsive mobile (`< 768px`) :* `flex-direction: column`, `margin-left: 0`, `margin-top: -24px`. - **Grille inférieure (3 colonnes : ARTICLE 1, ARTICLE 2, ARTICLE 3) :** - - Grille CSS `grid-template-columns: repeat(3, 1fr); gap: 1.5rem;` (1 colonne sous `768px`). + - Grille CSS `grid-template-columns: repeat(3, 1fr); gap: 1.5rem; max-width: 1040px; margin: 0 auto;` (1 colonne sous `768px`). - Chaque carte affiche l'image en haut (`height: 190px; width: 100%; object-fit: cover; border-radius: 14px;`), puis une **boîte blanche arrondie qui remonte par-dessus le bas de l'image** (`margin-top: -24px; position: relative; z-index: 2; background: #fff; border: 1px solid #cbd5e1; border-radius: 14px; padding: 1.25rem; box-shadow: 0 6px 18px rgba(0,0,0,0.05); display: flex; flex-direction: column; text-align: center; flex: 1;`) contenant : - Le titre de l'article en gras. - La description courte (~120 caractères). - - En bas (`margin-top: auto; padding-top: 1rem;`), le bouton pilule encadré **« LIEN ↗ »** (`target="_blank" rel="noopener noreferrer"`). + - En bas (`margin-top: auto; padding-top: 1rem;`), le bouton pilule encadré **« Lire la suite → »** (`target="_blank" rel="noopener noreferrer"`). ### Section 5 : Parcours « SALARIÉS ISOLÉS & TPE : VOUS N'ÊTES PAS SEULS » - **Fond :** Blanc (`#ffffff`). - **Contenu (entièrement éditable dans l'admin) :** - Bloc pédagogique destiné aux salariés des petites entreprises (< 11 salariés), commerces, artisanat ou chantiers sans CSE[cite: 6]. - - Met en avant la **confidentialité absolue** de toute démarche vis-à-vis de l'employeur, le rôle du syndicat **Multipros**, et l'accompagnement par les **Conseillers du Salarié** lors des entretiens préalables[cite: 6]. + - Met en avant la **confidentialité absolue** de toute démarche vis-à-vis de l'employeur, le rattachement direct au syndicat **Multipros** de l'Union Locale, et l'accompagnement par les **Conseillers du Salarié** lors des entretiens préalables[cite: 6]. - Bouton d'action rouge CGT : *« Contacter l'UL en toute confidentialité »*[cite: 6]. ### Section 6 : Vie Militante, Formation Syndicale (CFESES) & Appli Mobile - **Fond :** Gris doux (`#f1f3f5`). -- **Grille 2 colonnes (`grid-template-columns: 1fr 1fr; gap: 2rem;`) :** +- **Grille adaptative (2 colonnes si l'Appli Mobile est activée, 1 colonne centrée sinon) :** 1. **Colonne gauche — Formation Syndicale (CFESES) :** Rappelle le droit légal de chaque salarié au **Congé de Formation Économique, Sociale, Environnementale et Syndicale** (maintien de salaire, demande à remettre à l'employeur 30 jours avant) et l'aide à la vie syndicale[cite: 6, 8]. - 2. **Colonne droite — L'Application Mobile UL CGT Foix Haute-Ariège :** Présente l'application Android/KMP (actualités locales, alertes mobilisation Push, carte de syndiqué virtuelle & 13 timbres 100 % hors-ligne) avec emplacement pour le lien de téléchargement direct (`.apk` / Play Store) et l'image QR Code (configurables dans l'admin)[cite: 8]. + 2. **Colonne droite — L'Application Mobile UL CGT Foix Haute-Ariège (désactivable et désactivée par défaut) :** Présente l'application Android/KMP (actualités locales, alertes mobilisation Push, carte de syndiqué virtuelle & 13 timbres 100 % hors-ligne) avec emplacement pour le lien de téléchargement direct (`.apk` / Play Store) et l'image QR Code (configurables et activables dans l'admin)[cite: 8]. ### Section 7 : Réseau Syndical CGT - **Fond :** Blanc (`#ffffff`). -- Bandeau de liens structurés vers l'écosystème confédéral et départemental : **UD CGT 09**, **Confédération CGT (`cgt.fr`)**, **La NVO (`nvo.fr`)**, **IHS CGT**, **INDECOSA-CGT**[cite: 6]. +- Grille de **5 cartes verticales** (logo officiel circulaire/encadré en haut, titre, description courte, bouton *« Visiter le site ↗ »*) vers l'écosystème confédéral et départemental : **UD CGT 09**, **Confédération CGT (`cgt.fr`)**, **La NVO (`nvo.fr`)**, **IHS CGT**, **INDECOSA-CGT**[cite: 6]. --- ## 5. PIED DE PAGE (`footer.php`) — SOUVERAIN & PRATIQUE - **Aucune mention** *"Propulsé par WordPress"* ou *"Thème..."*[cite: 6]. - **Fond sombre (`#1c1e21`), texte blanc/gris clair, grille 3 colonnes :** - 1. **Union Locale CGT Foix Haute-Ariège :** Logo (version claire/monochrome), courte présentation, et appel militant : *« Une lutte dans votre entreprise, une info à partager ? Contactez l'UL »*. + 1. **Union Locale CGT Foix Haute-Ariège :** Logo version blanche (`assets/img/banniere-ul-blanc.png`, modifiable dans l'admin), courte présentation (union de tous les syndicats du périmètre & syndicat Multipros pour les isolé·e·s), et appel militant : *« Une lutte dans votre entreprise, une info à partager ? Contactez l'UL »*. 2. **Nos Locaux & Permanences (visible sur 100 % des pages) :**[cite: 6] - Adresse : `Espace Sabart, 10 Av. de l'Ayroule, 09400 Tarascon-sur-Ariège` (modifiable dans l'admin)[cite: 7]. - Téléphone cliquable : `07 60 49 64 69`[cite: 7] @@ -179,10 +181,15 @@ Crée une page de réglages complète et intuitive dans **Apparence > Réglages --- -## 8. GESTION DE VERSIONS & PUBLICATION GITEA -- **Dépôt distant :** `https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme.git` (branche principale : `main`). -- **Fichier `.gitignore` :** Crée un fichier `.gitignore` propre à la racine pour exclure les fichiers système (`.DS_Store`, `Thumbs.db`), les éditeurs (`.vscode/`, `.idea/`), et d'éventuels dossiers temporaires, tout en conservant le code source du thème et le fichier `AGENTS.md`. -- **Compatibilité déploiement VPS :** Assure-toi que tous les chemins dans le thème utilisent exclusivement les fonctions natives WordPress (`get_template_directory()`, `get_template_directory_uri()`) afin que le déploiement automatisé vers `/var/www/fastuser/data/www/cgt-fha.fr/wp-content/themes/cgt-fha/` fonctionne sans aucune dépendance de chemin en dur[cite: 6]. +## 8. GESTION DE VERSIONS, MISE À JOUR GITEA & PUBLICATION +- **Dépôt distant public :** `https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme.git` (branche principale : `main`). +- **Mise à jour en un clic depuis WordPress (`inc/class-cgt-gitea-updater.php`) :** + - Vérification directe sans jeton d'authentification sur `https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme/raw/branch/main/style.css` (cache Transient 6 h). + - Téléchargement via `https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme/archive/main.zip` et renommage automatique du dossier extrait vers `get_stylesheet()` (`upgrader_source_selection`, compatible mises à jour unitaires et groupées). + - Préservation stricte de `wp_options` (`cgt_fha_options`), des menus et des médias lors de chaque mise à jour. +- **Documentation & Aperçu :** `README.md` (documentation d'utilisation et de mise à jour en français) et `screenshot.png` (`1200x900px` pour *Apparence > Thèmes*). +- **Fichier `.gitignore` :** Exclut les fichiers système (`.DS_Store`, `Thumbs.db`), les éditeurs (`.vscode/`, `.idea/`), et d'éventuels dossiers temporaires, tout en conservant le code source du thème, `README.md` et `AGENTS.md`. +- **Compatibilité déploiement VPS :** Tous les chemins dans le thème utilisent exclusivement les fonctions natives WordPress (`get_template_directory()`, `get_template_directory_uri()`, `get_stylesheet()`) afin que le déploiement automatisé vers `/var/www/fastuser/data/www/cgt-fha.fr/wp-content/themes/cgt-fha/` fonctionne sans aucune dépendance de chemin en dur[cite: 6]. --- @@ -190,6 +197,8 @@ Crée une page de réglages complète et intuitive dans **Apparence > Réglages ```text / ├── AGENTS.md # Ce cahier des charges +├── README.md # Documentation du thème & procédure de mise à jour Gitea +├── screenshot.png # Aperçu visuel 1200x900px dans Apparence > Thèmes ├── .gitignore # Exclusions propres pour Gitea ├── style.css # En-tête thème WP + Variables CSS + Design System complet ├── theme.json # Palette couleurs CGT (#d20012) & réglages éditeur @@ -202,10 +211,13 @@ Crée une page de réglages complète et intuitive dans **Apparence > Réglages ├── page.php # Gabarit de page standard (Contact, Calendrier, Syndiquez-vous...) ├── archive.php # Archives par catégorie/étiquette locale ├── 404.php # Page d'erreur 404 utile avec recherche et liens vers l'UL +├── index.php # Gabarit de secours WordPress requis ├── inc/ │ ├── class-cgt-confederal-feed.php # Scraper cgt.fr + cache Transients + WP-Cron + rendu wireframe -│ ├── theme-options.php # Page d'admin WP : Réglages UL CGT (onglets + médias WP) +│ ├── class-cgt-gitea-updater.php # Mise à jour WordPress en un clic depuis le dépôt public Gitea +│ ├── theme-options.php # Page d'admin WP : Réglages UL CGT + migration non destructive │ └── seo-performance.php # Titres SEO locaux, OpenGraph, alerte indexation, nettoyage WP └── assets/ ├── js/main.js # Menu mobile accessible & onglets admin (Vanilla JS) - └── img/ # Visuels par défaut (hero-ariege.jpg/svg, logo-ul.svg, placeholder.svg) \ No newline at end of file + └── img/ # Bannières UL (noir/blanc), hero-ariege.jpg, logos réseau CGT, SVG +``` \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..083a0b7 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# Thème WordPress `cgt-wp-theme` — Union Locale CGT Foix Haute-Ariège + +Thème WordPress sur mesure, rapide et souverain conçu pour le site officiel de l'**Union Locale CGT Foix Haute-Ariège** (`https://cgt-fha.fr/`). + +L'Union Locale CGT de Foix et de la Haute-Ariège rassemble tous les syndicats CGT de son périmètre et assure le rattachement direct des salarié·e·s isolé·e·s (TPE, PME, commerces, artisanat, associations sans section syndicale) grâce à son **syndicat multiprofessionnel (Multipros)**. + +--- + +## Fonctionnalités principales + +- **Bandeau d'alerte mobilisation auto-expirant :** bandeau rouge CGT pleine largeur paramétrable (texte, lien, date et heure d'expiration automatique). +- **Vitrine locale prioritaire :** mise en avant des 3 derniers articles locaux publiés dans WordPress (*Sur le terrain à Foix et en Haute-Ariège*), avec hooks prêts pour l'agenda (`cgt-wp-calendar`) et les formulaires militants (`cgt-wp-forms`). +- **Actualité confédérale `cgt.fr` automatisée :** récupération directe depuis `https://www.cgt.fr/` et `/actualites` (1 article *À la une* + 3 articles récents) avec cache Transient (3 h), sauvegarde hors-ligne, tâche `WP-Cron` (2 h) et bouton de rafraîchissement manuel — sans polluer `wp_posts`. +- **Parcours militants & Réseau syndical :** section dédiée aux salarié·e·s isolé·e·s & TPE (*Multipros*), rappel du droit à la formation syndicale (*CFESES*), bloc Application Mobile activable à la demande et cartes illustrées du réseau CGT (UD CGT 09, Confédération CGT, La NVO, IHS CGT, INDECOSA-CGT). +- **SEO local, OpenGraph & Performance :** balises ``, `<meta name="description">` et OpenGraph natifs (`fr_FR`), alerte si l'indexation WordPress est bloquée, chargement léger sans jQuery, sans Bootstrap/Tailwind CDN ni Google Fonts externes (conforme RGPD). +- **Administration 100 % paramétrable & préservation des réglages :** tous les textes, visuels, icônes, coordonnées, menus et liens sont modifiables depuis **Apparence > Réglages UL CGT**. Lors des mises à jour, **100 % des personnalisations enregistrées en base (`wp_options`), les menus et les médias sont strictement préservés**. + +--- + +## Installation + +1. Téléchargez l'archive du thème depuis le dépôt Gitea (`https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme/archive/main.zip`) ou clonez le dépôt dans `wp-content/themes/` : + ```bash + git clone https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme.git cgt-fha + ``` +2. Dans l'administration WordPress, rendez-vous dans **Apparence > Thèmes** et activez **Union Locale CGT Foix Haute-Ariège**. +3. Personnalisez vos textes, logos, coordonnées et le bandeau mobilisation dans **Apparence > Réglages UL CGT**, puis configurez vos menus dans **Apparence > Menus**. + +--- + +## Mise à jour en un clic depuis WordPress (Gitea public) + +Le module `inc/class-cgt-gitea-updater.php` connecte nativement WordPress au dépôt public Gitea sans nécessiter aucun jeton d'authentification : + +1. **Détection automatique :** WordPress interroge périodiquement (ou lors d'un clic sur *Vérifier à nouveau* dans **Tableau de bord > Mises à jour**) l'en-tête `Version:` de `https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme/raw/branch/main/style.css`. +2. **Téléchargement & renommage transparent :** lorsque vous lancez la mise à jour en un clic depuis WordPress, l'archive `/archive/main.zip` est téléchargée puis automatiquement renommée vers le dossier actif du thème (`get_stylesheet()`, par exemple `cgt-fha`). +3. **Préservation totale des données :** aucune mise à jour ne réinitialise `cgt_fha_options`, les menus WordPress ou les médias téléversés. Les réglages restent lus dynamiquement via `wp_parse_args( get_option( 'cgt_fha_options' ), $defaults )`. + +### Publier une nouvelle version sur Gitea + +1. Modifiez les fichiers du thème selon vos besoins. +2. Incrémentez le numéro de version dans : + - l'en-tête `Version:` de `style.css` ; + - la constante `CGT_FHA_VERSION` dans `functions.php` (et `CGT_FHA_SCHEMA_VERSION` dans `inc/theme-options.php` en cas de migration structurelle non destructive). +3. Validez et poussez sur la branche `main` : + ```bash + git add . + git commit -m "Release vX.Y.Z" + git push origin main + ``` +4. Dans WordPress (**Tableau de bord > Mises à jour** ou **Apparence > Thèmes**), cliquez sur **Mettre à jour**. diff --git a/functions.php b/functions.php index b70cbd8..c420438 100644 --- a/functions.php +++ b/functions.php @@ -9,7 +9,7 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } -define( 'CGT_FHA_VERSION', '1.2.0' ); +define( 'CGT_FHA_VERSION', '1.2.1' ); define( 'CGT_FHA_DIR', get_template_directory() ); define( 'CGT_FHA_URI', get_template_directory_uri() ); @@ -19,6 +19,7 @@ define( 'CGT_FHA_URI', get_template_directory_uri() ); require_once CGT_FHA_DIR . '/inc/theme-options.php'; require_once CGT_FHA_DIR . '/inc/class-cgt-confederal-feed.php'; require_once CGT_FHA_DIR . '/inc/seo-performance.php'; +require_once CGT_FHA_DIR . '/inc/class-cgt-gitea-updater.php'; /** * Configuration des supports WordPress et menus. diff --git a/inc/class-cgt-gitea-updater.php b/inc/class-cgt-gitea-updater.php new file mode 100644 index 0000000..e603049 --- /dev/null +++ b/inc/class-cgt-gitea-updater.php @@ -0,0 +1,209 @@ +<?php +/** + * Module de mise à jour automatique du thème depuis le dépôt public Gitea. + * + * - Interroge directement raw/branch/main/style.css sans aucun token API. + * - Télécharge /archive/main.zip lors d'une mise à jour en un clic depuis WordPress. + * - Renomme automatiquement le dossier extrait vers get_stylesheet(). + * - Ne touche jamais aux réglages enregistrés en base (wp_options), menus ou médias. + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class CGT_FHA_Gitea_Updater { + + public const REPO_URL = 'https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme'; + public const RAW_STYLE_URL = 'https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme/raw/branch/main/style.css'; + public const PACKAGE_URL = 'https://git.cgt-fha.fr/Union_Locale_CGT_Foix_Haute-Ariege/cgt-wp-theme/archive/main.zip'; + public const VERSION_TRANSIENT = 'cgt_fha_gitea_remote_version'; + public const ERROR_TRANSIENT = 'cgt_fha_gitea_remote_error'; + public const CACHE_TTL = 6 * HOUR_IN_SECONDS; + + /** + * Enregistre les hooks natifs WordPress de vérification et d'installation des mises à jour. + */ + public static function init(): void { + add_filter( 'pre_set_site_transient_update_themes', array( __CLASS__, 'check_for_update' ) ); + add_filter( 'upgrader_source_selection', array( __CLASS__, 'fix_extracted_folder_name' ), 10, 4 ); + add_action( 'upgrader_process_complete', array( __CLASS__, 'purge_version_cache' ), 10, 2 ); + } + + /** + * Injecte l'état de mise à jour du thème dans le transient update_themes. + * + * @param mixed $transient Objet transient WordPress. + * @return mixed + */ + public static function check_for_update( $transient ) { + if ( ! is_object( $transient ) || empty( $transient->checked ) ) { + return $transient; + } + + $theme_slug = get_stylesheet(); + $current_version = (string) wp_get_theme( $theme_slug )->get( 'Version' ); + if ( '' === $current_version ) { + return $transient; + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $force_check = ! empty( $_GET['force-check'] ); + $remote_version = self::get_remote_version( $force_check ); + if ( null === $remote_version || '' === $remote_version ) { + return $transient; + } + + $update_data = array( + 'theme' => $theme_slug, + 'new_version' => $remote_version, + 'url' => self::REPO_URL, + 'package' => self::PACKAGE_URL, + ); + + if ( ! isset( $transient->response ) || ! is_array( $transient->response ) ) { + $transient->response = array(); + } + if ( ! isset( $transient->no_update ) || ! is_array( $transient->no_update ) ) { + $transient->no_update = array(); + } + + if ( version_compare( $remote_version, $current_version, '>' ) ) { + $transient->response[ $theme_slug ] = $update_data; + unset( $transient->no_update[ $theme_slug ] ); + } else { + $transient->no_update[ $theme_slug ] = $update_data; + unset( $transient->response[ $theme_slug ] ); + } + + return $transient; + } + + /** + * Récupère la version distante dans style.css sur la branche main (avec cache Transient). + * + * @param bool $force_check Ignore le cache si une vérification manuelle est demandée. + * @return string|null + */ + private static function get_remote_version( bool $force_check = false ): ?string { + if ( ! $force_check ) { + $cached = get_transient( self::VERSION_TRANSIENT ); + if ( is_string( $cached ) && '' !== $cached ) { + return $cached; + } + if ( false !== get_transient( self::ERROR_TRANSIENT ) ) { + return null; + } + } + + $response = wp_remote_get( + self::RAW_STYLE_URL, + array( + 'timeout' => 10, + 'sslverify' => true, + 'headers' => array( + 'Accept' => 'text/plain', + ), + 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . home_url( '/' ), + ) + ); + + if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { + set_transient( self::ERROR_TRANSIENT, 1, 15 * MINUTE_IN_SECONDS ); + return null; + } + + $body = (string) wp_remote_retrieve_body( $response ); + if ( '' === $body || ! preg_match( '/^\s*\*?\s*Version\s*:\s*([0-9]+\.[0-9]+(?:\.[0-9]+)*(?:-[A-Za-z0-9._-]+)?)/mi', $body, $matches ) ) { + set_transient( self::ERROR_TRANSIENT, 1, 15 * MINUTE_IN_SECONDS ); + return null; + } + + $remote_version = trim( $matches[1] ); + delete_transient( self::ERROR_TRANSIENT ); + set_transient( self::VERSION_TRANSIENT, $remote_version, self::CACHE_TTL ); + + return $remote_version; + } + + /** + * Renomme automatiquement le dossier extrait de l'archive Gitea vers get_stylesheet(). + * + * @param string|WP_Error $source Chemin du dossier extrait. + * @param string $remote_source Dossier temporaire parent. + * @param WP_Upgrader $upgrader Instance du gestionnaire de mise à jour. + * @param array $hook_extra Métadonnées sur l'élément mis à jour. + * @return string|WP_Error + */ + public static function fix_extracted_folder_name( $source, $remote_source, $upgrader, $hook_extra ) { + if ( is_wp_error( $source ) || empty( $source ) || empty( $remote_source ) ) { + return $source; + } + + $theme_slug = get_stylesheet(); + $is_single = isset( $hook_extra['theme'] ) && $hook_extra['theme'] === $theme_slug; + $is_bulk = isset( $hook_extra['themes'] ) && is_array( $hook_extra['themes'] ) && in_array( $theme_slug, $hook_extra['themes'], true ); + + if ( ! $is_single && ! $is_bulk ) { + return $source; + } + + // Vérification d'intégrité (indispensable en mise à jour groupée) sur le Text Domain du thème. + $style_path = trailingslashit( (string) $source ) . 'style.css'; + if ( ! file_exists( $style_path ) ) { + return $source; + } + + $header_chunk = file_get_contents( $style_path, false, null, 0, 2048 ); + if ( false === $header_chunk || ! preg_match( '/^\s*\*?\s*Text\s+Domain\s*:\s*cgt-fha\b/mi', $header_chunk ) ) { + return $source; + } + + $corrected_source = trailingslashit( (string) $remote_source ) . $theme_slug . '/'; + if ( untrailingslashit( (string) $source ) === untrailingslashit( $corrected_source ) ) { + return $source; + } + + global $wp_filesystem; + if ( ! $wp_filesystem ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + WP_Filesystem(); + } + + if ( ! $wp_filesystem ) { + return $source; + } + + if ( $wp_filesystem->is_dir( $corrected_source ) ) { + $wp_filesystem->delete( $corrected_source, true ); + } + + if ( ! $wp_filesystem->move( (string) $source, $corrected_source, true ) ) { + return new WP_Error( + 'cgt_fha_rename_failed', + __( 'Impossible de renommer l\'archive du thème vers le dossier actif.', 'cgt-fha' ) + ); + } + + return $corrected_source; + } + + /** + * Purge uniquement les transients de version après une mise à jour de thème. + * + * @param WP_Upgrader $upgrader Instance du gestionnaire de mise à jour. + * @param array $hook_extra Métadonnées de la mise à jour terminée. + */ + public static function purge_version_cache( $upgrader, $hook_extra ): void { + if ( 'theme' !== ( $hook_extra['type'] ?? '' ) ) { + return; + } + + delete_transient( self::VERSION_TRANSIENT ); + delete_transient( self::ERROR_TRANSIENT ); + } +} + +CGT_FHA_Gitea_Updater::init(); diff --git a/inc/theme-options.php b/inc/theme-options.php index 2eb3e91..5a8d71f 100644 --- a/inc/theme-options.php +++ b/inc/theme-options.php @@ -13,7 +13,7 @@ if ( ! defined( 'ABSPATH' ) ) { } const CGT_FHA_OPTION_KEY = 'cgt_fha_options'; -const CGT_FHA_SCHEMA_VERSION = '1.2.0'; +const CGT_FHA_SCHEMA_VERSION = '1.2.1'; /** * Valeurs par défaut complètes de toutes les options du thème. @@ -183,67 +183,46 @@ function cgt_fha_get_options( bool $fresh = false ): array { } /** - * Migration automatique 1.0.0 -> 1.1.0 pour les sites où le thème est déjà activé. + * Migration structurelle versionnée et non destructive des options du thème. + * + * Aucun hook (init, after_switch_theme, upgrader_process_complete) ne doit jamais + * écraser les personnalisations existantes enregistrées dans cgt_fha_options. + * Toutes les valeurs manquantes sont fournies dynamiquement via wp_parse_args() + * dans cgt_fha_get_options(). */ -add_action( 'init', 'cgt_fha_maybe_upgrade_options', 5 ); -function cgt_fha_maybe_upgrade_options(): void { - if ( CGT_FHA_SCHEMA_VERSION === get_option( 'cgt_fha_schema_version', '1.0.0' ) ) { +add_action( 'init', 'cgt_fha_maybe_migrate_options', 5 ); +function cgt_fha_maybe_migrate_options(): void { + $installed_version = (string) get_option( 'cgt_fha_schema_version', '' ); + if ( CGT_FHA_SCHEMA_VERSION === $installed_version ) { return; } - $defaults = cgt_fha_get_defaults(); - $saved = get_option( CGT_FHA_OPTION_KEY, array() ); + $saved = get_option( CGT_FHA_OPTION_KEY, false ); - if ( is_array( $saved ) && ! empty( $saved ) ) { - // Bascule du logo de pied de page vers la bannière texte blanc si ancien défaut ou vide. - if ( empty( $saved['footer_logo_url'] ) || false !== strpos( (string) $saved['footer_logo_url'], 'logo-ul-light.svg' ) ) { - $saved['footer_logo_url'] = $defaults['footer_logo_url']; - } + if ( is_array( $saved ) && ! empty( $saved ) && '' !== $installed_version ) { + /** + * Table de correspondance réservée aux futures migrations structurelles + * (ex. 'ancienne_cle' => 'nouvelle_cle'). Ne modifie jamais une clé + * cible déjà personnalisée par l'utilisateur. + * + * @var array<string, string> $renamed_keys + */ + $renamed_keys = array(); + $migrated = false; - // Désactive le bloc Application Mobile par défaut tant que le contenu n'est pas prêt. - $saved['app_enabled'] = 0; - - // Remplace les anciennes chaînes par défaut 1.0.0 si elles n'ont pas été personnalisées. - $old_v1_defaults = array( - 'local_news_title' => 'SUR LE TERRAIN EN HAUTE-ARIÈGE', - 'footer_desc' => "Syndicat interprofessionnel de proximité au service des salarié·e·s, privé·e·s d'emploi et retraité·e·s de Foix et de la Haute-Ariège.", - 'mission_1_text' => "Construire le syndicat dans votre entreprise, préparer les élections CSE et ne plus rester isolé face à l'employeur.", - 'mission_2_text' => "Faire respecter vos droits, vos salaires et vos conditions de travail, avec l'appui de nos conseillers du salarié et juristes.", - 'mission_3_text' => 'Connaître les actualités des luttes en Haute-Ariège et bénéficier de vos droits à la formation syndicale (CFESES).', - 'tpe_badge' => 'Salariés sans CSE · TPE / PME', - 'tpe_text' => "Vous travaillez dans une petite entreprise (< 11 salariés), un commerce, l'artisanat ou sur un chantier sans représentant du personnel ? L'Union Locale CGT vous accueille et vous défend.", - 'tpe_point_1' => "Confidentialité absolue : aucune démarche n'est portée à la connaissance de votre employeur sans votre accord explicite.", - 'tpe_point_2' => "Syndicat Multipros : adhérez et organisez-vous collectivement même dans une très petite entreprise dépourvue de section syndicale.", - 'tpe_point_3' => 'Conseillers du Salarié : accompagnement gratuit lors des entretiens préalables à un licenciement ou une rupture.', - 'network_2_label' => 'Confédération CGT (cgt.fr)', - 'network_3_label' => 'La NVO (nvo.fr)', - ); - - foreach ( $old_v1_defaults as $key => $old_val ) { - if ( isset( $saved[ $key ] ) && $saved[ $key ] === $old_val ) { - $saved[ $key ] = $defaults[ $key ]; + foreach ( $renamed_keys as $old_key => $new_key ) { + if ( array_key_exists( $old_key, $saved ) && ! array_key_exists( $new_key, $saved ) ) { + $saved[ $new_key ] = $saved[ $old_key ]; + $migrated = true; } } - // Remplace les anciens placeholders SVG du Réseau CGT par les 5 logos officiels (1.2.0). - for ( $i = 1; $i <= 5; $i++ ) { - $logo_key = 'network_' . $i . '_logo'; - $cur_logo = (string) ( $saved[ $logo_key ] ?? '' ); - if ( '' === $cur_logo || ( false !== strpos( $cur_logo, 'network-' ) && false !== strpos( $cur_logo, '.svg' ) ) ) { - $saved[ $logo_key ] = $defaults[ $logo_key ]; - } + if ( $migrated ) { + update_option( CGT_FHA_OPTION_KEY, $saved ); + cgt_fha_get_options( true ); } - - $saved = wp_parse_args( $saved, $defaults ); - update_option( CGT_FHA_OPTION_KEY, $saved ); - cgt_fha_get_options( true ); } - // Purge le cache confédéral pour forcer le nouvel extracteur sur la page d'accueil. - delete_transient( 'cgt_fha_confederal_feed' ); - delete_transient( 'cgt_fha_confederal_feed_lock' ); - delete_option( 'cgt_fha_confederal_feed_backup' ); - update_option( 'cgt_fha_schema_version', CGT_FHA_SCHEMA_VERSION, true ); } @@ -294,15 +273,11 @@ function cgt_fha_register_settings(): void { } /** - * Pré-remplit l'option en base lors de l'activation du thème (autoload = true, taille ~5 Ko). + * Remarque : aucun hook after_switch_theme n'écrit dans cgt_fha_options. + * Toutes les valeurs par défaut sont appliquées dynamiquement à la lecture + * via wp_parse_args() dans cgt_fha_get_options(), préservant 100 % des + * réglages enregistrés en base lors d'une réactivation ou mise à jour. */ -add_action( 'after_switch_theme', 'cgt_fha_seed_default_options' ); -function cgt_fha_seed_default_options(): void { - if ( false === get_option( CGT_FHA_OPTION_KEY, false ) ) { - add_option( CGT_FHA_OPTION_KEY, cgt_fha_get_defaults(), '', 'yes' ); - } - update_option( 'cgt_fha_schema_version', CGT_FHA_SCHEMA_VERSION, true ); -} /** * Nettoyage et validation de toutes les options soumises dans l'admin. @@ -311,10 +286,11 @@ function cgt_fha_seed_default_options(): void { * @return array<string, mixed> */ function cgt_fha_sanitize_options( $input ): array { - $input = is_array( $input ) ? $input : array(); - $defaults = cgt_fha_get_defaults(); - $existing = cgt_fha_get_options( true ); - $clean = array(); + $input = is_array( $input ) ? $input : array(); + $defaults = cgt_fha_get_defaults(); + $existing = cgt_fha_get_options( true ); + $is_form_submit = ! empty( $input['_form_submit'] ); + $clean = array(); $text_fields = array( 'phone', @@ -387,7 +363,7 @@ function cgt_fha_sanitize_options( $input ): array { $clean['email'] = isset( $input['email'] ) ? sanitize_email( wp_unslash( (string) $input['email'] ) ) - : $defaults['email']; + : (string) ( $existing['email'] ?? $defaults['email'] ); $url_fields = array( 'facebook_url', @@ -438,18 +414,36 @@ function cgt_fha_sanitize_options( $input ): array { 'network_5_enabled', ); foreach ( $checkbox_fields as $field ) { - $clean[ $field ] = ! empty( $input[ $field ] ) ? 1 : 0; + if ( $is_form_submit ) { + $clean[ $field ] = ! empty( $input[ $field ] ) ? 1 : 0; + } else { + $clean[ $field ] = array_key_exists( $field, $input ) + ? ( ! empty( $input[ $field ] ) ? 1 : 0 ) + : (int) ( $existing[ $field ] ?? $defaults[ $field ] ); + } } // Validation datetime-local (YYYY-MM-DDTHH:MM). - $raw_expires = isset( $input['banner_expires'] ) ? sanitize_text_field( wp_unslash( (string) $input['banner_expires'] ) ) : $defaults['banner_expires']; - $clean['banner_expires'] = preg_match( '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/', $raw_expires ) ? $raw_expires : $defaults['banner_expires']; + if ( isset( $input['banner_expires'] ) ) { + $raw_expires = sanitize_text_field( wp_unslash( (string) $input['banner_expires'] ) ); + $clean['banner_expires'] = preg_match( '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/', $raw_expires ) + ? $raw_expires + : (string) ( $existing['banner_expires'] ?? $defaults['banner_expires'] ); + } else { + $clean['banner_expires'] = (string) ( $existing['banner_expires'] ?? $defaults['banner_expires'] ); + } // Validation des icônes de mission. $valid_icons = array_keys( cgt_fha_get_mission_icon_choices() ); foreach ( array( 'mission_1_icon', 'mission_2_icon', 'mission_3_icon' ) as $icon_key ) { - $chosen = isset( $input[ $icon_key ] ) ? sanitize_key( (string) $input[ $icon_key ] ) : $defaults[ $icon_key ]; - $clean[ $icon_key ] = in_array( $chosen, $valid_icons, true ) ? $chosen : $defaults[ $icon_key ]; + if ( isset( $input[ $icon_key ] ) ) { + $chosen = sanitize_key( (string) $input[ $icon_key ] ); + $clean[ $icon_key ] = in_array( $chosen, $valid_icons, true ) + ? $chosen + : (string) ( $existing[ $icon_key ] ?? $defaults[ $icon_key ] ); + } else { + $clean[ $icon_key ] = (string) ( $existing[ $icon_key ] ?? $defaults[ $icon_key ] ); + } } return $clean; @@ -570,6 +564,7 @@ function cgt_fha_render_admin_page(): void { <form method="post" action="options.php" style="margin-top:20px;"> <?php settings_fields( 'cgt_fha_settings_group' ); ?> + <input type="hidden" name="<?php echo esc_attr( CGT_FHA_OPTION_KEY ); ?>[_form_submit]" value="1" /> <input type="hidden" name="cgt_fha_active_tab" value="<?php echo esc_attr( $active_tab ); ?>" /> <!-- ONGLET 1 : IDENTITÉ & COORDONNÉES --> diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..bbcdb36 Binary files /dev/null and b/screenshot.png differ diff --git a/style.css b/style.css index 235d31e..7528f99 100644 --- a/style.css +++ b/style.css @@ -4,7 +4,7 @@ Theme URI: https://cgt-fha.fr/ Author: Union Locale CGT Foix Haute-Ariège Author URI: https://cgt-fha.fr/ Description: Thème WordPress sur mesure, rapide et souverain pour l'Union Locale CGT Foix Haute-Ariège (Ariège 09). Zéro dépendance externe, 100 % paramétrable depuis Apparence > Réglages UL CGT. -Version: 1.2.0 +Version: 1.2.1 Requires at least: 6.4 Tested up to: 6.9 Requires PHP: 8.0