commit 4c51761c7567bb874df844aa1ed831a49bf2ea5f Author: Union Locale CGT Foix Haute-Ariège Date: Thu Nov 12 21:27:56 2026 +0100 feat: thème WordPress sur mesure Union Locale CGT Foix Haute-Ariège (v1.2.0) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..375c4a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Système +.DS_Store +Thumbs.db +desktop.ini +ehthumbs.db + +# Éditeurs & IDE +.vscode/ +.idea/ +*.sublime-workspace +*.sublime-project +*.swp +*.swo +*~ + +# Logs & temporaires +*.log +*.tmp +*.bak +.env +.env.local + +# Dépendances locales éventuelles (non requises par le thème) +node_modules/ +vendor/ +composer.phar diff --git a/404.php b/404.php new file mode 100644 index 0000000..a3bf9ec --- /dev/null +++ b/404.php @@ -0,0 +1,41 @@ + + +
+ +
+ + 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. +- **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]. + +--- + +## 2. IDENTITÉ VISUELLE & CHARTE GRAPHIQUE +- **Couleur primaire (Rouge CGT) :** `--cgt-red: #d20012;` (hover : `--cgt-red-dark: #a8000e;`)[cite: 7] +- **Couleurs neutres & surfaces :** + - Fond principal : `--bg-main: #ffffff;` + - Fond alterné (section Confédérale / Formation / Footer) : `--bg-soft: #f1f3f5;` + - Barre supérieure contact : `--bg-topbar: #1c1e21;` + - Texte principal : `--text-main: #1a1a1a;` + - Texte secondaire : `--text-muted: #4a5568;` + - Bordures des cartes : `--border-card: #cbd5e1;` + - Rayons d'arrondi : `--radius-card: 12px;` à `14px`, boutons pilules `--radius-pill: 999px;`. + +--- + +## 3. EN-TÊTE (`header.php`) & NAVIGATION +Respecte exactement le haut de la maquette de référence : +1. **Barre supérieure (`topbar`) :** + - Fond sombre (`#1c1e21`), texte blanc fin (`13px`). + - À gauche : icône téléphone + lien `tel:` (`07 60 49 64 69` par défaut) et icône enveloppe + lien `mailto:` (`union-locale@cgt-fha.fr` par défaut)[cite: 7]. + - À droite : icônes réseaux sociaux (Facebook officiel de l'UL `https://www.facebook.com/profile.php?id=61552410568856`, etc., éditables dans les réglages)[cite: 7]. +2. **Bandeau d'Alerte Mobilisation Auto-Expirant :** + - Bandeau pleine largeur sur fond rouge CGT (`#d20012`), texte blanc centré en gras (ex. : `MOBILISATION : Grève et manifestation à Foix - Allées de Villote`), cliquable si une URL est renseignée[cite: 6]. + - **Condition stricte d'affichage :** S'affiche uniquement si la case *Activer le bandeau* est cochée **ET** que l'horodatage actuel (`current_time('timestamp')`) est strictement inférieur à la *Date et heure d'expiration* configurée dans l'admin[cite: 6]. Passé ce délai, le bandeau disparaît automatiquement[cite: 6]. +3. **Barre de Navigation Principale (`
`) :** + - Fond blanc, hauteur confortable, ombre légère au défilement. + - À gauche : Logo officiel de l'Union Locale CGT Foix Haute-Ariège (modifiable via `custom_logo` dans WordPress, avec fallback SVG/PNG local propre). + - À droite : Menu principal horizontal (`wp_nav_menu` : **Accueil**, **Actualités**, **Calendrier**, **Contact**), avec soulignement rouge sur l'élément actif. + - **IMPORTANT :** Ne mets **PAS** de bouton rouge « Syndiquez-vous » dans cette barre de navigation (pour éviter la répétition avec le Hero juste en dessous). + - Sur mobile (`< 768px`) : bouton menu hamburger accessible (`aria-expanded="false"`, contrôle clavier `Escape`). + +--- + +## 4. PAGE D'ACCUEIL (`front-page.php`) — STRUCTURE COMPLÈTE + +La page d'accueil s'articule en **7 sections verticales** dans cet ordre exact : + +### Section 1 : Bannière Hero Plein Écran (Ancrage Territorial & Action) +- **Visuel de fond :** Image panoramique des montagnes d'Ariège / territoire (configurable dans l'admin avec une image par défaut intégrée dans `assets/img/hero-ariege.jpg`), recouverte d'un voile sombre dégradé (`rgba(15, 18, 22, 0.62)`) assurant une lisibilité parfaite du texte blanc. +- **Contenu centré verticalement et horizontalement :** + - Titre principal (`

`, blanc, grand, gras, 2 lignes) : + `Union Locale CGT Foix Haute-Ariège` + `Votre syndicat de proximité !` + - Citation centrée en dessous (blanc doux, italique, éditable dans l'admin) : + `« La régression sociale ne se négocie pas, elle se combat ! » — Henri Krasucki, Secrétaire Général de la CGT de 1982 à 1992` + - **Deux boutons d'action centrés côte à côte** (`display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap;`) : + 1. Bouton gauche (fond blanc `#ffffff`, texte noir `#1a1a1a`, coins arrondis `8px`, gras) : **« J'ai besoin d'aide »** (lien configurable, par défaut vers la page Contact / Urgence salarié)[cite: 6]. + 2. Bouton droit (fond sombre translucide ou rouge foncé, bordure blanche/rouge, texte blanc, coins arrondis `8px`, gras) : **« Rejoindre la CGT »** (lien configurable, par défaut vers la page Syndiquez-vous)[cite: 6]. + +### Section 2 : Module « À QUOI NOUS SERVONS ? » (3 Cartes de Mission) +Placé **immédiatement sous le Hero et avant les actualités** (fond blanc `#ffffff`) : +- **Titre de section :** `À QUOI NOUS SERVONS ?` centré en majuscules (`

`). +- **Grille 3 colonnes** (`grid-template-columns: repeat(3, 1fr); gap: 1.5rem;`, 1 colonne sur mobile) : + - Chaque carte possède un fond blanc, une bordure fine (`1px solid #cbd5e1`), des coins arrondis (`border-radius: 12px`), un padding généreux (`1.75rem`) et un contenu centré : + 1. **Carte 1 :** Icône poing levé / collectif (SVG propre) • Titre `S'ORGANISER` • Texte par défaut : *« Construire le syndicat dans votre entreprise, préparer les élections CSE et ne plus rester isolé face à l'employeur. »* + 2. **Carte 2 :** Icône bouclier (SVG propre) • Titre `SE DÉFENDRE` • Texte par défaut : *« Faire respecter vos droits, vos salaires et vos conditions de travail, avec l'appui de nos conseillers du salarié et juristes. »* + 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) +- **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 (`

`). +- **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. + - Juste sous l'image : une pilule rouge CGT (`background: #d20012; color: #fff; border-radius: 999px; font-size: 12px; padding: 2px 10px;`) affichant la catégorie principale (ex. *Mobilisation*, *Vie syndicale*) suivie de la date de publication (`d.m.Y`). + - Titre de l'article en gras (`18px`) cliquable vers `single.php`. + - Extrait nettoyé (`wp_trim_words`, ~20 mots). +- **Pied de section :** Bouton lien discret centré *« Voir toutes les actualités locales → »* vers la page des articles (`get_permalink(get_option('page_for_posts'))`), suivi du hook `do_action('cgt_fha_after_local_news');` permettant d'injecter les prochains rendez-vous de `cgt-wp-calendar`[cite: 6]. + +### Section 4 : Vitrine 2 — « ACTUALITÉ CONFÉDÉRALE » (Récupération automatique `cgt.fr`) +- **Fond de section :** Gris très doux (`#f1f3f5`) pour bien la distinguer des actualités locales. +- **Titre de section :** `ACTUALITÉ CONFÉDÉRALE` centré en majuscules (`

`). +- **Moteur PHP (`inc/class-cgt-confederal-feed.php`) :** + - Interroge `https://www.cgt.fr/` via `wp_remote_get()` (timeout 10s, User-Agent propre) et parse le HTML avec `DOMDocument` + `DOMXPath` (sans polluer `wp_posts`) : + - **1 article `featured` (« À la une ») :** extrait depuis la section *"à la une de la cgt"* (`title`, `summary`, `image_url` convertie en URL absolue `https://www.cgt.fr/...`, `category`, `url`). + - **3 articles `latest` (`ARTICLE 1`, `ARTICLE 2`, `ARTICLE 3`) :** extraits depuis les 3 premiers articles de la section suivante *"mais aussi..."* (complétés via `https://www.cgt.fr/actualites` si un résumé est manquant). + - **Fallback de sécurité :** Si la page d'accueil `cgt.fr` échoue ou change de structure DOM, bascule automatiquement sur les 4 premiers articles de `https://www.cgt.fr/actualites` (le 1er devient `featured`, les 3 suivants deviennent `latest`). + - **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`. + - À 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`). + - 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"`). + +### 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]. + - 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;`) :** + 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]. + +### 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]. + +--- + +## 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 »*. + 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] + - Email cliquable : `union-locale@cgt-fha.fr`[cite: 7] + - Horaires des permanences d'accueil et juridiques (éditables)[cite: 6]. + - Lien vers la page Facebook officielle de l'UL[cite: 7]. + 3. **Accès rapides & Mentions :** Liens vers *Actualités*, *Calendrier*, *Se syndiquer*, *Urgence salarié*, *Mentions légales & Confidentialité*. + +--- + +## 6. PANNEAU D'ADMINISTRATION WORDPRESS (`inc/theme-options.php`) +Crée une page de réglages complète et intuitive dans **Apparence > Réglages UL CGT** (organisée en onglets propres sans dépendance JS externe) permettant de modifier sans coder : +1. **Onglet 1 — Identité & Coordonnées :** Téléphone, E-mail, Adresse des locaux, Horaires des permanences, URL Facebook, URL page *Syndiquez-vous*, URL page *Besoin d'aide / Contact*. +2. **Onglet 2 — Bandeau Mobilisation :** Case *Activer*, Texte du mot d'ordre & lieu, Lien URL optionnel, Champ `datetime-local` d'expiration automatique[cite: 6]. +3. **Onglet 3 — Page d'accueil (Hero & Blocs statiques) :** + - Sélecteur d'image de fond du Hero (via la Médiathèque WP `wp_enqueue_media()`), Titre H1, Citation, Libellés et liens des 2 boutons du Hero. + - Champs pour les 3 cartes *« À quoi nous servons ? »* (choix d'icône SVG, titre, description pour chaque carte). + - Champs pour les blocs *Salariés isolés / TPE*, *Formation CFESES* et *Application Mobile* (URL APK/Store + sélecteur image QR Code + cases pour afficher/masquer chaque bloc)[cite: 6, 8]. +4. **Onglet 4 — Flux Confédéral (`cgt.fr`) :** État du cache actuel (date de dernière synchronisation, aperçu de l'article *À la une* en cache) et bouton de rafraîchissement manuel immédiat. + +--- + +## 7. SEO LOCAL, OPENGRAPH & PERFORMANCE (`inc/seo-performance.php`) +1. **Balises `` et `<meta name="description">` :** + - Sur la page d'accueil : `Union Locale CGT Foix Haute-Ariège | Défense des salariés et action syndicale`[cite: 6]. + - Sur les articles et pages : `[Titre] | Union Locale CGT Foix Haute-Ariège` + meta description extraite automatiquement du contenu nettoyé[cite: 6]. +2. **Balises OpenGraph :** Injection automatique de `og:title`, `og:description`, `og:url`, `og:type`, `og:locale` (`fr_FR`) et `og:image` (image à la une ou image Hero par défaut) pour des aperçus propres sur Facebook, WhatsApp et SMS[cite: 6]. +3. **Alerte Indexation Admin :** Si `get_option('blog_public') == '0'`, affiche une alerte rouge dans le tableau de bord WordPress invitant à autoriser l'indexation dans *Réglages > Lecture*[cite: 6]. +4. **Optimisation & Nettoyage :** Suppression de `wp-emoji` et `wp-embed`, ajout de `loading="lazy"` sur les images hors ligne de flottaison, échappement systématique (`esc_html`, `esc_url`, `esc_attr`, `wp_kses_post`)[cite: 6]. + +--- + +## 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]. + +--- + +## 9. ARBORESCENCE DES FICHIERS À GÉNÉRER +```text +/ +├── AGENTS.md # Ce cahier des charges +├── .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 +├── functions.php # Chargement des modules, supports WP, menus, enqueues +├── header.php # Topbar, Bandeau Mobilisation auto-expirant, Nav responsive +├── footer.php # Footer 3 colonnes avec coordonnées Espace Sabart & liens CGT +├── front-page.php # Page d'accueil complète (7 sections fidèles à la maquette) +├── home.php # Liste paginée des actualités locales de l'UL +├── single.php # Gabarit d'article local (partage social, catégorie, navigation) +├── 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 +├── 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) +│ └── 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 diff --git a/archive.php b/archive.php new file mode 100644 index 0000000..f84b63c --- /dev/null +++ b/archive.php @@ -0,0 +1,61 @@ +<?php +/** + * Gabarit d'archives par catégorie, étiquette, date ou recherche (archive.php). + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +get_header(); +?> + +<section class="cgt-page-hero"> + <div class="cgt-container"> + <nav class="cgt-breadcrumb" aria-label="<?php esc_attr_e( 'Fil d\'Ariane', 'cgt-fha' ); ?>"> + <a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php esc_html_e( 'Accueil', 'cgt-fha' ); ?></a> + <span aria-hidden="true">/</span> + <a href="<?php echo esc_url( cgt_fha_get_local_news_archive_url() ); ?>"><?php esc_html_e( 'Actualités', 'cgt-fha' ); ?></a> + <span aria-hidden="true">/</span> + <span><?php echo esc_html( wp_strip_all_tags( get_the_archive_title() ) ); ?></span> + </nav> + <h1 class="cgt-page-hero__title"><?php the_archive_title(); ?></h1> + <?php if ( get_the_archive_description() ) : ?> + <div class="cgt-page-hero__desc"><?php the_archive_description(); ?></div> + <?php endif; ?> + </div> +</section> + +<section class="cgt-section cgt-section--white"> + <div class="cgt-container"> + <?php if ( have_posts() ) : ?> + <div class="cgt-news-grid"> + <?php + while ( have_posts() ) : + the_post(); + cgt_fha_render_local_post_card(); + endwhile; + ?> + </div> + + <div class="cgt-pagination"> + <?php + the_posts_pagination( + array( + 'mid_size' => 2, + 'prev_text' => '← Précédent', + 'next_text' => 'Suivant →', + ) + ); + ?> + </div> + <?php else : ?> + <p><?php esc_html_e( 'Aucun article trouvé dans cette rubrique.', 'cgt-fha' ); ?></p> + <?php endif; ?> + </div> +</section> + +<?php +get_footer(); diff --git a/assets/img/banniere-ul-blanc.png b/assets/img/banniere-ul-blanc.png new file mode 100644 index 0000000..f2df223 Binary files /dev/null and b/assets/img/banniere-ul-blanc.png differ diff --git a/assets/img/banniere-ul-noir.png b/assets/img/banniere-ul-noir.png new file mode 100644 index 0000000..280f827 Binary files /dev/null and b/assets/img/banniere-ul-noir.png differ diff --git a/assets/img/hero-ariege.jpg b/assets/img/hero-ariege.jpg new file mode 100644 index 0000000..8009101 Binary files /dev/null and b/assets/img/hero-ariege.jpg differ diff --git a/assets/img/logo-cgt-09.jpg b/assets/img/logo-cgt-09.jpg new file mode 100644 index 0000000..c30f488 Binary files /dev/null and b/assets/img/logo-cgt-09.jpg differ diff --git a/assets/img/logo-cgt.png b/assets/img/logo-cgt.png new file mode 100644 index 0000000..772f586 Binary files /dev/null and b/assets/img/logo-cgt.png differ diff --git a/assets/img/logo-ihs.jpg b/assets/img/logo-ihs.jpg new file mode 100644 index 0000000..4a71f2d Binary files /dev/null and b/assets/img/logo-ihs.jpg differ diff --git a/assets/img/logo-indecosa.png b/assets/img/logo-indecosa.png new file mode 100644 index 0000000..9885e2e Binary files /dev/null and b/assets/img/logo-indecosa.png differ diff --git a/assets/img/logo-nvo.png b/assets/img/logo-nvo.png new file mode 100644 index 0000000..4f4a1f0 Binary files /dev/null and b/assets/img/logo-nvo.png differ diff --git a/assets/img/logo-ul-light.svg b/assets/img/logo-ul-light.svg new file mode 100644 index 0000000..f463bce --- /dev/null +++ b/assets/img/logo-ul-light.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 72" width="360" height="72" role="img" aria-label="Union Locale CGT Foix Haute-Ariège"> + <rect x="0" y="2" width="68" height="68" rx="4" fill="#d20012"/> + <text x="16" y="27" fill="#ffe600" font-family="Georgia, serif" font-style="italic" font-weight="700" font-size="22">la</text> + <text x="5" y="61" fill="#ffffff" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="36" letter-spacing="-1.5">cgt</text> + <text x="84" y="31" fill="#ffffff" font-family="system-ui, -apple-system, sans-serif" font-weight="800" font-size="21" letter-spacing="0.4">UNION LOCALE</text> + <text x="84" y="59" fill="#e2e8f0" font-family="system-ui, -apple-system, sans-serif" font-weight="800" font-size="21" letter-spacing="0.4">FOIX HAUTE-ARIÈGE</text> +</svg> diff --git a/assets/img/logo-ul.svg b/assets/img/logo-ul.svg new file mode 100644 index 0000000..51b30c1 --- /dev/null +++ b/assets/img/logo-ul.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 72" width="360" height="72" role="img" aria-label="Union Locale CGT Foix Haute-Ariège"> + <rect x="0" y="2" width="68" height="68" fill="#d20012"/> + <text x="16" y="27" fill="#ffe600" font-family="Georgia, serif" font-style="italic" font-weight="700" font-size="22">la</text> + <text x="5" y="61" fill="#ffffff" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="36" letter-spacing="-1.5">cgt</text> + <text x="84" y="31" fill="#1a1a1a" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="21" letter-spacing="0.4">UNION LOCALE</text> + <text x="84" y="59" fill="#1a1a1a" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="21" letter-spacing="0.4">FOIX HAUTE-ARIÈGE</text> +</svg> diff --git a/assets/img/placeholder.svg b/assets/img/placeholder.svg new file mode 100644 index 0000000..cf3e47a --- /dev/null +++ b/assets/img/placeholder.svg @@ -0,0 +1,19 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 480" width="800" height="480" role="img" aria-label="Union Locale CGT Foix Haute-Ariège"> + <defs> + <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1"> + <stop offset="0%" stop-color="#1c1e21"/> + <stop offset="100%" stop-color="#2d333b"/> + </linearGradient> + <linearGradient id="accent" x1="0" y1="0" x2="1" y2="0"> + <stop offset="0%" stop-color="#d20012"/> + <stop offset="100%" stop-color="#a8000e"/> + </linearGradient> + </defs> + <rect width="800" height="480" fill="url(#bg)"/> + <path d="M0 360 L140 245 L270 325 L430 190 L585 305 L700 235 L800 315 L800 480 L0 480 Z" fill="#252a30"/> + <path d="M0 400 L180 310 L350 375 L540 285 L720 355 L800 330 L800 480 L0 480 Z" fill="#181b1f"/> + <rect x="330" y="135" width="140" height="140" rx="12" fill="url(#accent)"/> + <text x="400" y="192" text-anchor="middle" fill="#ffe600" font-family="Georgia, serif" font-style="italic" font-weight="700" font-size="34">la</text> + <text x="400" y="248" text-anchor="middle" fill="#ffffff" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="60" letter-spacing="-2">cgt</text> + <text x="400" y="330" text-anchor="middle" fill="#f1f5f9" font-family="system-ui, -apple-system, sans-serif" font-weight="700" font-size="22" letter-spacing="1.5">UNION LOCALE FOIX HAUTE-ARIÈGE</text> +</svg> diff --git a/assets/img/qr-placeholder.svg b/assets/img/qr-placeholder.svg new file mode 100644 index 0000000..a2f6203 --- /dev/null +++ b/assets/img/qr-placeholder.svg @@ -0,0 +1,34 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180" width="180" height="180" role="img" aria-label="Emplacement QR Code application mobile CGT"> + <rect width="180" height="180" rx="12" fill="#ffffff" stroke="#cbd5e1" stroke-width="2"/> + <g fill="#1a1a1a"> + <rect x="18" y="18" width="42" height="42" rx="4"/> + <rect x="25" y="25" width="28" height="28" fill="#ffffff"/> + <rect x="32" y="32" width="14" height="14"/> + <rect x="120" y="18" width="42" height="42" rx="4"/> + <rect x="127" y="25" width="28" height="28" fill="#ffffff"/> + <rect x="134" y="32" width="14" height="14"/> + <rect x="18" y="120" width="42" height="42" rx="4"/> + <rect x="25" y="127" width="28" height="28" fill="#ffffff"/> + <rect x="32" y="134" width="14" height="14"/> + <rect x="72" y="22" width="10" height="10"/> + <rect x="94" y="22" width="12" height="10"/> + <rect x="72" y="42" width="18" height="10"/> + <rect x="98" y="42" width="10" height="18"/> + <rect x="22" y="74" width="14" height="10"/> + <rect x="44" y="74" width="16" height="12"/> + <rect x="22" y="96" width="10" height="14"/> + <rect x="40" y="96" width="20" height="10"/> + <rect x="72" y="120" width="12" height="12"/> + <rect x="96" y="120" width="14" height="10"/> + <rect x="122" y="120" width="18" height="12"/> + <rect x="148" y="120" width="14" height="18"/> + <rect x="72" y="144" width="24" height="14"/> + <rect x="108" y="142" width="16" height="16"/> + <rect x="134" y="146" width="26" height="12"/> + <rect x="124" y="74" width="16" height="14"/> + <rect x="148" y="74" width="14" height="26"/> + <rect x="120" y="96" width="20" height="14"/> + </g> + <rect x="68" y="68" width="44" height="44" rx="8" fill="#d20012"/> + <text x="90" y="97" text-anchor="middle" fill="#ffffff" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="18">cgt</text> +</svg> diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..eabae38 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,177 @@ +/** + * Scripts Vanilla ES6 — Union Locale CGT Foix Haute-Ariège + * Menu mobile accessible + ombre header + partage + onglets & médias admin WP. + */ +(function () { + 'use strict'; + + /* ------------------------------------------------------------------------- + * 1. Navigation mobile accessible & sticky header + * ---------------------------------------------------------------------- */ + const header = document.querySelector('.cgt-header'); + const navToggle = document.querySelector('.cgt-nav-toggle'); + const navMenu = document.getElementById('cgt-primary-nav'); + + if (header) { + const onScroll = () => { + header.classList.toggle('is-scrolled', window.scrollY > 10); + }; + onScroll(); + window.addEventListener('scroll', onScroll, { passive: true }); + } + + if (navToggle && navMenu) { + const setOpen = (isOpen) => { + navToggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); + navToggle.setAttribute( + 'aria-label', + isOpen ? 'Fermer le menu principal' : 'Ouvrir le menu principal' + ); + navMenu.classList.toggle('is-open', isOpen); + }; + + navToggle.addEventListener('click', () => { + const expanded = navToggle.getAttribute('aria-expanded') === 'true'; + setOpen(!expanded); + }); + + document.addEventListener('keydown', (event) => { + if (event.key === 'Escape' && navMenu.classList.contains('is-open')) { + setOpen(false); + navToggle.focus(); + } + }); + + document.addEventListener('click', (event) => { + if ( + navMenu.classList.contains('is-open') && + !navMenu.contains(event.target) && + !navToggle.contains(event.target) + ) { + setOpen(false); + } + }); + } + + /* ------------------------------------------------------------------------- + * 2. Bouton copier le lien sur les articles + * ---------------------------------------------------------------------- */ + const copyBtns = document.querySelectorAll('[data-cgt-copy-url]'); + copyBtns.forEach((btn) => { + btn.addEventListener('click', async () => { + const url = btn.getAttribute('data-cgt-copy-url'); + if (!url || !navigator.clipboard) { + return; + } + try { + await navigator.clipboard.writeText(url); + const original = btn.textContent; + btn.textContent = 'Lien copié !'; + setTimeout(() => { + btn.textContent = original; + }, 2200); + } catch (e) { + // Ignore silencieusement si l'API clipboard est restreinte + } + }); + }); + + /* ------------------------------------------------------------------------- + * 3. Interface d'administration : onglets + sélecteur média WP + * ---------------------------------------------------------------------- */ + const adminWrap = document.querySelector('.cgt-admin-wrap'); + if (!adminWrap) { + return; + } + + const tabButtons = adminWrap.querySelectorAll('[data-cgt-tab]'); + const tabPanels = adminWrap.querySelectorAll('[data-cgt-panel]'); + const activeTabInput = adminWrap.querySelector('input[name="cgt_fha_active_tab"]'); + const refererInput = adminWrap.querySelector('input[name="_wp_http_referer"]'); + + const activateTab = (tabId) => { + tabButtons.forEach((btn) => { + const match = btn.getAttribute('data-cgt-tab') === tabId; + btn.classList.toggle('nav-tab-active', match); + btn.setAttribute('aria-selected', match ? 'true' : 'false'); + }); + tabPanels.forEach((panel) => { + const match = panel.getAttribute('data-cgt-panel') === tabId; + panel.hidden = !match; + }); + if (activeTabInput) { + activeTabInput.value = tabId; + } + if (refererInput) { + try { + const url = new URL(refererInput.value, window.location.origin); + url.searchParams.set('tab', tabId); + refererInput.value = url.pathname + url.search; + } catch (e) { + // Ignore si l'URL est invalide + } + } + }; + + tabButtons.forEach((btn) => { + btn.addEventListener('click', (event) => { + event.preventDefault(); + activateTab(btn.getAttribute('data-cgt-tab')); + }); + }); + + // Sélecteurs de médias WordPress (wp.media) + if (typeof wp !== 'undefined' && wp.media) { + adminWrap.querySelectorAll('[data-cgt-media-select]').forEach((selectBtn) => { + selectBtn.addEventListener('click', (event) => { + event.preventDefault(); + const targetId = selectBtn.getAttribute('data-cgt-media-select'); + const inputEl = document.getElementById(targetId); + const previewEl = document.getElementById(targetId + '_preview'); + + const frame = wp.media({ + title: 'Choisir une image', + button: { text: 'Utiliser cette image' }, + library: { type: 'image' }, + multiple: false, + }); + + frame.on('select', () => { + const attachment = frame.state().get('selection').first().toJSON(); + if (inputEl && attachment.url) { + inputEl.value = attachment.url; + } + if (previewEl && attachment.url) { + previewEl.src = attachment.url; + previewEl.hidden = false; + } + }); + + frame.open(); + }); + }); + + adminWrap.querySelectorAll('[data-cgt-media-clear]').forEach((clearBtn) => { + clearBtn.addEventListener('click', (event) => { + event.preventDefault(); + const targetId = clearBtn.getAttribute('data-cgt-media-clear'); + const defaultUrl = clearBtn.getAttribute('data-cgt-default-url') || ''; + const inputEl = document.getElementById(targetId); + const previewEl = document.getElementById(targetId + '_preview'); + + if (inputEl) { + inputEl.value = defaultUrl; + } + if (previewEl) { + if (defaultUrl) { + previewEl.src = defaultUrl; + previewEl.hidden = false; + } else { + previewEl.src = ''; + previewEl.hidden = true; + } + } + }); + }); + } +})(); diff --git a/footer.php b/footer.php new file mode 100644 index 0000000..8656d5e --- /dev/null +++ b/footer.php @@ -0,0 +1,112 @@ +<?php +/** + * Pied de page souverain (3 colonnes) — Union Locale CGT Foix Haute-Ariège. + * Aucune mention "Propulsé par WordPress". + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +$opts = cgt_fha_get_options(); +$phone_raw = (string) $opts['phone']; +$phone_href = preg_replace( '/[^0-9+]/', '', $phone_raw ); +$email = (string) $opts['email']; +$logo_url = cgt_fha_get_footer_logo_url(); +?> +</main> + +<footer class="cgt-footer" role="contentinfo"> + <div class="cgt-container"> + <div class="cgt-footer__grid"> + <!-- Colonne 1 : Identité & appel militant --> + <div> + <a href="<?php echo esc_url( home_url( '/' ) ); ?>" aria-label="<?php esc_attr_e( 'Retour à l\'accueil', 'cgt-fha' ); ?>"> + <img + src="<?php echo esc_url( $logo_url ); ?>" + alt="Union Locale CGT Foix Haute-Ariège" + class="cgt-footer__logo" + loading="lazy" + decoding="async" + /> + </a> + <?php if ( ! empty( $opts['footer_desc'] ) ) : ?> + <p class="cgt-footer__text"><?php echo esc_html( (string) $opts['footer_desc'] ); ?></p> + <?php endif; ?> + <?php if ( ! empty( $opts['footer_call'] ) ) : ?> + <p class="cgt-footer__call"><?php echo esc_html( (string) $opts['footer_call'] ); ?></p> + <?php endif; ?> + </div> + + <!-- Colonne 2 : Nos Locaux & Permanences (visible sur 100 % des pages) --> + <div> + <h2 class="cgt-footer__title"><?php esc_html_e( 'Nos Locaux & Permanences', 'cgt-fha' ); ?></h2> + <ul class="cgt-footer__list"> + <?php if ( ! empty( $opts['address'] ) ) : ?> + <li> + <?php echo cgt_fha_render_icon( 'map-pin', 17 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + <span><?php echo esc_html( (string) $opts['address'] ); ?></span> + </li> + <?php endif; ?> + + <?php if ( '' !== $phone_raw ) : ?> + <li> + <?php echo cgt_fha_render_icon( 'phone', 17 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + <a href="<?php echo esc_url( 'tel:' . $phone_href ); ?>"><?php echo esc_html( $phone_raw ); ?></a> + </li> + <?php endif; ?> + + <?php if ( '' !== $email ) : ?> + <li> + <?php echo cgt_fha_render_icon( 'mail', 17 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + <a href="<?php echo esc_url( 'mailto:' . $email ); ?>"><?php echo esc_html( $email ); ?></a> + </li> + <?php endif; ?> + + <?php if ( ! empty( $opts['hours'] ) ) : ?> + <li> + <?php echo cgt_fha_render_icon( 'clock', 17 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + <span><?php echo esc_html( (string) $opts['hours'] ); ?></span> + </li> + <?php endif; ?> + + <?php if ( ! empty( $opts['facebook_url'] ) ) : ?> + <li> + <?php echo cgt_fha_render_icon( 'facebook', 17 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + <a href="<?php echo esc_url( (string) $opts['facebook_url'] ); ?>" target="_blank" rel="noopener noreferrer"> + <?php esc_html_e( 'Page Facebook officielle de l\'UL', 'cgt-fha' ); ?> + </a> + </li> + <?php endif; ?> + </ul> + </div> + + <!-- Colonne 3 : Accès rapides & Mentions --> + <div> + <h2 class="cgt-footer__title"><?php esc_html_e( 'Accès rapides & Mentions', 'cgt-fha' ); ?></h2> + <?php + wp_nav_menu( + array( + 'theme_location' => 'footer', + 'container' => false, + 'menu_class' => 'cgt-footer__list', + 'depth' => 1, + 'fallback_cb' => 'cgt_fha_default_footer_menu', + ) + ); + ?> + </div> + </div> + + <div class="cgt-footer__bottom"> + <span>© <?php echo esc_html( wp_date( 'Y' ) ); ?> Union Locale CGT Foix Haute-Ariège — Tous droits réservés.</span> + <span><?php esc_html_e( 'Union des syndicats CGT — Foix & Haute-Ariège (09)', 'cgt-fha' ); ?></span> + </div> + </div> +</footer> + +<?php wp_footer(); ?> +</body> +</html> diff --git a/front-page.php b/front-page.php new file mode 100644 index 0000000..30360c3 --- /dev/null +++ b/front-page.php @@ -0,0 +1,271 @@ +<?php +/** + * Gabarit de la Page d'Accueil — 7 sections verticales fidèles à la maquette. + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +get_header(); +$opts = cgt_fha_get_options(); +?> + +<!-- ========================================================================= + SECTION 1 : BANNIÈRE HERO PLEIN ÉCRAN + ========================================================================= --> +<section class="cgt-hero" aria-label="<?php esc_attr_e( 'Présentation de l\'Union Locale CGT Foix Haute-Ariège', 'cgt-fha' ); ?>"> + <img + src="<?php echo esc_url( (string) $opts['hero_bg_url'] ); ?>" + alt="" + class="cgt-hero__bg" + fetchpriority="high" + decoding="async" + /> + <div class="cgt-hero__overlay" aria-hidden="true"></div> + + <div class="cgt-hero__content"> + <h1 class="cgt-hero__title"> + <span><?php echo esc_html( (string) $opts['hero_title_line1'] ); ?></span> + <?php if ( '' !== trim( (string) $opts['hero_title_line2'] ) ) : ?> + <span><?php echo esc_html( (string) $opts['hero_title_line2'] ); ?></span> + <?php endif; ?> + </h1> + + <?php if ( '' !== trim( (string) $opts['hero_quote'] ) ) : ?> + <p class="cgt-hero__quote"><?php echo esc_html( (string) $opts['hero_quote'] ); ?></p> + <?php endif; ?> + + <div class="cgt-hero__actions"> + <a href="<?php echo esc_url( (string) $opts['hero_btn1_url'] ); ?>" class="cgt-btn cgt-btn--white"> + <?php echo esc_html( (string) $opts['hero_btn1_label'] ); ?> + </a> + <a href="<?php echo esc_url( (string) $opts['hero_btn2_url'] ); ?>" class="cgt-btn cgt-btn--hero-dark"> + <?php echo esc_html( (string) $opts['hero_btn2_label'] ); ?> + </a> + </div> + </div> +</section> + +<!-- ========================================================================= + SECTION 2 : À QUOI NOUS SERVONS ? (3 Cartes de Mission) + ========================================================================= --> +<section class="cgt-section cgt-section--white cgt-missions-section" aria-labelledby="cgt-missions-heading"> + <div class="cgt-container"> + <h2 id="cgt-missions-heading" class="cgt-section__title"><?php echo esc_html( (string) $opts['missions_title'] ); ?></h2> + <?php if ( ! empty( $opts['missions_subtitle'] ) ) : ?> + <p class="cgt-section__subtitle"><?php echo esc_html( (string) $opts['missions_subtitle'] ); ?></p> + <?php endif; ?> + + <div class="cgt-missions-grid"> + <?php for ( $i = 1; $i <= 3; $i++ ) : ?> + <article class="cgt-mission-card"> + <div class="cgt-mission-card__icon" aria-hidden="true"> + <?php echo cgt_fha_render_icon( (string) $opts[ 'mission_' . $i . '_icon' ], 40 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + </div> + <h3 class="cgt-mission-card__title"><?php echo esc_html( (string) $opts[ 'mission_' . $i . '_title' ] ); ?></h3> + <p class="cgt-mission-card__text"><?php echo esc_html( (string) $opts[ 'mission_' . $i . '_text' ] ); ?></p> + </article> + <?php endfor; ?> + </div> + </div> +</section> + +<!-- ========================================================================= + SECTION 3 : SUR LE TERRAIN EN HAUTE-ARIÈGE (3 derniers articles locaux) + ========================================================================= --> +<section class="cgt-section cgt-section--white cgt-local-section" aria-labelledby="cgt-local-news-heading"> + <div class="cgt-container"> + <h2 id="cgt-local-news-heading" class="cgt-section__title"><?php echo esc_html( (string) $opts['local_news_title'] ); ?></h2> + + <?php + $local_query = new WP_Query( + array( + 'post_type' => 'post', + 'post_status' => 'publish', + 'posts_per_page' => 3, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + ) + ); + ?> + + <?php if ( $local_query->have_posts() ) : ?> + <div class="cgt-news-grid"> + <?php + while ( $local_query->have_posts() ) : + $local_query->the_post(); + cgt_fha_render_local_post_card(); + endwhile; + wp_reset_postdata(); + ?> + </div> + <?php else : ?> + <p class="cgt-section__subtitle"> + <?php esc_html_e( 'Les prochaines actualités locales et comptes-rendus de luttes à Foix et en Haute-Ariège seront publiés ici très prochainement.', 'cgt-fha' ); ?> + </p> + <?php endif; ?> + + <div class="cgt-section__footer"> + <a href="<?php echo esc_url( cgt_fha_get_local_news_archive_url() ); ?>" class="cgt-btn cgt-btn--subtle"> + <?php echo esc_html( (string) $opts['local_news_more'] ); ?> + </a> + </div> + + <?php + // Hook pour injecter les prochains rendez-vous de l'extension cgt-wp-calendar. + do_action( 'cgt_fha_after_local_news' ); + ?> + </div> +</section> + +<!-- ========================================================================= + SECTION 4 : ACTUALITÉ CONFÉDÉRALE (cgt.fr) + ========================================================================= --> +<?php CGT_FHA_Confederal_Feed::render_section( (string) $opts['confederal_title'] ); ?> + +<!-- ========================================================================= + SECTION 5 : SALARIÉS ISOLÉS & TPE : VOUS N'ÊTES PAS SEULS + ========================================================================= --> +<?php if ( ! empty( $opts['tpe_enabled'] ) ) : ?> + <section class="cgt-section cgt-section--white" aria-labelledby="cgt-tpe-heading"> + <div class="cgt-container"> + <div class="cgt-tpe-box"> + <div> + <?php if ( ! empty( $opts['tpe_badge'] ) ) : ?> + <span class="cgt-tpe-box__badge"><?php echo esc_html( (string) $opts['tpe_badge'] ); ?></span> + <?php endif; ?> + <h2 id="cgt-tpe-heading" class="cgt-tpe-box__title"><?php echo esc_html( (string) $opts['tpe_title'] ); ?></h2> + <p class="cgt-tpe-box__text"><?php echo esc_html( (string) $opts['tpe_text'] ); ?></p> + <a href="<?php echo esc_url( (string) $opts['tpe_btn_url'] ); ?>" class="cgt-btn cgt-btn--primary"> + <?php echo esc_html( (string) $opts['tpe_btn_label'] ); ?> + </a> + </div> + + <ul class="cgt-tpe-points"> + <?php for ( $p = 1; $p <= 3; $p++ ) : ?> + <?php if ( ! empty( $opts[ 'tpe_point_' . $p ] ) ) : ?> + <li> + <span class="cgt-tpe-points__icon"> + <?php echo cgt_fha_render_icon( 'check', 18 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + </span> + <span><?php echo esc_html( (string) $opts[ 'tpe_point_' . $p ] ); ?></span> + </li> + <?php endif; ?> + <?php endfor; ?> + </ul> + </div> + <?php do_action( 'cgt_fha_tpe_section_after' ); ?> + </div> + </section> +<?php endif; ?> + +<!-- ========================================================================= + SECTION 6 : FORMATION SYNDICALE (CFESES) & APPLICATION MOBILE + ========================================================================= --> +<?php +$split_count = ( ! empty( $opts['cfeses_enabled'] ) ? 1 : 0 ) + ( ! empty( $opts['app_enabled'] ) ? 1 : 0 ); +if ( $split_count > 0 ) : + $split_class = 1 === $split_count ? 'cgt-split-grid cgt-split-grid--single' : 'cgt-split-grid'; + ?> + <section class="cgt-section cgt-section--soft" aria-label="<?php esc_attr_e( 'Formation syndicale et application mobile', 'cgt-fha' ); ?>"> + <div class="cgt-container"> + <div class="<?php echo esc_attr( $split_class ); ?>"> + <?php if ( ! empty( $opts['cfeses_enabled'] ) ) : ?> + <article class="cgt-feature-card"> + <span class="cgt-feature-card__eyebrow"><?php echo esc_html( (string) $opts['cfeses_eyebrow'] ); ?></span> + <h2 class="cgt-feature-card__title"><?php echo esc_html( (string) $opts['cfeses_title'] ); ?></h2> + <p class="cgt-feature-card__text"><?php echo esc_html( (string) $opts['cfeses_text'] ); ?></p> + <div class="cgt-feature-card__footer"> + <a href="<?php echo esc_url( (string) $opts['cfeses_btn_url'] ); ?>" class="cgt-btn cgt-btn--primary"> + <?php echo esc_html( (string) $opts['cfeses_btn_label'] ); ?> + </a> + </div> + </article> + <?php endif; ?> + + <?php if ( ! empty( $opts['app_enabled'] ) ) : ?> + <article class="cgt-feature-card"> + <span class="cgt-feature-card__eyebrow"><?php echo esc_html( (string) $opts['app_eyebrow'] ); ?></span> + <h2 class="cgt-feature-card__title"><?php echo esc_html( (string) $opts['app_title'] ); ?></h2> + <div class="cgt-app-layout"> + <div> + <p class="cgt-feature-card__text"><?php echo esc_html( (string) $opts['app_text'] ); ?></p> + </div> + <?php if ( ! empty( $opts['app_qr_url'] ) ) : ?> + <img + src="<?php echo esc_url( (string) $opts['app_qr_url'] ); ?>" + alt="<?php esc_attr_e( 'QR Code pour télécharger l\'application mobile UL CGT Foix Haute-Ariège', 'cgt-fha' ); ?>" + class="cgt-app-qr" + loading="lazy" + decoding="async" + /> + <?php endif; ?> + </div> + <div class="cgt-feature-card__footer"> + <a href="<?php echo esc_url( (string) $opts['app_download_url'] ); ?>" class="cgt-btn cgt-btn--outline-dark" target="_blank" rel="noopener noreferrer"> + <?php echo esc_html( (string) $opts['app_btn_label'] ); ?> + </a> + </div> + </article> + <?php endif; ?> + </div> + </div> + </section> +<?php endif; ?> + +<!-- ========================================================================= + SECTION 7 : RÉSEAU SYNDICAL CGT + ========================================================================= --> +<?php if ( ! empty( $opts['network_enabled'] ) ) : ?> + <section class="cgt-section cgt-section--white" aria-labelledby="cgt-network-heading"> + <div class="cgt-container"> + <h2 id="cgt-network-heading" class="cgt-section__title"><?php echo esc_html( (string) $opts['network_title'] ); ?></h2> + <div class="cgt-network-grid"> + <?php for ( $n = 1; $n <= 5; $n++ ) : ?> + <?php + if ( empty( $opts[ 'network_' . $n . '_enabled' ] ) ) { + continue; + } + $n_logo = (string) ( $opts[ 'network_' . $n . '_logo' ] ?? '' ); + $n_label = (string) ( $opts[ 'network_' . $n . '_label' ] ?? '' ); + $n_desc = (string) ( $opts[ 'network_' . $n . '_desc' ] ?? '' ); + $n_btn = (string) ( $opts[ 'network_' . $n . '_btn' ] ?? 'Visiter le site ↗' ); + $n_url = (string) ( $opts[ 'network_' . $n . '_url' ] ?? '' ); + if ( '' === $n_label || '' === $n_url ) { + continue; + } + ?> + <article class="cgt-network-card"> + <?php if ( '' !== $n_logo ) : ?> + <div class="cgt-network-card__logo-wrap"> + <img + src="<?php echo esc_url( $n_logo ); ?>" + alt="<?php echo esc_attr( $n_label ); ?>" + class="cgt-network-card__logo" + loading="lazy" + decoding="async" + /> + </div> + <?php endif; ?> + <h3 class="cgt-network-card__title"><?php echo esc_html( $n_label ); ?></h3> + <?php if ( '' !== $n_desc ) : ?> + <p class="cgt-network-card__desc"><?php echo esc_html( $n_desc ); ?></p> + <?php endif; ?> + <div class="cgt-network-card__cta"> + <a href="<?php echo esc_url( $n_url ); ?>" class="cgt-network-card__btn" target="_blank" rel="noopener noreferrer"> + <?php echo esc_html( $n_btn ); ?> + </a> + </div> + </article> + <?php endfor; ?> + </div> + </div> + </section> +<?php endif; ?> + +<?php +get_footer(); diff --git a/functions.php b/functions.php new file mode 100644 index 0000000..b70cbd8 --- /dev/null +++ b/functions.php @@ -0,0 +1,303 @@ +<?php +/** + * Fonctions principales du thème Union Locale CGT Foix Haute-Ariège. + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +define( 'CGT_FHA_VERSION', '1.2.0' ); +define( 'CGT_FHA_DIR', get_template_directory() ); +define( 'CGT_FHA_URI', get_template_directory_uri() ); + +/** + * Chargement des modules du thème. + */ +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'; + +/** + * Configuration des supports WordPress et menus. + */ +add_action( 'after_setup_theme', 'cgt_fha_setup' ); +function cgt_fha_setup(): void { + load_theme_textdomain( 'cgt-fha', CGT_FHA_DIR . '/languages' ); + + add_theme_support( 'title-tag' ); + add_theme_support( 'post-thumbnails' ); + add_image_size( 'cgt-card', 640, 380, true ); + add_image_size( 'cgt-featured', 960, 560, true ); + + add_theme_support( + 'custom-logo', + array( + 'height' => 120, + 'width' => 520, + 'flex-height' => true, + 'flex-width' => true, + ) + ); + + add_theme_support( + 'html5', + array( + 'search-form', + 'comment-form', + 'comment-list', + 'gallery', + 'caption', + 'style', + 'script', + 'navigation-widgets', + ) + ); + + add_theme_support( 'responsive-embeds' ); + add_theme_support( 'align-wide' ); + + register_nav_menus( + array( + 'primary' => __( 'Menu principal (En-tête)', 'cgt-fha' ), + 'footer' => __( 'Accès rapides (Pied de page)', 'cgt-fha' ), + ) + ); +} + +/** + * Chargement des styles et scripts frontend (zéro dépendance externe). + */ +add_action( 'wp_enqueue_scripts', 'cgt_fha_enqueue_assets' ); +function cgt_fha_enqueue_assets(): void { + $style_ver = file_exists( CGT_FHA_DIR . '/style.css' ) ? (string) filemtime( CGT_FHA_DIR . '/style.css' ) : CGT_FHA_VERSION; + $js_ver = file_exists( CGT_FHA_DIR . '/assets/js/main.js' ) ? (string) filemtime( CGT_FHA_DIR . '/assets/js/main.js' ) : CGT_FHA_VERSION; + + wp_enqueue_style( 'cgt-fha-style', get_stylesheet_uri(), array(), $style_ver ); + wp_enqueue_script( + 'cgt-fha-main', + CGT_FHA_URI . '/assets/js/main.js', + array(), + $js_ver, + array( + 'in_footer' => true, + 'strategy' => 'defer', + ) + ); +} + +/** + * Chargement des scripts admin uniquement sur la page de réglages du thème. + */ +add_action( 'admin_enqueue_scripts', 'cgt_fha_admin_enqueue_assets' ); +function cgt_fha_admin_enqueue_assets( string $hook_suffix ): void { + if ( 'appearance_page_cgt-fha-settings' !== $hook_suffix ) { + return; + } + + wp_enqueue_media(); + $js_ver = file_exists( CGT_FHA_DIR . '/assets/js/main.js' ) ? (string) filemtime( CGT_FHA_DIR . '/assets/js/main.js' ) : CGT_FHA_VERSION; + wp_enqueue_script( 'cgt-fha-admin', CGT_FHA_URI . '/assets/js/main.js', array(), $js_ver, true ); +} + +/** + * URL du logo d'en-tête (Personnaliser > Logo personnalisé ou Réglages UL CGT ou bannière par défaut). + */ +function cgt_fha_get_header_logo_url(): string { + $custom_logo_id = (int) get_theme_mod( 'custom_logo' ); + if ( $custom_logo_id > 0 ) { + $src = wp_get_attachment_image_url( $custom_logo_id, 'full' ); + if ( $src ) { + return $src; + } + } + + $opts = cgt_fha_get_options(); + return ! empty( $opts['header_logo_url'] ) + ? (string) $opts['header_logo_url'] + : CGT_FHA_URI . '/assets/img/banniere-ul-noir.png'; +} + +/** + * URL du logo clair du pied de page. + */ +function cgt_fha_get_footer_logo_url(): string { + $opts = cgt_fha_get_options(); + return ! empty( $opts['footer_logo_url'] ) + ? (string) $opts['footer_logo_url'] + : CGT_FHA_URI . '/assets/img/banniere-ul-blanc.png'; +} + +/** + * Retourne l'URL de la page listant toutes les actualités locales. + */ +function cgt_fha_get_local_news_archive_url(): string { + $posts_page_id = (int) get_option( 'page_for_posts' ); + if ( $posts_page_id > 0 ) { + $link = get_permalink( $posts_page_id ); + if ( $link ) { + return $link; + } + } + + $opts = cgt_fha_get_options(); + return ! empty( $opts['news_url'] ) ? (string) $opts['news_url'] : home_url( '/actualites/' ); +} + +/** + * Menu de secours pour la navigation principale si aucun menu n'est encore assigné dans Apparence > Menus. + */ +function cgt_fha_default_primary_menu(): void { + $opts = cgt_fha_get_options(); + $items = array( + array( + 'label' => 'Accueil', + 'url' => home_url( '/' ), + 'active' => is_front_page(), + ), + array( + 'label' => 'Actualités', + 'url' => cgt_fha_get_local_news_archive_url(), + 'active' => is_home() || is_singular( 'post' ) || is_category() || is_tag(), + ), + array( + 'label' => 'Calendrier', + 'url' => (string) $opts['calendar_url'], + 'active' => is_page( 'calendrier' ), + ), + array( + 'label' => 'Contact', + 'url' => (string) $opts['contact_url'], + 'active' => is_page( 'contact' ), + ), + ); + + echo '<ul class="cgt-nav__list">'; + foreach ( $items as $item ) { + $classes = $item['active'] ? 'menu-item current-menu-item' : 'menu-item'; + $aria = $item['active'] ? ' aria-current="page"' : ''; + printf( + '<li class="%s"><a href="%s"%s>%s</a></li>', + esc_attr( $classes ), + esc_url( $item['url'] ), + $aria, + esc_html( $item['label'] ) + ); + } + echo '</ul>'; +} + +/** + * Menu de secours pour les accès rapides du pied de page. + */ +function cgt_fha_default_footer_menu(): void { + $opts = cgt_fha_get_options(); + $items = array( + 'Actualités' => cgt_fha_get_local_news_archive_url(), + 'Calendrier' => (string) $opts['calendar_url'], + 'Se syndiquer' => (string) $opts['join_url'], + 'Urgence salarié' => (string) $opts['contact_url'], + 'Mentions légales & Confidentialité' => (string) $opts['legal_url'], + ); + + echo '<ul class="cgt-footer__list">'; + foreach ( $items as $label => $url ) { + printf( + '<li><a href="%s">%s</a></li>', + esc_url( $url ), + esc_html( $label ) + ); + } + echo '</ul>'; +} + +/** + * Icônes SVG propres intégrées (zéro police d'icônes externe). + */ +function cgt_fha_render_icon( string $name, int $size = 24 ): string { + $s = (int) $size; + $icons = array( + 'phone' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg>', + 'mail' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>', + 'map-pin' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>', + 'clock' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>', + 'facebook' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13.5 22v-8h2.7l.4-3h-3.1V9.1c0-.9.3-1.5 1.6-1.5h1.7V4.9c-.3 0-1.3-.1-2.4-.1-2.4 0-4 1.4-4 4.1V11H8v3h2.9v8h2.6Z"/></svg>', + 'instagram' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2" width="20" height="20" rx="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"/></svg>', + 'youtube' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M21.8 8s-.2-1.4-.8-2c-.8-.8-1.6-.8-2-.9C16.2 5 12 5 12 5h0s-4.2 0-7 .1c-.4.1-1.2.1-2 .9-.6.6-.8 2-.8 2S2 9.6 2 11.2v1.5C2 14.4 2.2 16 2.2 16s.2 1.4.8 2c.8.8 1.8.8 2.2.9 1.6.1 6.8.1 6.8.1s4.2 0 7-.1c.4-.1 1.2-.1 2-.9.6-.6.8-2 .8-2s.2-1.6.2-3.2v-1.5c0-1.7-.2-3.3-.2-3.3ZM9.9 14.6V9.2l5.4 2.7-5.4 2.7Z"/></svg>', + 'menu' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>', + 'check' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>', + // Icônes des 3 cartes de mission. + 'fist' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2"/><path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2"/><path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8"/><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"/></svg>', + 'shield' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>', + 'chat' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v5Z"/><path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/></svg>', + 'megaphone' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 11 18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/></svg>', + 'scale' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z"/><path d="m2 16 3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1Z"/><path d="M7 21h10"/><path d="M12 3v18"/><path d="M3 7h2c2 0 5-1 7-2 2 1 5 2 7 2h2"/></svg>', + 'users' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>', + 'book' => '<svg xmlns="http://www.w3.org/2000/svg" width="' . $s . '" height="' . $s . '" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/></svg>', + ); + + return $icons[ $name ] ?? $icons['fist']; +} + +/** + * Rendu réutilisable d'une carte d'article local (front-page, home, archive). + */ +function cgt_fha_render_local_post_card(): void { + $cats = get_the_category(); + $cat_name = 'Vie syndicale'; + if ( ! empty( $cats ) ) { + foreach ( $cats as $cat ) { + if ( ! in_array( $cat->slug, array( 'uncategorized', 'non-classe' ), true ) && 'Uncategorized' !== $cat->name ) { + $cat_name = $cat->name; + break; + } + } + } + $placeholder = CGT_FHA_URI . '/assets/img/placeholder.svg'; + ?> + <article id="post-<?php the_ID(); ?>" <?php post_class( 'cgt-news-card' ); ?>> + <a href="<?php the_permalink(); ?>" class="cgt-news-card__thumb-link" aria-hidden="true" tabindex="-1"> + <?php if ( has_post_thumbnail() ) : ?> + <?php + the_post_thumbnail( + 'cgt-card', + array( + 'class' => 'cgt-news-card__thumb', + 'loading' => 'lazy', + 'alt' => esc_attr( get_the_title() ), + ) + ); + ?> + <?php else : ?> + <img + src="<?php echo esc_url( $placeholder ); ?>" + alt="<?php echo esc_attr( get_the_title() ); ?>" + class="cgt-news-card__thumb" + loading="lazy" + decoding="async" + /> + <?php endif; ?> + </a> + + <div class="cgt-news-card__body"> + <div class="cgt-news-card__meta"> + <span class="cgt-pill-cat"><?php echo esc_html( $cat_name ); ?></span> + <time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>" class="cgt-news-card__date"> + <?php echo esc_html( get_the_date( 'd.m.Y' ) ); ?> + </time> + </div> + + <h3 class="cgt-news-card__title"> + <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> + </h3> + + <p class="cgt-news-card__excerpt"> + <?php echo esc_html( wp_trim_words( get_the_excerpt(), 20, '…' ) ); ?> + </p> + </div> + </article> + <?php +} + diff --git a/header.php b/header.php new file mode 100644 index 0000000..2e00c9b --- /dev/null +++ b/header.php @@ -0,0 +1,133 @@ +<?php +/** + * En-tête du thème Union Locale CGT Foix Haute-Ariège. + * + * 1. Topbar sombre (#1c1e21) avec téléphone, e-mail et réseaux sociaux + * 2. Bandeau d'alerte mobilisation auto-expirant (#d20012) + * 3. Barre de navigation blanche (logo à gauche, menu horizontal à droite) + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +$opts = cgt_fha_get_options(); +$phone_raw = (string) $opts['phone']; +$phone_href = preg_replace( '/[^0-9+]/', '', $phone_raw ); +$email = (string) $opts['email']; +$logo_url = cgt_fha_get_header_logo_url(); +?> +<!DOCTYPE html> +<html <?php language_attributes(); ?>> +<head> + <meta charset="<?php bloginfo( 'charset' ); ?>" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <?php wp_head(); ?> +</head> +<body <?php body_class(); ?>> +<?php wp_body_open(); ?> +<a class="skip-link" href="#cgt-main-content"><?php esc_html_e( 'Aller au contenu principal', 'cgt-fha' ); ?></a> + +<!-- 1. Topbar sombre --> +<div class="cgt-topbar" role="region" aria-label="<?php esc_attr_e( 'Coordonnées rapides et réseaux sociaux', 'cgt-fha' ); ?>"> + <div class="cgt-container cgt-topbar__inner"> + <div class="cgt-topbar__contacts"> + <?php if ( '' !== $phone_raw ) : ?> + <a href="<?php echo esc_url( 'tel:' . $phone_href ); ?>" class="cgt-topbar__link"> + <?php echo cgt_fha_render_icon( 'phone', 14 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + <span><?php echo esc_html( $phone_raw ); ?></span> + </a> + <?php endif; ?> + + <?php if ( '' !== $phone_raw && '' !== $email ) : ?> + <span class="cgt-topbar__sep" aria-hidden="true">/</span> + <?php endif; ?> + + <?php if ( '' !== $email ) : ?> + <a href="<?php echo esc_url( 'mailto:' . $email ); ?>" class="cgt-topbar__link"> + <?php echo cgt_fha_render_icon( 'mail', 14 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + <span><?php echo esc_html( $email ); ?></span> + </a> + <?php endif; ?> + </div> + + <div class="cgt-topbar__socials"> + <?php if ( ! empty( $opts['facebook_url'] ) ) : ?> + <a href="<?php echo esc_url( (string) $opts['facebook_url'] ); ?>" class="cgt-topbar__social" target="_blank" rel="noopener noreferrer" aria-label="Facebook Union Locale CGT Foix Haute-Ariège"> + <?php echo cgt_fha_render_icon( 'facebook', 16 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + </a> + <?php endif; ?> + <?php if ( ! empty( $opts['instagram_url'] ) ) : ?> + <a href="<?php echo esc_url( (string) $opts['instagram_url'] ); ?>" class="cgt-topbar__social" target="_blank" rel="noopener noreferrer" aria-label="Instagram Union Locale CGT Foix Haute-Ariège"> + <?php echo cgt_fha_render_icon( 'instagram', 16 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + </a> + <?php endif; ?> + <?php if ( ! empty( $opts['youtube_url'] ) ) : ?> + <a href="<?php echo esc_url( (string) $opts['youtube_url'] ); ?>" class="cgt-topbar__social" target="_blank" rel="noopener noreferrer" aria-label="YouTube Union Locale CGT Foix Haute-Ariège"> + <?php echo cgt_fha_render_icon( 'youtube', 16 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + </a> + <?php endif; ?> + </div> + </div> +</div> + +<!-- 2. Bandeau d'Alerte Mobilisation Auto-Expirant --> +<?php if ( cgt_fha_is_banner_active() ) : ?> + <div class="cgt-mob-banner" role="alert"> + <?php if ( ! empty( $opts['banner_url'] ) ) : ?> + <a href="<?php echo esc_url( (string) $opts['banner_url'] ); ?>" class="cgt-mob-banner__inner"> + <span><?php echo esc_html( (string) $opts['banner_text'] ); ?></span> + </a> + <?php else : ?> + <div class="cgt-mob-banner__inner"> + <span><?php echo esc_html( (string) $opts['banner_text'] ); ?></span> + </div> + <?php endif; ?> + </div> +<?php endif; ?> + +<!-- 3. Barre de Navigation Principale --> +<header class="cgt-header" role="banner"> + <div class="cgt-container cgt-header__inner"> + <a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="cgt-brand" rel="home"> + <img + src="<?php echo esc_url( $logo_url ); ?>" + alt="<?php echo esc_attr( get_bloginfo( 'name' ) ?: 'Union Locale CGT Foix Haute-Ariège' ); ?>" + class="cgt-brand__logo" + decoding="async" + fetchpriority="high" + /> + </a> + + <button + type="button" + class="cgt-nav-toggle" + aria-controls="cgt-primary-nav" + aria-expanded="false" + aria-label="<?php esc_attr_e( 'Ouvrir le menu principal', 'cgt-fha' ); ?>" + > + <?php echo cgt_fha_render_icon( 'menu', 22 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + </button> + + <nav id="cgt-primary-nav" class="cgt-nav" aria-label="<?php esc_attr_e( 'Navigation principale', 'cgt-fha' ); ?>"> + <?php + wp_nav_menu( + array( + 'theme_location' => 'primary', + 'container' => false, + 'menu_class' => 'cgt-nav__list', + 'depth' => 1, + 'fallback_cb' => 'cgt_fha_default_primary_menu', + ) + ); + ?> + </nav> + </div> +</header> + +<main id="cgt-main-content" tabindex="-1"> +<?php +// Hook pour extensions éventuelles juste après l'ouverture du contenu principal. +do_action( 'cgt_fha_main_open' ); diff --git a/home.php b/home.php new file mode 100644 index 0000000..e83e353 --- /dev/null +++ b/home.php @@ -0,0 +1,61 @@ +<?php +/** + * Gabarit de la liste paginée des actualités locales de l'UL (home.php). + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +get_header(); +$posts_page_id = (int) get_option( 'page_for_posts' ); +$page_title = $posts_page_id > 0 ? get_the_title( $posts_page_id ) : 'Actualités locales — Foix & Haute-Ariège'; +?> + +<section class="cgt-page-hero"> + <div class="cgt-container"> + <nav class="cgt-breadcrumb" aria-label="<?php esc_attr_e( 'Fil d\'Ariane', 'cgt-fha' ); ?>"> + <a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php esc_html_e( 'Accueil', 'cgt-fha' ); ?></a> + <span aria-hidden="true">/</span> + <span><?php echo esc_html( $page_title ); ?></span> + </nav> + <h1 class="cgt-page-hero__title"><?php echo esc_html( $page_title ); ?></h1> + <p class="cgt-page-hero__desc"> + <?php esc_html_e( 'Retrouvez les communiqués, mobilisations, tracts et comptes-rendus des syndicats CGT de Foix et de la Haute-Ariège.', 'cgt-fha' ); ?> + </p> + </div> +</section> + +<section class="cgt-section cgt-section--white"> + <div class="cgt-container"> + <?php if ( have_posts() ) : ?> + <div class="cgt-news-grid"> + <?php + while ( have_posts() ) : + the_post(); + cgt_fha_render_local_post_card(); + endwhile; + ?> + </div> + + <div class="cgt-pagination"> + <?php + the_posts_pagination( + array( + 'mid_size' => 2, + 'prev_text' => '← Précédent', + 'next_text' => 'Suivant →', + ) + ); + ?> + </div> + <?php else : ?> + <p><?php esc_html_e( 'Aucun article local publié pour le moment.', 'cgt-fha' ); ?></p> + <?php endif; ?> + </div> +</section> + +<?php +get_footer(); diff --git a/inc/class-cgt-confederal-feed.php b/inc/class-cgt-confederal-feed.php new file mode 100644 index 0000000..5c50fac --- /dev/null +++ b/inc/class-cgt-confederal-feed.php @@ -0,0 +1,840 @@ +<?php +/** + * Moteur de récupération et rendu de l'actualité confédérale depuis https://www.cgt.fr/ + * + * - Interroge cgt.fr via wp_remote_get() (timeout 10s, User-Agent propre) + * - Parse le HTML Drupal via DOMDocument + DOMXPath sans polluer wp_posts + * - Extrait 1 article "À la une" + 3 articles "mais aussi..." + * - Enrichit les résumés via https://www.cgt.fr/actualites et, si besoin, depuis la page article + * - Cache Transient (3h) + sauvegarde de secours (autoload = false) + WP-Cron (2h) + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +class CGT_FHA_Confederal_Feed { + + public const TRANSIENT_KEY = 'cgt_fha_confederal_feed'; + public const BACKUP_OPTION = 'cgt_fha_confederal_feed_backup'; + public const LOCK_TRANSIENT = 'cgt_fha_confederal_feed_lock'; + public const CRON_HOOK = 'cgt_fha_refresh_confederal_feed'; + public const CRON_INTERVAL = 'cgt_fha_two_hours'; + + /** + * Enregistre les hooks WP-Cron, Admin Bar et rafraîchissement manuel. + */ + public static function init(): void { + add_filter( 'cron_schedules', array( __CLASS__, 'register_cron_interval' ) ); + add_action( 'init', array( __CLASS__, 'ensure_cron_scheduled' ) ); + add_action( self::CRON_HOOK, array( __CLASS__, 'cron_refresh' ) ); + add_action( 'switch_theme', array( __CLASS__, 'clear_cron' ) ); + add_action( 'admin_bar_menu', array( __CLASS__, 'add_admin_bar_button' ), 95 ); + add_action( 'admin_post_cgt_fha_refresh_feed', array( __CLASS__, 'handle_manual_refresh' ) ); + } + + /** + * Ajoute un intervalle WP-Cron de 2 heures. + * + * @param array<string, array<string, mixed>> $schedules Planifications existantes. + * @return array<string, array<string, mixed>> + */ + public static function register_cron_interval( array $schedules ): array { + if ( ! isset( $schedules[ self::CRON_INTERVAL ] ) ) { + $schedules[ self::CRON_INTERVAL ] = array( + 'interval' => 2 * HOUR_IN_SECONDS, + 'display' => 'Toutes les 2 heures (Actus Confédérales CGT)', + ); + } + return $schedules; + } + + /** + * Planifie la tâche WP-Cron si elle n'est pas déjà active. + */ + public static function ensure_cron_scheduled(): void { + if ( ! wp_next_scheduled( self::CRON_HOOK ) ) { + wp_schedule_event( time() + 300, self::CRON_INTERVAL, self::CRON_HOOK ); + } + } + + /** + * Supprime la tâche WP-Cron lors du changement de thème. + */ + public static function clear_cron(): void { + $timestamp = wp_next_scheduled( self::CRON_HOOK ); + while ( $timestamp ) { + wp_unschedule_event( $timestamp, self::CRON_HOOK ); + $timestamp = wp_next_scheduled( self::CRON_HOOK ); + } + } + + /** + * Callback exécuté par WP-Cron toutes les 2 heures. + */ + public static function cron_refresh(): void { + self::get_feed( true ); + } + + /** + * Bouton « Actualiser les actus confédérales » dans la barre d'admin WordPress. + */ + public static function add_admin_bar_button( WP_Admin_Bar $wp_admin_bar ): void { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + $url = wp_nonce_url( + admin_url( 'admin-post.php?action=cgt_fha_refresh_feed' ), + 'cgt_fha_refresh_feed_action', + 'cgt_fha_refresh_feed_nonce' + ); + + $wp_admin_bar->add_node( + array( + 'id' => 'cgt-fha-refresh-confederal', + 'title' => 'Actualiser les actus confédérales', + 'href' => $url, + 'meta' => array( + 'title' => 'Synchroniser immédiatement les actualités depuis cgt.fr', + ), + ) + ); + } + + /** + * Traitement du rafraîchissement manuel sécurisé par nonce. + */ + public static function handle_manual_refresh(): void { + if ( ! current_user_can( 'manage_options' ) ) { + wp_die( esc_html__( 'Action non autorisée.', 'cgt-fha' ), 403 ); + } + + check_admin_referer( 'cgt_fha_refresh_feed_action', 'cgt_fha_refresh_feed_nonce' ); + + delete_transient( self::LOCK_TRANSIENT ); + self::get_feed( true ); + + $redirect = add_query_arg( + array( + 'page' => 'cgt-fha-settings', + 'tab' => 'confederal', + 'cgt_feed_refreshed' => '1', + ), + admin_url( 'themes.php' ) + ); + + wp_safe_redirect( $redirect ); + exit; + } + + /** + * Retourne les données du flux confédéral (cache -> réseau -> backup -> fallback). + * + * @param bool $force_refresh Force une requête réseau immédiate. + * @return array<string, mixed> + */ + public static function get_feed( bool $force_refresh = false ): array { + if ( ! $force_refresh ) { + $cached = get_transient( self::TRANSIENT_KEY ); + if ( is_array( $cached ) && ! empty( $cached['featured'] ) ) { + return $cached; + } + } + + // Anti-tempête : évite de relancer des requêtes HTTP bloquantes si cgt.fr vient d'échouer. + if ( $force_refresh || false === get_transient( self::LOCK_TRANSIENT ) ) { + $fresh = self::fetch_and_parse(); + if ( ! empty( $fresh['featured']['title'] ) && ! empty( $fresh['latest'] ) ) { + set_transient( self::TRANSIENT_KEY, $fresh, 3 * HOUR_IN_SECONDS ); + update_option( self::BACKUP_OPTION, $fresh, false ); + delete_transient( self::LOCK_TRANSIENT ); + return $fresh; + } + + set_transient( self::LOCK_TRANSIENT, 1, 5 * MINUTE_IN_SECONDS ); + } + + $backup = get_option( self::BACKUP_OPTION, array() ); + if ( is_array( $backup ) && ! empty( $backup['featured']['title'] ) ) { + return $backup; + } + + return self::get_static_fallback(); + } + + /** + * Retourne l'état du cache pour l'onglet d'administration. + * + * @return array<string, mixed> + */ + public static function get_cache_status(): array { + $transient = get_transient( self::TRANSIENT_KEY ); + $backup = get_option( self::BACKUP_OPTION, array() ); + $data = is_array( $transient ) && ! empty( $transient ) ? $transient : ( is_array( $backup ) ? $backup : array() ); + + return array( + 'has_transient' => is_array( $transient ) && ! empty( $transient ), + 'fetched_at' => $data['fetched_at'] ?? 0, + 'source' => $data['source'] ?? 'Fallback local', + 'featured' => $data['featured'] ?? array(), + ); + } + + /** + * Télécharge et parse cgt.fr (avec enrichissement sur /actualites puis sur la page article si besoin). + * + * @return array<string, mixed> + */ + private static function fetch_and_parse(): array { + $home_html = self::remote_get_html( 'https://www.cgt.fr/' ); + $featured = array(); + $latest = array(); + $source = 'https://www.cgt.fr/'; + + if ( '' !== $home_html ) { + $parsed = self::parse_homepage_html( $home_html ); + $featured = $parsed['featured']; + $latest = $parsed['latest']; + } + + $needs_actualites = empty( $featured['title'] ) + || '' === trim( (string) ( $featured['summary'] ?? '' ) ) + || empty( $featured['image_url'] ) + || count( $latest ) < 3; + + if ( ! $needs_actualites ) { + foreach ( $latest as $item ) { + if ( '' === trim( (string) ( $item['summary'] ?? '' ) ) || empty( $item['image_url'] ) ) { + $needs_actualites = true; + break; + } + } + } + + if ( $needs_actualites ) { + $actus_html = self::remote_get_html( 'https://www.cgt.fr/actualites' ); + if ( '' !== $actus_html ) { + $actus_items = self::parse_actualites_html( $actus_html ); + + if ( ! empty( $actus_items ) ) { + $by_path = array(); + foreach ( $actus_items as $act ) { + $key = self::normalize_url_key( $act['url'] ); + if ( '' !== $key ) { + $by_path[ $key ] = $act; + } + } + + // Enrichit "featured" sans écraser une image haute définition déjà extraite. + if ( ! empty( $featured['url'] ) ) { + $f_key = self::normalize_url_key( $featured['url'] ); + if ( isset( $by_path[ $f_key ] ) ) { + if ( '' === trim( (string) ( $featured['summary'] ?? '' ) ) && ! empty( $by_path[ $f_key ]['summary'] ) ) { + $featured['summary'] = $by_path[ $f_key ]['summary']; + } + if ( empty( $featured['image_url'] ) && ! empty( $by_path[ $f_key ]['image_url'] ) ) { + $featured['image_url'] = $by_path[ $f_key ]['image_url']; + } + if ( ( empty( $featured['category'] ) || 'Confédération CGT' === $featured['category'] ) && ! empty( $by_path[ $f_key ]['category'] ) ) { + $featured['category'] = $by_path[ $f_key ]['category']; + } + } + } + + // Enrichit les 3 cartes "latest" depuis /actualites sans écraser image_a_la_une. + foreach ( $latest as $idx => $item ) { + $l_key = self::normalize_url_key( $item['url'] ); + if ( isset( $by_path[ $l_key ] ) ) { + if ( '' === trim( (string) ( $item['summary'] ?? '' ) ) && ! empty( $by_path[ $l_key ]['summary'] ) ) { + $latest[ $idx ]['summary'] = $by_path[ $l_key ]['summary']; + } + if ( empty( $item['image_url'] ) && ! empty( $by_path[ $l_key ]['image_url'] ) ) { + $latest[ $idx ]['image_url'] = $by_path[ $l_key ]['image_url']; + } + if ( ( empty( $item['category'] ) || 'Confédération CGT' === $item['category'] ) && ! empty( $by_path[ $l_key ]['category'] ) ) { + $latest[ $idx ]['category'] = $by_path[ $l_key ]['category']; + } + } + } + + // Fallback complet si la page d'accueil a changé de structure. + if ( empty( $featured['title'] ) && count( $actus_items ) >= 2 ) { + $featured = array_shift( $actus_items ); + $latest = array_slice( $actus_items, 0, 3 ); + $source = 'https://www.cgt.fr/actualites (fallback)'; + } elseif ( count( $latest ) < 3 ) { + $seen = array( self::normalize_url_key( (string) ( $featured['url'] ?? '' ) ) ); + foreach ( $latest as $l ) { + $seen[] = self::normalize_url_key( (string) $l['url'] ); + } + foreach ( $actus_items as $act ) { + if ( count( $latest ) >= 3 ) { + break; + } + $act_key = self::normalize_url_key( $act['url'] ); + if ( '' !== $act_key && ! in_array( $act_key, $seen, true ) ) { + $latest[] = $act; + $seen[] = $act_key; + } + } + } + } + } + } + + if ( empty( $featured['title'] ) || empty( $latest ) ) { + return array(); + } + + // Fallback final : complète un éventuel article épinglé absent de la page 1 d'/actualites. + if ( '' === trim( (string) ( $featured['summary'] ?? '' ) ) || empty( $featured['image_url'] ) ) { + $featured = self::enrich_from_article_page( $featured ); + } + foreach ( $latest as $idx => $item ) { + if ( '' === trim( (string) ( $item['summary'] ?? '' ) ) || empty( $item['image_url'] ) ) { + $latest[ $idx ] = self::enrich_from_article_page( $item ); + } + } + + return array( + 'fetched_at' => time(), + 'source' => $source, + 'featured' => self::normalize_article( $featured, 240 ), + 'latest' => array_map( + static fn( array $item ): array => self::normalize_article( $item, 190 ), + array_slice( $latest, 0, 3 ) + ), + ); + } + + /** + * Requête HTTP GET sécurisée avec timeout 10s. + */ + private static function remote_get_html( string $url ): string { + $response = wp_remote_get( + $url, + array( + 'timeout' => 10, + 'redirection' => 3, + 'user-agent' => 'Mozilla/5.0 (compatible; CGT-FHA-Theme/1.1; +https://cgt-fha.fr/)', + 'headers' => array( + 'Accept' => 'text/html,application/xhtml+xml', + 'Accept-Language' => 'fr-FR,fr;q=0.9', + ), + ) + ); + + if ( is_wp_error( $response ) ) { + return ''; + } + + $code = (int) wp_remote_retrieve_response_code( $response ); + if ( $code < 200 || $code >= 400 ) { + return ''; + } + + return (string) wp_remote_retrieve_body( $response ); + } + + /** + * Parse la page d'accueil cgt.fr : + * - "À la une de la CGT" (#block-bluedrop-views-block-derniers-ajouts-block-2, .relative-wrapper) + * - "mais aussi..." (#block-views-block-derniers-ajouts-block-1, .views-row) + * + * @return array{featured: array<string, string>, latest: array<int, array<string, string>>} + */ + private static function parse_homepage_html( string $html ): array { + $xpath = self::create_xpath( $html ); + if ( ! $xpath ) { + return array( 'featured' => array(), 'latest' => array() ); + } + + $featured = array(); + $latest = array(); + + // 1. Bloc « à la une de la cgt » (ID Drupal exact + secours par titre de section). + $une_root = null; + $une_nodes = $xpath->query( '//section[@id="block-bluedrop-views-block-derniers-ajouts-block-2"]' ); + if ( $une_nodes instanceof DOMNodeList && $une_nodes->length > 0 ) { + $une_root = $une_nodes->item( 0 ); + } else { + $une_root = self::find_section_by_heading_text( $xpath, 'la une de la cgt' ); + } + + if ( $une_root ) { + $wrappers = $xpath->query( './/div[contains(@class,"relative-wrapper")]', $une_root ); + $card_el = ( $wrappers instanceof DOMNodeList && $wrappers->length > 0 ) ? $wrappers->item( 0 ) : $une_root; + $parsed = self::extract_drupal_row( $xpath, $card_el ); + if ( ! empty( $parsed['title'] ) && ! empty( $parsed['url'] ) ) { + $featured = $parsed; + } + } + + // 2. Bloc « mais aussi... » (ID Drupal exact + secours par titre de section). + $mais_root = null; + $mais_nodes = $xpath->query( '//section[@id="block-views-block-derniers-ajouts-block-1"]' ); + if ( $mais_nodes instanceof DOMNodeList && $mais_nodes->length > 0 ) { + $mais_root = $mais_nodes->item( 0 ); + } else { + $mais_root = self::find_section_by_heading_text( $xpath, 'mais aussi' ); + } + + if ( $mais_root ) { + $rows = $xpath->query( './/div[contains(@class,"views-row")]', $mais_root ); + if ( $rows instanceof DOMNodeList ) { + $seen = array( self::normalize_url_key( (string) ( $featured['url'] ?? '' ) ) ); + foreach ( $rows as $row ) { + if ( count( $latest ) >= 3 ) { + break; + } + $item = self::extract_drupal_row( $xpath, $row ); + $key = self::normalize_url_key( (string) ( $item['url'] ?? '' ) ); + if ( ! empty( $item['title'] ) && '' !== $key && ! in_array( $key, $seen, true ) ) { + $latest[] = $item; + $seen[] = $key; + } + } + } + } + + return array( + 'featured' => $featured, + 'latest' => $latest, + ); + } + + /** + * Parse la liste des articles de https://www.cgt.fr/actualites (jusqu'à 25 lignes .views-row). + * + * @return array<int, array<string, string>> + */ + private static function parse_actualites_html( string $html ): array { + $xpath = self::create_xpath( $html ); + if ( ! $xpath || ! $xpath->document->documentElement ) { + return array(); + } + + $rows = $xpath->query( '//main//div[contains(@class,"views-row")]|//div[contains(@class,"view-content")]//div[contains(@class,"views-row")]' ); + if ( ! ( $rows instanceof DOMNodeList ) || 0 === $rows->length ) { + return array(); + } + + $articles = array(); + $seen = array(); + foreach ( $rows as $row ) { + if ( count( $articles ) >= 25 ) { + break; + } + $item = self::extract_drupal_row( $xpath, $row ); + $key = self::normalize_url_key( (string) ( $item['url'] ?? '' ) ); + if ( ! empty( $item['title'] ) && '' !== $key && ! isset( $seen[ $key ] ) ) { + $seen[ $key ] = true; + $articles[] = $item; + } + } + + return $articles; + } + + /** + * Extrait proprement un article depuis un conteneur Drupal (bloc À la une ou .views-row). + * + * @return array<string, string> + */ + private static function extract_drupal_row( DOMXPath $xpath, DOMNode $row ): array { + $link_el = null; + $h_links = $xpath->query( './/h3//a[@href]|.//h2//a[@href]|.//h4//a[@href]', $row ); + if ( $h_links instanceof DOMNodeList ) { + foreach ( $h_links as $cand ) { + $href = trim( $cand->getAttribute( 'href' ) ); + if ( self::is_article_href( $href ) ) { + $link_el = $cand; + break; + } + } + } + + if ( ! $link_el ) { + $fallback_links = $xpath->query( './/a[@href and not(contains(@href,"/terms/")) and not(contains(@href,"/taxonomy/")) and not(contains(@class,"taxonomy-style")) and not(contains(@class,"more-link"))]', $row ); + if ( $fallback_links instanceof DOMNodeList ) { + foreach ( $fallback_links as $cand ) { + $href = trim( $cand->getAttribute( 'href' ) ); + if ( self::is_article_href( $href ) ) { + $link_el = $cand; + break; + } + } + } + } + + if ( ! $link_el ) { + return array(); + } + + $url = self::absolutize_url( $link_el->getAttribute( 'href' ) ); + $title = self::normalize_space( $link_el->textContent ); + if ( '' === $url || strlen( $title ) < 6 ) { + return array(); + } + + // Catégories (.taxonomy-style hors badge .highlighted). + $cats = array(); + $cat_nodes = $xpath->query( './/*[contains(@class,"taxonomy-style")]', $row ); + if ( $cat_nodes instanceof DOMNodeList ) { + foreach ( $cat_nodes as $cat_node ) { + if ( $cat_node instanceof DOMElement && false !== strpos( $cat_node->getAttribute( 'class' ), 'highlighted' ) ) { + continue; + } + $cat_txt = self::normalize_space( $cat_node->textContent ); + if ( '' !== $cat_txt && strlen( $cat_txt ) < 45 && ! in_array( $cat_txt, $cats, true ) ) { + $cats[] = $cat_txt; + } + } + } + $category = ! empty( $cats ) ? implode( ' | ', array_slice( $cats, 0, 2 ) ) : 'Confédération CGT'; + + // Résumé (.field-type-text-long ou paragraphe dans .txt-container). + $summary = ''; + $p_nodes = $xpath->query( './/*[contains(@class,"field-type-text-long")]|.//div[contains(@class,"txt-container")]//p|.//p', $row ); + if ( $p_nodes instanceof DOMNodeList ) { + foreach ( $p_nodes as $p ) { + $txt = self::normalize_space( $p->textContent ); + if ( strlen( $txt ) >= 10 && $txt !== $title ) { + $summary = $txt; + break; + } + } + } + + // Image (.views-field-field-imagette-media img en priorité, sinon premier img). + $image_url = ''; + $img_nodes = $xpath->query( './/div[contains(@class,"views-field-field-imagette-media")]//img|.//img', $row ); + if ( $img_nodes instanceof DOMNodeList && $img_nodes->length > 0 ) { + $img = $img_nodes->item( 0 ); + if ( $img instanceof DOMElement ) { + $src = $img->getAttribute( 'data-src' ) ?: $img->getAttribute( 'src' ); + if ( '' === $src && $img->hasAttribute( 'srcset' ) ) { + $parts = explode( ',', $img->getAttribute( 'srcset' ) ); + $first = trim( (string) $parts[0] ); + $src = strtok( $first, ' ' ) ?: ''; + } + $image_url = self::absolutize_url( $src ); + } + } + + return array( + 'title' => $title, + 'summary' => $summary, + 'image_url' => $image_url, + 'category' => $category, + 'url' => $url, + ); + } + + /** + * Enrichit un article directement depuis sa page individuelle sur cgt.fr si résumé ou image manquent. + * + * @param array<string, string> $item Article partiel. + * @return array<string, string> + */ + private static function enrich_from_article_page( array $item ): array { + if ( empty( $item['url'] ) ) { + return $item; + } + + $html = self::remote_get_html( $item['url'] ); + if ( '' === $html ) { + return $item; + } + + $xpath = self::create_xpath( $html ); + if ( ! $xpath ) { + return $item; + } + + if ( '' === trim( (string) ( $item['summary'] ?? '' ) ) ) { + $candidates = array( + './/*[contains(@class,"chapo") or contains(@class,"field-name-field-chapo")]', + '//meta[@property="og:description"]/@content', + '//meta[@name="description"]/@content', + '//main//article//p', + ); + foreach ( $candidates as $query ) { + $nodes = $xpath->query( $query ); + if ( $nodes instanceof DOMNodeList && $nodes->length > 0 ) { + foreach ( $nodes as $node ) { + $txt = self::normalize_space( $node instanceof DOMAttr ? $node->value : $node->textContent ); + if ( strlen( $txt ) >= 15 && $txt !== ( $item['title'] ?? '' ) ) { + $item['summary'] = $txt; + break 2; + } + } + } + } + } + + if ( empty( $item['image_url'] ) ) { + $og_img = $xpath->query( '//meta[@property="og:image"]/@content' ); + if ( $og_img instanceof DOMNodeList && $og_img->length > 0 ) { + $src = trim( (string) $og_img->item( 0 )->nodeValue ); + if ( '' !== $src ) { + $item['image_url'] = self::absolutize_url( $src ); + } + } + } + + return $item; + } + + /** + * Localise la section ancêtre correspondant à un titre textuel donné. + */ + private static function find_section_by_heading_text( DOMXPath $xpath, string $needle ): ?DOMNode { + $headings = $xpath->query( '//h1|//h2|//h3' ); + if ( ! ( $headings instanceof DOMNodeList ) ) { + return null; + } + + foreach ( $headings as $heading ) { + $text = self::normalize_space( $heading->textContent ); + $low = function_exists( 'mb_strtolower' ) ? mb_strtolower( $text, 'UTF-8' ) : strtolower( $text ); + if ( false !== strpos( $low, $needle ) ) { + $cur = $heading->parentNode; + while ( $cur && $cur instanceof DOMElement ) { + if ( 'section' === strtolower( $cur->tagName ) ) { + return $cur; + } + $cur = $cur->parentNode; + } + return $heading->parentNode; + } + } + + return null; + } + + /** + * Vérifie qu'un href pointe vers un article (et non une taxonomie ou l'index /actualites). + */ + private static function is_article_href( string $href ): bool { + if ( '' === $href || '#' === $href || 0 === strpos( $href, 'mailto:' ) || 0 === strpos( $href, 'tel:' ) ) { + return false; + } + if ( false !== strpos( $href, '/terms/' ) || false !== strpos( $href, '/taxonomy/' ) ) { + return false; + } + $path = untrailingslashit( (string) wp_parse_url( $href, PHP_URL_PATH ) ); + return '' !== $path && '/actualites' !== $path && '/' !== $path; + } + + /** + * Normalise une URL sur son chemin pour la correspondance fiable entre accueil et /actualites. + */ + private static function normalize_url_key( string $url ): string { + $path = (string) wp_parse_url( trim( $url ), PHP_URL_PATH ); + $path = strtolower( untrailingslashit( $path ) ); + return '' === $path || '/' === $path ? '' : $path; + } + + /** + * Initialise un DOMXPath UTF-8 fiable sur PHP 8.x sans altérer les accents ni apostrophes typographiques. + */ + private static function create_xpath( string $html ): ?DOMXPath { + if ( '' === trim( $html ) || ! class_exists( 'DOMDocument' ) ) { + return null; + } + + if ( function_exists( 'mb_encode_numericentity' ) ) { + $prepared = mb_encode_numericentity( $html, array( 0x80, 0x10FFFF, 0, ~0 ), 'UTF-8' ); + } else { + $prepared = preg_replace( '/<head\b[^>]*>/i', '$0<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">', $html, 1 ) ?: $html; + } + + $prev = libxml_use_internal_errors( true ); + $dom = new DOMDocument( '1.0', 'UTF-8' ); + $dom->loadHTML( $prepared, LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR ); + libxml_clear_errors(); + libxml_use_internal_errors( $prev ); + + return new DOMXPath( $dom ); + } + + /** + * Convertit une URL relative cgt.fr en URL absolue. + */ + private static function absolutize_url( string $raw ): string { + $raw = trim( $raw ); + if ( '' === $raw || 0 === strpos( $raw, 'data:' ) || 0 === strpos( $raw, 'javascript:' ) ) { + return ''; + } + if ( 0 === strpos( $raw, '//' ) ) { + return 'https:' . $raw; + } + if ( preg_match( '#^https?://#i', $raw ) ) { + return esc_url_raw( $raw ); + } + if ( '/' === $raw[0] ) { + return esc_url_raw( 'https://www.cgt.fr' . $raw ); + } + return esc_url_raw( 'https://www.cgt.fr/' . ltrim( $raw, '/' ) ); + } + + /** + * Normalise les espaces blancs et entités HTML. + */ + private static function normalize_space( string $text ): string { + $decoded = html_entity_decode( wp_strip_all_tags( $text ), ENT_QUOTES | ENT_HTML5, 'UTF-8' ); + return trim( (string) preg_replace( '/\s+/u', ' ', $decoded ) ); + } + + /** + * Nettoie et tronque un article pour l'affichage sans phrase générique répétitive. + * + * @param array<string, string> $item Données brutes de l'article. + * @return array<string, string> + */ + private static function normalize_article( array $item, int $max_chars = 190 ): array { + $placeholder = CGT_FHA_URI . '/assets/img/placeholder.svg'; + $summary = self::normalize_space( (string) ( $item['summary'] ?? '' ) ); + if ( '' !== $summary && function_exists( 'mb_strlen' ) && mb_strlen( $summary, 'UTF-8' ) > $max_chars ) { + $summary = rtrim( mb_substr( $summary, 0, $max_chars - 1, 'UTF-8' ) ) . '…'; + } + + return array( + 'title' => self::normalize_space( (string) ( $item['title'] ?? '' ) ), + 'summary' => $summary, + 'image_url' => ! empty( $item['image_url'] ) ? esc_url_raw( (string) $item['image_url'] ) : $placeholder, + 'category' => self::normalize_space( (string) ( $item['category'] ?? 'Confédération CGT' ) ), + 'url' => ! empty( $item['url'] ) ? esc_url_raw( (string) $item['url'] ) : 'https://www.cgt.fr/actualites', + ); + } + + /** + * Fallback statique si cgt.fr est inaccessible lors du premier démarrage. + * + * @return array<string, mixed> + */ + private static function get_static_fallback(): array { + $placeholder = CGT_FHA_URI . '/assets/img/placeholder.svg'; + return array( + 'fetched_at' => 0, + 'source' => 'Fallback éditorial local', + 'featured' => array( + 'title' => 'Salaires, retraites, services publics : toutes et tous mobilisés avec la CGT', + 'summary' => 'Retrouvez les dossiers nationaux, les revendications confédérales et les appels à l\'action portés par la CGT partout en France.', + 'image_url' => $placeholder, + 'category' => 'À la une', + 'url' => 'https://www.cgt.fr/actualites', + ), + 'latest' => array( + array( + 'title' => 'Pouvoir d\'achat : augmenter les salaires et le SMIC', + 'summary' => 'Les propositions économiques de la CGT pour indexer les salaires sur l\'inflation et revaloriser les grilles.', + 'image_url' => $placeholder, + 'category' => 'Salaires', + 'url' => 'https://www.cgt.fr/actualites', + ), + array( + 'title' => 'Conditions de travail & santé au travail', + 'summary' => 'Prévenir l\'usure professionnelle, renforcer le rôle des élus CSE et défendre les droits des salarié·e·s.', + 'image_url' => $placeholder, + 'category' => 'Travail', + 'url' => 'https://www.cgt.fr/actualites', + ), + array( + 'title' => 'Services publics de proximité dans nos territoires', + 'summary' => 'Défendre l\'hôpital public, l\'école, les transports et les services publics essentiels en milieu rural.', + 'image_url' => $placeholder, + 'category' => 'Services publics', + 'url' => 'https://www.cgt.fr/actualites', + ), + ), + ); + } + + /** + * Rendu HTML/CSS de la Section 4 (fidèle à la maquette). + */ + public static function render_section( string $section_title = 'ACTUALITÉ CONFÉDÉRALE' ): void { + $feed = self::get_feed(); + $featured = $feed['featured'] ?? array(); + $latest = $feed['latest'] ?? array(); + + if ( empty( $featured ) ) { + return; + } + ?> + <section class="cgt-section cgt-section--soft" aria-labelledby="cgt-confederal-heading"> + <div class="cgt-container"> + <h2 id="cgt-confederal-heading" class="cgt-section__title"><?php echo esc_html( $section_title ); ?></h2> + + <!-- Bloc supérieur « À LA UNE » --> + <article class="cgt-confederal-featured"> + <div class="cgt-confederal-featured__media"> + <img + src="<?php echo esc_url( $featured['image_url'] ); ?>" + alt="<?php echo esc_attr( $featured['title'] ); ?>" + class="cgt-confederal-featured__img" + loading="lazy" + decoding="async" + /> + <span class="cgt-confederal-featured__badge">À LA UNE</span> + </div> + <div class="cgt-confederal-featured__box"> + <span class="cgt-confederal-featured__kicker"><?php echo esc_html( $featured['category'] ?: 'À LA UNE' ); ?></span> + <h3 class="cgt-confederal-featured__title"><?php echo esc_html( $featured['title'] ); ?></h3> + <?php if ( '' !== trim( (string) ( $featured['summary'] ?? '' ) ) ) : ?> + <p class="cgt-confederal-featured__summary"><?php echo esc_html( $featured['summary'] ); ?></p> + <?php endif; ?> + <a + href="<?php echo esc_url( $featured['url'] ); ?>" + class="cgt-btn cgt-btn--outline-dark" + target="_blank" + rel="noopener noreferrer" + >LIRE SUR CGT.FR ↗</a> + </div> + </article> + + <!-- Grille inférieure : 3 articles --> + <?php if ( ! empty( $latest ) ) : ?> + <div class="cgt-confederal-grid"> + <?php foreach ( $latest as $item ) : ?> + <article class="cgt-confederal-card"> + <img + src="<?php echo esc_url( $item['image_url'] ); ?>" + alt="<?php echo esc_attr( $item['title'] ); ?>" + class="cgt-confederal-card__img" + loading="lazy" + decoding="async" + /> + <div class="cgt-confederal-card__box"> + <h3 class="cgt-confederal-card__title"><?php echo esc_html( $item['title'] ); ?></h3> + <?php if ( '' !== trim( (string) ( $item['summary'] ?? '' ) ) ) : ?> + <p class="cgt-confederal-card__summary"><?php echo esc_html( $item['summary'] ); ?></p> + <?php endif; ?> + <div class="cgt-confederal-card__cta"> + <a + href="<?php echo esc_url( $item['url'] ); ?>" + class="cgt-confederal-more" + target="_blank" + rel="noopener noreferrer" + > + <span class="cgt-confederal-more__label">Lire la suite</span> + <span class="cgt-confederal-more__arrow" aria-hidden="true">→</span> + </a> + </div> + </div> + </article> + <?php endforeach; ?> + </div> + <?php endif; ?> + </div> + </section> + <?php + } +} + +CGT_FHA_Confederal_Feed::init(); diff --git a/inc/seo-performance.php b/inc/seo-performance.php new file mode 100644 index 0000000..27da903 --- /dev/null +++ b/inc/seo-performance.php @@ -0,0 +1,180 @@ +<?php +/** + * SEO local, OpenGraph, Alerte Indexation Admin & Optimisation Performance. + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +/** + * Titre SEO personnalisé. + */ +add_filter( 'pre_get_document_title', 'cgt_fha_document_title', 20 ); +function cgt_fha_document_title( string $title ): string { + $site_brand = 'Union Locale CGT Foix Haute-Ariège'; + + if ( is_front_page() ) { + return $site_brand . ' | Défense des salariés et action syndicale'; + } + + if ( is_singular() ) { + return wp_strip_all_tags( get_the_title() ) . ' | ' . $site_brand; + } + + if ( is_home() ) { + return 'Actualités locales | ' . $site_brand; + } + + if ( is_archive() ) { + return wp_strip_all_tags( get_the_archive_title() ) . ' | ' . $site_brand; + } + + if ( is_404() ) { + return 'Page introuvable | ' . $site_brand; + } + + return $title ?: $site_brand; +} + +/** + * Calcule la meta description adaptée au contexte courant. + */ +function cgt_fha_get_meta_description(): string { + $opts = cgt_fha_get_options(); + + if ( is_front_page() ) { + return wp_strip_all_tags( + "Union Locale CGT de Foix et de la Haute-Ariège : union des syndicats CGT du territoire, rattachement des salarié·e·s isolé·e·s via le syndicat Multipros et permanences à l'Espace Sabart." + ); + } + + if ( is_singular() ) { + $post = get_queried_object(); + if ( $post instanceof WP_Post ) { + $raw = has_excerpt( $post ) ? $post->post_excerpt : $post->post_content; + $txt = trim( (string) preg_replace( '/\s+/u', ' ', wp_strip_all_tags( strip_shortcodes( $raw ) ) ) ); + if ( '' !== $txt ) { + return wp_html_excerpt( $txt, 158, '…' ); + } + } + } + + if ( is_category() || is_tag() ) { + $desc = trim( wp_strip_all_tags( term_description() ) ); + if ( '' !== $desc ) { + return wp_html_excerpt( $desc, 158, '…' ); + } + } + + return wp_html_excerpt( (string) $opts['footer_desc'], 158, '…' ); +} + +/** + * Injection automatique des balises <meta name="description"> et OpenGraph. + */ +add_action( 'wp_head', 'cgt_fha_render_meta_and_og', 2 ); +function cgt_fha_render_meta_and_og(): void { + $opts = cgt_fha_get_options(); + $title = cgt_fha_document_title( wp_get_document_title() ); + $description = cgt_fha_get_meta_description(); + $og_type = is_single() ? 'article' : 'website'; + + if ( is_front_page() ) { + $url = home_url( '/' ); + } elseif ( is_singular() ) { + $url = get_permalink() ?: home_url( '/' ); + } else { + global $wp; + $url = home_url( add_query_arg( array(), $wp->request ?? '' ) ); + } + + $og_image = (string) $opts['hero_bg_url']; + if ( is_singular() && has_post_thumbnail() ) { + $thumb_url = get_the_post_thumbnail_url( get_queried_object_id(), 'cgt-featured' ); + if ( $thumb_url ) { + $og_image = $thumb_url; + } + } + ?> + <meta name="description" content="<?php echo esc_attr( $description ); ?>" /> + <meta property="og:locale" content="fr_FR" /> + <meta property="og:site_name" content="Union Locale CGT Foix Haute-Ariège" /> + <meta property="og:type" content="<?php echo esc_attr( $og_type ); ?>" /> + <meta property="og:title" content="<?php echo esc_attr( $title ); ?>" /> + <meta property="og:description" content="<?php echo esc_attr( $description ); ?>" /> + <meta property="og:url" content="<?php echo esc_url( $url ); ?>" /> + <?php if ( '' !== $og_image ) : ?> + <meta property="og:image" content="<?php echo esc_url( $og_image ); ?>" /> + <?php endif; ?> + <meta name="twitter:card" content="summary_large_image" /> + <?php +} + +/** + * Alerte rouge dans le tableau de bord si l'indexation des moteurs de recherche est bloquée. + */ +add_action( 'admin_notices', 'cgt_fha_indexation_warning_notice' ); +function cgt_fha_indexation_warning_notice(): void { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + if ( '0' === (string) get_option( 'blog_public' ) ) { + $reading_url = admin_url( 'options-reading.php' ); + ?> + <div class="notice notice-error" style="border-left-color:#d20012; background:#fff5f5;"> + <p> + <strong style="color:#d20012;">Alerte SEO — Indexation bloquée :</strong> + Votre site demande actuellement aux moteurs de recherche de ne pas l'indexer. + <a href="<?php echo esc_url( $reading_url ); ?>">Rendez-vous dans Réglages > Lecture</a> + et décochez « Demander aux moteurs de recherche de ne pas indexer ce site ». + </p> + </div> + <?php + } +} + +/** + * Nettoyage du head WordPress et suppression de wp-emoji / wp-embed pour un TTFB et LCP optimaux. + */ +add_action( 'init', 'cgt_fha_cleanup_wp_bloat' ); +function cgt_fha_cleanup_wp_bloat(): void { + remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); + remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); + remove_action( 'wp_print_styles', 'print_emoji_styles' ); + remove_action( 'admin_print_styles', 'print_emoji_styles' ); + remove_filter( 'the_content_feed', 'wp_staticize_emoji' ); + remove_filter( 'comment_text_rss', 'wp_staticize_emoji' ); + remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' ); + + remove_action( 'wp_head', 'rsd_link' ); + remove_action( 'wp_head', 'wlwmanifest_link' ); + remove_action( 'wp_head', 'wp_generator' ); + remove_action( 'wp_head', 'wp_shortlink_wp_head' ); + remove_action( 'wp_head', 'wp_oembed_add_discovery_links' ); +} + +add_action( 'wp_footer', 'cgt_fha_disable_wp_embed' ); +function cgt_fha_disable_wp_embed(): void { + wp_deregister_script( 'wp-embed' ); +} + +/** + * Ajoute loading="lazy" et decoding="async" sur les images hors ligne de flottaison. + * + * @param array<string, string> $attr Attributs de la balise img. + * @return array<string, string> + */ +add_filter( 'wp_get_attachment_image_attributes', 'cgt_fha_lazy_image_attributes', 10, 1 ); +function cgt_fha_lazy_image_attributes( array $attr ): array { + if ( empty( $attr['fetchpriority'] ) && empty( $attr['loading'] ) ) { + $attr['loading'] = 'lazy'; + } + if ( empty( $attr['decoding'] ) ) { + $attr['decoding'] = 'async'; + } + return $attr; +} diff --git a/inc/theme-options.php b/inc/theme-options.php new file mode 100644 index 0000000..2eb3e91 --- /dev/null +++ b/inc/theme-options.php @@ -0,0 +1,776 @@ +<?php +/** + * Panneau d'administration WordPress : Apparence > Réglages UL CGT + * + * Toutes les options sont pré-remplies par défaut via wp_parse_args() + * afin que le thème soit immédiatement fonctionnel dès son activation. + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +const CGT_FHA_OPTION_KEY = 'cgt_fha_options'; +const CGT_FHA_SCHEMA_VERSION = '1.2.0'; + +/** + * Valeurs par défaut complètes de toutes les options du thème. + * + * @return array<string, mixed> + */ +function cgt_fha_get_defaults(): array { + return array( + // Onglet 1 : Identité & Coordonnées. + 'phone' => '07 60 49 64 69', + 'email' => 'union-locale@cgt-fha.fr', + 'address' => "Espace Sabart, 10 Av. de l'Ayroule, 09400 Tarascon-sur-Ariège", + 'hours' => "Permanences d'accueil et juridiques : Mardi et Jeudi de 9h30 à 12h30, et sur rendez-vous.", + 'facebook_url' => 'https://www.facebook.com/profile.php?id=61552410568856', + 'instagram_url' => '', + 'youtube_url' => '', + 'header_logo_url' => CGT_FHA_URI . '/assets/img/banniere-ul-noir.png', + 'footer_logo_url' => CGT_FHA_URI . '/assets/img/banniere-ul-blanc.png', + 'footer_desc' => "L'Union Locale CGT de Foix et de la Haute-Ariège rassemble tous les syndicats CGT de son périmètre et rattache directement les salarié·e·s isolé·e·s au sein de son syndicat multiprofessionnel (Multipros).", + 'footer_call' => "Une lutte dans votre entreprise, une info à partager ? Contactez l'UL", + 'news_url' => home_url( '/actualites/' ), + 'calendar_url' => home_url( '/calendrier/' ), + 'contact_url' => home_url( '/contact/' ), + 'join_url' => home_url( '/syndiquez-vous/' ), + 'legal_url' => home_url( '/mentions-legales/' ), + + // Onglet 2 : Bandeau Mobilisation auto-expirant. + 'banner_enabled' => 0, + 'banner_text' => 'MOBILISATION : Grève et manifestation à Foix - Allées de Villote', + 'banner_url' => '', + 'banner_expires' => wp_date( 'Y-m-d\TH:i', time() + 7 * DAY_IN_SECONDS ), + + // Onglet 3 : Page d'accueil — Hero. + 'hero_bg_url' => CGT_FHA_URI . '/assets/img/hero-ariege.jpg', + 'hero_title_line1' => 'Union Locale CGT Foix Haute-Ariège', + 'hero_title_line2' => 'Votre syndicat de proximité !', + 'hero_quote' => '« La régression sociale ne se négocie pas, elle se combat ! » — Henri Krasucki, Secrétaire Général de la CGT de 1982 à 1992', + 'hero_btn1_label' => "J'ai besoin d'aide", + 'hero_btn1_url' => home_url( '/contact/' ), + 'hero_btn2_label' => 'Rejoindre la CGT', + 'hero_btn2_url' => home_url( '/syndiquez-vous/' ), + + // Onglet 3 : Section 2 — À quoi nous servons ? + 'missions_title' => 'À QUOI NOUS SERVONS ?', + 'missions_subtitle' => "L'Union Locale fédère l'ensemble des syndicats CGT du bassin de Foix et de la Haute-Ariège, et accueille directement les salarié·e·s sans syndicat d'entreprise grâce à son syndicat multiprofessionnel (Multipros).", + 'mission_1_icon' => 'fist', + 'mission_1_title' => "S'ORGANISER", + 'mission_1_text' => "Unir tous les syndicats CGT de Foix et de la Haute-Ariège, créer une section dans votre entreprise ou vous syndiquer directement via le syndicat Multipros de l'Union Locale.", + 'mission_2_icon' => 'shield', + 'mission_2_title' => 'SE DÉFENDRE', + 'mission_2_text' => "Faire respecter vos droits, vos salaires et vos conditions de travail à Foix comme en Haute-Ariège, avec l'appui solidaire de nos syndicats, conseillers du salarié et défenseurs.", + 'mission_3_icon' => 'chat', + 'mission_3_title' => "S'INFORMER & SE FORMER", + 'mission_3_text' => "Partager l'actualité des luttes à Foix et en Haute-Ariège et exercer votre droit légal à la formation syndicale (CFESES) pour agir collectivement.", + + // Onglet 3 : Titres Actualités locales & confédérales. + 'local_news_title' => 'SUR LE TERRAIN À FOIX ET EN HAUTE-ARIÈGE', + 'local_news_more' => 'Voir toutes les actualités locales →', + 'confederal_title' => 'ACTUALITÉ CONFÉDÉRALE', + + // Onglet 3 : Section 5 — Salariés isolés & TPE (Multipros). + 'tpe_enabled' => 1, + 'tpe_badge' => "Syndicat Multipros de l'UL · TPE, PME & Isolé·e·s", + 'tpe_title' => "SALARIÉS ISOLÉS & TPE : VOUS N'ÊTES PAS SEULS", + 'tpe_text' => "À Foix comme en Haute-Ariège, même sans section syndicale ni CSE dans votre entreprise (TPE, commerce, artisanat, chantier, association…), l'Union Locale est votre maison commune : vous êtes rattaché·e directement au syndicat multiprofessionnel (Multipros) de l'UL.", + 'tpe_point_1' => "Rattachement au syndicat Multipros de l'UL : ne restez plus isolé·e et rejoignez la solidarité de tous les syndicats CGT de Foix et de la Haute-Ariège.", + 'tpe_point_2' => "Confidentialité absolue : aucune prise de contact ni adhésion n'est portée à la connaissance de votre employeur sans votre accord.", + 'tpe_point_3' => "Conseillers du Salarié : accompagnement gratuit et défense à vos côtés lors d'un entretien préalable à licenciement ou d'un litige.", + 'tpe_btn_label' => "Contacter l'UL en toute confidentialité", + 'tpe_btn_url' => home_url( '/contact/' ), + + // Onglet 3 : Section 6 — Formation Syndicale (CFESES) & Appli Mobile (désactivée par défaut). + 'cfeses_enabled' => 1, + 'cfeses_eyebrow' => 'Droit individuel & vie militante', + 'cfeses_title' => 'Formation Syndicale (CFESES)', + 'cfeses_text' => "Tout·e salarié·e a droit au Congé de Formation Économique, Sociale, Environnementale et Syndicale : jusqu'à 12 jours par an avec maintien intégral de salaire. Remettez votre demande à l'employeur au moins 30 jours avant le stage.", + 'cfeses_btn_label' => 'Demander une formation syndicale', + 'cfeses_btn_url' => home_url( '/contact/' ), + + 'app_enabled' => 0, + 'app_eyebrow' => 'Outil militant 100 % hors-ligne', + 'app_title' => "L'Application Mobile UL CGT Foix Haute-Ariège", + 'app_text' => 'Suivez les actualités locales, recevez les alertes mobilisation en temps réel et gardez votre carte de syndiqué virtuelle & vos 13 timbres toujours sur vous (Android / KMP).', + 'app_btn_label' => "Télécharger l'application (.apk / Store)", + 'app_download_url' => 'https://cgt-fha.fr/', + 'app_qr_url' => CGT_FHA_URI . '/assets/img/qr-placeholder.svg', + + // Onglet 3 : Section 7 — Réseau syndical CGT (5 cartes verticales). + 'network_enabled' => 1, + 'network_title' => 'RÉSEAU SYNDICAL CGT', + + 'network_1_enabled' => 1, + 'network_1_logo' => CGT_FHA_URI . '/assets/img/logo-cgt-09.jpg', + 'network_1_label' => 'UD CGT 09', + 'network_1_desc' => "Union Départementale des syndicats CGT de l'Ariège : coordination départementale des luttes et solidarité interprofessionnelle.", + 'network_1_btn' => 'Visiter le site ↗', + 'network_1_url' => 'https://ariege.cgt.fr/', + + 'network_2_enabled' => 1, + 'network_2_logo' => CGT_FHA_URI . '/assets/img/logo-cgt.png', + 'network_2_label' => 'Confédération CGT', + 'network_2_desc' => 'Site national de la CGT : actualités confédérales, campagnes interprofessionnelles, analyses et repères revendicatifs.', + 'network_2_btn' => 'Visiter le site ↗', + 'network_2_url' => 'https://www.cgt.fr/', + + 'network_3_enabled' => 1, + 'network_3_logo' => CGT_FHA_URI . '/assets/img/logo-nvo.png', + 'network_3_label' => 'La NVO', + 'network_3_desc' => "La Nouvelle Vie Ouvrière : le média d'information sociale et syndicale de la CGT au service du monde du travail.", + 'network_3_btn' => 'Visiter le site ↗', + 'network_3_url' => 'https://www.nvo.fr/', + + 'network_4_enabled' => 1, + 'network_4_logo' => CGT_FHA_URI . '/assets/img/logo-ihs.jpg', + 'network_4_label' => 'IHS CGT', + 'network_4_desc' => "Institut CGT d'Histoire Sociale : mémoire vivante des luttes sociales, archives et recherches sur le mouvement ouvrier.", + 'network_4_btn' => 'Visiter le site ↗', + 'network_4_url' => 'https://www.ihs.cgt.fr/', + + 'network_5_enabled' => 1, + 'network_5_logo' => CGT_FHA_URI . '/assets/img/logo-indecosa.png', + 'network_5_label' => 'INDECOSA-CGT', + 'network_5_desc' => 'Association de la CGT pour l\'information et la défense des consommateurs salarié·e·s (logement, énergie, consommation).', + 'network_5_btn' => 'Visiter le site ↗', + 'network_5_url' => 'https://www.indecosa-cgt.fr/', + ); +} + +/** + * Retourne les options fusionnées avec les valeurs par défaut (mise en cache mémoire par requête). + * + * @param bool $fresh Force la relecture depuis la base de données. + * @return array<string, mixed> + */ +function cgt_fha_get_options( bool $fresh = false ): array { + static $cached = null; + if ( null !== $cached && ! $fresh ) { + return $cached; + } + + $saved = get_option( CGT_FHA_OPTION_KEY, array() ); + $saved = is_array( $saved ) ? $saved : array(); + $merged = wp_parse_args( $saved, cgt_fha_get_defaults() ); + + $media_keys = array( + 'hero_bg_url', + 'header_logo_url', + 'footer_logo_url', + 'app_qr_url', + 'network_1_logo', + 'network_2_logo', + 'network_3_logo', + 'network_4_logo', + 'network_5_logo', + ); + + // Garantit que les visuels critiques vidés par erreur retombent sur le défaut. + $defaults = cgt_fha_get_defaults(); + foreach ( $media_keys as $media_key ) { + if ( empty( $merged[ $media_key ] ) ) { + $merged[ $media_key ] = $defaults[ $media_key ]; + } + } + + $cached = $merged; + return $cached; +} + +/** + * Migration automatique 1.0.0 -> 1.1.0 pour les sites où le thème est déjà activé. + */ +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' ) ) { + return; + } + + $defaults = cgt_fha_get_defaults(); + $saved = get_option( CGT_FHA_OPTION_KEY, array() ); + + 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']; + } + + // 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 ]; + } + } + + // 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 ]; + } + } + + $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 ); +} + +/** + * Vérifie si le bandeau d'alerte mobilisation doit être affiché. + */ +function cgt_fha_is_banner_active(): bool { + $opts = cgt_fha_get_options(); + if ( empty( $opts['banner_enabled'] ) || empty( $opts['banner_text'] ) || empty( $opts['banner_expires'] ) ) { + return false; + } + + $tz = wp_timezone(); + $expire_dt = DateTimeImmutable::createFromFormat( 'Y-m-d\TH:i', (string) $opts['banner_expires'], $tz ); + if ( ! $expire_dt ) { + return false; + } + + $now_ts = (int) current_time( 'timestamp' ); + return $now_ts < $expire_dt->getTimestamp(); +} + +/** + * Enregistrement du réglage et de la page d'administration. + */ +add_action( 'admin_menu', 'cgt_fha_register_admin_page' ); +function cgt_fha_register_admin_page(): void { + add_theme_page( + 'Réglages UL CGT Foix Haute-Ariège', + 'Réglages UL CGT', + 'manage_options', + 'cgt-fha-settings', + 'cgt_fha_render_admin_page' + ); +} + +add_action( 'admin_init', 'cgt_fha_register_settings' ); +function cgt_fha_register_settings(): void { + register_setting( + 'cgt_fha_settings_group', + CGT_FHA_OPTION_KEY, + array( + 'type' => 'array', + 'sanitize_callback' => 'cgt_fha_sanitize_options', + 'default' => cgt_fha_get_defaults(), + ) + ); +} + +/** + * Pré-remplit l'option en base lors de l'activation du thème (autoload = true, taille ~5 Ko). + */ +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. + * + * @param mixed $input Données brutes du formulaire. + * @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(); + + $text_fields = array( + 'phone', + 'address', + 'footer_call', + 'banner_text', + 'hero_title_line1', + 'hero_title_line2', + 'hero_btn1_label', + 'hero_btn2_label', + 'missions_title', + 'mission_1_title', + 'mission_2_title', + 'mission_3_title', + 'local_news_title', + 'local_news_more', + 'confederal_title', + 'tpe_badge', + 'tpe_title', + 'tpe_btn_label', + 'cfeses_eyebrow', + 'cfeses_title', + 'cfeses_btn_label', + 'app_eyebrow', + 'app_title', + 'app_btn_label', + 'network_title', + 'network_1_label', + 'network_1_btn', + 'network_2_label', + 'network_2_btn', + 'network_3_label', + 'network_3_btn', + 'network_4_label', + 'network_4_btn', + 'network_5_label', + 'network_5_btn', + ); + + foreach ( $text_fields as $field ) { + $val = isset( $input[ $field ] ) ? sanitize_text_field( wp_unslash( (string) $input[ $field ] ) ) : ( $existing[ $field ] ?? $defaults[ $field ] ); + $clean[ $field ] = $val; + } + + $textarea_fields = array( + 'hours', + 'footer_desc', + 'hero_quote', + 'missions_subtitle', + 'mission_1_text', + 'mission_2_text', + 'mission_3_text', + 'tpe_text', + 'tpe_point_1', + 'tpe_point_2', + 'tpe_point_3', + 'cfeses_text', + 'app_text', + 'network_1_desc', + 'network_2_desc', + 'network_3_desc', + 'network_4_desc', + 'network_5_desc', + ); + + foreach ( $textarea_fields as $field ) { + $val = isset( $input[ $field ] ) ? sanitize_textarea_field( wp_unslash( (string) $input[ $field ] ) ) : ( $existing[ $field ] ?? $defaults[ $field ] ); + $clean[ $field ] = $val; + } + + $clean['email'] = isset( $input['email'] ) + ? sanitize_email( wp_unslash( (string) $input['email'] ) ) + : $defaults['email']; + + $url_fields = array( + 'facebook_url', + 'instagram_url', + 'youtube_url', + 'header_logo_url', + 'footer_logo_url', + 'news_url', + 'calendar_url', + 'contact_url', + 'join_url', + 'legal_url', + 'banner_url', + 'hero_bg_url', + 'hero_btn1_url', + 'hero_btn2_url', + 'tpe_btn_url', + 'cfeses_btn_url', + 'app_download_url', + 'app_qr_url', + 'network_1_logo', + 'network_1_url', + 'network_2_logo', + 'network_2_url', + 'network_3_logo', + 'network_3_url', + 'network_4_logo', + 'network_4_url', + 'network_5_logo', + 'network_5_url', + ); + + foreach ( $url_fields as $field ) { + $raw = isset( $input[ $field ] ) ? trim( (string) wp_unslash( $input[ $field ] ) ) : ( $existing[ $field ] ?? $defaults[ $field ] ); + $clean[ $field ] = '' === $raw ? '' : esc_url_raw( $raw ); + } + + $checkbox_fields = array( + 'banner_enabled', + 'tpe_enabled', + 'cfeses_enabled', + 'app_enabled', + 'network_enabled', + 'network_1_enabled', + 'network_2_enabled', + 'network_3_enabled', + 'network_4_enabled', + 'network_5_enabled', + ); + foreach ( $checkbox_fields as $field ) { + $clean[ $field ] = ! empty( $input[ $field ] ) ? 1 : 0; + } + + // 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']; + + // 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 ]; + } + + return $clean; +} + +/** + * Liste des icônes disponibles dans le sélecteur admin des 3 cartes de mission. + * + * @return array<string, string> + */ +function cgt_fha_get_mission_icon_choices(): array { + return array( + 'fist' => 'Poing levé / Collectif', + 'shield' => 'Bouclier / Défense des droits', + 'chat' => 'Bulles d\'échange / Information & Formation', + 'megaphone' => 'Mégaphone / Mobilisation', + 'scale' => 'Balance / Prud\'hommes & Juridique', + 'users' => 'Collectif salarié·e·s', + 'book' => 'Livre / Formation CFESES', + ); +} + +/** + * Helper d'affichage d'un champ texte/URL/textarea dans l'admin. + */ +function cgt_fha_admin_field_row( string $key, string $label, string $type = 'text', string $desc = '' ): void { + $opts = cgt_fha_get_options(); + $val = $opts[ $key ] ?? ''; + $id = 'cgt_fha_' . $key; + $name = CGT_FHA_OPTION_KEY . '[' . $key . ']'; + ?> + <tr> + <th scope="row"><label for="<?php echo esc_attr( $id ); ?>"><?php echo esc_html( $label ); ?></label></th> + <td> + <?php if ( 'textarea' === $type ) : ?> + <textarea id="<?php echo esc_attr( $id ); ?>" name="<?php echo esc_attr( $name ); ?>" rows="3" class="large-text"><?php echo esc_textarea( (string) $val ); ?></textarea> + <?php elseif ( 'checkbox' === $type ) : ?> + <label> + <input type="checkbox" id="<?php echo esc_attr( $id ); ?>" name="<?php echo esc_attr( $name ); ?>" value="1" <?php checked( ! empty( $val ) ); ?> /> + <span><?php echo esc_html( $desc ); ?></span> + </label> + <?php else : ?> + <input type="<?php echo esc_attr( $type ); ?>" id="<?php echo esc_attr( $id ); ?>" name="<?php echo esc_attr( $name ); ?>" value="<?php echo esc_attr( (string) $val ); ?>" class="<?php echo 'datetime-local' === $type ? 'regular-text' : 'large-text'; ?>" /> + <?php endif; ?> + <?php if ( '' !== $desc && 'checkbox' !== $type ) : ?> + <p class="description"><?php echo esc_html( $desc ); ?></p> + <?php endif; ?> + </td> + </tr> + <?php +} + +/** + * Helper d'affichage d'un sélecteur d'image via la Médiathèque WordPress. + */ +function cgt_fha_admin_media_row( string $key, string $label, string $default_url, string $desc = '', bool $dark_preview = false ): void { + $opts = cgt_fha_get_options(); + $val = ! empty( $opts[ $key ] ) ? (string) $opts[ $key ] : $default_url; + $id = 'cgt_fha_' . $key; + $name = CGT_FHA_OPTION_KEY . '[' . $key . ']'; + $preview_bg = $dark_preview ? '#1c1e21' : '#f8fafc'; + ?> + <tr> + <th scope="row"><label for="<?php echo esc_attr( $id ); ?>"><?php echo esc_html( $label ); ?></label></th> + <td> + <div style="display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-bottom:10px;"> + <input type="url" id="<?php echo esc_attr( $id ); ?>" name="<?php echo esc_attr( $name ); ?>" value="<?php echo esc_url( $val ); ?>" class="regular-text" /> + <button type="button" class="button button-secondary" data-cgt-media-select="<?php echo esc_attr( $id ); ?>">Choisir dans la Médiathèque</button> + <button type="button" class="button" data-cgt-media-clear="<?php echo esc_attr( $id ); ?>" data-cgt-default-url="<?php echo esc_url( $default_url ); ?>">Rétablir par défaut</button> + </div> + <img id="<?php echo esc_attr( $id . '_preview' ); ?>" src="<?php echo esc_url( $val ); ?>" alt="" style="max-width:260px; max-height:110px; border:1px solid #cbd5e1; border-radius:6px; background:<?php echo esc_attr( $preview_bg ); ?>; padding:6px;" /> + <?php if ( '' !== $desc ) : ?> + <p class="description"><?php echo esc_html( $desc ); ?></p> + <?php endif; ?> + </td> + </tr> + <?php +} + +/** + * Rendu de la page d'administration Apparence > Réglages UL CGT. + */ +function cgt_fha_render_admin_page(): void { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + $defaults = cgt_fha_get_defaults(); + $opts = cgt_fha_get_options( true ); + $active_tab = isset( $_GET['tab'] ) ? sanitize_key( (string) $_GET['tab'] ) : 'identity'; + if ( ! in_array( $active_tab, array( 'identity', 'banner', 'homepage', 'confederal' ), true ) ) { + $active_tab = 'identity'; + } + + $feed_status = class_exists( 'CGT_FHA_Confederal_Feed' ) ? CGT_FHA_Confederal_Feed::get_cache_status() : array(); + ?> + <div class="wrap cgt-admin-wrap"> + <h1 style="display:flex; align-items:center; gap:10px;"> + <span style="display:inline-block; width:14px; height:14px; background:#d20012; border-radius:3px;"></span> + Réglages de l'Union Locale CGT Foix Haute-Ariège + </h1> + <p>Tous les textes, images, icônes, coordonnées et liens du thème sont modifiables ici sans toucher au code.</p> + + <?php if ( isset( $_GET['cgt_feed_refreshed'] ) ) : ?> + <div class="notice notice-success is-dismissible"> + <p><strong>Actualités confédérales (`cgt.fr`) synchronisées avec succès.</strong></p> + </div> + <?php endif; ?> + + <?php settings_errors(); ?> + + <nav class="nav-tab-wrapper" role="tablist" aria-label="Onglets de réglages UL CGT"> + <a href="#identity" class="nav-tab <?php echo 'identity' === $active_tab ? 'nav-tab-active' : ''; ?>" data-cgt-tab="identity" role="tab" aria-selected="<?php echo 'identity' === $active_tab ? 'true' : 'false'; ?>">1. Identité & Coordonnées</a> + <a href="#banner" class="nav-tab <?php echo 'banner' === $active_tab ? 'nav-tab-active' : ''; ?>" data-cgt-tab="banner" role="tab" aria-selected="<?php echo 'banner' === $active_tab ? 'true' : 'false'; ?>">2. Bandeau Mobilisation</a> + <a href="#homepage" class="nav-tab <?php echo 'homepage' === $active_tab ? 'nav-tab-active' : ''; ?>" data-cgt-tab="homepage" role="tab" aria-selected="<?php echo 'homepage' === $active_tab ? 'true' : 'false'; ?>">3. Page d'accueil & Blocs</a> + <a href="#confederal" class="nav-tab <?php echo 'confederal' === $active_tab ? 'nav-tab-active' : ''; ?>" data-cgt-tab="confederal" role="tab" aria-selected="<?php echo 'confederal' === $active_tab ? 'true' : 'false'; ?>">4. Flux Confédéral (cgt.fr)</a> + </nav> + + <form method="post" action="options.php" style="margin-top:20px;"> + <?php settings_fields( 'cgt_fha_settings_group' ); ?> + <input type="hidden" name="cgt_fha_active_tab" value="<?php echo esc_attr( $active_tab ); ?>" /> + + <!-- ONGLET 1 : IDENTITÉ & COORDONNÉES --> + <div data-cgt-panel="identity" <?php echo 'identity' !== $active_tab ? 'hidden' : ''; ?>> + <h2>Coordonnées & Identité visuelle</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_media_row( 'header_logo_url', 'Logo En-tête', $defaults['header_logo_url'], 'Par défaut : bannière officielle Union Locale CGT Foix Haute-Ariège (assets/img/banniere-ul-noir.png).' ); + cgt_fha_admin_media_row( 'footer_logo_url', 'Logo Pied de page (version blanche)', $defaults['footer_logo_url'], 'Affiché sur le fond sombre du pied de page (assets/img/banniere-ul-blanc.png par défaut).', true ); + cgt_fha_admin_field_row( 'phone', 'Téléphone', 'text', 'Affiché dans la barre supérieure et le pied de page.' ); + cgt_fha_admin_field_row( 'email', 'Adresse e-mail', 'email' ); + cgt_fha_admin_field_row( 'address', 'Adresse des locaux', 'text' ); + cgt_fha_admin_field_row( 'hours', 'Horaires des permanences', 'textarea' ); + cgt_fha_admin_field_row( 'facebook_url', 'URL Page Facebook', 'url' ); + cgt_fha_admin_field_row( 'instagram_url', 'URL Instagram (optionnel)', 'url' ); + cgt_fha_admin_field_row( 'youtube_url', 'URL YouTube (optionnel)', 'url' ); + cgt_fha_admin_field_row( 'footer_desc', 'Présentation pied de page', 'textarea' ); + cgt_fha_admin_field_row( 'footer_call', 'Appel militant pied de page', 'text' ); + ?> + </table> + + <h2>Pages clés de l'Union Locale</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_field_row( 'news_url', 'URL page Actualités', 'url' ); + cgt_fha_admin_field_row( 'calendar_url', 'URL page Calendrier', 'url' ); + cgt_fha_admin_field_row( 'contact_url', "URL page Besoin d'aide / Contact", 'url' ); + cgt_fha_admin_field_row( 'join_url', 'URL page Syndiquez-vous', 'url' ); + cgt_fha_admin_field_row( 'legal_url', 'URL Mentions légales & Confidentialité', 'url' ); + ?> + </table> + <?php submit_button( 'Enregistrer les réglages' ); ?> + </div> + + <!-- ONGLET 2 : BANDEAU MOBILISATION --> + <div data-cgt-panel="banner" <?php echo 'banner' !== $active_tab ? 'hidden' : ''; ?>> + <h2>Bandeau d'Alerte Mobilisation Auto-Expirant</h2> + <p class="description"> + Le bandeau rouge CGT s'affiche uniquement si la case <strong>Activer le bandeau</strong> est cochée <strong>ET</strong> que l'heure actuelle du site + (<code><?php echo esc_html( current_time( 'd/m/Y H:i' ) ); ?></code>) est antérieure à la date d'expiration. + </p> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_field_row( 'banner_enabled', 'Activer le bandeau', 'checkbox', 'Afficher le bandeau rouge de mobilisation sous la barre supérieure.' ); + cgt_fha_admin_field_row( 'banner_text', 'Mot d\'ordre & lieu', 'text', 'Exemple : MOBILISATION : Grève et manifestation à Foix - Allées de Villote' ); + cgt_fha_admin_field_row( 'banner_url', 'Lien URL au clic (optionnel)', 'url', 'Laissez vide si le bandeau ne doit pas être cliquable.' ); + cgt_fha_admin_field_row( 'banner_expires', 'Date et heure d\'expiration automatique', 'datetime-local', 'Passé cet horaire, le bandeau disparaît automatiquement.' ); + ?> + </table> + <?php submit_button( 'Enregistrer les réglages' ); ?> + </div> + + <!-- ONGLET 3 : PAGE D'ACCUEIL --> + <div data-cgt-panel="homepage" <?php echo 'homepage' !== $active_tab ? 'hidden' : ''; ?>> + <h2>Section 1 — Bannière Hero Plein Écran</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_media_row( 'hero_bg_url', 'Image panoramique de fond', $defaults['hero_bg_url'], 'Image sombre de montagne/Ariège recommandée (1600×720 px).' ); + cgt_fha_admin_field_row( 'hero_title_line1', 'Titre H1 — Ligne 1', 'text' ); + cgt_fha_admin_field_row( 'hero_title_line2', 'Titre H1 — Ligne 2', 'text' ); + cgt_fha_admin_field_row( 'hero_quote', 'Citation sous le titre', 'textarea' ); + cgt_fha_admin_field_row( 'hero_btn1_label', 'Bouton gauche — Libellé', 'text' ); + cgt_fha_admin_field_row( 'hero_btn1_url', 'Bouton gauche — Lien URL', 'url' ); + cgt_fha_admin_field_row( 'hero_btn2_label', 'Bouton droit — Libellé', 'text' ); + cgt_fha_admin_field_row( 'hero_btn2_url', 'Bouton droit — Lien URL', 'url' ); + ?> + </table> + + <hr /> + <h2>Section 2 — « À QUOI NOUS SERVONS ? » (3 Cartes)</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_field_row( 'missions_title', 'Titre de la section', 'text' ); + cgt_fha_admin_field_row( 'missions_subtitle', 'Chapeau de présentation (Union des syndicats & Multipros)', 'textarea' ); + $icon_choices = cgt_fha_get_mission_icon_choices(); + for ( $i = 1; $i <= 3; $i++ ) : + $icon_key = 'mission_' . $i . '_icon'; + $title_key = 'mission_' . $i . '_title'; + $text_key = 'mission_' . $i . '_text'; + ?> + <tr> + <th scope="row"><label for="<?php echo esc_attr( 'cgt_fha_' . $icon_key ); ?>">Carte <?php echo (int) $i; ?> — Icône SVG</label></th> + <td> + <select id="<?php echo esc_attr( 'cgt_fha_' . $icon_key ); ?>" name="<?php echo esc_attr( CGT_FHA_OPTION_KEY . '[' . $icon_key . ']' ); ?>"> + <?php foreach ( $icon_choices as $slug => $label ) : ?> + <option value="<?php echo esc_attr( $slug ); ?>" <?php selected( $opts[ $icon_key ], $slug ); ?>><?php echo esc_html( $label ); ?></option> + <?php endforeach; ?> + </select> + </td> + </tr> + <?php + cgt_fha_admin_field_row( $title_key, 'Carte ' . $i . ' — Titre', 'text' ); + cgt_fha_admin_field_row( $text_key, 'Carte ' . $i . ' — Description', 'textarea' ); + endfor; + ?> + </table> + + <hr /> + <h2>Sections 3 & 4 — Titres Actualités</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_field_row( 'local_news_title', 'Titre Actualités locales', 'text' ); + cgt_fha_admin_field_row( 'local_news_more', 'Libellé lien « Voir toutes... »', 'text' ); + cgt_fha_admin_field_row( 'confederal_title', 'Titre Actualité confédérale', 'text' ); + ?> + </table> + + <hr /> + <h2>Section 5 — Salariés isolés & TPE (Syndicat Multipros)</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_field_row( 'tpe_enabled', 'Afficher ce bloc', 'checkbox', 'Afficher la section Salariés isolés & TPE sur la page d\'accueil.' ); + cgt_fha_admin_field_row( 'tpe_badge', 'Surtitre / Badge', 'text' ); + cgt_fha_admin_field_row( 'tpe_title', 'Titre du bloc', 'text' ); + cgt_fha_admin_field_row( 'tpe_text', 'Paragraphe explicatif', 'textarea' ); + cgt_fha_admin_field_row( 'tpe_point_1', 'Point clé 1 (Rattachement Multipros)', 'textarea' ); + cgt_fha_admin_field_row( 'tpe_point_2', 'Point clé 2 (Confidentialité)', 'textarea' ); + cgt_fha_admin_field_row( 'tpe_point_3', 'Point clé 3 (Conseillers du Salarié)', 'textarea' ); + cgt_fha_admin_field_row( 'tpe_btn_label', 'Libellé du bouton', 'text' ); + cgt_fha_admin_field_row( 'tpe_btn_url', 'Lien URL du bouton', 'url' ); + ?> + </table> + + <hr /> + <h2>Section 6 — Formation Syndicale (CFESES) & Application Mobile</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_field_row( 'cfeses_enabled', 'Afficher le bloc CFESES', 'checkbox', 'Afficher la colonne Formation Syndicale (CFESES).' ); + cgt_fha_admin_field_row( 'cfeses_eyebrow', 'CFESES — Surtitre', 'text' ); + cgt_fha_admin_field_row( 'cfeses_title', 'CFESES — Titre', 'text' ); + cgt_fha_admin_field_row( 'cfeses_text', 'CFESES — Texte explicatif', 'textarea' ); + cgt_fha_admin_field_row( 'cfeses_btn_label', 'CFESES — Libellé bouton', 'text' ); + cgt_fha_admin_field_row( 'cfeses_btn_url', 'CFESES — Lien URL', 'url' ); + + cgt_fha_admin_field_row( + 'app_enabled', + 'Afficher le bloc Appli Mobile', + 'checkbox', + 'Désactivé par défaut tant que le contenu / fichier de téléchargement n\'est pas prêt. Cochez pour afficher ce bloc sur la page d\'accueil.' + ); + cgt_fha_admin_field_row( 'app_eyebrow', 'Appli — Surtitre', 'text' ); + cgt_fha_admin_field_row( 'app_title', 'Appli — Titre', 'text' ); + cgt_fha_admin_field_row( 'app_text', 'Appli — Présentation', 'textarea' ); + cgt_fha_admin_field_row( 'app_btn_label', 'Appli — Libellé bouton téléchargement', 'text' ); + cgt_fha_admin_field_row( 'app_download_url', 'Appli — Lien direct (.apk / Play Store)', 'url' ); + cgt_fha_admin_media_row( 'app_qr_url', 'Appli — Image QR Code', $defaults['app_qr_url'], 'Téléversez le QR Code pointant vers votre fichier .apk ou fiche Store.' ); + ?> + </table> + + <hr /> + <h2>Section 7 — Réseau Syndical CGT (Cartes avec logo, description et bouton)</h2> + <table class="form-table" role="presentation"> + <?php + cgt_fha_admin_field_row( 'network_enabled', 'Afficher la section Réseau CGT', 'checkbox', 'Afficher la grille de cartes du réseau syndical CGT en bas de page d\'accueil.' ); + cgt_fha_admin_field_row( 'network_title', 'Titre de la section', 'text' ); + for ( $i = 1; $i <= 5; $i++ ) { + cgt_fha_admin_field_row( 'network_' . $i . '_enabled', 'Carte ' . $i . ' — Afficher', 'checkbox', 'Afficher cette carte dans le Réseau Syndical CGT.' ); + cgt_fha_admin_media_row( 'network_' . $i . '_logo', 'Carte ' . $i . ' — Logo', $defaults[ 'network_' . $i . '_logo' ] ); + cgt_fha_admin_field_row( 'network_' . $i . '_label', 'Carte ' . $i . ' — Titre', 'text' ); + cgt_fha_admin_field_row( 'network_' . $i . '_desc', 'Carte ' . $i . ' — Description courte', 'textarea' ); + cgt_fha_admin_field_row( 'network_' . $i . '_btn', 'Carte ' . $i . ' — Libellé bouton', 'text' ); + cgt_fha_admin_field_row( 'network_' . $i . '_url', 'Carte ' . $i . ' — URL du site', 'url' ); + } + ?> + </table> + <?php submit_button( 'Enregistrer les réglages' ); ?> + </div> + </form> + + <!-- ONGLET 4 : FLUX CONFÉDÉRAL (hors formulaire options.php pour le bouton POST dédié) --> + <div data-cgt-panel="confederal" <?php echo 'confederal' !== $active_tab ? 'hidden' : ''; ?> style="margin-top:20px;"> + <h2>État du flux automatique « Actualité Confédérale » (`cgt.fr`)</h2> + <div class="card" style="max-width:760px; padding:16px 20px;"> + <p> + <strong>Statut du cache :</strong> + <?php echo ! empty( $feed_status['has_transient'] ) ? '<span style="color:#0a7d2b;font-weight:700;">Actif (Transient valide)</span>' : '<span style="color:#b45309;font-weight:700;">Expiré ou en attente de synchronisation</span>'; ?> + </p> + <p> + <strong>Dernière synchronisation :</strong> + <?php echo ! empty( $feed_status['fetched_at'] ) ? esc_html( wp_date( 'd/m/Y à H:i:s', (int) $feed_status['fetched_at'] ) ) : 'Pas encore effectuée'; ?> + </p> + <p> + <strong>Source utilisée :</strong> + <?php echo esc_html( (string) ( $feed_status['source'] ?? 'Non définie' ) ); ?> + </p> + + <?php if ( ! empty( $feed_status['featured']['title'] ) ) : ?> + <div style="margin:14px 0; padding:12px 14px; background:#f8fafc; border:1px solid #cbd5e1; border-radius:8px;"> + <p style="margin:0 0 4px;"><strong>Article « À la une » en cache :</strong></p> + <p style="margin:0 0 4px; font-size:15px;"><strong><?php echo esc_html( $feed_status['featured']['title'] ); ?></strong></p> + <p style="margin:0; color:#4a5568;"><?php echo esc_html( $feed_status['featured']['summary'] ?? '' ); ?></p> + </div> + <?php endif; ?> + + <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> + <input type="hidden" name="action" value="cgt_fha_refresh_feed" /> + <?php wp_nonce_field( 'cgt_fha_refresh_feed_action', 'cgt_fha_refresh_feed_nonce' ); ?> + <button type="submit" class="button button-primary">Actualiser les actus confédérales maintenant</button> + </form> + </div> + </div> + </div> + <?php +} diff --git a/index.php b/index.php new file mode 100644 index 0000000..7bce34d --- /dev/null +++ b/index.php @@ -0,0 +1,65 @@ +<?php +/** + * Gabarit de secours général (recherche et boucle générique WordPress). + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +get_header(); + +$heading = is_search() + ? sprintf( 'Résultats de recherche pour « %s »', get_search_query() ) + : ( get_bloginfo( 'name' ) ?: 'Union Locale CGT Foix Haute-Ariège' ); +?> + +<section class="cgt-page-hero"> + <div class="cgt-container"> + <nav class="cgt-breadcrumb" aria-label="<?php esc_attr_e( 'Fil d\'Ariane', 'cgt-fha' ); ?>"> + <a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php esc_html_e( 'Accueil', 'cgt-fha' ); ?></a> + <span aria-hidden="true">/</span> + <span><?php echo esc_html( $heading ); ?></span> + </nav> + <h1 class="cgt-page-hero__title"><?php echo esc_html( $heading ); ?></h1> + </div> +</section> + +<section class="cgt-section cgt-section--white"> + <div class="cgt-container"> + <?php if ( have_posts() ) : ?> + <div class="cgt-news-grid"> + <?php + while ( have_posts() ) : + the_post(); + cgt_fha_render_local_post_card(); + endwhile; + ?> + </div> + + <div class="cgt-pagination"> + <?php + the_posts_pagination( + array( + 'mid_size' => 2, + 'prev_text' => '← Précédent', + 'next_text' => 'Suivant →', + ) + ); + ?> + </div> + <?php else : ?> + <p><?php esc_html_e( 'Aucun résultat ne correspond à votre recherche.', 'cgt-fha' ); ?></p> + <form role="search" method="get" class="cgt-search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>" style="margin-left:0;"> + <label for="cgt-index-search" class="screen-reader-text"><?php esc_html_e( 'Rechercher sur le site', 'cgt-fha' ); ?></label> + <input type="search" id="cgt-index-search" name="s" value="<?php echo esc_attr( get_search_query() ); ?>" placeholder="<?php esc_attr_e( 'Nouvelle recherche…', 'cgt-fha' ); ?>" /> + <button type="submit" class="cgt-btn cgt-btn--primary"><?php esc_html_e( 'Rechercher', 'cgt-fha' ); ?></button> + </form> + <?php endif; ?> + </div> +</section> + +<?php +get_footer(); diff --git a/page.php b/page.php new file mode 100644 index 0000000..2c5b74d --- /dev/null +++ b/page.php @@ -0,0 +1,61 @@ +<?php +/** + * Gabarit de page standard (page.php) : Contact, Calendrier, Syndiquez-vous, Mentions légales... + * Compatible avec les shortcodes et hooks des extensions sœurs cgt-wp-calendar et cgt-wp-forms. + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +get_header(); +?> + +<?php +while ( have_posts() ) : + the_post(); + ?> + <section class="cgt-page-hero"> + <div class="cgt-container--narrow"> + <nav class="cgt-breadcrumb" aria-label="<?php esc_attr_e( 'Fil d\'Ariane', 'cgt-fha' ); ?>"> + <a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php esc_html_e( 'Accueil', 'cgt-fha' ); ?></a> + <span aria-hidden="true">/</span> + <span><?php the_title(); ?></span> + </nav> + <h1 class="cgt-page-hero__title"><?php the_title(); ?></h1> + </div> + </section> + + <article id="post-<?php the_ID(); ?>" <?php post_class( 'cgt-article-wrap' ); ?>> + <div class="cgt-container--narrow"> + <?php if ( has_post_thumbnail() ) : ?> + <div class="cgt-article__featured"> + <?php the_post_thumbnail( 'cgt-featured', array( 'fetchpriority' => 'high' ) ); ?> + </div> + <?php endif; ?> + + <div class="entry-content"> + <?php + the_content(); + + wp_link_pages( + array( + 'before' => '<div class="page-links">' . esc_html__( 'Pages :', 'cgt-fha' ), + 'after' => '</div>', + ) + ); + ?> + </div> + + <?php + // Emplacement propre pour accueillir cgt-wp-calendar ou cgt-wp-forms selon le slug de page. + do_action( 'cgt_fha_after_page_content', get_post_field( 'post_name', get_the_ID() ) ); + ?> + </div> + </article> +<?php endwhile; ?> + +<?php +get_footer(); diff --git a/single.php b/single.php new file mode 100644 index 0000000..5348736 --- /dev/null +++ b/single.php @@ -0,0 +1,131 @@ +<?php +/** + * Gabarit d'article local (single.php) : catégorie, date, contenu, partage social sans traceur et navigation. + * + * @package CGT_FHA + */ + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +get_header(); +?> + +<?php +while ( have_posts() ) : + the_post(); + $cats = get_the_category(); + $cat_obj = ! empty( $cats ) ? $cats[0] : null; + $share_url = get_permalink(); + $share_txt = get_the_title(); + ?> + <section class="cgt-page-hero"> + <div class="cgt-container--narrow"> + <nav class="cgt-breadcrumb" aria-label="<?php esc_attr_e( 'Fil d\'Ariane', 'cgt-fha' ); ?>"> + <a href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php esc_html_e( 'Accueil', 'cgt-fha' ); ?></a> + <span aria-hidden="true">/</span> + <a href="<?php echo esc_url( cgt_fha_get_local_news_archive_url() ); ?>"><?php esc_html_e( 'Actualités', 'cgt-fha' ); ?></a> + <?php if ( $cat_obj ) : ?> + <span aria-hidden="true">/</span> + <a href="<?php echo esc_url( get_category_link( $cat_obj->term_id ) ); ?>"><?php echo esc_html( $cat_obj->name ); ?></a> + <?php endif; ?> + </nav> + <h1 class="cgt-page-hero__title"><?php the_title(); ?></h1> + </div> + </section> + + <article id="post-<?php the_ID(); ?>" <?php post_class( 'cgt-article-wrap' ); ?>> + <div class="cgt-container--narrow"> + <header class="cgt-article__header"> + <div class="cgt-article__meta"> + <?php if ( $cat_obj ) : ?> + <a href="<?php echo esc_url( get_category_link( $cat_obj->term_id ) ); ?>" class="cgt-pill-cat"> + <?php echo esc_html( $cat_obj->name ); ?> + </a> + <?php endif; ?> + <time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>"> + <?php echo esc_html( get_the_date( 'j F Y' ) ); ?> + </time> + <span aria-hidden="true">•</span> + <span><?php esc_html_e( 'Union Locale CGT Foix Haute-Ariège', 'cgt-fha' ); ?></span> + </div> + </header> + + <?php if ( has_post_thumbnail() ) : ?> + <div class="cgt-article__featured"> + <?php the_post_thumbnail( 'cgt-featured', array( 'fetchpriority' => 'high' ) ); ?> + </div> + <?php endif; ?> + + <div class="entry-content"> + <?php + the_content(); + + wp_link_pages( + array( + 'before' => '<div class="page-links">' . esc_html__( 'Pages :', 'cgt-fha' ), + 'after' => '</div>', + ) + ); + ?> + </div> + + <!-- Partage social souverain (liens directs sans script traceur) --> + <aside class="cgt-share-box" aria-label="<?php esc_attr_e( 'Partager cet article', 'cgt-fha' ); ?>"> + <span class="cgt-share-box__label"><?php esc_html_e( 'Partager cette information :', 'cgt-fha' ); ?></span> + <div class="cgt-share-box__actions"> + <a + href="<?php echo esc_url( 'https://www.facebook.com/sharer/sharer.php?u=' . rawurlencode( $share_url ) ); ?>" + class="cgt-share-btn" + target="_blank" + rel="noopener noreferrer" + >Facebook</a> + <a + href="<?php echo esc_url( 'https://wa.me/?text=' . rawurlencode( $share_txt . ' — ' . $share_url ) ); ?>" + class="cgt-share-btn" + target="_blank" + rel="noopener noreferrer" + >WhatsApp</a> + <a + href="<?php echo esc_url( 'mailto:?subject=' . rawurlencode( $share_txt ) . '&body=' . rawurlencode( $share_url ) ); ?>" + class="cgt-share-btn" + >E-mail</a> + <button type="button" class="cgt-share-btn" data-cgt-copy-url="<?php echo esc_url( $share_url ); ?>"> + <?php esc_html_e( 'Copier le lien', 'cgt-fha' ); ?> + </button> + </div> + </aside> + + <?php + $prev_post = get_previous_post(); + $next_post = get_next_post(); + if ( $prev_post || $next_post ) : + ?> + <nav class="cgt-post-nav" aria-label="<?php esc_attr_e( 'Navigation entre articles', 'cgt-fha' ); ?>"> + <div> + <?php if ( $prev_post ) : ?> + <a href="<?php echo esc_url( get_permalink( $prev_post ) ); ?>" class="cgt-post-nav__item"> + <span class="cgt-post-nav__dir">← <?php esc_html_e( 'Article précédent', 'cgt-fha' ); ?></span> + <strong><?php echo esc_html( get_the_title( $prev_post ) ); ?></strong> + </a> + <?php endif; ?> + </div> + <div> + <?php if ( $next_post ) : ?> + <a href="<?php echo esc_url( get_permalink( $next_post ) ); ?>" class="cgt-post-nav__item" style="text-align:right;"> + <span class="cgt-post-nav__dir"><?php esc_html_e( 'Article suivant', 'cgt-fha' ); ?> →</span> + <strong><?php echo esc_html( get_the_title( $next_post ) ); ?></strong> + </a> + <?php endif; ?> + </div> + </nav> + <?php endif; ?> + + <?php do_action( 'cgt_fha_after_single_post', get_the_ID() ); ?> + </div> + </article> +<?php endwhile; ?> + +<?php +get_footer(); diff --git a/style.css b/style.css new file mode 100644 index 0000000..235d31e --- /dev/null +++ b/style.css @@ -0,0 +1,1541 @@ +/* +Theme Name: Union Locale CGT Foix Haute-Ariège +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 +Requires at least: 6.4 +Tested up to: 6.9 +Requires PHP: 8.0 +License: GNU General Public License v2 or later +License URI: https://www.gnu.org/licenses/gpl-2.0.html +Text Domain: cgt-fha +*/ + +/* ========================================================================== + 1. DESIGN TOKENS & RESET MODERNE + ========================================================================== */ +:root { + --cgt-red: #d20012; + --cgt-red-dark: #a8000e; + --cgt-red-soft: rgba(210, 0, 18, 0.08); + --bg-main: #ffffff; + --bg-soft: #f1f3f5; + --bg-topbar: #1c1e21; + --text-main: #1a1a1a; + --text-muted: #4a5568; + --border-card: #cbd5e1; + --border-subtle: #e2e8f0; + --radius-btn: 8px; + --radius-card: 12px; + --radius-panel: 14px; + --radius-pill: 999px; + --shadow-card: 0 4px 14px rgba(15, 23, 42, 0.05); + --shadow-float: 0 10px 25px rgba(15, 23, 42, 0.07); + --container-max: 1160px; + --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --transition-fast: 180ms ease; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; + font-family: var(--font-sans); + font-size: 16px; + line-height: 1.65; + color: var(--text-main); + background-color: var(--bg-main); + -webkit-font-smoothing: antialiased; +} + +img, +svg { + display: block; + max-width: 100%; +} + +img { + height: auto; +} + +a { + color: var(--cgt-red); + text-decoration: none; + transition: color var(--transition-fast), background-color var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast); +} + +a:hover { + color: var(--cgt-red-dark); +} + +a:focus-visible, +button:focus-visible, +input:focus-visible, +textarea:focus-visible, +select:focus-visible { + outline: 3px solid var(--cgt-red); + outline-offset: 2px; +} + +h1, h2, h3, h4 { + margin-top: 0; + color: var(--text-main); + line-height: 1.25; +} + +.screen-reader-text { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.skip-link { + position: absolute; + left: -9999px; + top: 8px; + z-index: 10000; + background: var(--cgt-red); + color: #ffffff; + padding: 10px 18px; + border-radius: var(--radius-btn); + font-weight: 700; +} + +.skip-link:focus { + left: 12px; + color: #ffffff; +} + +.cgt-container { + width: min(var(--container-max), 100% - 2rem); + margin-inline: auto; +} + +.cgt-container--narrow { + width: min(860px, 100% - 2rem); + margin-inline: auto; +} + +/* ========================================================================== + 2. EN-TÊTE : TOPBAR, BANDEAU MOBILISATION & NAVIGATION + ========================================================================== */ +.cgt-topbar { + background-color: var(--bg-topbar); + color: #f8fafc; + font-size: 13px; + line-height: 1.4; + padding-block: 8px; +} + +.cgt-topbar__inner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + flex-wrap: wrap; +} + +.cgt-topbar__contacts { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.65rem; +} + +.cgt-topbar__sep { + color: rgba(255, 255, 255, 0.45); + user-select: none; +} + +.cgt-topbar__link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: #f1f5f9; + font-weight: 400; +} + +.cgt-topbar__link:hover { + color: #ffffff; + text-decoration: underline; +} + +.cgt-topbar__socials { + display: flex; + align-items: center; + gap: 0.85rem; +} + +.cgt-topbar__social { + display: inline-flex; + align-items: center; + justify-content: center; + color: #e2e8f0; + width: 24px; + height: 24px; + border-radius: 4px; +} + +.cgt-topbar__social:hover { + color: #ffffff; + transform: translateY(-1px); +} + +/* Bandeau d'alerte mobilisation auto-expirant */ +.cgt-mob-banner { + background-color: var(--cgt-red); + color: #ffffff; + text-align: center; + font-size: 14px; + font-weight: 700; + letter-spacing: 0.01em; +} + +.cgt-mob-banner__inner { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 10px 1rem; + color: #ffffff; +} + +a.cgt-mob-banner__inner:hover { + background-color: var(--cgt-red-dark); + color: #ffffff; +} + +/* Barre de navigation principale */ +.cgt-header { + position: sticky; + top: 0; + z-index: 900; + background-color: var(--bg-main); + border-bottom: 1px solid var(--border-subtle); + transition: box-shadow var(--transition-fast); +} + +.admin-bar .cgt-header { + top: 32px; +} + +@media (max-width: 782px) { + .admin-bar .cgt-header { + top: 46px; + } +} + +.cgt-header.is-scrolled { + box-shadow: 0 6px 20px rgba(15, 23, 42, 0.08); +} + +.cgt-header__inner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.5rem; + min-height: 76px; + padding-block: 0.65rem; +} + +.cgt-brand { + display: inline-flex; + align-items: center; + gap: 0.75rem; + flex-shrink: 0; + color: var(--text-main); +} + +.cgt-brand__logo { + height: 48px; + width: auto; + max-width: 280px; + object-fit: contain; +} + +.cgt-nav-toggle { + display: none; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + border: 1px solid var(--border-card); + border-radius: var(--radius-btn); + background: var(--bg-main); + color: var(--text-main); + cursor: pointer; +} + +.cgt-nav-toggle:hover { + border-color: var(--cgt-red); + color: var(--cgt-red); +} + +.cgt-nav__list { + display: flex; + align-items: center; + list-style: none; + margin: 0; + padding: 0; + gap: 2rem; +} + +.cgt-nav__list a { + position: relative; + display: inline-block; + padding-block: 6px; + color: var(--text-main); + font-weight: 500; + font-size: 15.5px; +} + +.cgt-nav__list a::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 2.5px; + background-color: var(--cgt-red); + transform: scaleX(0); + transform-origin: center; + transition: transform var(--transition-fast); +} + +.cgt-nav__list a:hover, +.cgt-nav__list .current-menu-item > a, +.cgt-nav__list .current_page_item > a, +.cgt-nav__list .current-menu-ancestor > a, +.cgt-nav__list a[aria-current="page"] { + color: var(--text-main); + font-weight: 600; +} + +.cgt-nav__list a:hover::after, +.cgt-nav__list .current-menu-item > a::after, +.cgt-nav__list .current_page_item > a::after, +.cgt-nav__list .current-menu-ancestor > a::after, +.cgt-nav__list a[aria-current="page"]::after { + transform: scaleX(1); +} + +@media (max-width: 767px) { + .cgt-brand__logo { + height: 40px; + max-width: 220px; + } + + .cgt-nav-toggle { + display: inline-flex; + } + + .cgt-nav { + display: none; + position: absolute; + top: 100%; + left: 0; + right: 0; + background: var(--bg-main); + border-bottom: 2px solid var(--cgt-red); + box-shadow: 0 14px 28px rgba(15, 23, 42, 0.14); + padding: 1rem 1.25rem 1.5rem; + } + + .cgt-nav.is-open { + display: block; + } + + .cgt-nav__list { + flex-direction: column; + align-items: flex-start; + gap: 0.35rem; + } + + .cgt-nav__list li { + width: 100%; + } + + .cgt-nav__list a { + width: 100%; + padding: 0.65rem 0.25rem; + font-size: 1rem; + border-bottom: 1px solid var(--border-subtle); + } + + .cgt-nav__list a::after { + display: none; + } + + .cgt-nav__list .current-menu-item > a, + .cgt-nav__list .current_page_item > a, + .cgt-nav__list a[aria-current="page"] { + color: var(--cgt-red); + } +} + +/* ========================================================================== + 3. BOUTONS & UTILITAIRES COMMUNS + ========================================================================== */ +.cgt-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.72rem 1.65rem; + border-radius: var(--radius-btn); + font-weight: 700; + font-size: 15px; + line-height: 1.25; + cursor: pointer; + border: 1.5px solid transparent; + text-align: center; +} + +.cgt-btn--primary { + background-color: var(--cgt-red); + color: #ffffff; +} + +.cgt-btn--primary:hover { + background-color: var(--cgt-red-dark); + color: #ffffff; + transform: translateY(-1px); +} + +.cgt-btn--white { + background-color: #ffffff; + color: var(--text-main); + border-color: #ffffff; +} + +.cgt-btn--white:hover { + background-color: #f8fafc; + color: var(--cgt-red); + transform: translateY(-1px); +} + +.cgt-btn--hero-dark { + background-color: rgba(116, 20, 26, 0.88); + color: #ffffff; + border-color: rgba(255, 255, 255, 0.45); +} + +.cgt-btn--hero-dark:hover { + background-color: var(--cgt-red); + border-color: #ffffff; + color: #ffffff; + transform: translateY(-1px); +} + +.cgt-btn--outline-dark { + background-color: transparent; + color: var(--text-main); + border-color: var(--text-main); + border-radius: var(--radius-pill); + padding: 6px 28px; + font-size: 13.5px; + letter-spacing: 0.03em; + text-transform: uppercase; +} + +.cgt-btn--outline-dark:hover { + background-color: var(--text-main); + color: #ffffff; +} + +.cgt-btn--subtle { + background-color: transparent; + color: var(--cgt-red); + border-color: var(--cgt-red); + border-radius: var(--radius-pill); + padding: 0.6rem 1.5rem; + font-size: 14.5px; +} + +.cgt-btn--subtle:hover { + background-color: var(--cgt-red); + color: #ffffff; +} + +.cgt-section { + padding-block: 3.75rem; +} + +.cgt-section--white { + background-color: var(--bg-main); +} + +.cgt-section--soft { + background-color: var(--bg-soft); +} + +.cgt-section__title { + text-align: center; + text-transform: uppercase; + font-size: clamp(1.45rem, 2.5vw, 1.95rem); + font-weight: 800; + letter-spacing: 0.03em; + margin-bottom: 2.25rem; + color: var(--text-main); +} + +.cgt-section__subtitle { + text-align: center; + max-width: 680px; + margin: -1.4rem auto 2.25rem; + color: var(--text-muted); + font-size: 1rem; +} + +.cgt-pill-cat { + display: inline-block; + background-color: var(--cgt-red); + color: #ffffff; + border-radius: var(--radius-pill); + font-size: 12px; + font-weight: 600; + line-height: 1.4; + padding: 2px 10px; +} + +/* ========================================================================== + 4. PAGE D'ACCUEIL — 7 SECTIONS FIDÈLES À LA MAQUETTE + ========================================================================== */ + +/* Section 1 : Hero */ +.cgt-hero { + position: relative; + min-height: clamp(410px, 54vh, 510px); + display: flex; + align-items: center; + justify-content: center; + text-align: center; + overflow: hidden; + background-color: #151a1f; + color: #ffffff; + padding-block: 3.5rem; +} + +.cgt-hero__bg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + z-index: 0; +} + +.cgt-hero__overlay { + position: absolute; + inset: 0; + background: linear-gradient(180deg, rgba(15, 18, 22, 0.54) 0%, rgba(15, 18, 22, 0.68) 100%); + z-index: 1; +} + +.cgt-hero__content { + position: relative; + z-index: 2; + max-width: 820px; + padding-inline: 1rem; +} + +.cgt-hero__title { + color: #ffffff; + font-size: clamp(1.75rem, 3.6vw, 2.75rem); + font-weight: 800; + line-height: 1.18; + margin-bottom: 1.1rem; + text-shadow: 0 2px 12px rgba(0, 0, 0, 0.35); +} + +.cgt-hero__title span { + display: block; +} + +.cgt-hero__quote { + margin: 0 auto 1.85rem; + max-width: 660px; + color: rgba(255, 255, 255, 0.92); + font-style: italic; + font-size: clamp(0.95rem, 1.3vw, 1.08rem); + line-height: 1.55; +} + +.cgt-hero__actions { + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; + flex-wrap: wrap; +} + +/* Section 2 : À quoi nous servons ? */ +.cgt-missions-section { + padding-block: 3.5rem 2.25rem; +} + +.cgt-missions-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + max-width: 1040px; + margin-inline: auto; +} + +.cgt-mission-card { + background-color: var(--bg-main); + border: 1px solid var(--border-card); + border-radius: var(--radius-card); + padding: 1.75rem 1.45rem; + text-align: center; + box-shadow: var(--shadow-card); + display: flex; + flex-direction: column; + align-items: center; +} + +.cgt-mission-card__icon { + width: 44px; + height: 44px; + color: var(--text-main); + margin-bottom: 1rem; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.cgt-mission-card__icon svg { + width: 40px; + height: 40px; +} + +.cgt-mission-card__title { + font-size: 1.05rem; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.02em; + margin-bottom: 0.65rem; +} + +.cgt-mission-card__text { + margin: 0; + font-size: 0.92rem; + color: var(--text-muted); + line-height: 1.55; +} + +/* Section 3 : Sur le terrain en Haute-Ariège */ +.cgt-local-section { + padding-block: 2.25rem 3.75rem; +} + +.cgt-news-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + max-width: 1040px; + margin-inline: auto; +} + +.cgt-news-card { + background-color: var(--bg-main); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-card); + box-shadow: var(--shadow-card); + overflow: hidden; + display: flex; + flex-direction: column; + transition: transform var(--transition-fast), box-shadow var(--transition-fast); +} + +.cgt-news-card:hover { + transform: translateY(-3px); + box-shadow: var(--shadow-float); +} + +.cgt-news-card__thumb-link { + display: block; + overflow: hidden; + background: #1c1e21; +} + +.cgt-news-card__thumb { + width: 100%; + height: 190px; + object-fit: cover; + transition: transform 280ms ease; +} + +.cgt-news-card:hover .cgt-news-card__thumb { + transform: scale(1.03); +} + +.cgt-news-card__body { + padding: 1.15rem 1.25rem 1.35rem; + display: flex; + flex-direction: column; + flex: 1; +} + +.cgt-news-card__meta { + display: flex; + align-items: center; + gap: 0.6rem; + flex-wrap: wrap; + margin-bottom: 0.65rem; +} + +.cgt-news-card__date { + font-size: 12.5px; + color: var(--text-muted); + font-weight: 500; +} + +.cgt-news-card__title { + font-size: 18px; + font-weight: 700; + line-height: 1.3; + margin-bottom: 0.5rem; +} + +.cgt-news-card__title a { + color: var(--text-main); +} + +.cgt-news-card__title a:hover { + color: var(--cgt-red); +} + +.cgt-news-card__excerpt { + margin: 0; + font-size: 0.9rem; + color: var(--text-muted); + line-height: 1.5; +} + +.cgt-section__footer { + margin-top: 2.25rem; + text-align: center; +} + +/* Section 4 : Actualité Confédérale (cgt.fr) */ +.cgt-confederal-featured { + max-width: 1040px; + margin: 0 auto 2.5rem auto; + display: flex; + align-items: center; +} + +.cgt-confederal-featured__media { + flex: 0 0 58%; + position: relative; + border-radius: var(--radius-panel); + overflow: hidden; + background: #1c1e21; + box-shadow: var(--shadow-card); +} + +.cgt-confederal-featured__img { + width: 100%; + height: 310px; + object-fit: cover; +} + +.cgt-confederal-featured__badge { + position: absolute; + right: 56px; + bottom: 14px; + background: rgba(26, 26, 26, 0.82); + color: #ffffff; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + padding: 4px 10px; + border-radius: 6px; + backdrop-filter: blur(4px); +} + +.cgt-confederal-featured__box { + flex: 1; + margin-left: -40px; + position: relative; + z-index: 2; + background-color: var(--bg-main); + border: 1px solid var(--border-card); + border-radius: var(--radius-panel); + padding: 1.75rem; + box-shadow: var(--shadow-float); + text-align: center; +} + +.cgt-confederal-featured__kicker { + display: inline-block; + font-size: 11.5px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--cgt-red); + margin-bottom: 0.45rem; +} + +.cgt-confederal-featured__title { + font-size: 1.15rem; + font-weight: 800; + line-height: 1.3; + margin-bottom: 0.65rem; +} + +.cgt-confederal-featured__summary { + font-size: 0.92rem; + color: var(--text-muted); + margin: 0 0 1.25rem; + line-height: 1.55; +} + +.cgt-confederal-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + max-width: 1040px; + margin-inline: auto; +} + +.cgt-confederal-card { + display: flex; + flex-direction: column; +} + +.cgt-confederal-card__img { + width: 100%; + height: 190px; + object-fit: cover; + border-radius: var(--radius-panel); + background: #1c1e21; +} + +.cgt-confederal-card__box { + margin: -24px 0.5rem 0; + position: relative; + z-index: 2; + background-color: var(--bg-main); + border: 1px solid var(--border-card); + border-radius: var(--radius-panel); + padding: 1.25rem; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.05); + display: flex; + flex-direction: column; + text-align: center; + flex: 1; +} + +.cgt-confederal-card__title { + font-size: 1rem; + font-weight: 800; + line-height: 1.3; + margin-bottom: 0.5rem; +} + +.cgt-confederal-card__summary { + font-size: 0.88rem; + color: var(--text-muted); + margin: 0; + line-height: 1.5; +} + +.cgt-confederal-card__cta { + margin-top: auto; + padding-top: 1rem; +} + +.cgt-confederal-more { + display: inline-flex; + align-items: center; + gap: 0.4rem; + color: var(--text-main); + font-weight: 700; + font-size: 0.92rem; + padding-bottom: 3px; +} + +.cgt-confederal-more__label { + border-bottom: 1.5px solid var(--cgt-red); + padding-bottom: 2px; + transition: color var(--transition-fast), border-color var(--transition-fast); +} + +.cgt-confederal-more__arrow { + color: var(--cgt-red); + font-weight: 800; + transition: transform var(--transition-fast); +} + +.cgt-confederal-more:hover { + color: var(--cgt-red); +} + +.cgt-confederal-more:hover .cgt-confederal-more__label { + border-bottom-color: var(--cgt-red-dark); +} + +.cgt-confederal-more:hover .cgt-confederal-more__arrow { + transform: translateX(4px); +} + +/* Section 5 : Salariés isolés & TPE */ +.cgt-tpe-box { + max-width: 1040px; + margin-inline: auto; + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + border: 1px solid var(--border-card); + border-left: 5px solid var(--cgt-red); + border-radius: var(--radius-panel); + padding: 2.25rem 2.5rem; + box-shadow: var(--shadow-card); + display: grid; + grid-template-columns: 1.45fr 1fr; + gap: 2rem; + align-items: center; +} + +.cgt-tpe-box__badge { + display: inline-block; + background: var(--cgt-red-soft); + color: var(--cgt-red); + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 4px 12px; + border-radius: var(--radius-pill); + margin-bottom: 0.85rem; +} + +.cgt-tpe-box__title { + font-size: clamp(1.35rem, 2.2vw, 1.75rem); + font-weight: 800; + margin-bottom: 0.85rem; +} + +.cgt-tpe-box__text { + color: var(--text-muted); + margin: 0 0 1.25rem; +} + +.cgt-tpe-points { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 0.85rem; +} + +.cgt-tpe-points li { + display: flex; + align-items: flex-start; + gap: 0.75rem; + background: #ffffff; + border: 1px solid var(--border-subtle); + border-radius: var(--radius-card); + padding: 0.9rem 1rem; + font-size: 0.92rem; +} + +.cgt-tpe-points__icon { + flex-shrink: 0; + color: var(--cgt-red); + margin-top: 2px; +} + +/* Section 6 : Formation syndicale (CFESES) & Appli mobile */ +.cgt-split-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 2rem; + max-width: 1040px; + margin-inline: auto; +} + +.cgt-split-grid--single { + grid-template-columns: 1fr; + max-width: 780px; +} + +.cgt-feature-card { + background-color: var(--bg-main); + border: 1px solid var(--border-card); + border-radius: var(--radius-panel); + padding: 2rem; + box-shadow: var(--shadow-card); + display: flex; + flex-direction: column; +} + +.cgt-feature-card__eyebrow { + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--cgt-red); + margin-bottom: 0.5rem; +} + +.cgt-feature-card__title { + font-size: 1.35rem; + font-weight: 800; + margin-bottom: 0.85rem; +} + +.cgt-feature-card__text { + color: var(--text-muted); + margin: 0 0 1.25rem; +} + +.cgt-feature-card__footer { + margin-top: auto; + padding-top: 0.5rem; +} + +.cgt-app-layout { + display: flex; + align-items: center; + gap: 1.25rem; + margin-top: 0.5rem; +} + +.cgt-app-qr { + flex-shrink: 0; + width: 118px; + height: 118px; + border-radius: 10px; + border: 1px solid var(--border-card); + padding: 6px; + background: #ffffff; + object-fit: contain; +} + +/* Section 7 : Réseau Syndical CGT (Cartes verticales + liens 404) */ +.cgt-network-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)); + gap: 1.15rem; + max-width: 1040px; + margin-inline: auto; +} + +.cgt-network-card { + background-color: var(--bg-main); + border: 1px solid var(--border-card); + border-radius: var(--radius-panel); + padding: 1.6rem 1.2rem; + box-shadow: var(--shadow-card); + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast); +} + +.cgt-network-card:hover { + transform: translateY(-3px); + box-shadow: var(--shadow-float); + border-color: var(--cgt-red); +} + +.cgt-network-card__logo-wrap { + width: 100%; + height: 96px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1rem; + flex-shrink: 0; +} + +.cgt-network-card__logo { + max-height: 88px; + max-width: 140px; + width: auto; + height: auto; + object-fit: contain; + border-radius: 6px; +} + +.cgt-network-card__title { + font-size: 1rem; + font-weight: 800; + margin-bottom: 0.5rem; + color: var(--text-main); +} + +.cgt-network-card__desc { + margin: 0 0 1.25rem; + font-size: 0.875rem; + color: var(--text-muted); + line-height: 1.5; +} + +.cgt-network-card__cta { + margin-top: auto; + width: 100%; + display: flex; + justify-content: center; +} + +.cgt-network-card__btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.35rem; + padding: 0.45rem 1.15rem; + border: 1.5px solid var(--text-main); + border-radius: var(--radius-btn); + color: var(--text-main); + font-size: 0.82rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.03em; +} + +.cgt-network-card__btn:hover { + background-color: var(--cgt-red); + border-color: var(--cgt-red); + color: #ffffff; +} + +.cgt-network-strip { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.9rem; + max-width: 1040px; + margin-inline: auto; +} + +.cgt-network-link { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.65rem 1.25rem; + border: 1px solid var(--border-card); + border-radius: var(--radius-pill); + background: var(--bg-main); + color: var(--text-main); + font-weight: 600; + font-size: 0.92rem; +} + +.cgt-network-link:hover { + border-color: var(--cgt-red); + color: var(--cgt-red); + background: var(--cgt-red-soft); + transform: translateY(-1px); +} + +/* ========================================================================== + 5. GABARITS INTÉRIEURS (HOME, ARCHIVE, SINGLE, PAGE, 404) + ========================================================================== */ +.cgt-page-hero { + background: linear-gradient(135deg, #1c1e21 0%, #2a2f36 100%); + color: #ffffff; + padding-block: 2.75rem; + border-bottom: 4px solid var(--cgt-red); +} + +.cgt-page-hero__title { + color: #ffffff; + font-size: clamp(1.6rem, 3vw, 2.35rem); + font-weight: 800; + margin-bottom: 0.5rem; +} + +.cgt-page-hero__desc { + color: #cbd5e1; + margin: 0; + max-width: 720px; +} + +.cgt-breadcrumb { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + font-size: 13px; + color: #cbd5e1; + margin-bottom: 0.85rem; +} + +.cgt-breadcrumb a { + color: #ffffff; + text-decoration: underline; +} + +.cgt-article-wrap { + padding-block: 3rem 4rem; +} + +.cgt-article__header { + margin-bottom: 1.75rem; +} + +.cgt-article__meta { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.75rem; + margin-bottom: 0.9rem; + font-size: 14px; + color: var(--text-muted); +} + +.cgt-article__title { + font-size: clamp(1.75rem, 3.2vw, 2.45rem); + font-weight: 800; + margin-bottom: 0; +} + +.cgt-article__featured { + margin-bottom: 2rem; + border-radius: var(--radius-panel); + overflow: hidden; + box-shadow: var(--shadow-card); +} + +.cgt-article__featured img { + width: 100%; + max-height: 460px; + object-fit: cover; +} + +.entry-content { + font-size: 1.05rem; + line-height: 1.75; + color: var(--text-main); +} + +.entry-content > * + * { + margin-top: 1.2rem; +} + +.entry-content h2, +.entry-content h3, +.entry-content h4 { + margin-top: 2rem; +} + +.entry-content blockquote { + margin-inline: 0; + padding: 1rem 1.35rem; + border-left: 4px solid var(--cgt-red); + background: var(--bg-soft); + border-radius: 0 var(--radius-btn) var(--radius-btn) 0; + font-style: italic; +} + +.entry-content img { + border-radius: var(--radius-card); +} + +/* Partage social sans traceur */ +.cgt-share-box { + margin-top: 2.5rem; + padding: 1.25rem 1.5rem; + background: var(--bg-soft); + border: 1px solid var(--border-card); + border-radius: var(--radius-card); + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; +} + +.cgt-share-box__label { + font-weight: 700; + font-size: 0.95rem; +} + +.cgt-share-box__actions { + display: flex; + flex-wrap: wrap; + gap: 0.6rem; +} + +.cgt-share-btn { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.45rem 0.95rem; + border-radius: var(--radius-pill); + border: 1px solid var(--border-card); + background: #ffffff; + color: var(--text-main); + font-size: 13.5px; + font-weight: 600; + cursor: pointer; +} + +.cgt-share-btn:hover { + border-color: var(--cgt-red); + color: var(--cgt-red); +} + +.cgt-post-nav { + margin-top: 2.5rem; + padding-top: 1.75rem; + border-top: 1px solid var(--border-subtle); + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; +} + +.cgt-post-nav__item { + padding: 1rem 1.25rem; + border: 1px solid var(--border-card); + border-radius: var(--radius-card); + display: block; + color: var(--text-main); +} + +.cgt-post-nav__item:hover { + border-color: var(--cgt-red); +} + +.cgt-post-nav__dir { + display: block; + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + color: var(--cgt-red); + margin-bottom: 0.25rem; +} + +/* Pagination */ +.cgt-pagination { + margin-top: 2.75rem; + display: flex; + justify-content: center; +} + +.cgt-pagination .nav-links { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.cgt-pagination .page-numbers { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 40px; + height: 40px; + padding-inline: 12px; + border: 1px solid var(--border-card); + border-radius: var(--radius-btn); + color: var(--text-main); + font-weight: 600; +} + +.cgt-pagination .page-numbers.current, +.cgt-pagination .page-numbers:hover { + background-color: var(--cgt-red); + border-color: var(--cgt-red); + color: #ffffff; +} + +/* Page 404 */ +.cgt-404 { + text-align: center; + padding-block: 4.5rem; +} + +.cgt-404__code { + display: inline-block; + font-size: clamp(3rem, 8vw, 5rem); + font-weight: 900; + color: var(--cgt-red); + line-height: 1; + margin-bottom: 0.75rem; +} + +.cgt-search-form { + display: flex; + justify-content: center; + gap: 0.5rem; + max-width: 480px; + margin: 1.5rem auto 2rem; +} + +.cgt-search-form input[type="search"] { + flex: 1; + padding: 0.7rem 1rem; + border: 1.5px solid var(--border-card); + border-radius: var(--radius-btn); + font-size: 1rem; +} + +/* ========================================================================== + 6. PIED DE PAGE SOUVERAIN (3 COLONNES) + ========================================================================== */ +.cgt-footer { + background-color: var(--bg-topbar); + color: #e2e8f0; + padding-block: 3.5rem 1.75rem; + font-size: 14.5px; +} + +.cgt-footer__grid { + display: grid; + grid-template-columns: 1.2fr 1.15fr 0.85fr; + gap: 2.5rem; + padding-bottom: 2.5rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.12); +} + +.cgt-footer__logo { + height: 50px; + width: auto; + max-width: 260px; + object-fit: contain; + margin-bottom: 1rem; +} + +.cgt-footer__title { + color: #ffffff; + font-size: 1rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + margin-bottom: 1rem; +} + +.cgt-footer__text { + color: #cbd5e1; + margin: 0 0 0.85rem; +} + +.cgt-footer__call { + color: #ffffff; + font-weight: 600; + border-left: 3px solid var(--cgt-red); + padding-left: 0.75rem; + margin: 0; +} + +.cgt-footer__list { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 0.6rem; +} + +.cgt-footer__list li { + display: flex; + align-items: flex-start; + gap: 0.55rem; + color: #cbd5e1; +} + +.cgt-footer__list svg { + flex-shrink: 0; + margin-top: 3px; + color: var(--cgt-red); +} + +.cgt-footer a { + color: #f1f5f9; +} + +.cgt-footer a:hover { + color: #ffffff; + text-decoration: underline; +} + +.cgt-footer__bottom { + padding-top: 1.5rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 0.75rem; + font-size: 13px; + color: #94a3b8; +} + +/* ========================================================================== + 7. RESPONSIVE TABLETTE & MOBILE + ========================================================================== */ +@media (max-width: 960px) { + .cgt-missions-grid, + .cgt-news-grid, + .cgt-confederal-grid { + grid-template-columns: repeat(2, 1fr); + } + + .cgt-tpe-box, + .cgt-split-grid, + .cgt-footer__grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 767px) { + .cgt-missions-grid, + .cgt-news-grid, + .cgt-confederal-grid, + .cgt-post-nav { + grid-template-columns: 1fr; + } + + .cgt-confederal-featured { + flex-direction: column; + } + + .cgt-confederal-featured__media { + flex: none; + width: 100%; + } + + .cgt-confederal-featured__img { + height: 220px; + } + + .cgt-confederal-featured__badge { + top: 14px; + right: 14px; + bottom: auto; + } + + .cgt-confederal-featured__box { + margin-left: 0.65rem; + margin-right: 0.65rem; + margin-top: -24px; + width: calc(100% - 1.3rem); + } + + .cgt-tpe-box { + padding: 1.5rem 1.25rem; + } + + .cgt-app-layout { + flex-direction: column; + align-items: flex-start; + } +} diff --git a/theme.json b/theme.json new file mode 100644 index 0000000..14daef4 --- /dev/null +++ b/theme.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 3, + "settings": { + "appearanceTools": false, + "color": { + "custom": false, + "customGradient": false, + "defaultPalette": false, + "defaultGradients": false, + "palette": [ + { + "name": "Rouge CGT", + "slug": "cgt-red", + "color": "#d20012" + }, + { + "name": "Rouge CGT Foncé", + "slug": "cgt-red-dark", + "color": "#a8000e" + }, + { + "name": "Fond Principal", + "slug": "bg-main", + "color": "#ffffff" + }, + { + "name": "Fond Doux", + "slug": "bg-soft", + "color": "#f1f3f5" + }, + { + "name": "Sombre Topbar & Footer", + "slug": "bg-topbar", + "color": "#1c1e21" + }, + { + "name": "Texte Principal", + "slug": "text-main", + "color": "#1a1a1a" + }, + { + "name": "Texte Secondaire", + "slug": "text-muted", + "color": "#4a5568" + }, + { + "name": "Bordure Carte", + "slug": "border-card", + "color": "#cbd5e1" + } + ] + }, + "typography": { + "customFontSize": true, + "defaultFontSizes": false, + "fontFamilies": [ + { + "name": "Système Moderne", + "slug": "system-sans", + "fontFamily": "system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif" + } + ], + "fontSizes": [ + { + "name": "Petit", + "slug": "small", + "size": "0.875rem" + }, + { + "name": "Normal", + "slug": "medium", + "size": "1rem" + }, + { + "name": "Grand", + "slug": "large", + "size": "1.25rem" + }, + { + "name": "Très grand", + "slug": "xlarge", + "size": "1.75rem" + }, + { + "name": "Titre Principal", + "slug": "xxlarge", + "size": "2.5rem" + } + ] + }, + "border": { + "radiusSizes": [ + { + "name": "Bouton", + "slug": "button", + "size": "8px" + }, + { + "name": "Carte", + "slug": "card", + "size": "12px" + }, + { + "name": "Encart", + "slug": "panel", + "size": "14px" + }, + { + "name": "Pilule", + "slug": "pill", + "size": "999px" + } + ] + }, + "layout": { + "contentSize": "820px", + "wideSize": "1160px" + } + }, + "styles": { + "color": { + "background": "var(--wp--preset--color--bg-main)", + "text": "var(--wp--preset--color--text-main)" + }, + "typography": { + "fontFamily": "var(--wp--preset--font-family--system-sans)", + "fontSize": "var(--wp--preset--font-size--medium)", + "lineHeight": "1.65" + }, + "elements": { + "link": { + "color": { + "text": "var(--wp--preset--color--cgt-red)" + }, + ":hover": { + "color": { + "text": "var(--wp--preset--color--cgt-red-dark)" + } + } + }, + "button": { + "color": { + "background": "var(--wp--preset--color--cgt-red)", + "text": "#ffffff" + }, + "border": { + "radius": "8px" + }, + ":hover": { + "color": { + "background": "var(--wp--preset--color--cgt-red-dark)" + } + } + } + } + } +}