From ccf32dcece365471834128934f2edb56064c84eb Mon Sep 17 00:00:00 2001 From: Valentin Le Moign Date: Tue, 12 May 2026 23:33:46 +0200 Subject: [PATCH] Initial commit --- .gitignore | 17 + 404.php | 3 + README.md | 86 + assets/fonts/Gelasio-Regular.woff2 | Bin 0 -> 41560 bytes assets/fonts/NewsCycle-Regular.woff2 | Bin 0 -> 59552 bytes assets/images/cnrs.png | Bin 0 -> 4172 bytes assets/images/ens.png | Bin 0 -> 20957 bytes assets/images/sorbonne.png | Bin 0 -> 8061 bytes assets/images/thalim-logo.png | Bin 0 -> 22405 bytes assets/images/thalim-logo.svg | 87 + assets/logo-shapes/fillshape.svg | 6 + assets/logo-shapes/shape1.svg | 8 + assets/logo-shapes/shape2.svg | 8 + assets/logo-shapes/shape3.svg | 8 + assets/logo-shapes/shape4.svg | 8 + assets/logo-shapes/shape5.svg | 8 + author.php | 16 + category.php | 272 ++ composer.json | 5 + composer.lock | 434 +++ css/admin.css | 923 ++++++ css/style.css | 2901 +++++++++++++++++++ functions.php | 1375 +++++++++ inc/admin-users-filter.php | 48 + inc/author-helpers.php | 250 ++ inc/membres-helpers.php | 244 ++ inc/pods-conditional-required.php | 94 + inc/pods-save-error-handler.php | 195 ++ inc/post-card-helpers.php | 172 ++ inc/post-title-required.php | 57 + inc/single-helpers.php | 447 +++ index.php | 263 ++ js/adminDashboardMods.js | 883 ++++++ js/adminFormRestore.js | 119 + js/agendaView.js | 146 + js/animatedLogo.js | 577 ++++ js/annoncesSwiper.js | 28 + js/categoryFilters.js | 138 + js/coloredWordsHero.js | 22 + js/fitPostCardTitle.js | 43 + js/frenchTypography.js | 37 + js/imageSwiper.js | 16 + js/infiniteScroll.js | 71 + js/keywordCloud.js | 249 ++ js/membresFilters.js | 229 ++ js/membresPopover.js | 81 + js/messageLabo.js | 57 + js/navAxesToggle.js | 32 + js/overlay.js | 133 + js/quickLinks.js | 24 + js/seanceToggle.js | 39 + js/stickyHeader.js | 91 + page-annonces.php | 165 ++ page-announcements.php | 2 + page-le-laboratoire.php | 110 + page-membres.php | 8 + page-programmes-de-recherche.php | 48 + page.php | 8 + scss/_author.scss | 164 ++ scss/_base.scss | 3 + scss/_category.scss | 557 ++++ scss/_filters.scss | 344 +++ scss/_footer.scss | 55 + scss/_header.scss | 330 +++ scss/_index.scss | 562 ++++ scss/_layout.scss | 44 + scss/_membres.scss | 211 ++ scss/_mixins.scss | 32 + scss/_navigation.scss | 205 ++ scss/_page-laboratoire.scss | 183 ++ scss/_postcard.scss | 114 + scss/_reset.scss | 46 + scss/_search.scss | 153 + scss/_single.scss | 584 ++++ scss/_typography.scss | 69 + scss/_variables.scss | 31 + scss/style.scss | 25 + search.php | 248 ++ single.php | 21 + style.css | 9 + tag.php | 2 + taxonomy.php | 186 ++ templates/404.twig | 21 + templates/author.twig | 181 ++ templates/base.twig | 21 + templates/category.twig | 132 + templates/index.twig | 135 + templates/page-annonces.twig | 43 + templates/page-le-laboratoire.twig | 123 + templates/page-membres.twig | 107 + templates/page-programmes-de-recherche.twig | 60 + templates/page.twig | 30 + templates/partials/agenda-card.twig | 25 + templates/partials/author-card.twig | 18 + templates/partials/category-filters.twig | 171 ++ templates/partials/footer.twig | 24 + templates/partials/header.twig | 57 + templates/partials/navigation.twig | 34 + templates/partials/post-card.twig | 116 + templates/partials/search-panel.twig | 17 + templates/partials/swiper-section.twig | 27 + templates/search.twig | 92 + templates/single.twig | 487 ++++ templates/taxonomy.twig | 49 + 104 files changed, 17439 insertions(+) create mode 100644 .gitignore create mode 100644 404.php create mode 100644 README.md create mode 100644 assets/fonts/Gelasio-Regular.woff2 create mode 100644 assets/fonts/NewsCycle-Regular.woff2 create mode 100644 assets/images/cnrs.png create mode 100644 assets/images/ens.png create mode 100644 assets/images/sorbonne.png create mode 100644 assets/images/thalim-logo.png create mode 100644 assets/images/thalim-logo.svg create mode 100644 assets/logo-shapes/fillshape.svg create mode 100644 assets/logo-shapes/shape1.svg create mode 100644 assets/logo-shapes/shape2.svg create mode 100644 assets/logo-shapes/shape3.svg create mode 100644 assets/logo-shapes/shape4.svg create mode 100644 assets/logo-shapes/shape5.svg create mode 100644 author.php create mode 100644 category.php create mode 100755 composer.json create mode 100755 composer.lock create mode 100644 css/admin.css create mode 100755 css/style.css create mode 100755 functions.php create mode 100644 inc/admin-users-filter.php create mode 100644 inc/author-helpers.php create mode 100644 inc/membres-helpers.php create mode 100644 inc/pods-conditional-required.php create mode 100644 inc/pods-save-error-handler.php create mode 100644 inc/post-card-helpers.php create mode 100644 inc/post-title-required.php create mode 100644 inc/single-helpers.php create mode 100755 index.php create mode 100644 js/adminDashboardMods.js create mode 100644 js/adminFormRestore.js create mode 100644 js/agendaView.js create mode 100644 js/animatedLogo.js create mode 100644 js/annoncesSwiper.js create mode 100644 js/categoryFilters.js create mode 100644 js/coloredWordsHero.js create mode 100644 js/fitPostCardTitle.js create mode 100644 js/frenchTypography.js create mode 100644 js/imageSwiper.js create mode 100644 js/infiniteScroll.js create mode 100644 js/keywordCloud.js create mode 100644 js/membresFilters.js create mode 100644 js/membresPopover.js create mode 100644 js/messageLabo.js create mode 100644 js/navAxesToggle.js create mode 100644 js/overlay.js create mode 100644 js/quickLinks.js create mode 100644 js/seanceToggle.js create mode 100644 js/stickyHeader.js create mode 100644 page-annonces.php create mode 100644 page-announcements.php create mode 100644 page-le-laboratoire.php create mode 100644 page-membres.php create mode 100644 page-programmes-de-recherche.php create mode 100644 page.php create mode 100644 scss/_author.scss create mode 100644 scss/_base.scss create mode 100644 scss/_category.scss create mode 100644 scss/_filters.scss create mode 100644 scss/_footer.scss create mode 100644 scss/_header.scss create mode 100644 scss/_index.scss create mode 100644 scss/_layout.scss create mode 100644 scss/_membres.scss create mode 100644 scss/_mixins.scss create mode 100644 scss/_navigation.scss create mode 100644 scss/_page-laboratoire.scss create mode 100644 scss/_postcard.scss create mode 100644 scss/_reset.scss create mode 100644 scss/_search.scss create mode 100644 scss/_single.scss create mode 100644 scss/_typography.scss create mode 100644 scss/_variables.scss create mode 100644 scss/style.scss create mode 100644 search.php create mode 100644 single.php create mode 100644 style.css create mode 100644 tag.php create mode 100644 taxonomy.php create mode 100644 templates/404.twig create mode 100644 templates/author.twig create mode 100644 templates/base.twig create mode 100644 templates/category.twig create mode 100644 templates/index.twig create mode 100644 templates/page-annonces.twig create mode 100644 templates/page-le-laboratoire.twig create mode 100644 templates/page-membres.twig create mode 100644 templates/page-programmes-de-recherche.twig create mode 100644 templates/page.twig create mode 100644 templates/partials/agenda-card.twig create mode 100644 templates/partials/author-card.twig create mode 100644 templates/partials/category-filters.twig create mode 100644 templates/partials/footer.twig create mode 100644 templates/partials/header.twig create mode 100644 templates/partials/navigation.twig create mode 100644 templates/partials/post-card.twig create mode 100644 templates/partials/search-panel.twig create mode 100644 templates/partials/swiper-section.twig create mode 100644 templates/search.twig create mode 100644 templates/single.twig create mode 100644 templates/taxonomy.twig diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f271996 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# --- Dépendances Composer --- +vendor/ + +# --- Artefacts SASS --- +.sass-cache/ +*.css.map + +# --- Node --- +node_modules/ + +# --- OS / éditeur --- +.DS_Store +Thumbs.db +*.swp +*~ +.idea/ +.vscode/ diff --git a/404.php b/404.php new file mode 100644 index 0000000..2563540 --- /dev/null +++ b/404.php @@ -0,0 +1,3 @@ + true` sur les args de `WP_Query`. Filtre de plage : `'thalim_event_date_filter' => ['from' => …, 'to' => …]`. + +### Admin très customisée +- Onglets FR/EN sur l'éditeur de corps de post (`body` natif WP + `body_en` Pods) +- Renommage « Article » → « Annonce » dans toute l'UI +- Visibilité conditionnelle des metaboxes Pods + groupement des axes thématiques par période +- Restauration automatique des champs en cas d'erreur de validation Pods (transient + JS) +- Restrictions des contributeurs (édition limitée aux posts où ils figurent comme membre) +- Dashboard et menu Outils masqués pour les non-admins +- Voir `CLAUDE.md` côté stack pour le détail + +### AJAX et infinite scroll +Les pages d'archives utilisent un système d'infinite scroll AJAX (`wp_ajax_load_more_posts`) avec recherche Relevanssi, filtres par axe / date / taxonomie / catégorie, et override de langue côté serveur. + +## Compilation des styles + +Les fichiers SCSS dans `scss/` sont compilés **manuellement** vers `css/`. + +## Structure + +``` +. +├── functions.php # setup, i18n, contexte Twig, AJAX, filtres de requête, mods admin (≈1400 lignes) +├── index.php, single.php … # templates PHP qui chargent les Twig correspondants +├── templates/ # templates Twig (base.twig = layout, autres l'étendent) +│ └── partials/ # header, footer, post-card, agenda-card, search-panel… +├── scss/ → css/ # sources SASS → CSS compilé (commité) +├── js/ # scripts frontend + adminDashboardMods.js +├── inc/ # helpers PHP par contexte : +│ ├── single-helpers.php # résolution champs Pods d'un post +│ ├── author-helpers.php # profil membre + posts liés +│ ├── membres-helpers.php # page /membres (groupes par rôle) +│ ├── post-card-helpers.php # données pour les cards +│ ├── pods-conditional-required.php # patch validation Pods +│ ├── pods-save-error-handler.php # restauration des champs en cas d'erreur +│ ├── post-title-required.php # titre obligatoire +│ └── admin-users-filter.php # filtre Statut sur /wp-admin/users.php +├── assets/ # fonts, images, logo-shapes (SVG) +├── composer.json # dépendance : timber/timber ^2.3 +└── vendor/ # Composer (gitignoré, à reconstruire après clone) +``` + +## Architecture détaillée + +Voir le fichier `CLAUDE.md` à la racine du repo [`thalim-stack`](https://figureslibres.io/valentin_le_moign/thalim-stack) pour la documentation exhaustive (conventions Pods, customisations admin, restrictions de contenu, helpers, rewrite rules, etc.). diff --git a/assets/fonts/Gelasio-Regular.woff2 b/assets/fonts/Gelasio-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e0e8bbeb431eeb5bb60712eb404ac397dc6a5b81 GIT binary patch literal 41560 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Ũ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

  • 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 NML|=(TGk@${{hAOr{(|v literal 0 HcmV?d00001 diff --git a/assets/images/thalim-logo.png b/assets/images/thalim-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4249d775d4654dcb0cbd7d940c4f0d0dcf9f148e GIT binary patch literal 22405 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

    4Z2n6ufp!9rE=d^M^TWrokCOkg)*aiK;t^K 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 + + + diff --git a/assets/logo-shapes/fillshape.svg b/assets/logo-shapes/fillshape.svg new file mode 100644 index 0000000..c3d78c7 --- /dev/null +++ b/assets/logo-shapes/fillshape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/logo-shapes/shape1.svg b/assets/logo-shapes/shape1.svg new file mode 100644 index 0000000..8aea71b --- /dev/null +++ b/assets/logo-shapes/shape1.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/logo-shapes/shape2.svg b/assets/logo-shapes/shape2.svg new file mode 100644 index 0000000..ef961b5 --- /dev/null +++ b/assets/logo-shapes/shape2.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/logo-shapes/shape3.svg b/assets/logo-shapes/shape3.svg new file mode 100644 index 0000000..bd1559d --- /dev/null +++ b/assets/logo-shapes/shape3.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/logo-shapes/shape4.svg b/assets/logo-shapes/shape4.svg new file mode 100644 index 0000000..635a6ff --- /dev/null +++ b/assets/logo-shapes/shape4.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/logo-shapes/shape5.svg b/assets/logo-shapes/shape5.svg new file mode 100644 index 0000000..b058c9b --- /dev/null +++ b/assets/logo-shapes/shape5.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/author.php b/author.php new file mode 100644 index 0000000..db748b5 --- /dev/null +++ b/author.php @@ -0,0 +1,16 @@ +set_404(); + status_header(404); + return; +} + +$context['author'] = thalim_get_author_data($user->ID); +$context['author_posts'] = thalim_get_author_posts_by_category($user->ID); +$context['author_edit_link'] = current_user_can('edit_user', $user->ID) ? get_edit_user_link($user->ID) : ''; + +Timber::render('author.twig', $context); diff --git a/category.php b/category.php new file mode 100644 index 0000000..152c61d --- /dev/null +++ b/category.php @@ -0,0 +1,272 @@ +parent) { + $parent_cat = get_category($category->parent); + $context['parent_slug'] = $parent_cat->slug; + $context['active_rubrique'] = $parent_cat->term_id; +} else { + $context['parent_slug'] = $category->slug; + $context['active_rubrique'] = $category->term_id; +} + +// Read filter query params +$active_axe = isset($_GET['axe']) ? intval($_GET['axe']) : 0; +$active_date_from = isset($_GET['date_from']) ? sanitize_text_field($_GET['date_from']) : ''; +$active_date_to = isset($_GET['date_to']) ? sanitize_text_field($_GET['date_to']) : ''; + +$context['active_axe'] = $active_axe; +$context['active_date_from'] = $active_date_from; +$context['active_date_to'] = $active_date_to; + +// Build query param string to preserve across filter links +$filter_query = http_build_query(array_filter([ + 'axe' => $active_axe ?: null, + 'date_from' => $active_date_from ?: null, + 'date_to' => $active_date_to ?: null, +])); +$context['filter_query'] = $filter_query; + +// Build extra query args for axe/date filtering +$extra_query_args = []; +if ($active_axe) { + $extra_query_args['meta_query'] = [[ + 'key' => 'axes_thematiques', + 'value' => $active_axe, + 'type' => 'NUMERIC', + ]]; +} +if ($active_date_from || $active_date_to) { + $extra_query_args['thalim_event_date_filter'] = ['from' => $active_date_from, 'to' => $active_date_to]; +} + +// Build parent categories for filter bar (with links) +$all_cats = get_categories([ + 'taxonomy' => 'category', + 'hide_empty' => false, + 'exclude' => $excluded_ids, +]); + +$filter_parents = []; +foreach ($all_cats as $cat) { + if ($cat->parent == 0) { + $link = get_category_link($cat->term_id); + if ($filter_query) $link .= '?' . $filter_query; + $filter_parents[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => $link, + ]; + } +} +$context['filter_parents'] = $filter_parents; + +// Children of active rubrique for catégorie filter (with links) +$active_rubrique_id = $context['active_rubrique']; +$is_direct = (bool) get_query_var('thalim_direct_posts'); +$lang = thalim_current_language(); + +$filter_categories = []; +foreach ($all_cats as $cat) { + if ($cat->parent == $active_rubrique_id) { + $link = get_category_link($cat->term_id); + if ($filter_query) $link .= '?' . $filter_query; + $filter_categories[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => $link, + ]; + } +} + +// Add "Autres" entry if the active rubrique has posts directly assigned to it +if ($is_direct) { + $has_direct_posts = true; +} else { + $direct_check = new WP_Query([ + 'post_type' => 'post', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'lang' => '', + 'tax_query' => [[ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$active_rubrique_id], + 'include_children' => false, + ]], + ]); + $has_direct_posts = $direct_check->have_posts(); +} +if ($has_direct_posts && !empty($filter_categories)) { + $autres_link = trailingslashit(get_category_link($active_rubrique_id)) . 'autres/'; + if ($filter_query) $autres_link .= '?' . $filter_query; + $filter_categories[] = [ + 'id' => 'autres', + 'name' => $lang === 'en' ? 'Other' : 'Autres', + 'slug' => 'autres', + 'link' => $autres_link, + ]; +} + +$context['filter_categories'] = $filter_categories; +$context['active_category_id'] = $is_direct ? 'autres' : $category->term_id; + +// Axes thématiques for filter dropdown +$axes_groups = thalim_get_axes_filter_groups(); +$current_axes = $axes_groups[0]['terms'] ?? []; +$context['filter_axes'] = $current_axes; +$context['axe_stay_on_page'] = true; + +// Fetch posts for initial display +$children = get_categories([ + 'parent' => $category->term_id, + 'taxonomy' => 'category', + 'hide_empty' => true, + 'exclude' => $excluded_ids, +]); + +// Ordre personnalisé des sous-catégories (term_id => position). +// Les termes absents du tableau sont placés en dernier (position 999). +$subcategory_order = [ + // Publications et productions (parent: 4) + 15 => 0, // Ouvrages + 16 => 1, // Articles + 65 => 2, // Revues et collections + 17 => 3, // Multimédia + // Activités (parent: 3) + 11 => 0, // Séminaires + 10 => 1, // Colloques et journées d'études + 13 => 2, // Communications + 14 => 3, // Soutenances +]; +usort($children, function($a, $b) use ($subcategory_order) { + $pos_a = $subcategory_order[$a->term_id] ?? 999; + $pos_b = $subcategory_order[$b->term_id] ?? 999; + return $pos_a - $pos_b; +}); + +$context['category_id'] = $category->term_id; +$context['agenda_include_children'] = ( ! $is_direct && ! empty( $children ) ) ? 1 : 0; + +// Helper: move pinned posts to the front (same logic as homepage diaporamas) +$sort_with_pinned = function ( $posts ) { + $today = date( 'Y-m-d' ); + $pinned = []; + $normal = []; + foreach ( $posts as $post ) { + $epingle = get_post_meta( $post->ID, 'epingler_dans_la_categorie', true ); + $fin = get_post_meta( $post->ID, 'date_de_fin_depinglage', true ); + $active = $epingle == '1' && ( empty( $fin ) || $fin === '0000-00-00' || $fin >= $today ); + if ( $active ) { $pinned[] = $post; } else { $normal[] = $post; } + } + return array_merge( $pinned, $normal ); +}; + +if (!$is_direct && !empty($children)) { + $context['is_parent'] = true; + $context['subcategories'] = []; + + foreach ($children as $child) { + $query_args = array_merge([ + 'post_type' => 'post', + 'tax_query' => [[ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$child->term_id], + 'include_children' => false, + ]], + 'posts_per_page' => 3, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'thalim_event_date_order' => true, + ], $extra_query_args); + $posts = $sort_with_pinned( Timber::get_posts($query_args) ); + $context['cards'] += thalim_get_cards_data($posts); + $context['subcategories'][] = [ + 'term' => Timber::get_term($child), + 'posts' => $posts, + ]; + } + + // Fetch posts directly in the parent category (no child category assigned) + if ($has_direct_posts) { + $direct_query_args = array_merge([ + 'post_type' => 'post', + 'tax_query' => [[ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$category->term_id], + 'include_children' => false, + ]], + 'posts_per_page' => 3, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'thalim_event_date_order' => true, + ], $extra_query_args); + $direct_posts = $sort_with_pinned( Timber::get_posts($direct_query_args) ); + if (!empty($direct_posts)) { + $context['cards'] += thalim_get_cards_data($direct_posts); + $context['direct_posts'] = $direct_posts; + $autres_link = trailingslashit(get_category_link($category->term_id)) . 'autres/'; + if ($filter_query) $autres_link .= '?' . $filter_query; + $context['autres_link'] = $autres_link; + } + } +} else { + $context['is_parent'] = false; + $context['is_direct'] = $is_direct; + $query_args = array_merge([ + 'post_type' => 'post', + 'tax_query' => [[ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$category->term_id], + 'include_children' => false, + ]], + 'posts_per_page' => 12, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'thalim_event_date_order' => true, + ], $extra_query_args); + $posts = $sort_with_pinned( Timber::get_posts($query_args) ); + $context['cards'] = thalim_get_cards_data($posts); + $context['posts'] = $posts; +} + +// View mode toggle (?view=agenda) +$view_mode = ( isset( $_GET['view'] ) && $_GET['view'] === 'agenda' ) ? 'agenda' : 'grid'; +$context['view_mode'] = $view_mode; + +// Toggle URL (used as href fallback on the button) +$toggle_base = get_category_link( $category->term_id ); +$toggle_params = array_filter([ + 'axe' => $active_axe ?: null, + 'date_from' => $active_date_from ?: null, + 'date_to' => $active_date_to ?: null, +]); +if ( $view_mode === 'grid' ) { + $toggle_params['view'] = 'agenda'; +} +// When toggling back to grid we omit ?view entirely +$context['agenda_toggle_url'] = add_query_arg( $toggle_params, $toggle_base ); + +// Custom Pods presentation fields +$cat_lang = thalim_current_language(); +$pres_fr = get_term_meta( $category->term_id, 'presentation', true ) ?: ''; +$pres_en = get_term_meta( $category->term_id, 'presentation_en', true ) ?: ''; +$context['term_presentation'] = wpautop( ( $cat_lang === 'en' && $pres_en ) ? $pres_en : $pres_fr ); + +Timber::render('category.twig', $context); diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..1f128f1 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "timber/timber": "^2.3" + } +} diff --git a/composer.lock b/composer.lock new file mode 100755 index 0000000..cf0e951 --- /dev/null +++ b/composer.lock @@ -0,0 +1,434 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "977b0c3760f52f4b6ad4694d7ac704ba", + "packages": [ + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "timber/timber", + "version": "v2.3.3", + "source": { + "type": "git", + "url": "https://github.com/timber/timber.git", + "reference": "7a87ac27c0b9deedffe419388b63a0c95d8798ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/timber/timber/zipball/7a87ac27c0b9deedffe419388b63a0c95d8798ca", + "reference": "7a87ac27c0b9deedffe419388b63a0c95d8798ca", + "shasum": "" + }, + "require": { + "php": "^8.1", + "twig/twig": "^3.19" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.28", + "php-parallel-lint/php-parallel-lint": "^1.3", + "php-stubs/wp-cli-stubs": "^2.0", + "phpro/grumphp": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^9.0", + "rector/rector": "^2.0", + "squizlabs/php_codesniffer": "^3.0", + "symplify/easy-coding-standard": "^12", + "szepeviktor/phpstan-wordpress": "^2", + "twig/cache-extra": "^3.17", + "wpackagist-plugin/advanced-custom-fields": "^6.0", + "wpackagist-plugin/co-authors-plus": "^3.6", + "yoast/wp-test-utils": "^1.2" + }, + "suggest": { + "php-coveralls/php-coveralls": "^2.0 for code coverage", + "twig/cache-extra": "For using the cache tag in Twig" + }, + "type": "library", + "autoload": { + "psr-4": { + "Timber\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Erik van der Bas", + "email": "erik@basedonline.nl", + "homepage": "https://basedonline.nl" + }, + { + "name": "Lukas Gächter", + "email": "lukas.gaechter@mind.ch", + "homepage": "https://www.mind.ch" + }, + { + "name": "Nicolas Lemoine", + "email": "nico@n5s.dev", + "homepage": "https://n5s.dev" + }, + { + "name": "Jared Novack", + "email": "jared@upstatement.com", + "homepage": "https://upstatement.com" + }, + { + "name": "Timber Community", + "homepage": "https://github.com/timber/timber" + } + ], + "description": "Create WordPress themes with beautiful OOP code and the Twig Template Engine", + "homepage": "https://timber.upstatement.com", + "keywords": [ + "templating", + "themes", + "timber", + "twig", + "wordpress" + ], + "support": { + "docs": "https://timber.github.io/docs/", + "issues": "https://github.com/timber/timber/issues", + "source": "https://github.com/timber/timber" + }, + "funding": [ + { + "url": "https://github.com/timber", + "type": "github" + }, + { + "url": "https://opencollective.com/timber", + "type": "open_collective" + } + ], + "time": "2025-09-24T14:07:33+00:00" + }, + { + "name": "twig/twig", + "version": "v3.22.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "4509984193026de413baf4ba80f68590a7f2c51d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/4509984193026de413baf4ba80f68590a7f2c51d", + "reference": "4509984193026de413baf4ba80f68590a7f2c51d", + "shasum": "" + }, + "require": { + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.22.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2025-10-29T15:56:47+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/css/admin.css b/css/admin.css new file mode 100644 index 0000000..b84105e --- /dev/null +++ b/css/admin.css @@ -0,0 +1,923 @@ +/* Fade in transition for post edit and profile pages */ +body.post-php #wpbody, +body.post-new-php #wpbody, +body.profile-php #wpbody, +body.user-edit-php #wpbody, +body.user-new-php #wpbody { + opacity: 0; + transition: opacity 0.3s ease; +} + +body.post-php.admin-mods-ready #wpbody, +body.post-new-php.admin-mods-ready #wpbody, +body.profile-php.admin-mods-ready #wpbody, +body.user-edit-php.admin-mods-ready #wpbody, +body.user-new-php.admin-mods-ready #wpbody { + opacity: 1; +} + +/* Post edit pages: hide elements */ +body.post-php #advanced-sortables, +body.post-new-php #advanced-sortables, +body.post-php #preview-action, +body.post-new-php #preview-action, +body.post-php #visibility, +body.post-new-php #visibility, +body.post-php #add_pod_button, +body.post-new-php #add_pod_button, +body.post-php #wp-content-editor-tools > .wp-editor-tabs, +body.post-new-php #wp-content-editor-tools > .wp-editor-tabs, +body.post-php #mceu_0, +body.post-new-php #mceu_0, +body.post-php #mceu_6, +body.post-new-php #mceu_6, +body.post-php #mceu_7, +body.post-new-php #mceu_7, +body.post-php #mceu_8, +body.post-new-php #mceu_8, +body.post-php #mceu_10, +body.post-new-php #mceu_10, +body.post-php #mceu_11, +body.post-new-php #mceu_11, +body.post-php #postexcerpt, +body.post-new-php #postexcerpt, +body.post-php #postcustom, +body.post-new-php #postcustom, +body.post-php #slugdiv, +body.post-new-php #slugdiv, +body.post-php #authordiv, +body.post-new-php #authordiv, +body.post-php #screen-meta-links, +body.post-new-php #screen-meta-links, +body.post-php .handle-order-higher, +body.post-new-php .handle-order-higher, +body.post-php .handle-order-lower, +body.post-new-php .handle-order-lower, +body.post-php #pods-meta-seances-seminaire .pods-field-wrapper > div:first-of-type, +body.post-new-php #pods-meta-seances-seminaire .pods-field-wrapper > div:first-of-type, +body.post-php #tagsdiv-post_tag, +body.post-new-php #tagsdiv-post_tag, +body.post-php #wp-content-media-buttons, +body.post-new-php #wp-content-media-buttons, +body.post-php #members-cp, +body.post-new-php #members-cp { + display: none !important; +} + +body.post-php .postbox-header .hndle, +body.post-new-php .postbox-header .hndle { + pointer-events: none; + cursor: default; + overflow: visible; + justify-content: start; +} + +body.post-php #pods-form-ui-pods-meta-categorie option[value="31"], +body.post-new-php #pods-form-ui-pods-meta-categorie option[value="31"] { + display: none; +} + +body.post-php #pods-form-ui-pods-meta-categorie option:disabled, +body.post-new-php #pods-form-ui-pods-meta-categorie option:disabled { + color: #a7aaad; +} + +/* Documents joints: margin-top to separate it from the body editor section */ +body.post-php #pods-meta-documents-joints, +body.post-new-php #pods-meta-documents-joints { + margin-top: 20px; +} + +/* Pods iframe modal: hide elements */ +body.pods-modal-window #ml_box, +body.pods-modal-window #pods-meta-liens-externes, +body.pods-modal-window #pods-meta-documents-joints { + display: none; +} + +/* Axes thématiques checkbox group headers */ +body.post-php .pods-form-ui-row-name-axes-thematiques li.axes-group-label, +body.post-new-php .pods-form-ui-row-name-axes-thematiques li.axes-group-label { + font-size: 0.72rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: #646970; + background-color: #f0f0f1; + padding: 0.4rem 0.5rem; + cursor: default; + pointer-events: none; + border-top: 1px solid #dcdcde; + margin-top: 0.4rem; + width: 100%; +} +body.post-php .pods-form-ui-row-name-axes-thematiques li.axes-group-label:first-child, +body.post-new-php .pods-form-ui-row-name-axes-thematiques li.axes-group-label:first-child { + border-top: none; + margin-top: 0; +} + +/* Remove WP's JS-injected padding-top on the editor wrap (compensation for sticky toolbar) */ +body.post-php #wp-content-editor-tools, +body.post-new-php #wp-content-editor-tools { + padding-top: 0 !important; +} + +body.post-php .pods-dfv-container-wysiwyg, +body.post-new-php .pods-dfv-container-wysiwyg { + max-width: unset !important; +} + +body.post-php .pods-tinymce-reinit, +body.post-new-php .pods-tinymce-reinit { + display: none; +} + +/* Keep statusbar in layout so TinyMCE's reposition() can read its getBoundingClientRect(). + display:none would return top=0, making mceStatusbarTop=windowHeight → editorHeight<0 → toolbar always hidden. */ +body.post-php .pods-dfv-container-wysiwyg .mce-statusbar, +body.post-new-php .pods-dfv-container-wysiwyg .mce-statusbar { + opacity: 0; + pointer-events: none; + user-select: none; +} + +body.post-php #pods-form-ui-pods-meta-body-en_ifr, +body.post-new-php #pods-form-ui-pods-meta-body-en_ifr { + min-height: 334px !important; +} + +body.post-php #content_ifr, +body.post-new-php #content_ifr, +body.post-php #pods-form-ui-pods-meta-body-en_ifr, +body.post-new-php #pods-form-ui-pods-meta-body-en_ifr { + cursor: text; +} + +/* Pods body-en: hide Visuel/Code tabs and same toolbar buttons removed from native editor + (paragraph format, alignment ×3, "Lire la suite", toggle-advanced-toolbar). + Uses aria-label selectors — more stable than MCE numeric IDs used for the native editor. */ +body.post-php #pods-meta-body-en .wp-editor-tabs, +body.post-new-php #pods-meta-body-en .wp-editor-tabs, +body.post-php #pods-meta-body-en .mce-listbox.mce-fixed-width, +body.post-new-php #pods-meta-body-en .mce-listbox.mce-fixed-width, +body.post-php #pods-meta-body-en [aria-label*="Aligner"], +body.post-new-php #pods-meta-body-en [aria-label*="Aligner"], +body.post-php #pods-meta-body-en [aria-label*="Centrer"], +body.post-new-php #pods-meta-body-en [aria-label*="Centrer"], +body.post-php #pods-meta-body-en [aria-label*="Lire la suite"], +body.post-new-php #pods-meta-body-en [aria-label*="Lire la suite"], +body.post-php #pods-meta-body-en [aria-label*="Permuter la barre"], +body.post-new-php #pods-meta-body-en [aria-label*="Permuter la barre"] { + display: none !important; +} + +/* Référence bibliographique: hide tabs, media buttons, status bar, + and all toolbar buttons except Italic (only formatting allowed). */ +#wp-pods-form-ui-pods-meta-reference-bibliographique-wrap .wp-editor-tabs, +#wp-pods-form-ui-pods-meta-reference-bibliographique-wrap .wp-media-buttons, +#wp-pods-form-ui-pods-meta-reference-bibliographique-wrap .mce-statusbar { + display: none !important; +} +#wp-pods-form-ui-pods-meta-reference-bibliographique-wrap .mce-toolbar .mce-btn { + display: none !important; +} +#wp-pods-form-ui-pods-meta-reference-bibliographique-wrap .mce-toolbar .mce-btn:has(.mce-i-italic), +#wp-pods-form-ui-pods-meta-reference-bibliographique-wrap .mce-toolbar .mce-btn:has(.mce-i-bold) { + display: inline-block !important; +} + +/* Profile / user-edit: fixed submit button */ +body.profile-php #your-profile p.submit::before, +body.user-edit-php #your-profile p.submit::before { + content: "Pensez à sauvegarder les modifications"; + display: block; + font-size: 11px; + color: #646970; + margin-bottom: 6px; +} + +body.profile-php #your-profile p.submit, +body.user-edit-php #your-profile p.submit { + position: fixed; + bottom: 0; + right: 0; + margin: 0; + padding: 12px 16px; + background: #f0f0f1; + border-top: 1px solid #c3c4c7; + border-left: 1px solid #c3c4c7; + z-index: 100; +} + +/* Profile / user-edit / user-new / taxonomy term pages: TinyMCE editor mods (mirrors post-edit treatment) */ +body.profile-php .wp-editor-tabs, +body.user-edit-php .wp-editor-tabs, +body.user-new-php .wp-editor-tabs, +body.edit-tags-php .wp-editor-tabs, +body.term-php .wp-editor-tabs, +body.profile-php .mce-listbox.mce-fixed-width, +body.user-edit-php .mce-listbox.mce-fixed-width, +body.user-new-php .mce-listbox.mce-fixed-width, +body.edit-tags-php .mce-listbox.mce-fixed-width, +body.term-php .mce-listbox.mce-fixed-width, +body.profile-php [aria-label*="Aligner"], +body.user-edit-php [aria-label*="Aligner"], +body.user-new-php [aria-label*="Aligner"], +body.edit-tags-php [aria-label*="Aligner"], +body.term-php [aria-label*="Aligner"], +body.profile-php [aria-label*="Centrer"], +body.user-edit-php [aria-label*="Centrer"], +body.user-new-php [aria-label*="Centrer"], +body.edit-tags-php [aria-label*="Centrer"], +body.term-php [aria-label*="Centrer"], +body.profile-php [aria-label*="Lire la suite"], +body.user-edit-php [aria-label*="Lire la suite"], +body.user-new-php [aria-label*="Lire la suite"], +body.edit-tags-php [aria-label*="Lire la suite"], +body.term-php [aria-label*="Lire la suite"], +body.profile-php [aria-label*="Permuter la barre"], +body.user-edit-php [aria-label*="Permuter la barre"], +body.user-new-php [aria-label*="Permuter la barre"], +body.edit-tags-php [aria-label*="Permuter la barre"], +body.term-php [aria-label*="Permuter la barre"], +body.profile-php .wp-media-buttons, +body.user-edit-php .wp-media-buttons, +body.user-new-php .wp-media-buttons, +body.edit-tags-php .wp-media-buttons, +body.term-php .wp-media-buttons, +body.profile-php .pods-tinymce-reinit, +body.user-edit-php .pods-tinymce-reinit, +body.user-new-php .pods-tinymce-reinit, +body.edit-tags-php .pods-tinymce-reinit, +body.term-php .pods-tinymce-reinit { + display: none !important; +} + +body.profile-php .wp-editor-tools, +body.user-edit-php .wp-editor-tools, +body.user-new-php .wp-editor-tools, +body.edit-tags-php .wp-editor-tools, +body.term-php .wp-editor-tools { + padding-top: 0 !important; +} + +body.profile-php .pods-dfv-container-wysiwyg, +body.user-edit-php .pods-dfv-container-wysiwyg, +body.user-new-php .pods-dfv-container-wysiwyg, +body.edit-tags-php .pods-dfv-container-wysiwyg, +body.term-php .pods-dfv-container-wysiwyg { + max-width: unset !important; +} + +body.profile-php .wp-editor-container iframe, +body.user-edit-php .wp-editor-container iframe, +body.user-new-php .wp-editor-container iframe, +body.edit-tags-php .wp-editor-container iframe, +body.term-php .wp-editor-container iframe { + cursor: text; +} + +/* Fade-in transition for tab panels (opacity is set to 0 by JS before reveal, then cleared) */ +body.post-php #postdivrich, +body.post-new-php #postdivrich, +body.post-php #pods-meta-body-en, +body.post-new-php #pods-meta-body-en { + transition: opacity 0.15s ease; +} + +/* Body FR/EN language tabs */ +body.post-php .body-lang-tabs, +body.post-new-php .body-lang-tabs { + display: flex; + gap: 0; + margin-bottom: -1px; + position: relative; + z-index: 1; +} + +body.post-php .body-lang-tab, +body.post-new-php .body-lang-tab { + padding: 5px 14px; + background: #f0f0f1; + border: 1px solid #c3c4c7; + border-bottom-color: #c3c4c7; + cursor: pointer; + font-size: 13px; + line-height: 1.5; + color: #50575e; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} + +body.post-php .body-lang-tab.is-active, +body.post-new-php .body-lang-tab.is-active { + background: #fff; + border-bottom-color: #fff; + color: #1d2327; + font-weight: 600; +} + +/* Hide postbox headers for both body editors — tabs replace them */ +body.post-php #postdivrich .postbox-header, +body.post-new-php #postdivrich .postbox-header, +body.post-php #pods-meta-body-en .postbox-header, +body.post-new-php #pods-meta-body-en .postbox-header { + display: none !important; +} + +/* Strip Pods table layout from body-en — make it full width like native editor */ +body.post-php #pods-meta-body-en .inside, +body.post-new-php #pods-meta-body-en .inside { + margin: 0; + padding: 0; +} + +body.post-php #pods-meta-body-en .form-table, +body.post-new-php #pods-meta-body-en .form-table, +body.post-php #pods-meta-body-en .form-table tbody, +body.post-new-php #pods-meta-body-en .form-table tbody, +body.post-php #pods-meta-body-en .form-table tr, +body.post-new-php #pods-meta-body-en .form-table tr, +body.post-php #pods-meta-body-en .form-table td, +body.post-new-php #pods-meta-body-en .form-table td { + display: block; + width: 100%; + padding: 0; + margin: 0; +} + +body.post-php #pods-meta-body-en .form-table th, +body.post-new-php #pods-meta-body-en .form-table th { + display: none; +} + +body.post-php #pods-meta-body-en .pods-submittable-fields, +body.post-new-php #pods-meta-body-en .pods-submittable-fields, +body.post-php #pods-meta-body-en .pods-dfv-container, +body.post-new-php #pods-meta-body-en .pods-dfv-container { + padding: 0; +} + +/* Allow TinyMCE floating panels (link popover, inline toolbar) to escape the Pods + container chain — any overflow:hidden in Pods DFV elements would clip them */ +body.post-php #pods-meta-body-en .inside, +body.post-new-php #pods-meta-body-en .inside, +body.post-php #pods-meta-body-en .pods-submittable-fields, +body.post-new-php #pods-meta-body-en .pods-submittable-fields, +body.post-php #pods-meta-body-en .pods-dfv-field, +body.post-new-php #pods-meta-body-en .pods-dfv-field, +body.post-php #pods-meta-body-en .pods-field-option, +body.post-new-php #pods-meta-body-en .pods-field-option, +body.post-php #pods-meta-body-en .pods-field-option__field, +body.post-new-php #pods-meta-body-en .pods-field-option__field, +body.post-php #pods-meta-body-en .pods-dfv-container-wysiwyg, +body.post-new-php #pods-meta-body-en .pods-dfv-container-wysiwyg, +body.post-php #pods-meta-body-en .pods-field-wrapper, +body.post-new-php #pods-meta-body-en .pods-field-wrapper, +body.post-php #pods-meta-body-en .wp-editor-wrap, +body.post-new-php #pods-meta-body-en .wp-editor-wrap, +body.post-php #pods-meta-body-en .wp-editor-container, +body.post-new-php #pods-meta-body-en .wp-editor-container { + overflow: visible !important; +} + +/* Info popovers */ +.thalim-info-wrapper { + position: relative; + display: inline-flex; + align-items: center; + margin-left: 6px; + vertical-align: middle; +} + +.thalim-info-btn { + display: inline-flex; + align-items: center; + justify-content: center; + background: none; + border: none; + padding: 2px; + cursor: pointer; + color: #646970; + line-height: 1; + border-radius: 50%; + transition: color 0.15s, background 0.15s; + pointer-events: auto; +} + +.thalim-info-btn:hover, +.thalim-info-btn:focus { + color: #2271b1; + background: #f0f6fc; + outline: none; +} + +.thalim-info-popover { + display: none; + position: fixed; + transform: translateX(-50%); + z-index: 9999; + background: #fff; + border: 1px solid #c3c4c7; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); + padding: 10px 12px; + min-width: 220px; + max-width: 320px; + font-size: 12px; + font-weight: normal; + line-height: 1.5; + color: #3c434a; + white-space: normal; + text-align: left; + pointer-events: auto; +} + +.thalim-info-popover.is-open { + display: block; +} + +.thalim-info-popover p { + margin: 0 0 4px; +} + +.thalim-info-popover p:last-child { + margin-bottom: 0; +} + +/* Translate popovers */ +.thalim-translate-btn { + display: inline-flex; + align-items: center; + justify-content: center; + background: none; + border: none; + padding: 2px; + cursor: pointer; + color: #2a7a4f; + line-height: 1; + border-radius: 50%; + transition: color 0.15s, background 0.15s; + pointer-events: auto; +} + +.thalim-translate-btn:hover, +.thalim-translate-btn:focus { + color: #1d5c3a; + background: #edfaf3; + outline: none; +} + +.thalim-translate-popover { + border-color: #b2dfcc; +} + +/* Required field indicator */ +abbr.required::after { + content: " Champ nécessaire"; + font-size: 0.72rem; + font-weight: normal; + font-style: normal; + color: #646970; + text-decoration: none; +} + +/* Profile pages: hide sections */ +body.user-new-php #simple-local-avatar-section, +body.profile-php .user-admin-color-wrap, +body.user-edit-php .user-admin-color-wrap, +body.user-new-php .user-admin-color-wrap, +body.profile-php .user-admin-bar-front-wrap, +body.user-edit-php .user-admin-bar-front-wrap, +body.user-new-php .user-admin-bar-front-wrap, +body.profile-php .user-nickname-wrap, +body.user-edit-php .user-nickname-wrap, +body.user-new-php .user-nickname-wrap, +body.profile-php .user-display-name-wrap, +body.user-edit-php .user-display-name-wrap, +body.user-new-php .user-display-name-wrap, +body.profile-php .user-profile-picture, +body.user-edit-php .user-profile-picture, +body.user-new-php .user-profile-picture, +body.profile-php .ratings-row, +body.user-edit-php .ratings-row, +body.user-new-php .ratings-row, +body.profile-php .application-passwords, +body.user-edit-php .application-passwords, +body.user-new-php .application-passwords, +body.profile-php .user-comment-shortcuts-wrap, +body.user-edit-php .user-comment-shortcuts-wrap, +body.user-new-php .user-comment-shortcuts-wrap, +body.profile-php .form-table:has(.user-description-wrap), +body.user-edit-php .form-table:has(.user-description-wrap), +body.user-new-php .form-table:has(.user-description-wrap) { + display: none !important; +} + +body.profile-php .form-table, +body.user-edit-php .form-table, +body.user-new-php .form-table { + display: block; +} + +body.profile-php .form-table tbody, +body.user-edit-php .form-table tbody, +body.user-new-php .form-table tbody { + display: grid; + grid-template-columns: 1fr; + background: #fff; + border: 1px solid #c3c4c7; + padding: 8px 16px; + margin-bottom: 16px; +} + +body.profile-php .form-table tbody tr, +body.user-edit-php .form-table tbody tr, +body.user-new-php .form-table tbody tr { + display: flex; + flex-direction: column; + grid-column: 1 / -1; +} + +body.profile-php .form-table tbody tr th, +body.user-edit-php .form-table tbody tr th, +body.user-new-php .form-table tbody tr th, +body.profile-php .form-table tbody tr td, +body.user-edit-php .form-table tbody tr td, +body.user-new-php .form-table tbody tr td { + display: block; + width: 100%; + padding: 0 0 4px; +} + +@media (min-width: 768px) { + /* Side-by-side section pairs */ + body.profile-php .profile-section-row, + body.user-edit-php .profile-section-row, + body.user-new-php .profile-section-row { + display: flex; + gap: 24px; + align-items: flex-start; + } + + body.profile-php .profile-section-col, + body.user-edit-php .profile-section-col, + body.user-new-php .profile-section-col { + flex: 1; + min-width: 0; + } + + /* Tables inside a col are already at 50% width — keep fields single-column */ + body.profile-php .profile-section-col .form-table tbody, + body.user-edit-php .profile-section-col .form-table tbody, + body.user-new-php .profile-section-col .form-table tbody { + grid-template-columns: 1fr; + } + + + body.profile-php .form-table tbody, + body.user-edit-php .form-table tbody, + body.user-new-php .form-table tbody { + grid-template-columns: 1fr 1fr; + gap: 0 15px; + &:has(.pods-form-ui-row-name-role-1) { + grid-template-columns: 1fr 1fr 1fr; + } + } + + body.profile-php .form-table tbody .user-user-login-wrap, + body.user-edit-php .form-table tbody .user-user-login-wrap, + body.user-new-php .form-table tbody .user-user-login-wrap, + body.profile-php .form-table tbody .user-first-name-wrap, + body.user-edit-php .form-table tbody .user-first-name-wrap, + body.user-new-php .form-table tbody .user-first-name-wrap, + body.profile-php .form-table tbody .user-email-wrap, + body.user-edit-php .form-table tbody .user-email-wrap, + body.user-new-php .form-table tbody .user-email-wrap, + body.profile-php .form-table tbody .pods-form-ui-row-name-lien-externe-1, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-lien-externe-1, + body.user-new-php .form-table tbody .pods-form-ui-row-name-lien-externe-1, + body.profile-php .form-table tbody .pods-form-ui-row-name-lien-externe-2, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-lien-externe-2, + body.user-new-php .form-table tbody .pods-form-ui-row-name-lien-externe-2, + body.profile-php .form-table tbody .pods-form-ui-row-name-lien-externe-3, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-lien-externe-3, + body.user-new-php .form-table tbody .pods-form-ui-row-name-lien-externe-3, + body.profile-php .form-table tbody .pods-form-ui-row-name-role-1, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-role-1, + body.user-new-php .form-table tbody .pods-form-ui-row-name-role-1, + body.profile-php .form-table tbody .pods-form-ui-row-name-role-2, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-role-2, + body.user-new-php .form-table tbody .pods-form-ui-row-name-role-2, + body.profile-php .form-table tbody .pods-form-ui-row-name-role-3, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-role-3, + body.user-new-php .form-table tbody .pods-form-ui-row-name-role-3, + body.profile-php .form-table tbody .pods-form-ui-row-name-affiliation, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-affiliation, + body.user-new-php .form-table tbody .pods-form-ui-row-name-affiliation, + body.profile-php .form-table tbody .pods-form-ui-row-name-titre-de-these, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-titre-de-these, + body.user-new-php .form-table tbody .pods-form-ui-row-name-titre-de-these, + body.profile-php .form-table tbody .pods-form-ui-row-name-directeur-de-these-thalim, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-directeur-de-these-thalim, + body.user-new-php .form-table tbody .pods-form-ui-row-name-directeur-de-these-thalim, + body.profile-php .form-table tbody .pods-form-ui-row-name-recherches-en-cours, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-recherches-en-cours, + body.user-new-php .form-table tbody .pods-form-ui-row-name-recherches-en-cours, + body.profile-php .form-table tbody .pods-form-ui-row-name-autres-domaines-de-recherches, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-autres-domaines-de-recherches, + body.user-new-php .form-table tbody .pods-form-ui-row-name-autres-domaines-de-recherches, + body.profile-php .form-table tbody .pods-form-ui-row-name-resume-de-la-these, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-resume-de-la-these, + body.user-new-php .form-table tbody .pods-form-ui-row-name-resume-de-la-these, + body.profile-php .form-table tbody .pods-form-ui-row-name-biographie, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-biographie, + body.user-new-php .form-table tbody .pods-form-ui-row-name-biographie { + grid-column: 1; + } + + body.profile-php .form-table tbody .user-role-wrap, + body.user-edit-php .form-table tbody .user-role-wrap, + body.user-new-php .form-table tbody .user-role-wrap, + body.profile-php .form-table tbody .user-last-name-wrap, + body.user-edit-php .form-table tbody .user-last-name-wrap, + body.user-new-php .form-table tbody .user-last-name-wrap, + body.profile-php .form-table tbody .user-url-wrap, + body.user-edit-php .form-table tbody .user-url-wrap, + body.user-new-php .form-table tbody .user-url-wrap, + body.profile-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-1, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-1, + body.user-new-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-1, + body.profile-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-2, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-2, + body.user-new-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-2, + body.profile-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-3, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-3, + body.user-new-php .form-table tbody .pods-form-ui-row-name-titre-du-lien-3, + body.profile-php .form-table tbody .pods-form-ui-row-name-complement-de-role-1, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-complement-de-role-1, + body.user-new-php .form-table tbody .pods-form-ui-row-name-complement-de-role-1, + body.profile-php .form-table tbody .pods-form-ui-row-name-complement-de-role-2, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-complement-de-role-2, + body.user-new-php .form-table tbody .pods-form-ui-row-name-complement-de-role-2, + body.profile-php .form-table tbody .pods-form-ui-row-name-complement-de-role-3, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-complement-de-role-3, + body.user-new-php .form-table tbody .pods-form-ui-row-name-complement-de-role-3, + body.profile-php .form-table tbody .pods-form-ui-row-name-affiliation-autre, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-affiliation-autre, + body.user-new-php .form-table tbody .pods-form-ui-row-name-affiliation-autre, + body.profile-php .form-table tbody .pods-form-ui-row-name-autre-directeur-de-these, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-autre-directeur-de-these, + body.user-new-php .form-table tbody .pods-form-ui-row-name-autre-directeur-de-these, + body.profile-php .form-table tbody .pods-form-ui-row-name-date-de-soutenance, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-date-de-soutenance, + body.user-new-php .form-table tbody .pods-form-ui-row-name-date-de-soutenance, + body.profile-php .form-table tbody .pods-form-ui-row-name-recherches-en-cours-en, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-recherches-en-cours-en, + body.user-new-php .form-table tbody .pods-form-ui-row-name-recherches-en-cours-en, + body.profile-php .form-table tbody .pods-form-ui-row-name-autres-domaines-de-recherches-en, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-autres-domaines-de-recherches-en, + body.user-new-php .form-table tbody .pods-form-ui-row-name-autres-domaines-de-recherches-en, + body.profile-php .form-table tbody .pods-form-ui-row-name-resume-de-la-these-en, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-resume-de-la-these-en, + body.user-new-php .form-table tbody .pods-form-ui-row-name-resume-de-la-these-en, + body.profile-php .form-table tbody .pods-form-ui-row-name-biographie-en, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-biographie-en, + body.user-new-php .form-table tbody .pods-form-ui-row-name-biographie-en { + grid-column: 2; + } + + body.profile-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-1, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-1, + body.user-new-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-1, + body.profile-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-2, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-2, + body.user-new-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-2, + body.profile-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-3, + body.user-edit-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-3, + body.user-new-php .form-table tbody .pods-form-ui-row-name-affichage-du-statut-3 { + grid-column: 3; + } + + body.profile-php .user-user-login-wrap span.description, + body.user-edit-php .user-user-login-wrap span.description, + body.user-new-php .user-user-login-wrap span.description { + display: block; + } +} + + +/* Profile FR/EN language overlay tabs */ +body.profile-php .profile-lang-tabs, +body.user-edit-php .profile-lang-tabs, +body.user-new-php .profile-lang-tabs { + display: flex; + gap: 0; + margin-bottom: -1px; + position: relative; + z-index: 1; +} +body.profile-php .profile-lang-tab, +body.user-edit-php .profile-lang-tab, +body.user-new-php .profile-lang-tab { + padding: 5px 14px; + background: #f0f0f1; + border: 1px solid #c3c4c7; + cursor: pointer; + font-size: 13px; + line-height: 1.5; + color: #50575e; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} +body.profile-php .profile-lang-tab.is-active, +body.user-edit-php .profile-lang-tab.is-active, +body.user-new-php .profile-lang-tab.is-active { + background: #fff; + border-bottom-color: #fff; + color: #1d2327; + font-weight: 600; +} +body.profile-php .form-table tbody .profile-lang-tabs-row td, +body.user-edit-php .form-table tbody .profile-lang-tabs-row td, +body.user-new-php .form-table tbody .profile-lang-tabs-row td { + padding: 0; + grid-column: 1 / -1; +} + + +/* Membres: 2-column explicit grid + Row 1: Fonction (col 1 only) + Row 2: Membres | Autrepersonnes + Row 3: full-width separator (injected ) + Row 4: Autre fonction (col 1 only) + Row 5: Autre membres | Autre autrepersonnes +*/ +body.post-php #pods-meta-membres .form-table, +body.post-new-php #pods-meta-membres .form-table { + display: block; +} + +body.post-php #pods-meta-membres .form-table tbody, +body.post-new-php #pods-meta-membres .form-table tbody { + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 16px; +} + +body.post-php #pods-meta-membres .form-table tr, +body.post-new-php #pods-meta-membres .form-table tr { + display: flex; + flex-direction: column; + padding: 6px 0; +} + +body.post-php #pods-meta-membres .form-table th, +body.post-new-php #pods-meta-membres .form-table th, +body.post-php #pods-meta-membres .form-table td, +body.post-new-php #pods-meta-membres .form-table td { + display: block; + width: 100%; + padding: 0 0 4px; +} + +body.post-php #pods-meta-membres .form-table input[type="text"], +body.post-new-php #pods-meta-membres .form-table input[type="text"] { + width: 100%; + box-sizing: border-box; + max-width: none; +} + +/* Row 1: Fonction — left column only */ +body.post-php #pods-meta-membres [class*="pods-form-ui-row-name-fonction-"], +body.post-new-php #pods-meta-membres [class*="pods-form-ui-row-name-fonction-"] { + grid-column: 1; + grid-row: 1; +} + +/* Row 2: Membres (left) | Autrepersonnes (right) */ +body.post-php #pods-meta-membres .pods-form-ui-row-name-membres, +body.post-new-php #pods-meta-membres .pods-form-ui-row-name-membres { + grid-column: 1; + grid-row: 2; +} + +body.post-php #pods-meta-membres .pods-form-ui-row-name-autrepersonnes, +body.post-new-php #pods-meta-membres .pods-form-ui-row-name-autrepersonnes { + grid-column: 2; + grid-row: 2; +} + +/* Row 3: full-width separator */ +body.post-php #pods-meta-membres .membres-grid-separator, +body.post-new-php #pods-meta-membres .membres-grid-separator { + grid-column: 1 / -1; + grid-row: 3; + border-top: 1px solid #dcdcde; + height: 0; + padding: 0; + margin: 4px 0; +} + +/* Row 4: Autre fonction — left column only */ +body.post-php #pods-meta-membres [class*="pods-form-ui-row-name-autre-fonction-"], +body.post-new-php #pods-meta-membres [class*="pods-form-ui-row-name-autre-fonction-"] { + grid-column: 1; + grid-row: 4; +} + +/* Row 5: Autre membres (left) | Autre autrepersonnes (right) */ +body.post-php #pods-meta-membres .pods-form-ui-row-name-autre-membres, +body.post-new-php #pods-meta-membres .pods-form-ui-row-name-autre-membres { + grid-column: 1; + grid-row: 5; +} + +body.post-php #pods-meta-membres .pods-form-ui-row-name-autre-autrepersonnes, +body.post-new-php #pods-meta-membres .pods-form-ui-row-name-autre-autrepersonnes { + grid-column: 2; + grid-row: 5; +} + +/* Liens externes: two-column layout — URL field and title field side by side per link */ +body.post-php #pods-meta-liens-externes .form-table, +body.post-new-php #pods-meta-liens-externes .form-table { + display: block; +} + +body.post-php #pods-meta-liens-externes .form-table tbody, +body.post-new-php #pods-meta-liens-externes .form-table tbody { + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 16px; +} + +body.post-php #pods-meta-liens-externes .form-table tr, +body.post-new-php #pods-meta-liens-externes .form-table tr { + display: flex; + flex-direction: column; + padding: 6px 0; +} + +body.post-php #pods-meta-liens-externes .form-table th, +body.post-new-php #pods-meta-liens-externes .form-table th, +body.post-php #pods-meta-liens-externes .form-table td, +body.post-new-php #pods-meta-liens-externes .form-table td { + display: block; + width: 100%; + padding: 0 0 4px; +} + +body.post-php #pods-meta-liens-externes .form-table input[type="text"], +body.post-new-php #pods-meta-liens-externes .form-table input[type="text"] { + width: 100%; + box-sizing: border-box; + max-width: none; +} + +/* Visual separator between link pairs 2 and 3 */ +body.post-php #pods-meta-liens-externes .pods-form-ui-row-name-lien-externe-2, +body.post-new-php #pods-meta-liens-externes .pods-form-ui-row-name-lien-externe-2, +body.post-php #pods-meta-liens-externes .pods-form-ui-row-name-titre-du-lien-externe-2, +body.post-new-php #pods-meta-liens-externes .pods-form-ui-row-name-titre-du-lien-externe-2, +body.post-php #pods-meta-liens-externes .pods-form-ui-row-name-lien-externe-3, +body.post-new-php #pods-meta-liens-externes .pods-form-ui-row-name-lien-externe-3, +body.post-php #pods-meta-liens-externes .pods-form-ui-row-name-titre-du-lien-externe-3, +body.post-new-php #pods-meta-liens-externes .pods-form-ui-row-name-titre-du-lien-externe-3 { + border-top: 1px solid #dcdcde; + padding-top: 8px; +} + +/* Dates: date de début and date de fin side by side */ +body.post-php #pods-meta-dates .form-table, +body.post-new-php #pods-meta-dates .form-table { + display: block; +} + +body.post-php #pods-meta-dates .form-table tbody, +body.post-new-php #pods-meta-dates .form-table tbody { + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 16px; +} + +body.post-php #pods-meta-dates .form-table tr, +body.post-new-php #pods-meta-dates .form-table tr { + display: flex; + flex-direction: column; + padding: 6px 0; +} + +body.post-php #pods-meta-dates .form-table th, +body.post-new-php #pods-meta-dates .form-table th, +body.post-php #pods-meta-dates .form-table td, +body.post-new-php #pods-meta-dates .form-table td { + display: block; + width: 100%; + padding: 0 0 4px; +} + +/* Taxonomy pages — hide unused description field */ +.edit-tags-php .term-description-wrap, +.term-php .term-description-wrap { + display: none; +} diff --git a/css/style.css b/css/style.css new file mode 100755 index 0000000..a28209b --- /dev/null +++ b/css/style.css @@ -0,0 +1,2901 @@ +@charset "UTF-8"; +*, *:before, *:after { + box-sizing: border-box; } + +html, body, div, span, object, iframe, figure, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, code, em, img, small, strike, strong, sub, sup, tt, b, u, i, ol, ul, li, fieldset, form, label, table, caption, tbody, tfoot, thead, tr, th, td, main, canvas, embed, footer, header, nav, section, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + text-size-adjust: none; } + +footer, header, nav, section, main { + display: block; } + +body { + line-height: 1; } + +ol, ul { + list-style: none; } + +blockquote, q { + quotes: none; } + +blockquote:before, blockquote:after, q:before, q:after { + content: ''; + content: none; } + +table { + border-collapse: collapse; + border-spacing: 0; } + +input { + -webkit-appearance: none; + border-radius: 0; } + +#wpadminbar { + position: fixed !important; } + +@font-face { + font-family: 'Gelasio'; + src: url("../assets/fonts/Gelasio-Regular.woff2") format("woff2"); + font-weight: normal; + font-style: normal; } +@font-face { + font-family: 'NewsCycle'; + src: url("../assets/fonts/NewsCycle-Regular.woff2") format("woff2"); + font-weight: normal; + font-style: normal; } +body { + font-family: "NewsCycle", sans-serif; } + +h1, h2, h3, h4, h5, h6 { + font-family: "Gelasio", serif; } + +a, +a:active { + color: #1a1a1a; + transition: color 0.2s ease-out; } + +a:hover { + color: #3e3e3e; } + +p { + line-height: 1.2; } + +.link-button { + display: inline-flex; + background-color: #eeeeee; + color: #1a1a1a; + padding: 0.6rem 0.7rem; + font-size: 0.9rem; + transition: background-color 0.3s ease-out; + text-decoration: none; + justify-content: center; + align-items: center; + word-break: break-all; + max-width: 100%; } + .link-button > i { + margin-right: 0.6rem; } + .link-button:hover { + background-color: #cccccc; } + @media (min-width: 768px) { + .link-button { + font-size: unset; + padding: 0.6rem 1rem; } } + .link-button--wrap-word { + word-break: normal; + overflow-wrap: break-word; } + +body { + display: flex; + flex-direction: column; + height: 100vh; + background-color: #eeeeee; } + +main { + width: 100vw; + display: flex; + flex-direction: column; + align-items: center; } + @media (min-width: 768px) { + main { + margin-top: 12vh; } } + +.container { + display: flex; + align-items: center; + justify-content: start; + flex-direction: column; + position: relative; + z-index: 1; + padding: 4vh 5vw; + max-width: 1640px; + width: 100vw; } + +.full-block { + width: 100%; + background-color: white; + padding: 2.5vh 5vw; + position: relative; + padding-bottom: 6vh; } + @media (min-width: 768px) { + .full-block { + padding: 3vh 3vw; + padding-bottom: 8vh; } } + .full-block::after { + content: ''; + display: block; + position: absolute; + height: 30px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; } + +body > +header { + display: flex; + flex-direction: column-reverse; + justify-content: space-between; + transition: height 0.3s ease-out; + background-color: white; + width: 100%; + position: relative; + z-index: 5; } + @media (min-width: 768px) { + body > + header { + position: fixed; + height: 12vh; + min-height: 100px; + max-height: 130px; + flex-direction: row; } } + +.header-left { + display: flex; + height: 6rem; + margin-top: 3rem; } + @media (min-width: 768px) { + .header-left { + margin-top: unset; + height: 100%; } } + +.main-logo-container { + display: inline-block; + background-color: #eeeeee; + height: 100%; } + @media (min-width: 768px) { + .main-logo-container { + flex-direction: row; } } + +.main-logo { + display: inline-block; + padding: 0.3rem 0.6rem; + height: 100%; + background: linear-gradient(to bottom, #eeeeee 60%, #f7ff29 100%); + background-position: bottom 0px left 0px; + background-repeat: no-repeat; + cursor: pointer; + transition: background 0.3s ease-out, padding 0.2s ease-out; } + .main-logo:hover { + background-position: bottom -10px left 0px; } + @media (min-width: 768px) { + .main-logo { + padding: 1.5rem 2rem; } } + .main-logo > img, + .main-logo > a > img { + height: 100%; + transform: scale(1); + transition: transform 0.2s ease-out; } + .main-logo:hover > img, .main-logo:hover > a > img { + transform: scale(1.05); } + +.description { + display: flex; + flex-direction: column; + align-items: start; + justify-content: center; + gap: 0.3rem; + margin-left: 1.2rem; + opacity: 1; + transition: opacity 0.2s ease-out; } + .description > div:first-of-type { + text-transform: uppercase; + font-size: 0.8rem; + background-color: #eeeeee; + padding: 0.2rem; } + .description > div:last-of-type { + font-size: 0.8rem; } + .description > div:last-of-type > sup { + font-size: 0.6rem; + vertical-align: super; } + +.header-right { + display: flex; + align-items: center; + gap: 2rem; + justify-content: space-between; + position: fixed; + width: 100%; + background-color: white; + height: 3rem; } + @media (min-width: 768px) { + .header-right { + padding-left: unset; + height: unset; + position: relative; + width: unset; } } + .header-right.scrolled .secondary-logo-container { + max-width: 40vw; } + .header-right.scrolled .secondary-logo-container .main-logo:hover { + background-position: unset; } + .header-right.scrolled .secondary-logo-container .main-logo:hover img { + transform: unset; } + +.secondary-logo-container { + height: 100%; + max-width: 0; + overflow: hidden; + transition: max-width 0.6s ease-out; } + @media (min-width: 768px) { + .secondary-logo-container { + display: none; } } + +.lang-switch { + text-transform: uppercase; + font-size: 0.8rem; + transition: font-size 0.2s ease-out; } + .lang-switch > ul { + display: flex; + gap: 1rem; } + .lang-switch > ul > li:not(.active) > a { + text-decoration: none; } + +.search-button { + margin-left: auto; } + .search-button > div { + background-color: #eeeeee; + padding: 0.5rem; + border-radius: 5rem; + min-width: 2rem; + aspect-ratio: 1 / 1; + display: flex; + justify-content: center; + align-items: center; + transition: background-color 0.3s ease-out; + cursor: pointer; } + .search-button > div:hover { + background-color: #cccccc; } + +.menu-toggle { + display: inline-block; + background-color: #eeeeee; + width: fit-content; + height: 100%; } + .menu-toggle > div { + height: 100%; + display: flex; + padding: 0.5rem; + flex-direction: column; + align-items: center; + justify-content: space-between; + gap: 0.3rem; + background: linear-gradient(to bottom, #eeeeee 60%, #f7ff29 100%); + background-position: bottom 0px left 0px; + background-repeat: no-repeat; + cursor: pointer; + transition: background 0.3s ease-out, padding 0.2s ease-out; } + .menu-toggle > div:hover { + background-position: bottom -10px left 0px; } + @media (min-width: 768px) { + .menu-toggle > div { + gap: unset; + padding: 2rem; } } + .menu-toggle > div > div { + font-size: 1rem; + transform: scale(1); + transition: transform 0.2s ease-out, font-size 0.2s ease-out; } + @media (min-width: 768px) { + .menu-toggle > div > div { + font-size: 2rem; } } + .menu-toggle > div > p { + font-family: NewsCycle; + text-transform: uppercase; + transform: scale(1); + font-size: 0.8rem; + transition: transform 0.2s ease-out, font-size 0.2s ease-out; } + @media (min-width: 768px) { + .menu-toggle > div > p { + font-size: 1rem; } } + .menu-toggle:hover > div > div, .menu-toggle:hover > div > p { + transform: scale(0.9); } + +.search-panel { + position: absolute; + right: 0; + width: 100%; + background-color: white; + z-index: 4; + border-top: 2px solid #eeeeee; + transition: top 0.4s ease-out, opacity 0.2s ease-out; + opacity: 0; + pointer-events: none; } + @media (min-width: 768px) { + .search-panel { + position: fixed; + width: 33.333%; } } + .search-panel.active { + opacity: 1; + pointer-events: all; } + .search-panel__inner { + padding: 2rem; + display: flex; + flex-direction: column; + gap: 1rem; } + .search-panel__title { + font-family: "NewsCycle", sans-serif; + font-size: 1.1rem; + text-transform: uppercase; + display: inline-block; + align-self: start; + position: relative; + line-height: 1.6; } + .search-panel__title::after { + content: ''; + display: block; + position: absolute; + height: 30px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + .search-panel__desc { + font-family: "NewsCycle", sans-serif; + color: #3e3e3e; } + .search-panel__input-wrap { + position: relative; } + .search-panel__icon-btn { + position: absolute; + right: 0.8rem; + top: 50%; + transform: translateY(-50%); + color: #3e3e3e; + background: none; + border: none; + padding: 0; + cursor: pointer; + line-height: 1; } + .search-panel__icon-btn:hover { + color: #1a1a1a; } + .search-panel__input { + width: 100%; + border: none; + padding: 0.6rem 2.5rem 0.6rem 0.8rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + outline: none; + background-color: #eeeeee; } + .search-panel__input::placeholder { + color: #3e3e3e; + text-transform: uppercase; } + .search-panel__input:focus { + border-color: #cccccc; } + .search-panel__submit { + display: block; + margin-left: auto; + background-color: #eeeeee; + padding: 0.5rem 1rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + text-transform: uppercase; + cursor: pointer; + margin-top: 1rem; + border: none; } + .search-panel__submit:hover { + background-color: #cccccc; } + +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #cccccc; + opacity: 0; + z-index: 3; + pointer-events: none; + transition: opacity 0.3s ease-out; } + .overlay.active { + opacity: 0.6; + pointer-events: all; } + +.main-menu { + overflow-y: scroll; + left: 0; + width: 100%; + background-color: white; + z-index: 4; + padding-bottom: 4vh; + transition: top 0.4s ease-out, opacity 0.2s ease-out; + border-top: 2px solid #eeeeee; + position: absolute; + opacity: 0; } + @media (min-width: 768px) { + .main-menu { + overflow-y: unset; + position: fixed; } } + .main-menu::after { + content: ''; + display: block; + position: absolute; + height: 30px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + opacity: 0; + transition: opacity 0.2s ease-out 0.3s; } + @media (min-width: 768px) { + .main-menu::after { + opacity: 1; } } + .main-menu.active { + opacity: 1; } + .main-menu.active::after { + position: fixed; + opacity: 1; } + @media (min-width: 768px) { + .main-menu.active::after { + position: absolute; } } + +.menu-navigation-container > ul, +.menu-navigation-en-container > ul { + display: grid; + gap: 3rem; + padding: 2rem 1.5rem; + max-width: 1400px; + margin: 0 auto; } + @media (min-width: 768px) { + .menu-navigation-container > ul, + .menu-navigation-en-container > ul { + padding: 3rem 4rem; + grid-template-columns: repeat(3, auto); } } + @media (min-width: 1024px) { + .menu-navigation-container > ul, + .menu-navigation-en-container > ul { + grid-template-columns: repeat(5, auto); } } + .menu-navigation-container > ul > li, + .menu-navigation-en-container > ul > li { + display: flex; + flex-direction: column; + gap: 0.8rem; + align-items: start; } + .menu-navigation-container > ul > li > a, + .menu-navigation-en-container > ul > li > a { + text-transform: uppercase; + text-decoration: none; + position: relative; + padding-bottom: 0.8rem; + margin-bottom: 0.5rem; } + .menu-navigation-container > ul > li > a::after, + .menu-navigation-en-container > ul > li > a::after { + content: ''; + display: block; + position: absolute; + height: 8px; + width: 100%; + bottom: 0; + left: 0; + z-index: 2; } + .menu-navigation-container > ul > li:nth-of-type(1) a:hover, + .menu-navigation-en-container > ul > li:nth-of-type(1) a:hover { + color: #e0775d; } + .menu-navigation-container > ul > li:nth-of-type(1) > a::after, + .menu-navigation-en-container > ul > li:nth-of-type(1) > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #e0775d 100%); } + .menu-navigation-container > ul > li:nth-of-type(1) > ul.sub-menu > li:first-of-type, + .menu-navigation-en-container > ul > li:nth-of-type(1) > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid #e0775d; + padding-bottom: 0.8rem; } + .menu-navigation-container > ul > li:nth-of-type(2) a:hover, + .menu-navigation-en-container > ul > li:nth-of-type(2) a:hover { + color: #7cc0c6; } + .menu-navigation-container > ul > li:nth-of-type(2) > a::after, + .menu-navigation-en-container > ul > li:nth-of-type(2) > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #7cc0c6 100%); } + .menu-navigation-container > ul > li:nth-of-type(2) > ul.sub-menu > li:first-of-type, + .menu-navigation-en-container > ul > li:nth-of-type(2) > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid #7cc0c6; + padding-bottom: 0.8rem; } + .menu-navigation-container > ul > li:nth-of-type(3) a:hover, + .menu-navigation-en-container > ul > li:nth-of-type(3) a:hover { + color: #46ae51; } + .menu-navigation-container > ul > li:nth-of-type(3) > a::after, + .menu-navigation-en-container > ul > li:nth-of-type(3) > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #46ae51 100%); } + .menu-navigation-container > ul > li:nth-of-type(3) > ul.sub-menu > li:first-of-type, + .menu-navigation-en-container > ul > li:nth-of-type(3) > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid #46ae51; + padding-bottom: 0.8rem; } + .menu-navigation-container > ul > li:nth-of-type(4) a:hover, + .menu-navigation-en-container > ul > li:nth-of-type(4) a:hover { + color: #e05680; } + .menu-navigation-container > ul > li:nth-of-type(4) > a::after, + .menu-navigation-en-container > ul > li:nth-of-type(4) > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #e05680 100%); } + .menu-navigation-container > ul > li:nth-of-type(4) > ul.sub-menu > li:first-of-type, + .menu-navigation-en-container > ul > li:nth-of-type(4) > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid #e05680; + padding-bottom: 0.8rem; } + .menu-navigation-container > ul > li:nth-of-type(5) a:hover, + .menu-navigation-en-container > ul > li:nth-of-type(5) a:hover { + color: #bb8dd9; } + .menu-navigation-container > ul > li:nth-of-type(5) > a::after, + .menu-navigation-en-container > ul > li:nth-of-type(5) > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #bb8dd9 100%); } + .menu-navigation-container > ul > li:nth-of-type(5) > ul.sub-menu > li:first-of-type, + .menu-navigation-en-container > ul > li:nth-of-type(5) > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid #bb8dd9; + padding-bottom: 0.8rem; } + .menu-navigation-container > ul > li > ul.sub-menu, + .menu-navigation-en-container > ul > li > ul.sub-menu { + display: flex; + flex-direction: column; + gap: 0.8rem; } + .menu-navigation-container > ul > li > ul.sub-menu > li > a, + .menu-navigation-en-container > ul > li > ul.sub-menu > li > a { + text-decoration: none; } + +.nav-axes-item .nav-axes-trigger { + background: none; + border: none; + padding: 0; + cursor: pointer; + font-family: "NewsCycle", sans-serif; + font-size: inherit; + color: inherit; + display: flex; + align-items: center; + gap: 0.3rem; } + .nav-axes-item .nav-axes-trigger i { + transition: transform 0.2s ease; } +.nav-axes-item.is-open .nav-axes-trigger i { + transform: rotate(180deg); } +.nav-axes-item .nav-axes-list { + display: none; + flex-direction: column; + gap: 0.8rem; + padding-top: 0.8rem; } + .nav-axes-item .nav-axes-list li a { + font-size: 0.8rem; + padding-left: 0.5rem; + text-decoration: none; } +.nav-axes-item.is-open .nav-axes-list { + display: flex; } + +.menu-navigation-container > ul > li:nth-of-type(1) .nav-axes-trigger:hover, +.menu-navigation-en-container > ul > li:nth-of-type(1) .nav-axes-trigger:hover { + color: #e0775d; } + +footer { + background-color: white; + width: 100%; + padding: 1rem 2rem; + margin-top: auto; } + +.footer-content { + margin: 0 auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + gap: 2rem; } + @media (min-width: 768px) { + .footer-content { + gap: unset; + flex-direction: row; } } + +.footer-nav ul#menu-footer, +.footer-nav ul#menu-footer-en { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 0.5rem 2rem; } + @media (min-width: 768px) { + .footer-nav ul#menu-footer, + .footer-nav ul#menu-footer-en { + justify-content: start; } } + .footer-nav ul#menu-footer li a, + .footer-nav ul#menu-footer-en li a { + text-transform: uppercase; + font-size: 0.75rem; } + +.footer-logos { + display: inline-flex; + height: 3rem; + gap: 1rem; + padding: 0.5rem; + align-items: start; } + .footer-logos > a { + height: 100%; } + .footer-logos > a > img { + height: 100%; } + +.hero-header { + display: flex; + flex-direction: column-reverse; + position: relative; } + @media (min-width: 768px) { + .hero-header { + flex-direction: row; } } + +.hero-logos { + position: absolute; + top: 2.5vh; + display: inline-flex; + height: 3rem; + gap: 1rem; + padding: 0.5rem; + align-items: start; + background-color: #eeeeee; } + @media (min-width: 768px) { + .hero-logos { + position: unset; + top: unset; } } + .hero-logos > a { + height: 100%; } + .hero-logos > a > img { + height: 100%; } + .hero-logos > a:nth-of-type(2) { + mix-blend-mode: darken; } + +.color-changer { + transition: color 0.3s ease-out; } + +.hero-presentation { + font-family: "Gelasio", serif; + font-size: 1.6rem; + line-height: 1.1; } + @media (min-width: 768px) { + .hero-presentation { + margin-top: 2rem; + font-size: 2.6rem; } } + +.hero-presentation-detail { + margin-top: 1.5rem; + width: 90%; } + +.hero-content > .link-button { + margin-top: 2.5rem; } + +@media (min-width: 768px) { + .hero-content { + width: 75%; } } + +#sketch { + z-index: 0; + position: relative; + display: block; + height: 200px; } + @media (min-width: 768px) { + #sketch { + height: unset; + width: 25%; + margin-top: 0 !important; } } + +.floating-shape { + position: absolute; + top: 0; + left: 0; + transform-origin: top left; + will-change: transform; + pointer-events: none; } + .floating-shape svg { + overflow: visible; } + .floating-shape path, .floating-shape polyline, .floating-shape polygon, .floating-shape line, .floating-shape circle, .floating-shape ellipse, .floating-shape rect { + transition: fill-opacity 0.5s ease-in-out; } + +.thalim-text { + position: absolute; + pointer-events: none; + transition: opacity 0.3s ease-out; + display: flex; + gap: 0; + z-index: 20; + font-family: 'NewsCycle', sans-serif; + font-size: 26px; } + @media (min-width: 768px) { + .thalim-text { + font-size: 48px; } } + +.swiper-section { + margin-top: 3rem; + position: relative; } + .swiper-section .section-title { + font-family: NewsCycle; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; } + .swiper-section .section-title::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + .swiper-section .swiper_content_controls { + display: flex; + gap: 0.8rem; + align-items: center; + margin-top: 2rem; } + .swiper-section .swiper_content_controls .swiper { + overflow: hidden; + flex: 1; } + .swiper-section .swiper_content_controls .swiper-button-prev, + .swiper-section .swiper_content_controls .swiper-button-next { + position: static; + width: 2.4rem; + height: 2.4rem; + aspect-ratio: 1; + margin: 0; + background-color: #eeeeee; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: #1a1a1a; + transition: background-color 0.2s ease-out; + cursor: pointer; + flex-shrink: 0; } + .swiper-section .swiper_content_controls .swiper-button-prev::after, + .swiper-section .swiper_content_controls .swiper-button-next::after { + display: none; } + .swiper-section .swiper_content_controls .swiper-button-prev:hover, + .swiper-section .swiper_content_controls .swiper-button-next:hover { + background-color: #cccccc; } + .swiper-section .swiper_content_controls .swiper-button-prev i, + .swiper-section .swiper_content_controls .swiper-button-next i { + font-size: 1.1rem; + line-height: 1; } + .swiper-section .button-annonces { + display: flex; + justify-content: center; + margin-top: 1.2rem; } + +.message-agenda-section { + display: flex; + flex-direction: column; + width: 100%; + gap: 2rem; + margin-top: 3rem; } + @media (min-width: 768px) { + .message-agenda-section { + flex-direction: row; } } + +.message-du-labo, +.agenda { + position: relative; + padding: 2.5vh 5vw; + padding-bottom: 6vh; + background-color: white; + display: flex; + flex-direction: column; + align-items: flex-start; } + .message-du-labo::after, + .agenda::after { + content: ''; + display: block; + position: absolute; + height: 30px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; } + .message-du-labo .section-title, + .agenda .section-title { + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 3rem; } + .message-du-labo .section-title::after, + .agenda .section-title::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + @media (min-width: 768px) { + .message-du-labo, + .agenda { + padding: 3vh 3vw; + padding-bottom: 8vh; } } + +@media (min-width: 768px) { + .message-du-labo { + flex: 4; } } + +.messages-list { + width: 100%; } + @media (min-width: 768px) { + .messages-list { + flex: 1; + overflow: hidden; } } + +@media (min-width: 768px) { + .agenda { + flex: 3; } } + +.message-date { + font-size: 0.75rem; + color: #3e3e3e; + display: block; + margin-bottom: 0.3rem; } + +.message-item + .message-item { + margin-top: 1.5rem; + padding-top: 1.5rem; + border-top: 1px solid #eeeeee; } + +.message-content { + margin-bottom: 1.5rem; + padding-right: 2rem; + position: relative; } + .message-content > p { + margin: 0.7rem 0; } + .message-content p { + margin-bottom: 1rem; + line-height: 1.6; } + .message-content p strong { + font-weight: bold; } + .message-content p em { + font-style: italic; } + .message-content ul, .message-content ol { + line-height: 1.6; + padding-left: 0.8rem; } + .message-content ul { + list-style: inside "· "; } + .message-content ol { + list-style: inside decimal; } + .message-content blockquote { + padding-left: 1rem; + margin-left: 1.5rem; + border-left: solid 1px #eeeeee; } + @media (min-width: 768px) { + .message-content { + overflow: hidden; } + .message-content.is-overflowing::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 8rem; + background: linear-gradient(to bottom, transparent 0%, white 70%); + pointer-events: none; } } + +.button-messages, +.button-agenda { + align-self: center; + margin-top: auto; } + +.message-read-more { + display: none; + position: absolute; + bottom: 1rem; + left: 0; + z-index: 1; + font-size: 0.85rem; + text-decoration: none; + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; } + .is-overflowing .message-read-more { + display: inline-block; } + +.agenda-content { + width: 100%; } + .agenda-content .agenda-item { + display: flex; + align-items: center; + gap: 1.5rem; + transform: scale(1); + transition: transform 0.2s ease-out; + margin-bottom: 2rem; + text-decoration: none; + color: inherit; } + .agenda-content .agenda-item:hover { + transform: scale(0.97); } + .agenda-content .date-container { + background-color: #eeeeee; + text-transform: uppercase; + font-family: "NewsCycle", sans-serif; + text-align: center; + padding: 0.5rem; + position: relative; + flex-shrink: 0; + min-width: 3rem; } + .agenda-content .date-container > p { + position: relative; + z-index: 1; } + .agenda-content .date-container > p:first-of-type { + font-size: 1.3rem; } + .agenda-content .date-container > p:last-of-type { + margin-bottom: 3px; } + .agenda-content .date-container::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, #eeeeee 0%, #7cc0c6 100%); + z-index: 0; } + .agenda-content .event-content { + padding-bottom: 1rem; + border-bottom: 1px solid #7cc0c6; + flex: 1; } + .agenda-content .event-content .meta { + font-family: "NewsCycle", sans-serif; + display: flex; + gap: 1rem; + padding-bottom: 0.5rem; + text-transform: uppercase; + font-size: 0.8rem; + flex-wrap: wrap; + opacity: 0.7; } + .agenda-content .event-content .event-title { + font-family: "Gelasio", serif; + font-size: 1.1rem; } + +.keyword-cloud { + margin-top: 4rem; + margin-bottom: 3rem; + width: 100%; } + @media (min-width: 768px) { + .keyword-cloud { + margin-bottom: 0; } } + .keyword-cloud .section-title { + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; } + .keyword-cloud .section-title::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + +#keyword-container { + position: relative; + width: 100%; + min-height: 120px; } + +.keyword { + position: absolute; + font-family: "NewsCycle", sans-serif; + font-size: 0.7rem; + text-transform: uppercase; + white-space: nowrap; + text-decoration: none; + color: #1a1a1a; + opacity: 0; + cursor: pointer; } + @media (min-width: 768px) { + .keyword { + font-size: 0.95rem; } } + .keyword.keyword--visible { + animation: keywordFadeIn 0.7s ease-out forwards; } + +@keyframes keywordFadeIn { + from { + opacity: 0; + transform: scale(0.92); } + to { + opacity: 1; + transform: scale(1); } } +.quick-links { + position: fixed; + display: block; + background-color: #fcfcfc; + right: 0; + top: 35vh; + z-index: 3; + font-family: "NewsCycle", sans-serif; + max-width: 2.2rem; + overflow: hidden; + transition: max-width 0.9s ease-out, top 0.2s ease; + text-decoration: none; + z-index: 10; } + @media (min-width: 768px) { + .quick-links { + right: 2vw; } } + @media (min-width: 1024px) { + .quick-links { + right: 4vw; } } + .quick-links > ul { + display: flex; + flex-direction: column; + align-items: end; + gap: 0.8rem; + padding: 0.8rem 0.6rem; } + .quick-links > ul > li > a { + display: flex; + align-items: center; + gap: 0.8rem; + text-decoration: none; + white-space: nowrap; } + .quick-links > ul > li > a:hover { + font-weight: bold; } + .quick-links:hover { + max-width: 40vw; } + .quick-links::after { + content: ""; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 0%, #f7ff29 100%); + z-index: 2; } + +.post-card { + padding-bottom: 0.8rem; + border-bottom: solid 1px; } + .post-card.gradient--le-laboratoire { + border-color: #e0775d; } + .post-card.gradient--le-laboratoire .gradient-container { + background: linear-gradient(to bottom, #eeeeee 60%, #e0775d); } + .post-card.gradient--manifestations-scientifiques { + border-color: #7cc0c6; } + .post-card.gradient--manifestations-scientifiques .gradient-container { + background: linear-gradient(to bottom, #eeeeee 60%, #7cc0c6); } + .post-card.gradient--publications-et-productions { + border-color: #46ae51; } + .post-card.gradient--publications-et-productions .gradient-container { + background: linear-gradient(to bottom, #eeeeee 60%, #46ae51); } + .post-card.gradient--mediation-scientifique { + border-color: #e05680; } + .post-card.gradient--mediation-scientifique .gradient-container { + background: linear-gradient(to bottom, #eeeeee 60%, #e05680); } + .post-card.gradient--ressources { + border-color: #bb8dd9; } + .post-card.gradient--ressources .gradient-container { + background: linear-gradient(to bottom, #eeeeee 60%, #bb8dd9); } + .post-card:hover .gradient-container img, .post-card:hover .gradient-container h2 { + transform: scale(0.98); } + .post-card .gradient-container { + height: 25vh; + padding: 0.7rem; + display: flex; + align-items: center; + justify-content: center; + text-decoration: unset; } + .post-card .gradient-container img { + max-height: 100%; + transition: transform 0.2s ease-out; + transform: scale(1); + max-width: 100%; } + .post-card .gradient-container h2 { + font-family: Gelasio; + font-size: 1.7rem; + line-height: 1.1; + padding: 1.5rem; + transition: transform 0.2s ease-out; + transform: scale(1); + text-decoration: unset; } + .post-card .gradient-container.text-only { + font-family: Gelasio; + font-size: 1.7rem; + padding: 1.5rem; } + .post-card .gradient-container.text-only p { + transition: transform 0.2s ease-out; + transform: scale(1); } + .post-card .contextual-infos { + text-transform: uppercase; + font-size: 0.8rem; + display: flex; + justify-content: space-between; + margin-top: 0.6rem; + line-height: 1.3; } + .post-card .contextual-infos .authors a { + text-decoration: none; } + .post-card .contextual-infos .authors > span:not(:last-of-type)::after { + content: ", "; } + .post-card .contextual-infos .date-category { + display: flex; + flex-direction: column; + align-items: flex-end; + text-align: right; } + .post-card .contextual-infos .date-category a { + text-decoration: none; } + .post-card .title-bottom { + font-size: 1.2rem; + margin-top: 0.8rem; + line-height: 1.2; } + .post-card .title-bottom a { + text-decoration: none; } + +.category-archive { + margin-top: 0; } + .category-archive .category-header-top { + display: flex; + justify-content: space-between; + flex-direction: column; + align-items: start; } + .category-archive .category-header-top .breadcrumb { + font-size: 0.85rem; + margin-bottom: 1.5rem; + text-transform: uppercase; + line-height: 1.3; } + .category-archive .category-header-top .breadcrumb__separator { + margin: 0 0.4rem; } + @media (min-width: 768px) { + .category-archive .category-header-top { + flex-direction: row; } } + .category-archive h1 { + font-family: Gelasio; + font-weight: normal; + font-size: 1.8rem; + position: relative; + margin-top: 2rem; + display: inline-block; } + .category-archive h1::after { + content: ''; + display: block; + position: absolute; + height: 6px; + width: 100%; + bottom: -1.2rem; + left: 0; + z-index: 2; } + @media (min-width: 768px) { + .category-archive h1 { + font-size: 2.6rem; } + .category-archive h1::after { + bottom: -0.4rem; } } + .category-archive .category--le-laboratoire h1::after { + background: linear-gradient(to bottom, transparent 0%, #e0775d 50%); } + .category-archive .category--manifestations-scientifiques h1::after { + background: linear-gradient(to bottom, transparent 0%, #7cc0c6 50%); } + .category-archive .category--publications-et-productions h1::after { + background: linear-gradient(to bottom, transparent 0%, #46ae51 50%); } + .category-archive .category--mediation-scientifique h1::after { + background: linear-gradient(to bottom, transparent 0%, #e05680 50%); } + .category-archive .category--ressources h1::after { + background: linear-gradient(to bottom, transparent 0%, #bb8dd9 50%); } + +.breadcrumb a:not(.breadcrumb__cat) { + text-decoration: underline; + text-decoration-color: #cccccc; + text-underline-offset: 3px; } + +.category--le-laboratoire .breadcrumb__cat, +.category--le-laboratoire .breadcrumb__current { + text-decoration: underline; + text-decoration-color: #e0775d; + text-underline-offset: 3px; } + +.category--manifestations-scientifiques .breadcrumb__cat, +.category--manifestations-scientifiques .breadcrumb__current { + text-decoration: underline; + text-decoration-color: #7cc0c6; + text-underline-offset: 3px; } + +.category--publications-et-productions .breadcrumb__cat, +.category--publications-et-productions .breadcrumb__current { + text-decoration: underline; + text-decoration-color: #46ae51; + text-underline-offset: 3px; } + +.category--mediation-scientifique .breadcrumb__cat, +.category--mediation-scientifique .breadcrumb__current { + text-decoration: underline; + text-decoration-color: #e05680; + text-underline-offset: 3px; } + +.category--ressources .breadcrumb__cat, +.category--ressources .breadcrumb__current { + text-decoration: underline; + text-decoration-color: #bb8dd9; + text-underline-offset: 3px; } + +.category-archive .taxonomy-description { + font-family: "NewsCycle", sans-serif; + margin-top: 2rem; + line-height: 1.6; + max-width: 70ch; } + .category-archive .taxonomy-description a { + text-decoration: underline; } + .category-archive .taxonomy-description p { + margin-bottom: 1rem; + line-height: 1.6; } + .category-archive .taxonomy-description p strong { + font-weight: bold; } + .category-archive .taxonomy-description p em { + font-style: italic; } + .category-archive .taxonomy-description p:first-child { + margin-top: 0 !important; } + .category-archive .taxonomy-description ul, .category-archive .taxonomy-description ol { + line-height: 1.6; + padding-left: 0.8rem; } + .category-archive .taxonomy-description ul { + list-style: inside "· "; } + .category-archive .taxonomy-description ol { + list-style: inside decimal; } + .category-archive .taxonomy-description blockquote { + padding-left: 1rem; + margin-left: 1.5rem; + border-left: solid 1px #eeeeee; } +.category-archive .subcategory-section { + margin-top: 5rem; } + .category-archive .subcategory-section:first-of-type { + margin-top: 2rem; } + .category-archive .subcategory-section .subcategory-section__title { + font-family: NewsCycle; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + font-size: 1.2rem; } + .category-archive .subcategory-section .subcategory-section__title::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + .category-archive .subcategory-section .post-grid { + display: grid; + align-items: start; + grid-template-columns: 1fr; + gap: 2rem; + width: 100%; } + .category-archive .subcategory-section .post-grid .post-card { + min-width: 0; } + @media (min-width: 768px) { + .category-archive .subcategory-section .post-grid { + grid-template-columns: repeat(2, 1fr); } } + @media (min-width: 1024px) { + .category-archive .subcategory-section .post-grid { + grid-template-columns: repeat(3, 1fr); } } + .category-archive .subcategory-section .category-section-footer { + margin-top: 3rem; + display: flex; + justify-content: center; } + +/* +.category-header { + width: 100%; + margin-bottom: 3vh; + + &__back { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-size: 0.9rem; + text-decoration: none; + margin-bottom: 1rem; + + i { + font-size: 1.1rem; + } + } + + &__title { + font-size: 2rem; + margin-bottom: 0.5rem; + + @media ($tablet) { + font-size: 2.5rem; + } + } + + &__description { + color: $less-dark-gray; + } +} + +// Sub-category sections on parent category page +.subcategory-section { + width: 100%; + margin-bottom: 4vh; + + &__header { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 2vh; + border-bottom: 2px solid $publications; + padding-bottom: 0.5rem; + } + + &__title { + font-size: 1.5rem; + + a { + text-decoration: none; + } + } + + &__see-all { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-size: 0.9rem; + text-decoration: none; + white-space: nowrap; + } + + &__empty { + color: $less-less-light-gray; + font-style: italic; + } +} + +// Post grid +.post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + width: 100%; + + @media ($tablet) { + grid-template-columns: repeat(2, 1fr); + } + + @media ($desktop) { + grid-template-columns: repeat(3, 1fr); + } +} + +// Post card +.post-card { + background-color: white; + display: flex; + flex-direction: column; + overflow: hidden; + + &__image { + width: 100%; + aspect-ratio: 4 / 3; + overflow: hidden; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + } + + &__content { + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.4rem; + } + + &__title { + font-size: 1.1rem; + line-height: 1.3; + + a { + text-decoration: none; + } + } + + &__subtitle { + font-size: 0.9rem; + color: $less-dark-gray; + font-style: italic; + } + + &__date { + font-size: 0.8rem; + color: $less-less-light-gray; + } + + &__authors { + font-size: 0.9rem; + display: flex; + flex-wrap: wrap; + gap: 0.2rem; + + .post-card__author { + &:not(:last-child)::after { + content: ','; + } + } + + &--external { + color: $less-dark-gray; + } + } + + &__role, + &__publisher, + &__journal { + font-size: 0.85rem; + color: $less-dark-gray; + } + + &__axes, + &__tags { + display: flex; + flex-wrap: wrap; + gap: 0.3rem; + } + + &__axe, + &__tag { + font-size: 0.75rem; + background-color: $light-gray; + padding: 0.15rem 0.5rem; + } + + &__axe { + border-left: 2px solid $publications; + } + + &__links { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.3rem; + } + + &__link { + font-size: 0.8rem; + + &--hal { + color: $publications; + } + } +} + +// Infinite scroll spinner +.scroll-spinner { + display: flex; + justify-content: center; + align-items: center; + gap: 0.4rem; + padding: 2rem 0; + + &__dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: $publications; + animation: scroll-spinner-bounce 1.2s infinite ease-in-out; + + &:nth-child(2) { + animation-delay: 0.2s; + } + + &:nth-child(3) { + animation-delay: 0.4s; + } + } +} + +@keyframes scroll-spinner-bounce { + 0%, 80%, 100% { + transform: scale(0.6); + opacity: 0.4; + } + 40% { + transform: scale(1); + opacity: 1; + } +} +*/ +.agenda-view-container { + display: none; + margin-top: 3rem; } + .agenda-view-container.is-active { + display: block; } + +.agenda-view-title { + font-family: NewsCycle; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + font-size: 1.2rem; } + .agenda-view-title::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + +.agenda-swiper-wrap { + display: flex; + align-items: center; + gap: 0.8rem; + margin-top: 2rem; } + +.agenda-swiper { + flex: 1; + overflow: hidden; } + +.agenda-swiper-prev, +.agenda-swiper-next { + flex-shrink: 0; + background: none; + border: none; + cursor: pointer; + font-size: 1.2rem; + padding: 0.4rem; + color: #1a1a1a; + line-height: 1; } + .agenda-swiper-prev:hover, + .agenda-swiper-next:hover { + color: black; } + .agenda-swiper-prev.swiper-button-disabled, + .agenda-swiper-next.swiper-button-disabled { + opacity: 0.25; + pointer-events: none; } + +.agenda-card { + display: flex; + flex-direction: column; + text-decoration: none; + color: inherit; + padding-bottom: 1.5rem; + border-bottom: 1px solid #eeeeee; + height: 100%; + position: relative; + transition: transform 0.2s ease-out; + margin-right: 0 !important; } + .agenda-card::before { + content: ''; + position: absolute; + top: 1.7rem; + left: 0; + right: 0; + height: 1px; + background-color: #cccccc; + z-index: 0; + width: calc(100% + 20px); } + @media (min-width: 768px) { + .agenda-card::before { + width: calc(100% + 24px); } } + @media (min-width: 1024px) { + .agenda-card::before { + width: calc(100% + 32px); } } + .agenda-card:hover { + transform: scale(0.98); } + .agenda-card__dates { + display: flex; + align-items: flex-start; + gap: 0.5rem; + margin-bottom: 1rem; } + .agenda-card__body { + flex: 1; } + .agenda-card__meta { + display: flex; + flex-wrap: wrap; + gap: 0.3rem 0.8rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.75rem; + text-transform: uppercase; + opacity: 0.65; + margin-bottom: 0.5rem; } + .agenda-card__title { + font-family: "Gelasio", serif; + font-size: 1rem; + line-height: 1.35; } + +.category--manifestations-scientifiques .agenda-card { + border-bottom-color: #7cc0c6; } + +.category--le-laboratoire .agenda-card { + border-bottom-color: #e0775d; } + +.category--publications-et-productions .agenda-card { + border-bottom-color: #46ae51; } + +.category--mediation-scientifique .agenda-card { + border-bottom-color: #e05680; } + +.category--ressources .agenda-card { + border-bottom-color: #bb8dd9; } + +.category--manifestations-scientifiques .agenda-date-box { + background: linear-gradient(to bottom, #eeeeee 60%, #7cc0c6); } + +.category--le-laboratoire .agenda-date-box { + background: linear-gradient(to bottom, #eeeeee 60%, #e0775d); } + +.category--publications-et-productions .agenda-date-box { + background: linear-gradient(to bottom, #eeeeee 60%, #46ae51); } + +.category--mediation-scientifique .agenda-date-box { + background: linear-gradient(to bottom, #eeeeee 60%, #e05680); } + +.category--ressources .agenda-date-box { + background: linear-gradient(to bottom, #eeeeee 60%, #bb8dd9); } + +.agenda-date-box { + position: relative; + z-index: 1; + background-color: #eeeeee; + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + text-align: center; + padding: 0.4rem 0.55rem; + flex-shrink: 0; + min-width: 2.6rem; } + .agenda-date-box .agenda-date-day { + display: block; + font-size: 1.3rem; + line-height: 1; } + .agenda-date-box .agenda-date-month { + display: block; + font-size: 0.65rem; + margin-top: 2px; } + .agenda-date-box .agenda-date-year { + display: block; + font-size: 0.6rem; + opacity: 0.65; } + .agenda-date-box sup { + font-size: 0.7rem; } + +.agenda-date-arrow { + font-size: 0.85rem; + opacity: 0.5; } + +.filters-bar { + margin-top: 2.5rem; + display: flex; + align-items: center; + gap: 0.8rem; + flex-wrap: wrap; + position: relative; + padding-bottom: 0.6rem; } + .filters-bar::after { + content: ''; + position: absolute; + bottom: 0; + left: -5vw; + width: calc(100% + 10vw); + height: 1px; + background-color: #eeeeee; } + @media (min-width: 768px) { + .filters-bar::after { + left: -3vw; + width: calc(100% + 6vw); } } + +.filters-toggle-btn { + display: flex; + align-items: center; + gap: 0.5rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.9rem; + text-transform: uppercase; + background-color: #eeeeee; + border: unset; + cursor: pointer; + padding: 0.4rem 0.8rem; + color: #1a1a1a; + flex-shrink: 0; } + .filters-toggle-btn .filters-chevron { + transition: transform 0.2s; } + .filters-toggle-btn.is-open .filters-chevron { + transform: rotate(180deg); } + .filters-toggle-btn:hover { + background-color: #cccccc; } + +.filters-active-chips { + display: flex; + align-items: center; + gap: 0.4rem; + flex-wrap: wrap; } + +.filter-chip { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.8rem; + text-transform: uppercase; + background-color: #eeeeee; + padding: 0.3rem 0.5rem; + text-decoration: none; + color: #1a1a1a; + border: none; + cursor: pointer; } + .filter-chip i { + font-size: 0.7rem; + opacity: 0.5; } + .filter-chip:hover { + background-color: #cccccc; } + .filter-chip:hover i { + opacity: 1; } + +.category-filters { + font-size: 0.9rem; + font-family: "NewsCycle", sans-serif; + margin-top: 0; + display: none; + padding: 2rem 5vw; + padding-bottom: 0.5rem !important; + margin-left: -5vw; + width: calc(100% + 10vw); + justify-content: space-between; + background-color: #fcfcfc; + border-top: solid 1px #eeeeee; + border-bottom: solid 1px #eeeeee; + flex-direction: column; } + .category-filters.is-open { + display: flex; } + .category-filters > div { + margin-top: 1.2rem; + margin-bottom: 1.2rem; + flex: auto; } + .category-filters > div.filtre-rubrique { + flex: 2; } + .category-filters > div.filtre-categorie { + flex: 1; } + .category-filters > div.filtre-date { + flex: 1; } + .category-filters > div.filtre-axe { + flex: 1; } + .category-filters > div:first-of-type { + margin-top: 0; + padding-left: 0; } + .category-filters > div:last-of-type { + margin-bottom: 0; } + .category-filters > div:not(:last-of-type) { + padding-bottom: 1.8rem; + border-bottom: solid 1px #cccccc; } + .category-filters > div .filter-section-header { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 2rem; } + .category-filters > div .section-title { + text-transform: uppercase; + position: relative; + display: inline-block; } + .category-filters > div .section-title::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + .category-filters > div ul { + display: flex; + justify-content: start; + flex-wrap: wrap; + align-items: start; + gap: 0.5rem 0.2rem; + list-style: none; + margin: 0; + padding: 0; } + .category-filters > div ul li { + font-size: 1rem; + cursor: pointer; + padding: 0.4rem 0.6rem; } + .category-filters > div ul li a { + text-decoration: none; } + .category-filters > div ul li:hover, .category-filters > div ul li.is-active { + background-color: #cccccc; } + .category-filters .filter-dd { + position: relative; + margin-top: 0; + display: inline-block; } + .category-filters .filter-dd .dd-title { + padding: 0.3rem 0.5rem; + display: flex; + align-items: center; + gap: 0.4rem; + cursor: pointer; } + .category-filters .filter-dd .dd-title > p, + .category-filters .filter-dd .dd-title > i { + font-size: 0.9rem; } + .category-filters .filter-dd .dd-title > i { + transition: transform 0.2s; } + .category-filters .filter-dd.is-open .dd-title > i { + transform: rotate(180deg); } + .category-filters .filter-dd.is-active .dd-title { + background-color: #cccccc; } + .category-filters .filter-dd .dd-content { + position: absolute; + padding: 0; + z-index: 5; + background: white; + border: 1px solid #cccccc; + min-width: 100%; } + .category-filters .filter-dd .dd-content ul { + gap: 0.3rem; + padding: 0.5rem; } + .category-filters .filter-dd .dd-content ul li { + padding: 0.4rem; + white-space: nowrap; + background-color: #eeeeee; } + .category-filters .filter-dd .dd-content#filter-axe-popover { + right: auto; + left: 0; + min-width: 280px; + max-width: min(420px, 90vw); } + .category-filters .filter-dd .dd-content#filter-axe-popover ul { + flex-direction: column; + padding: 0; + gap: 0; } + .category-filters .filter-dd .dd-content#filter-axe-popover ul li { + width: 100%; + margin: 0; + white-space: normal; } + .category-filters .filter-dd .dd-content#filter-axe-popover ul li:nth-of-type(odd) { + background-color: white; } + .category-filters .filter-dd .dd-content#filter-axe-popover ul li:first-of-type { + background-color: #cccccc; } + .category-filters .filter-dd .dd-content#filter-axe-popover ul li.dd-axe-group-label { + font-size: 0.72rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: #3e3e3e; + background-color: #eeeeee !important; + padding: 0.5rem 0.4rem; + cursor: default; + pointer-events: none; + border-top: solid 1px #cccccc; } + .category-filters .filter-dd .dd-content#filter-axe-popover ul li.dd-axe-group-label:first-child { + margin-top: 0; } + .category-filters .filter-dd .dd-date-fields { + padding: 0.6rem; + padding-top: 1rem; + display: flex; + flex-direction: column; + gap: 0.4rem; + border-top: 1px solid #cccccc; } + .category-filters .filter-dd .dd-date-fields label { + display: flex; + align-items: center; + gap: 0.4rem; + font-size: 0.85rem; + text-transform: uppercase; } + .category-filters .filter-dd .dd-date-fields input[type="date"] { + padding: 0.2rem 0.4rem; + border: 1px solid #cccccc; + font-size: 0.85rem; + font-family: "NewsCycle", sans-serif; } + .category-filters .filter-dd .dd-date-apply { + margin-top: 0.6rem; + width: 100%; + padding: 0.3rem; + border: none; + font-size: 0.85rem; + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + cursor: pointer; } + .category-filters .membres-search-input.is-active { + background-color: #cccccc; } + .category-filters .date-reset-link { + display: block; + font-size: 0.75rem; + text-transform: uppercase; + text-decoration: none; + color: inherit; + opacity: 0.6; + padding: 0 0.5rem 0.4rem; } + .category-filters .date-reset-link:hover { + opacity: 1; } + @media (min-width: 768px) { + .category-filters { + padding: 2rem 3vw; + margin-left: -3vw; + width: calc(100% + 6vw); + flex-direction: row; } + .category-filters > div { + margin-top: 0; + margin-bottom: 0; + padding-left: 1.5rem; } + .category-filters > div:not(:last-of-type) { + padding-right: 0.8rem; + border-bottom: unset; + border-right: solid 1px #cccccc; } } + +.article { + margin-top: 0; + width: 100%; } + .article .category-header-top { + display: flex; + flex-direction: column; + align-items: start; + justify-content: space-between; } + .article .category-header-top .breadcrumb { + font-size: 0.85rem; + margin-bottom: 1.5rem; + text-transform: uppercase; + line-height: 1.3; } + .article .category-header-top .breadcrumb__separator { + margin: 0 0.4rem; } + @media (min-width: 768px) { + .article .category-header-top { + flex-direction: row; } } + .article h2 { + font-family: Gelasio; + font-weight: normal; + font-size: 1.8rem; + position: relative; + display: inline-block; + margin-top: 2rem; + margin-bottom: 2rem; } + .article h2::after { + content: ''; + display: block; + position: absolute; + height: 5px; + width: 100%; + left: 0; + z-index: 2; } + .article h2 p { + line-height: 1.3; } + .article h2 p:last-of-type { + margin-top: 0.3rem; + margin-bottom: 0.3rem; } + .article h2 p + p { + font-size: 1.6rem; } + @media (min-width: 768px) { + .article h2 p { + font-size: 2.2rem !important; } + .article h2 p + p { + font-size: 1.6rem !important; } + .article h2::after { + bottom: -0.4rem; } } + .article.category--le-laboratoire h2::after { + background: linear-gradient(to bottom, transparent 0%, #e0775d 30%); } + .article.category--manifestations-scientifiques h2::after { + background: linear-gradient(to bottom, transparent 0%, #7cc0c6 30%); } + .article.category--publications-et-productions h2::after { + background: linear-gradient(to bottom, transparent 0%, #46ae51 30%); } + .article.category--mediation-scientifique h2::after { + background: linear-gradient(to bottom, transparent 0%, #e05680 30%); } + .article.category--ressources h2::after { + background: linear-gradient(to bottom, transparent 0%, #bb8dd9 30%); } + .article .article-type { + display: inline-block; + margin-top: 2rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + text-transform: uppercase; + background-color: #eeeeee; + padding: 0.2rem 0.6rem; } + .article .maj { + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem !important; + color: #3e3e3e; + margin-top: 1rem; + margin-bottom: 2.5rem; + text-transform: uppercase; } + .article .imgs { + width: 100%; + margin-bottom: 3rem; } + .article .imgs figure { + width: 100%; } + .article .imgs figure img { + width: 100%; + height: auto; } + .article .imgs figure figcaption { + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + color: #3e3e3e; + margin-top: 0.5rem; + font-style: italic; } + @media (min-width: 768px) { + .article .imgs figure { + width: 50%; } } + .article .imgs--swiper { + display: flex; + align-items: center; + gap: 0.5rem; } + @media (min-width: 768px) { + .article .imgs--swiper { + width: 50%; } } + .article .imgs--swiper .swiper { + flex: 1; + min-width: 0; } + .article .imgs--swiper figure { + width: 100%; } + .article .imgs--swiper .swiper-pagination { + position: static; + margin-top: 0.5rem; + text-align: center; } + .article .imgs--swiper .swiper-pagination-bullet-active { + background-color: #3e3e3e !important; } + .article .article-content { + display: flex; + flex-direction: column-reverse; + gap: 3rem; } + @media (min-width: 768px) { + .article .article-content { + flex-direction: row; } } + .article .sidebar { + background-color: #fcfcfc; } + @media (min-width: 768px) { + .article .sidebar { + width: 25%; + padding: 0.8rem; + flex-shrink: 0; } } + .article .sidebar .sidebar-container { + position: sticky; + top: 6rem; + display: flex; + flex-direction: column; + gap: 1.5rem; } + .article .sidebar .sidebar-section { + display: flex; + flex-direction: column; + gap: 0.8rem; + line-height: 1.4; } + .article .sidebar .sidebar-section p { + margin: 0; } + .article .sidebar .sidebar-section.reference-bibliographique { + display: inline-block; } + .article .sidebar .sidebar-section.reference-bibliographique em, .article .sidebar .sidebar-section.reference-bibliographique i { + font-style: italic; } + .article .sidebar .sidebar-section.reference-bibliographique strong { + font-weight: bold; } + .article .sidebar p { + font-family: "NewsCycle", sans-serif; } + .article .sidebar .link-button { + width: fit-content; } + @media (min-width: 768px) { + .article .sidebar .imgs--portrait { + width: calc(100% + 1.6rem); + margin-left: -0.8rem; + margin-right: -0.8rem; } } + .article .sidebar .imgs--portrait .sidebar-portrait { + width: 100%; } + .article .sidebar .sidebar-portrait { + width: 100%; } + .article .sidebar .sidebar-portrait img { + width: 100%; + height: auto; } + .article .sidebar .sidebar-portrait figcaption { + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + color: #3e3e3e; + margin-top: 0.5rem; + font-style: italic; } + .article .main-content-text { + margin-top: 1rem; + flex: 1; + min-height: unset; + /* Affichage posts newsletter */ } + .article .main-content-text:has(table[role=presentation]) p:not(table[role=presentation] p):not(.maj) { + display: none; } + .article .main-content-text:has(table[role=presentation]) table[role=presentation] p { + margin: unset; } + .article .main-content-text:has(table[role=presentation]) table td { + vertical-align: top; } + .article .main-content-text:has(table[role=presentation]) table br { + display: none; } + .article .main-content-text > *:not(.article-field) { + font-size: 1.25rem; } + .article .main-content-text a { + text-decoration: underline; } + .article .main-content-text p { + margin-bottom: 1rem; + line-height: 1.4; } + .article .main-content-text p strong { + font-weight: bold; } + .article .main-content-text p em { + font-style: italic; } + .article .main-content-text p:first-child { + margin-top: 0 !important; } + .article .main-content-text ul, .article .main-content-text ol { + line-height: 1.4; + padding-left: 0.8rem; } + .article .main-content-text ul { + list-style: inside "· "; } + .article .main-content-text ol { + list-style: inside decimal; } + .article .main-content-text blockquote { + padding-left: 1rem; + margin-left: 1.5rem; + border-left: solid 1px #eeeeee; } + .article .main-content-text p:first-of-type + .mots-cles { + margin-top: 1rem; } + .article .main-content-text .article-field { + font-size: 0.9rem !important; + line-height: 1.4 !important; + margin-bottom: 1.5rem !important; } + .article .main-content-text .article-field i { + font-style: italic; } + .article .main-content-text .mots-cles { + margin-top: 4rem; } + .article .main-content-text .mots-cles ~ .mots-cles { + margin-top: 1rem; } + .article .main-content-text .canal-u-embeds, + .article .main-content-text .youtube-embeds { + margin-top: 2rem; + display: flex; + flex-direction: column; + gap: 1.5rem; } + .article .main-content-text .canal-u-embed, + .article .main-content-text .video-embed { + position: relative; + width: 100%; + padding-bottom: 56.25%; + height: 0; } + .article .main-content-text .canal-u-embed iframe, + .article .main-content-text .video-embed iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; } + .article .inline-title { + text-transform: uppercase; + position: relative; } + .article .inline-title::after { + content: ''; + width: 100%; + height: 10px; + bottom: -1px; + left: 0; + position: absolute; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); } + .article .related-posts, + .article .seances-section { + margin-top: 5rem; } + .article .related-posts h3, + .article .seances-section h3 { + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; } + .article .related-posts h3::after, + .article .seances-section h3::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + .article .related-posts .post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; } + .article .related-posts .post-grid .post-card { + min-width: 0; } + .article .related-posts .post-grid .post-card a { + text-decoration: none; } + .article .related-posts .post-grid .post-card a h2::after { + display: none; } + @media (min-width: 768px) { + .article .related-posts .post-grid { + grid-template-columns: repeat(2, 1fr); } } + .article .seances-list { + display: flex; + flex-direction: column; + gap: 1.5rem; } + .article .seance-header { + display: flex; + align-items: stretch; + gap: 1.2rem; + padding: 0.1rem; + background-color: #eeeeee; + cursor: pointer; + transition: background-color 0.15s; } + .article .seance-header:hover { + background-color: #cccccc; } + @media (min-width: 768px) { + .article .seance-header { + padding: 0.8rem; } } + .article .seance-date { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 3rem; + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + line-height: 1.2; + padding: 0.4rem 0; + background: linear-gradient(to bottom, #fcfcfc 60%, #7cc0c6); } + .article .seance-date__day { + font-size: 1.4rem; } + .article .seance-date__month { + font-size: 0.85rem; } + .article .seance-date__year { + font-size: 0.75rem; } + .article .seance-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.2rem; } + .article .seance-info .seance-title { + font-family: "Gelasio", serif; + font-size: 1.15rem; + line-height: 1.2; + text-decoration: none; + padding: 0.6rem 0; } + @media (min-width: 768px) { + .article .seance-info .seance-title { + padding: unset; } } + .article .seance-info .seance-intervenants { + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + margin-top: 0.1rem; + color: black; } + .article .seance-info .seance-intervenants a { + text-decoration: none; } + .article .seance-info .seance-intervenants a:hover { + text-decoration: underline; } + .article .seance-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + align-self: center; + margin-right: 0.5rem; } + .article .seance-item.is-open .seance-chevron { + transform: rotate(180deg); } + .article .seance-content { + padding: 1rem 1rem 1rem 1rem; + background-color: #fcfcfc; + font-family: "NewsCycle", sans-serif; + font-size: 0.9rem; + line-height: 1.4; } + .article .seance-content .seance-content-infos { + display: flex; + width: 100%; + justify-content: space-between; + margin-bottom: 1rem; } + .article .seance-content .seance-content-infos > .seance-content-lieu { + text-align: right; } + .article .seance-content .seance-content-infos > .seance-content-lieu > p { + margin: 0; } + .article .seance-content p { + margin-bottom: 0.6rem; } + .article .seance-content .seance-images { + margin-top: 1.5rem; + margin-bottom: 0; } + @media (min-width: 768px) { + .article .seance-content .seance-images figure { + width: 50%; } } + .article .seance-content .seance-extras { + display: flex; + flex-direction: column; + align-items: start; + gap: 0.8rem; + margin-top: 1.5rem; } + .article .seance-content .seance-related { + margin-top: 1.5rem; } + .article .seance-content .seance-related h4 { + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + margin-bottom: 1rem; } + .article .seance-content .seance-related .post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; } + .article .seance-content .seance-related .post-grid .post-card { + min-width: 0; } + .article .seance-content .seance-related .post-grid .post-card a { + text-decoration: none; } + .article .seance-content .seance-related .post-grid .post-card a h2::after { + display: none; } + @media (min-width: 768px) { + .article .seance-content .seance-related .post-grid { + grid-template-columns: repeat(2, 1fr); } } + @media (min-width: 768px) { + .article .seance-content { + padding: 1rem 1rem 1rem 5.5rem; } } + +.author-header { + display: flex; + gap: 2rem; + align-items: flex-start; + margin: 2rem 0; } + +.author-avatar { + flex-shrink: 0; } + .author-avatar img { + width: 140px; + height: 140px; + object-fit: cover; } + @media (min-width: 768px) { + .author-avatar img { + width: 180px; + height: 180px; } } + +.author-identity h2 { + margin-top: 0; } +.author-identity + .author-bio { + margin-top: 2rem; } + +.author-role { + font-family: "NewsCycle", sans-serif; + margin-top: 0.4rem; + opacity: 0.85; + line-height: 1.4; } + +.author-bio { + margin-bottom: 1.5rem; + line-height: 1.6; } + .author-bio > p { + margin: 0.8rem 0; } + .author-bio hr { + display: none; } + .author-bio h3 { + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; } + +.author-resume-these { + margin-top: 1rem; + line-height: 1.6; } + .author-resume-these > p { + margin: 0.8rem 0; } + +.author-cat-footer { + display: flex; + justify-content: center; + margin-top: 1.5rem; + padding-top: 1rem; + border-top: 1px solid #eeeeee; } + +.author-titre-these { + font-family: "Gelasio", serif; + font-size: 1.4rem; + line-height: 1.3 !important; + margin-bottom: 0.8rem; } + +.these-inline-title { + text-transform: uppercase; } + +.domaines-autres > p:first-of-type, +.recherches-en-cours > p:first-of-type { + margin-top: 0.4rem; } + +.author-posts-section { + display: flex; + flex-direction: column; + gap: 1rem; + margin-top: 5rem; } + +.author-posts-header { + display: flex; + align-items: center; + gap: 1.2rem; + padding: 0.8rem; + background-color: #eeeeee; + cursor: pointer; + transition: background-color 0.15s; + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; } + .author-posts-header:hover { + background-color: #cccccc; } + +.author-posts-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + margin-left: auto; } + .author-posts-item.is-open .author-posts-chevron { + transform: rotate(180deg); } + +.author-posts-content { + padding: 1rem; + background-color: #fcfcfc; + position: relative; + padding-bottom: 3rem; } + .author-posts-content::after { + content: ''; + display: block; + position: absolute; + height: 30px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; } + .author-posts-content article a { + text-decoration: none !important; } + .author-posts-content article a h2 { + margin-top: 0 !important; } + +.author-post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + width: 100%; } + .author-post-grid h2::after { + display: none; } + @media (min-width: 768px) { + .author-post-grid { + grid-template-columns: repeat(2, 1fr); } } + +.membres-section { + margin-top: 4rem; + display: flex; + flex-direction: column; + gap: 1rem; } + +.membres-item.is-open { + position: relative; + padding-bottom: 1rem; } + .membres-item.is-open::after { + content: ''; + display: block; + position: absolute; + height: 30px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; } + +.membres-header { + display: flex; + align-items: center; + gap: 1.2rem; + padding: 0.8rem; + background-color: #eeeeee; + cursor: pointer; + transition: background-color 0.15s; + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; } + .membres-header:hover { + background-color: #cccccc; } + +.membres-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + margin-left: auto; } + .membres-item.is-open .membres-chevron { + transform: rotate(180deg); } + +.membres-sort-chevron { + font-size: 0.8rem; + opacity: 0.3; + transition: transform 0.2s, opacity 0.15s; + vertical-align: middle; + margin-left: 0.3rem; } + +.membres-content { + padding: 1rem; + background-color: #fcfcfc; + font-family: "NewsCycle", sans-serif; + font-size: 0.9rem; + line-height: 1.6; } + +.membres-table { + width: 100%; + table-layout: fixed; + border-collapse: collapse; + font-size: 0.9rem; } + .membres-table th { + width: 33.333%; + text-align: left; + text-transform: uppercase; + font-family: "NewsCycle", sans-serif; + font-weight: normal; + font-size: 0.8rem; + padding: 0.5rem 1.5rem 0.5rem 0.5rem; + background-color: #eeeeee; + cursor: pointer; + user-select: none; + white-space: nowrap; } + .membres-table th:hover .membres-sort-chevron { + opacity: 0.7; } + .membres-table th.sort-asc .membres-sort-chevron { + opacity: 1; + transform: rotate(180deg); } + .membres-table th.sort-desc .membres-sort-chevron { + opacity: 1; + transform: rotate(0deg); } + .membres-table tbody tr { + cursor: pointer; + transition: background-color 0.15s; + background-color: white; } + .membres-table tbody tr.is-even-row { + background-color: #eeeeee; } + .membres-table tbody tr:last-child { + border-bottom: none; } + .membres-table tbody tr:hover { + background-color: #cccccc; } + .membres-table td { + padding: 0.6rem 1.5rem 0.6rem 0.5rem; + vertical-align: top; + line-height: 1.4; } + .membres-table td:first-child { + white-space: nowrap; } + .membres-table td a { + text-decoration: none; } + +#membre-popover { + position: fixed; + z-index: 9999; + background: white; + pointer-events: none; + max-width: 380px; + min-width: 180px; + padding: 1rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + opacity: 0; + transition: opacity 0.1s; + border: solid 1px #eeeeee; } + #membre-popover.is-visible { + opacity: 1; } + #membre-popover .membre-popover-inner { + display: flex; + gap: 1rem; + align-items: flex-start; } + #membre-popover .membre-popover-pic { + width: 80px; + height: 80px; + object-fit: cover; + object-position: top; + flex-shrink: 0; } + #membre-popover .membre-popover-name { + font-size: 1.1rem; + font-weight: normal; + margin: 0 0 0.3rem; } + #membre-popover .membre-popover-status { + text-transform: uppercase; + color: #3e3e3e; + margin: 0 0 0.6rem; } + #membre-popover .membre-popover-domaines { + font-size: 0.8rem; + margin: 0 0 0.3rem; + line-height: 1.5; } + #membre-popover .membre-popover-autres { + font-size: 0.8rem; + margin: 0; + line-height: 1.5; + color: #3e3e3e; } + +.filtre-role { + flex: 2; } + +.filtre-recherche { + flex: 1; } + +.membres-search-input { + width: 100%; + border: none; + padding: 0.6rem 0.8rem; + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + background-color: #eeeeee; + outline: none; } + .membres-search-input::placeholder { + color: #3e3e3e; } + .membres-search-input:focus { + background-color: #cccccc; } + @media (min-width: 1024px) { + .membres-search-input { + width: 50%; } } + +.labo-images { + display: flex; + flex-wrap: wrap; + gap: 1.5rem; + margin-bottom: 3rem; } + +.labo-image { + flex: 0 0 auto; + width: 100%; + margin-top: 2rem; } + .labo-image img { + width: 100%; + height: auto; + display: block; } + .labo-image figcaption { + font-family: "NewsCycle", sans-serif; + font-size: 0.85rem; + color: #3e3e3e; + margin-top: 0.5rem; } + @media (min-width: 768px) { + .labo-image { + width: calc(50% - 0.75rem); } } + +.labo-section { + margin-top: 5rem; } + .labo-section > h3 { + font-family: "NewsCycle", sans-serif; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; } + .labo-section > h3::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; + bottom: -10px; } + +.labo-dropdowns { + display: flex; + flex-direction: column; + gap: 1rem; + margin-top: 5rem; } + .labo-section .labo-dropdowns { + margin-top: 0; } + +.labo-dropdown-item.is-open { + position: relative; + padding-bottom: 1rem; } + .labo-dropdown-item.is-open::after { + content: ''; + display: block; + position: absolute; + height: 30px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 50%, #f7ff29 100%); + z-index: 2; + pointer-events: none; } + +.labo-dropdown-header { + display: flex; + align-items: center; + gap: 1.2rem; + padding: 0.8rem; + background-color: #eeeeee; + cursor: pointer; + transition: background-color 0.15s; + font-family: "NewsCycle", sans-serif; + font-size: inherit; + font-weight: normal; + text-transform: uppercase; + margin: 0; } + .labo-dropdown-header:hover { + background-color: #cccccc; } + +.labo-dropdown-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + margin-left: auto; } + .labo-dropdown-item.is-open .labo-dropdown-chevron { + transform: rotate(180deg); } + +.labo-dropdown-content { + padding: 1rem 1rem 1rem 1rem; + background-color: #fcfcfc; + font-family: "NewsCycle", sans-serif; + font-size: 0.9rem; + line-height: 1.6; } + .labo-dropdown-content p { + margin-bottom: 0.6rem; } + .labo-dropdown-content p + ul { + margin-top: -1rem; } + .labo-dropdown-content ul, .labo-dropdown-content ol { + padding-left: 1.2rem; + margin-bottom: 0.6rem; } + +.labo-axes-list { + list-style: none; + padding: 0; + margin: 0; } + .labo-axes-list li { + padding: 0.5rem 0; + border-bottom: 1px solid #eeeeee; } + .labo-axes-list li:last-child { + border-bottom: none; } + .labo-axes-list li a { + text-decoration: none; } + .labo-axes-list li a:hover { + text-decoration: underline; } + +.labo-bibliotheques { + font-family: "NewsCycle", sans-serif; + font-size: 0.9rem; + line-height: 1.6; } + .labo-bibliotheques p { + margin-bottom: 0.8rem; } + .labo-bibliotheques p + ul { + margin-top: -1rem; + margin-bottom: 0.6rem; } + .labo-bibliotheques a { + text-decoration: underline; } + +.programme-description { + margin-bottom: 1.5rem; } + .programme-description p { + margin-bottom: 0.6rem; } + .programme-description ul, .programme-description ol { + padding-left: 1.2rem; } + .programme-description a { + text-decoration: underline; } + +.programme-link { + padding-top: 0.5rem; + border-top: 1px solid #eeeeee; } + +.search-page-form { + margin-top: 2rem; + margin-bottom: 3rem; } + @media (min-width: 1024px) { + .search-page-form { + width: 50%; } } + .search-page-form .search-panel__desc { + margin-bottom: 1.3rem; } + +.search-page-form + #category-filters { + margin-top: 0; } + +.author-results { + margin-bottom: 3rem; } + .author-results__title { + font-family: "NewsCycle", sans-serif; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1.2rem; + color: #3e3e3e; } + +.taxonomy-results { + margin-bottom: 3rem; } + .taxonomy-results__title { + font-family: "NewsCycle", sans-serif; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1.2rem; + color: #3e3e3e; } + .taxonomy-results__list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-wrap: wrap; + gap: 0.6rem; } + .taxonomy-results__link { + display: block; + padding: 0.5rem 1rem; + border: 1px solid #eeeeee; + text-decoration: none; + transition: border-color 0.15s; } + .taxonomy-results__link:hover { + border-color: #1a1a1a; } + .taxonomy-results__name { + font-family: "Gelasio", serif; + font-size: 0.95rem; + line-height: 1.3; } + .taxonomy-results__meta { + display: block; + font-family: "NewsCycle", sans-serif; + font-size: 0.7rem; + text-transform: uppercase; + color: #3e3e3e; + margin-top: 0.15rem; } + +.author-cards-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.5rem; } + @media (min-width: 768px) { + .author-cards-grid { + grid-template-columns: repeat(3, 1fr); } } + @media (min-width: 1024px) { + .author-cards-grid { + grid-template-columns: repeat(6, 1fr); } } + +.author-card { + border-bottom: solid 1px #e0775d; + padding-bottom: 0.4rem; } + .author-card__visual { + display: flex; + align-items: center; + justify-content: center; + height: 14vh; + padding: 0.7rem; + background-color: #f7dcd5; + overflow: hidden; + text-decoration: none; } + .author-card__visual img { + max-height: 100%; + max-width: 100%; + width: auto; + height: auto; + transition: transform 0.2s ease-out; } + .author-card__visual:hover img { + transform: scale(0.98); } + .author-card__initials { + font-family: "Gelasio", serif; + font-size: 2rem; + color: #e0775d; + user-select: none; } + .author-card__info { + padding-top: 0.5rem; } + .author-card__name { + font-family: "Gelasio", serif; + font-size: 1rem; + font-weight: normal; + line-height: 1.2; + margin-bottom: 0.3rem; } + .author-card__name a { + text-decoration: none; } + .author-card__role, .author-card__affiliation { + font-family: "NewsCycle", sans-serif; + font-size: 0.75rem; + text-transform: uppercase; + line-height: 1.3; + color: #3e3e3e; + margin: 0; } + +/* +Theme Name: Thalim +Author: Valentin Le Moign +Version: 1.0 +*/ + +/*# sourceMappingURL=style.css.map */ diff --git a/functions.php b/functions.php new file mode 100755 index 0000000..2be3aea --- /dev/null +++ b/functions.php @@ -0,0 +1,1375 @@ +user_email ) ) ); + $response = wp_remote_head( + 'https://www.gravatar.com/avatar/' . $hash . '?d=404', + [ 'timeout' => 3 ] + ); + + if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) { + $url = 'https://www.gravatar.com/avatar/' . $hash . '?s=300'; + set_transient( $cache_key, $url, WEEK_IN_SECONDS ); + return $url; + } + + // No Gravatar — cache the negative result too + set_transient( $cache_key, '', WEEK_IN_SECONDS ); + return ''; +} + +function thalim_en_url( string $url ): string { + if ( thalim_current_language() !== 'en' ) return $url; + $home = rtrim( home_url(), '/' ); + $path = substr( $url, strlen( $home ) ); + if ( str_starts_with( $path, '/en/' ) || $path === '/en' ) return $url; + return $home . '/en' . $path; +} + +// Auto-prefix all WP-generated internal URLs in EN mode. +// Safe on admin: THALIM_ORIGINAL_URI starts with /wp-admin/ there, +// so thalim_current_language() returns 'fr' and thalim_en_url() is a no-op. +add_filter( 'term_link', 'thalim_en_url' ); // get_term_link(), get_category_link(), get_tag_link() +add_filter( 'post_link', 'thalim_en_url' ); // get_permalink() on regular posts +add_filter( 'page_link', 'thalim_en_url' ); // get_permalink() on pages +add_filter( 'post_type_link', 'thalim_en_url' ); // get_permalink() on CPTs +add_filter( 'author_link', 'thalim_en_url' ); // get_author_posts_url() + +// Return the translated category name if viewing in EN and titre_anglais is set. +// Accepts a WP_Term, Timber\Term, term_id (int), or a name string + term_id. +function thalim_cat_name( $cat, string $lang = null ): string { + if ( $lang === null ) $lang = thalim_current_language(); + if ( is_object( $cat ) ) { + $term_id = $cat->term_id ?? ( $cat->id ?? 0 ); + $fallback = $cat->name; + } elseif ( is_numeric( $cat ) ) { + $term_id = (int) $cat; + $term = get_term( $term_id, 'category' ); + $fallback = $term && ! is_wp_error( $term ) ? $term->name : (string) $cat; + } else { + return (string) $cat; + } + if ( $lang !== 'en' ) return $fallback; + $en = get_term_meta( $term_id, 'titre_anglais', true ); + return ( $en !== '' && $en !== false ) ? $en : $fallback; +} + +// Register bilingual and en_url as Twig filters +add_filter( 'timber/twig', function ( $twig ) { + $twig->addFilter( new \Twig\TwigFilter( 'bilingual', 'thalim_bilingual' ) ); + $twig->addFilter( new \Twig\TwigFilter( 'en_url', 'thalim_en_url' ) ); + $twig->addFilter( new \Twig\TwigFilter( 'cat_name', 'thalim_cat_name' ) ); + return $twig; +} ); + +// Language switcher data (replaces pll_the_languages) +// Output matches the structure header.twig expects: slug, url, current_lang +function thalim_language_switcher(): array { + $uri = THALIM_ORIGINAL_URI; + $path = parse_url( $uri, PHP_URL_PATH ) ?? '/'; + $query = ( $q = parse_url( $uri, PHP_URL_QUERY ) ) ? '?' . $q : ''; + $is_en = thalim_current_language() === 'en'; + + $fr_path = $is_en ? ( substr( $path, 3 ) ?: '/' ) : $path; + $en_path = $is_en ? $path : '/en' . $path; + + return [ + 'fr' => [ 'slug' => 'fr', 'url' => home_url( $fr_path ) . $query, 'current_lang' => ! $is_en ], + 'en' => [ 'slug' => 'en', 'url' => home_url( $en_path ) . $query, 'current_lang' => $is_en ], + ]; +} + +function theme_enqueue_assets() { + wp_enqueue_style( + 'main-styles', + get_template_directory_uri() . '/css/style.css', + [], + filemtime(get_template_directory() . '/css/style.css') + ); + + wp_enqueue_style( + 'iconoir', + 'https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css', + [], + null + ); + + wp_enqueue_script( + 'overlay', + get_template_directory_uri() . '/js/overlay.js', + [], + filemtime(get_template_directory() . '/js/overlay.js'), + true + ); + + if (is_front_page() || is_404()) { + wp_enqueue_script( + 'animatedLogo', + get_template_directory_uri() . '/js/animatedLogo.js', + [], + filemtime(get_template_directory() . '/js/animatedLogo.js'), + true + ); + wp_add_inline_script( 'animatedLogo', 'var themeDirURI = ' . wp_json_encode( get_template_directory_uri() ) . ';', 'before' ); + } + + if ( is_category() ) { + wp_enqueue_style( 'swiper', 'https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css', [], null ); + wp_enqueue_script( 'swiper', 'https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js', [], null, true ); + wp_enqueue_script( 'agendaView', get_template_directory_uri() . '/js/agendaView.js', ['swiper'], filemtime( get_template_directory() . '/js/agendaView.js' ), true ); + wp_localize_script( 'agendaView', 'agendaViewData', [ + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'load_more_posts' ), + 'lang' => thalim_current_language(), + ]); + } + + if (is_front_page()) { + wp_enqueue_script( + 'coloredWordsHero', + get_template_directory_uri() . '/js/coloredWordsHero.js', + [], + filemtime(get_template_directory() . '/js/coloredWordsHero.js'), + true + ); + + wp_enqueue_style( + 'swiper', + 'https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css', + [], + null + ); + + wp_enqueue_script( + 'swiper', + 'https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js', + [], + null, + true + ); + + wp_enqueue_script( + 'annoncesSwiper', + get_template_directory_uri() . '/js/annoncesSwiper.js', + ['swiper'], + filemtime(get_template_directory() . '/js/annoncesSwiper.js'), + true + ); + + wp_enqueue_script( + 'messageLabo', + get_template_directory_uri() . '/js/messageLabo.js', + [], + filemtime(get_template_directory() . '/js/messageLabo.js'), + true + ); + + wp_enqueue_script( + 'keywordCloud', + get_template_directory_uri() . '/js/keywordCloud.js', + [], + filemtime(get_template_directory() . '/js/keywordCloud.js'), + true + ); + + wp_enqueue_script( + 'quickLinks', + get_template_directory_uri() . '/js/quickLinks.js', + [], + filemtime(get_template_directory() . '/js/quickLinks.js'), + true + ); + + $kw_tags = get_terms([ + 'taxonomy' => 'post_tag', + 'hide_empty' => true, + 'orderby' => 'name', + 'order' => 'ASC', + 'lang' => '', + ]); + if (!is_wp_error($kw_tags) && !empty($kw_tags)) { + $kw_lang = thalim_current_language(); + $kw_tags = array_filter($kw_tags, function ($tag) { + return !get_term_meta($tag->term_id, 'ne_pas_afficher_dans_le_nuage', true); + }); + wp_localize_script('keywordCloud', 'thalimTags', array_values(array_map(function ($tag) use ($kw_lang) { + return ['name' => html_entity_decode(thalim_bilingual($tag->name, $kw_lang), ENT_QUOTES | ENT_HTML5, 'UTF-8'), 'url' => get_term_link($tag)]; + }, $kw_tags))); + } + } + + + wp_enqueue_script( + 'navAxesToggle', + get_template_directory_uri() . '/js/navAxesToggle.js', + [], + filemtime(get_template_directory() . '/js/navAxesToggle.js'), + true + ); + + wp_enqueue_script( + 'stickyHeader', + get_template_directory_uri() . '/js/stickyHeader.js', + [], + filemtime(get_template_directory() . '/js/stickyHeader.js'), + true + ); + + wp_enqueue_script( + 'frenchTypography', + get_template_directory_uri() . '/js/frenchTypography.js', + [], + filemtime(get_template_directory() . '/js/frenchTypography.js'), + true + ); + + if (is_page('membres')) { + wp_enqueue_script( + 'membresFilters', + get_template_directory_uri() . '/js/membresFilters.js', + [], + filemtime(get_template_directory() . '/js/membresFilters.js'), + true + ); + wp_enqueue_script( + 'membresPopover', + get_template_directory_uri() . '/js/membresPopover.js', + [], + filemtime(get_template_directory() . '/js/membresPopover.js'), + true + ); + } + + if (is_single() || is_author() || is_page('membres') || is_page('le-laboratoire') || is_page('programmes-de-recherche')) { + wp_enqueue_script( + 'seanceToggle', + get_template_directory_uri() . '/js/seanceToggle.js', + [], + filemtime(get_template_directory() . '/js/seanceToggle.js'), + true + ); + } + + if (is_single()) { + wp_enqueue_style( + 'swiper', + 'https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css', + [], + null + ); + + wp_enqueue_script( + 'swiper', + 'https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js', + [], + null, + true + ); + + wp_enqueue_script( + 'imageSwiper', + get_template_directory_uri() . '/js/imageSwiper.js', + ['swiper'], + filemtime(get_template_directory() . '/js/imageSwiper.js'), + true + ); + } + + wp_enqueue_script( + 'fitPostCardTitle', + get_template_directory_uri() . '/js/fitPostCardTitle.js', + ['frenchTypography'], + filemtime(get_template_directory() . '/js/fitPostCardTitle.js'), + true + ); + + $is_archive_page = is_category() || is_tax() || is_tag() || is_page(['annonces', 'announcements']) || is_search(); + + if ($is_archive_page) { + wp_enqueue_script( + 'infiniteScroll', + get_template_directory_uri() . '/js/infiniteScroll.js', + [], + filemtime(get_template_directory() . '/js/infiniteScroll.js'), + true + ); + wp_localize_script('infiniteScroll', 'infiniteScrollData', [ + 'ajaxUrl' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('load_more_posts'), + 'lang' => thalim_current_language(), + ]); + } + + if ($is_archive_page) { + wp_enqueue_script( + 'categoryFilters', + get_template_directory_uri() . '/js/categoryFilters.js', + [], + filemtime(get_template_directory() . '/js/categoryFilters.js'), + true + ); + } + + // wp_enqueue_style('wp-block-library'); +} +add_action('wp_enqueue_scripts', 'theme_enqueue_assets'); + +// Auto-sync display_name from first_name + last_name on every profile save +add_action( 'profile_update', 'thalim_sync_display_name' ); +add_action( 'user_register', 'thalim_sync_display_name' ); +add_action( 'pods_api_post_save_pod_item_user', function( $pieces ) { + $user_id = isset( $pieces['id'] ) ? intval( $pieces['id'] ) : 0; + if ( $user_id ) thalim_sync_display_name( $user_id ); +} ); + +function thalim_sync_display_name( int $user_id ): void { + $first = get_user_meta( $user_id, 'first_name', true ); + $last = get_user_meta( $user_id, 'last_name', true ); + $name = trim( "$first $last" ); + if ( ! $name ) return; + // Bypass the filter to avoid infinite loop + remove_action( 'profile_update', 'thalim_sync_display_name' ); + wp_update_user( [ 'ID' => $user_id, 'display_name' => $name ] ); + add_action( 'profile_update', 'thalim_sync_display_name' ); +} + +add_action( 'admin_enqueue_scripts', 'enqueue_admin_js' ); + +function enqueue_admin_js(){ + wp_enqueue_style( + 'adminDashboardStyles', + get_template_directory_uri() . '/css/admin.css', + [], + filemtime(get_template_directory() . '/css/admin.css') + ); + + wp_enqueue_script( + 'adminDashboardMods', + get_template_directory_uri() . '/js/adminDashboardMods.js', + [], + filemtime(get_template_directory() . '/js/adminDashboardMods.js'), + true + ); + $axes_groups = thalim_get_axes_filter_groups(); + if ( current_user_can( 'contributor' ) && ! current_user_can( 'edit_others_posts' ) ) { + $axes_groups = array_slice( $axes_groups, 0, 1 ); + } + wp_localize_script( 'adminDashboardMods', 'thalimAxesGroups', $axes_groups ); + + // [DÉSACTIVÉ] adminFormRestore — décommenter pour réactiver + // $screen = get_current_screen(); + // if ( $screen && 'post' === $screen->base ) { + // wp_enqueue_script( + // 'adminFormRestore', + // get_template_directory_uri() . '/js/adminFormRestore.js', + // [ 'jquery' ], + // filemtime(get_template_directory() . '/js/adminFormRestore.js'), + // true + // ); + // } +} +add_theme_support('title-tag'); + +// Apply bilingual split to the browser tab title + translate category names +add_filter('document_title_parts', function ($title_parts) { + if (!empty($title_parts['title'])) { + $title_parts['title'] = thalim_bilingual($title_parts['title']); + // On category archives, replace the title with the translated category name + if ( is_category() ) { + $cat = get_queried_object(); + if ( $cat ) { + $title_parts['title'] = thalim_cat_name( $cat ); + } + } + } + return $title_parts; +}); + +function add_to_context($context) { + $current_lang = thalim_current_language(); + + // menus + $menu_slug = ($current_lang === 'en') ? 'Navigation-en' : 'Navigation'; + $context['menu'] = Timber::get_menu($menu_slug); + + $footer_menu_slug = ($current_lang === 'en') ? 'Footer-en' : 'Footer'; + $context['footer_menu'] = Timber::get_menu($footer_menu_slug); + + // contenus généraux (single post, bilingual) + $gc_posts = Timber::get_posts([ + 'post_type' => 'contenu_general', + 'posts_per_page' => 1, + 'orderby' => 'ID', + 'order' => 'ASC', + ]); + $gc_post = $gc_posts[0] ?? null; + if ( $gc_post ) { + $context['gc'] = [ + 'umr' => thalim_bilingual( $gc_post->umr ?: '', $current_lang ), + 'thalim' => thalim_bilingual( $gc_post->thalim ?: '', $current_lang ), + 'siecles' => thalim_bilingual( $gc_post->siecles ?: '', $current_lang ), + 'presentation' => ( $current_lang === 'en' && $gc_post->presentation_en ) ? $gc_post->presentation_en : $gc_post->presentation, + 'presentation_detail' => ( $current_lang === 'en' && $gc_post->presentation_detail_en ) ? $gc_post->presentation_detail_en : $gc_post->presentation_detail, + ]; + } else { + $context['gc'] = []; + } + + $context['current_language'] = $current_lang; + + // Axes thématiques courants (annee_fin >= current year) for navigation dropdown + $current_year = (int) date('Y'); + $all_axes = get_terms(['taxonomy' => 'axe_thematique', 'hide_empty' => false, 'orderby' => 'name']); + $axes_courants = []; + if (!is_wp_error($all_axes)) { + foreach ($all_axes as $axe) { + $fin = (int) get_term_meta($axe->term_id, 'annee_fin', true); + if ($fin >= $current_year) { + $link = get_term_link($axe); + if (!is_wp_error($link)) { + $axes_courants[] = [ + 'name' => thalim_bilingual($axe->name, $current_lang), + 'link' => $link, + 'ordre' => (int) get_term_meta($axe->term_id, 'ordre_daffichage', true), + ]; + } + } + } + usort($axes_courants, fn($a, $b) => $a['ordre'] <=> $b['ordre']); + } + $context['axes_courants'] = $axes_courants; + + // Annonces page URL (language-aware) + $annonces_page = get_page_by_path('annonces'); + $annonces_base = $annonces_page ? get_permalink($annonces_page->ID) : home_url('/annonces/'); + $context['annonces_url'] = $annonces_base; + + // Language switcher + $context['languages'] = thalim_language_switcher(); + + return $context; +} + +// Restrict Contributors to see only their own posts in admin, +// but also include posts where they appear in membres/autre_membres. +function restrict_contributor_posts( $query ) { + if ( ! is_admin() || ! $query->is_main_query() || current_user_can( 'edit_others_posts' ) ) { + return; + } + + global $user_ID, $wpdb; + + // Posts where the user is listed as membre or autre_membre. + $membre_ids = array_map( 'intval', (array) $wpdb->get_col( + $wpdb->prepare( + "SELECT DISTINCT post_id FROM {$wpdb->postmeta} + WHERE meta_key IN ('membres', 'autre_membres') AND meta_value = %s", + $user_ID + ) + ) ); + + if ( empty( $membre_ids ) ) { + // Fast path: no membre posts, use simple author filter. + $query->set( 'author', $user_ID ); + return; + } + + // Posts authored by this user. + $authored_ids = array_map( 'intval', (array) $wpdb->get_col( + $wpdb->prepare( + "SELECT ID FROM {$wpdb->posts} WHERE post_author = %d", + $user_ID + ) + ) ); + + $all_ids = array_unique( array_merge( $authored_ids, $membre_ids ) ); + + // post__in with [0] returns nothing when the combined set is empty. + $query->set( 'post__in', empty( $all_ids ) ? [ 0 ] : $all_ids ); +} +add_action( 'pre_get_posts', 'restrict_contributor_posts' ); + +/** + * Let contributors listed as membres/autre_membres on a post edit it. + * + * user_has_cap modifies capabilities only for this single check — + * it does not permanently alter the user's capability set. + */ +function thalim_membres_can_edit_post( $allcaps, $caps, $args, $user ) { + // Editors and above already have edit_others_posts — nothing to do. + if ( ! empty( $allcaps['edit_others_posts'] ) ) { + return $allcaps; + } + + if ( empty( $args[0] ) ) { + return $allcaps; + } + + $cap = $args[0]; + + // Meta caps that carry a post ID in $args[2] (e.g. wp-admin/post.php load). + $meta_caps_with_id = [ 'edit_post', 'edit_page' ]; + + // Primitive caps called during the admin save/publish flow *without* a + // post_id (e.g. wp-admin/includes/post.php:76 checks edit_others_posts + // directly when $post_author !== current user). We infer the post_id from + // the request so we can still authorize membres per-post. + $primitive_caps_in_save_flow = [ + 'edit_others_posts', + 'edit_others_pages', + 'edit_published_posts', + 'edit_published_pages', + 'publish_posts', + 'publish_pages', + ]; + + $post_id = 0; + if ( in_array( $cap, $meta_caps_with_id, true ) && ! empty( $args[2] ) ) { + $post_id = (int) $args[2]; + } elseif ( in_array( $cap, $primitive_caps_in_save_flow, true ) ) { + $post_id = (int) ( $_POST['post_ID'] ?? $_REQUEST['post'] ?? 0 ); + } + + if ( ! $post_id ) { + return $allcaps; + } + + $user_id = $user->ID; + + $membre_ids = array_map( + 'intval', + array_merge( + (array) get_post_meta( $post_id, 'membres', false ), + (array) get_post_meta( $post_id, 'autre_membres', false ) + ) + ); + + if ( in_array( $user_id, $membre_ids, true ) ) { + // Grant every primitive cap mapped for this check + // (e.g. edit_others_posts, edit_published_posts). + foreach ( $caps as $c ) { + $allcaps[ $c ] = true; + } + } + + return $allcaps; +} +add_filter( 'user_has_cap', 'thalim_membres_can_edit_post', 10, 4 ); + +// Prevent WP_Posts_List_Table from auto-redirecting contributors to the "Mine" +// view. The constructor sets $_GET['author'] = current_user_id() when the user +// lacks edit_others_posts and no other filter is active. Setting all_posts=1 +// before the list table is constructed short-circuits that condition. +add_action( 'load-edit.php', function () { + if ( current_user_can( 'edit_others_posts' ) ) { + return; + } + if ( + empty( $_REQUEST['post_status'] ) + && empty( $_REQUEST['all_posts'] ) + && empty( $_REQUEST['author'] ) + && empty( $_REQUEST['show_sticky'] ) + ) { + $_GET['all_posts'] = 1; + $_REQUEST['all_posts'] = 1; + } +} ); + +// Adjust post-status counts so contributors see only posts they can access +// (posts they authored + posts listed in membres/autre_membres), not all posts. +// The wp_count_posts filter runs even on cached values, so it won't pollute +// the shared cache. +add_filter( 'wp_count_posts', function ( $counts, $type, $perm ) { + if ( ! is_admin() || current_user_can( 'edit_others_posts' ) ) { + return $counts; + } + + global $wpdb; + $user_id = get_current_user_id(); + + $results = $wpdb->get_results( + $wpdb->prepare( + "SELECT post_status, COUNT(*) AS num_posts + FROM {$wpdb->posts} + WHERE post_type = %s + AND ID IN ( + SELECT ID FROM {$wpdb->posts} + WHERE post_author = %d AND post_type = %s + UNION + SELECT post_id FROM {$wpdb->postmeta} + WHERE meta_key IN ('membres', 'autre_membres') AND meta_value = %s + ) + GROUP BY post_status", + $type, + $user_id, + $type, + (string) $user_id + ), + ARRAY_A + ); + + $new_counts = array_fill_keys( get_post_stati(), 0 ); + foreach ( $results as $row ) { + $new_counts[ $row['post_status'] ] = (int) $row['num_posts']; + } + + return (object) $new_counts; +}, 10, 3 ); + +// Show Pods user meta fields on the Add New User page (user-new.php). +// Fires the same hook Pods listens to on profile.php / user-edit.php. +add_action('user_new_form', function($operation) { + if ($operation === 'add-existing-user') return; + $user = new WP_User(0); + do_action('show_user_profile', $user); +}); + +// Save Pods user meta fields when a new user is registered. +// Re-fires Pods' personal_options_update callback with the new user ID +// while $_POST still contains the submitted form data. +add_action('user_register', function($user_id) { + do_action('personal_options_update', $user_id); +}, 20); + +// Remove the Pods autocomplete 30-item cap for the "étiquettes" (post_tag) pick field +// so all tags are available in the dropdown, not just the first 30 alphabetically. +add_filter( 'pods_form_ui_field_pick_autocomplete_limit', function( $limit, $name ) { + if ( 'etiquettes' === $name ) { + return -1; + } + return $limit; +}, 10, 2 ); + +// Admin "Programmes de recherche" taxonomy list: +// - Filter by "Type de programme" (select injected via JS into the search form). +// - Add a "Type de programme" column to the list table. + +// Server-side filtering: read the GET param and add a meta_query on pre_get_terms. +add_action( 'pre_get_terms', function( $query ) { + if ( ! is_admin() ) { + return; + } + if ( ! in_array( 'programme_de_recherche', (array) $query->query_vars['taxonomy'], true ) ) { + return; + } + $type = isset( $_GET['type_de_programme'] ) ? sanitize_text_field( $_GET['type_de_programme'] ) : ''; + if ( '' === $type ) { + return; + } + $query->query_vars['meta_query'] = [ + [ + 'key' => 'type_de_programme', + 'value' => $type, + 'compare' => '=', + ], + ]; +} ); + +// Column: add "Type de programme" to the list table, replacing "Description". +add_filter( 'manage_edit-programme_de_recherche_columns', function( $columns ) { + unset( $columns['description'] ); + $columns['type_de_programme'] = __( 'Type de programme', 'thalim' ); + return $columns; +} ); + +add_filter( 'manage_programme_de_recherche_custom_column', function( $output, $column_name, $term_id ) { + if ( 'type_de_programme' !== $column_name ) { + return $output; + } + return esc_html( get_term_meta( (int) $term_id, 'type_de_programme', true ) ?: '—' ); +}, 10, 3 ); + +// Admin tag list: replace the "Description" column with the custom boolean field +// "ne_pas_afficher_dans_le_nuage" (Ne pas afficher dans le nuage de mots-clé). + +add_filter( 'manage_edit-post_tag_columns', function( $columns ) { + unset( $columns['description'] ); + $columns['nuage_exclus'] = __( 'Exclure du nuage', 'thalim' ); + return $columns; +} ); + +add_filter( 'manage_post_tag_custom_column', function( $output, $column_name, $term_id ) { + if ( 'nuage_exclus' !== $column_name ) { + return $output; + } + $val = get_term_meta( (int) $term_id, 'ne_pas_afficher_dans_le_nuage', true ); + return $val ? '✓' : '—'; +}, 10, 3 ); + +require_once __DIR__ . '/inc/admin-users-filter.php'; +require_once __DIR__ . '/inc/pods-conditional-required.php'; +require_once __DIR__ . '/inc/pods-save-error-handler.php'; +require_once __DIR__ . '/inc/post-title-required.php'; +require_once __DIR__ . '/inc/post-card-helpers.php'; +require_once __DIR__ . '/inc/single-helpers.php'; +require_once __DIR__ . '/inc/author-helpers.php'; + +// In admin post list, filter by exact category only (exclude subcategories) +function thalim_exact_category_filter( $query ) { + if ( is_admin() && $query->is_main_query() ) { + $cat = $query->get( 'cat' ); + if ( $cat ) { + $query->set( 'cat', '' ); + $query->set( 'tax_query', [ + [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [ (int) $cat ], + 'include_children' => false, + ], + ] ); + } + } +} +add_action( 'pre_get_posts', 'thalim_exact_category_filter' ); + +// ── "Vie du labo" (cat 9) — restricted to logged-in users ──── +add_action( 'pre_get_posts', function( $query ) { + if ( is_user_logged_in() ) return; + $excluded = $query->get( 'category__not_in' ); + if ( ! is_array( $excluded ) ) $excluded = $excluded ? [ $excluded ] : []; + if ( ! in_array( 9, $excluded ) ) { + $excluded[] = 9; + $query->set( 'category__not_in', $excluded ); + } +} ); + +add_action( 'template_redirect', function() { + if ( ! is_user_logged_in() && is_category( 9 ) ) { + wp_safe_redirect( home_url( '/' ) ); + exit; + } +} ); + +// Séance de séminaire (cat 12): redirect to parent séminaire with #seance-{ID} anchor +add_action( 'template_redirect', function() { + if ( ! is_single() ) return; + if ( ! has_category( 12 ) ) return; + + global $wpdb; + $seance_id = get_the_ID(); + $parent_id = $wpdb->get_var( $wpdb->prepare( + "SELECT pm.post_id FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE pm.meta_key = 'seances' AND pm.meta_value = %s + AND p.post_status = 'publish' + LIMIT 1", + (string) $seance_id + ) ); + + if ( $parent_id ) { + wp_redirect( get_permalink( (int) $parent_id ) . '#seance-' . $seance_id, 301 ); + exit; + } +} ); + +add_filter( 'wp_nav_menu_objects', function( $items, $args ) { + if ( is_user_logged_in() ) return $items; + return array_values( array_filter( $items, function( $item ) { + if ( $item->object === 'category' && (int) $item->object_id === 9 ) return false; + if ( strpos( $item->url, 'vie-du-labo' ) !== false ) return false; + return true; + } ) ); +}, 10, 2 ); + +// Rewrite rule for /category/{slug}/autres → posts directly in parent category +add_action('init', function() { + add_rewrite_rule( + 'category/([^/]+)/autres/?$', + 'index.php?category_name=$matches[1]&thalim_direct_posts=1', + 'top' + ); +}); +add_filter('query_vars', function($vars) { + $vars[] = 'thalim_direct_posts'; + return $vars; +}); + +// Admin bar customizations (front + back) +add_action('admin_bar_menu', function($wp_admin_bar) { + $wp_admin_bar->remove_node('wp-logo'); + $wp_admin_bar->remove_node('customize'); + foreach ($wp_admin_bar->get_nodes() as $node) { + if (empty($node->title) || stripos($node->title, 'article') === false) continue; + $node->title = preg_replace_callback('/article/i', function($m) { + $w = $m[0]; + if ($w === strtoupper($w)) return 'ANNONCE'; + if ($w[0] === strtoupper($w[0])) return 'Annonce'; + return 'annonce'; + }, $node->title); + $wp_admin_bar->add_node((array) $node); + } +}, 999); + +add_action('wp_before_admin_bar_render', function() { + global $wp_admin_bar; + $wp_admin_bar->remove_node('wpforms-menu'); +}); + +// Force visual (TinyMCE) editor for all users — our admin CSS hides the +// Visual/Code tabs, so if a user has "Disable the visual editor" checked +// in their profile they get stuck in code mode with no way to switch back. +add_filter( 'user_can_richedit', '__return_true' ); + +// Non-admins: hide dashboard and tools menu, redirect to posts list +add_action( 'admin_menu', function() { + if ( ! current_user_can( 'manage_options' ) ) { + remove_menu_page( 'tools.php' ); + remove_menu_page( 'index.php' ); + } +} ); + +// Redirect non-admins away from dashboard to their posts list +add_action( 'admin_init', function() { + if ( current_user_can( 'manage_options' ) ) return; + global $pagenow; + if ( $pagenow === 'index.php' ) { + wp_safe_redirect( admin_url( 'edit.php' ) ); + exit; + } +} ); + +// After login, send non-admins to posts list instead of dashboard +add_filter( 'login_redirect', function( $redirect_to, $requested, $user ) { + if ( ! is_wp_error( $user ) && ! $user->has_cap( 'manage_options' ) ) { + return admin_url( 'edit.php' ); + } + return $redirect_to; +}, 10, 3 ); + +// AJAX handler for infinite scroll on category pages +function thalim_load_more_posts() { + check_ajax_referer('load_more_posts', 'nonce'); + + $GLOBALS['thalim_lang_override'] = sanitize_key( $_POST['lang'] ?? 'fr' ); + + $page = intval($_POST['page']); + $category = isset($_POST['category']) ? intval($_POST['category']) : 0; + $axe = isset($_POST['axe']) ? intval($_POST['axe']) : 0; + $date_from = isset($_POST['date_from']) ? sanitize_text_field($_POST['date_from']) : ''; + $date_to = isset($_POST['date_to']) ? sanitize_text_field($_POST['date_to']) : ''; + $term_taxonomy = isset($_POST['taxonomy']) ? sanitize_key($_POST['taxonomy']) : ''; + $term_id = isset($_POST['term']) ? intval($_POST['term']) : 0; + $cat_filter = isset($_POST['filter_cat']) ? intval($_POST['filter_cat']) : 0; + $filter_autres = isset($_POST['filter_autres']) ? intval($_POST['filter_autres']) : 0; + $exclude_cats = isset($_POST['exclude_cats']) ? sanitize_text_field($_POST['exclude_cats']) : ''; + $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : ''; + + $query_args = [ + 'post_type' => 'post', + 'posts_per_page' => 12, + 'paged' => $page, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'thalim_event_date_order' => true, + ]; + if ($search) { + $query_args['s'] = $search; + $query_args['relevanssi'] = true; + $query_args['orderby'] = 'relevance'; + } + + // Build tax_query — may combine category page filter, taxonomy term, and cat filter + $tax_clauses = []; + if ($category) { + $tax_clauses[] = [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$category], + 'include_children' => false, + ]; + } + if ($term_taxonomy && $term_id) { + $tax_clauses[] = [ + 'taxonomy' => $term_taxonomy, + 'field' => 'term_id', + 'terms' => [$term_id], + ]; + // Exclure les séances de séminaire sur les pages de taxonomie + $tax_clauses[] = [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [12], + 'operator' => 'NOT IN', + ]; + } + if ($cat_filter) { + $tax_clauses[] = [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$cat_filter], + 'include_children' => !$filter_autres, + ]; + } + if ($exclude_cats) { + $ids = array_filter(array_map('intval', explode(',', $exclude_cats))); + if (!empty($ids)) { + $tax_clauses[] = [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => $ids, + 'operator' => 'NOT IN', + ]; + } + } + + if (!empty($tax_clauses)) { + $query_args['tax_query'] = count($tax_clauses) > 1 + ? array_merge(['relation' => 'AND'], $tax_clauses) + : $tax_clauses; + } + + if ($axe) { + $query_args['meta_query'] = [[ + 'key' => 'axes_thematiques', + 'value' => $axe, + 'type' => 'NUMERIC', + ]]; + } + + if ($date_from || $date_to) { + $query_args['thalim_event_date_filter'] = ['from' => $date_from, 'to' => $date_to]; + } + + // Exclude pinned posts on category pages to avoid duplicates (they already appear at the top) + if ($category) { + $today = date( 'Y-m-d' ); + $pinned_query = new WP_Query([ + 'post_type' => 'post', + 'posts_per_page' => -1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'lang' => '', + 'tax_query' => [[ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$category], + 'include_children' => false, + ]], + 'meta_query' => [[ + 'key' => 'epingler_dans_la_categorie', + 'value' => '1', + ]], + ]); + $pinned_ids = []; + foreach ( $pinned_query->posts as $pid ) { + $fin = get_post_meta( $pid, 'date_de_fin_depinglage', true ); + if ( empty( $fin ) || $fin === '0000-00-00' || $fin >= $today ) { + $pinned_ids[] = $pid; + } + } + if ( ! empty( $pinned_ids ) ) { + $query_args['post__not_in'] = $pinned_ids; + } + } + + $posts = Timber::get_posts($query_args); + + if (empty($posts)) { + wp_send_json_success(['html' => '']); + return; + } + + $cards = thalim_get_cards_data($posts); + + $html = ''; + foreach ($posts as $post) { + $html .= Timber::compile('partials/post-card.twig', [ + 'post' => $post, + 'card' => $cards[$post->ID], + 'show_category' => true, + 'type_only' => $category > 0, // category pages: type chip only; everywhere else (taxonomy, search, annonces): type or category name + ]); + } + + wp_send_json_success(['html' => $html]); +} +add_action('wp_ajax_load_more_posts', 'thalim_load_more_posts'); +add_action('wp_ajax_nopriv_load_more_posts', 'thalim_load_more_posts'); + +/** + * Build structured data for one agenda slider card. + */ +function thalim_get_agenda_card_data( $post_id, $lang = 'fr' ) { + $months_fr = ['jan.','fév.','mars','avr.','mai','juin','juil.','août','sept.','oct.','nov.','déc.']; + $months_en = ['Jan.','Feb.','Mar.','Apr.','May','Jun.','Jul.','Aug.','Sep.','Oct.','Nov.','Dec.']; + $months = $lang === 'en' ? $months_en : $months_fr; + + $raw_debut = get_post_meta( $post_id, 'date_de_debut', true ); + $raw_datetime = get_post_meta( $post_id, 'datetime', true ); + if ( $raw_debut && ! str_starts_with( $raw_debut, '0000' ) ) { + $ts = strtotime( $raw_debut ); + } elseif ( $raw_datetime && ! str_starts_with( $raw_datetime, '0000' ) ) { + $ts = strtotime( $raw_datetime ); + } else { + $ts = get_post_timestamp( $post_id ); + } + + $raw_fin = get_post_meta( $post_id, 'date_de_fin', true ); + $ts_debut = ( $raw_debut && ! str_starts_with( $raw_debut, '0000' ) ) ? strtotime( $raw_debut ) : 0; + $ts_fin = ( $raw_fin && ! str_starts_with( $raw_fin, '0000' ) ) ? strtotime( $raw_fin ) : 0; + + // Build date_label — same rules as single.twig sidebar + $fmt_debut = $ts_debut ? thalim_format_date( $raw_debut, $lang ) : ''; + $fmt_fin = $ts_fin ? thalim_format_date( $raw_fin, $lang ) : ''; + $fmt_dt = ( $raw_datetime && ! str_starts_with( $raw_datetime, '0000' ) ) + ? thalim_format_date( $raw_datetime, $lang ) : ''; + $h_debut = substr( get_post_meta( $post_id, 'heure_de_debut', true ) ?: '', 0, 5 ); + $h_fin = substr( get_post_meta( $post_id, 'heure_de_fin', true ) ?: '', 0, 5 ); + + if ( $fmt_debut || $fmt_fin ) { + if ( $ts_debut && $ts_fin && date( 'Y-m-d', $ts_debut ) === date( 'Y-m-d', $ts_fin ) ) { + // Same day + if ( $h_debut && $h_fin ) { + $date_label = ( $lang === 'en' ? 'On ' : 'Le ' ) . $fmt_debut + . ' ' . ( $lang === 'en' ? 'from ' : 'de ' ) . $h_debut + . ' ' . ( $lang === 'en' ? 'to ' : 'à ' ) . $h_fin; + } elseif ( $h_debut ) { + $date_label = $fmt_debut . ( $lang === 'en' ? ' at ' : ' à ' ) . $h_debut; + } else { + $date_label = $fmt_debut; + } + } elseif ( $fmt_debut && $fmt_fin ) { + $date_label = ( $lang === 'en' ? 'From ' : 'Du ' ) . $fmt_debut + . ' ' . ( $lang === 'en' ? 'to ' : 'au ' ) . $fmt_fin; + } elseif ( $fmt_debut ) { + $date_label = $h_debut + ? $fmt_debut . ( $lang === 'en' ? ' at ' : ' à ' ) . $h_debut + : $fmt_debut; + } else { + $date_label = ( $lang === 'en' ? 'Until ' : "Jusqu'au " ) . $fmt_fin; + } + } elseif ( $fmt_dt ) { + $date_label = $h_debut + ? $fmt_dt . ( $lang === 'en' ? ' at ' : ' à ' ) . $h_debut + : $fmt_dt; + } else { + $date_label = ''; + } + + $type_fields = [ + 'type_colloque_journee_d_etude', 'type_soutenance', 'type_evenement_culturel', + 'type_media', 'type_captation', 'type_revue_collection', 'type_autre', + ]; + $type_label = ''; + foreach ( $type_fields as $f ) { + $v = get_post_meta( $post_id, $f, true ); + if ( $v ) { $type_label = thalim_bilingual( $v, $lang ); break; } + } + if ( ! $type_label ) { + foreach ( get_the_category( $post_id ) as $cat ) { + if ( $cat->parent ) { $type_label = thalim_cat_name( $cat, $lang ); break; } + } + } + + $end_day = $end_month = $end_year = null; + if ( $ts_fin && date( 'Ymd', $ts_fin ) !== date( 'Ymd', $ts ) ) { + $end_day = (int) date( 'j', $ts_fin ); + $end_month = $months[ (int) date( 'n', $ts_fin ) - 1 ]; + $end_year = (int) date( 'Y', $ts_fin ); + } + + // Séance de séminaire (cat 12): link to parent séminaire at #seance-{ID} + $link = get_permalink( $post_id ); + if ( in_array( 12, wp_list_pluck( get_the_category( $post_id ), 'term_id' ) ) ) { + global $wpdb; + $parent_id = $wpdb->get_var( $wpdb->prepare( + "SELECT pm.post_id FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE pm.meta_key = 'seances' AND pm.meta_value = %s + AND p.post_status = 'publish' + LIMIT 1", + (string) $post_id + ) ); + if ( $parent_id ) { + $link = get_permalink( (int) $parent_id ) . '#seance-' . $post_id; + } + } + + return [ + 'day' => (int) date( 'j', $ts ), + 'month' => $months[ (int) date( 'n', $ts ) - 1 ], + 'year' => (int) date( 'Y', $ts ), + 'end_day' => $end_day, + 'end_month' => $end_month, + 'end_year' => $end_year, + 'type_label' => $type_label, + 'date_label' => $date_label, + 'lieu' => thalim_bilingual( get_post_meta( $post_id, 'lieu', true ) ?: '', $lang ), + 'link' => $link, + ]; +} + +function thalim_load_more_agenda() { + check_ajax_referer( 'load_more_posts', 'nonce' ); + + $lang = sanitize_key( $_POST['lang'] ?? 'fr' ); + $GLOBALS['thalim_lang_override'] = $lang; + $page = intval( $_POST['page'] ); + $category = isset( $_POST['category'] ) ? intval( $_POST['category'] ) : 0; + $axe = isset( $_POST['axe'] ) ? intval( $_POST['axe'] ) : 0; + $date_from = isset( $_POST['date_from'] ) ? sanitize_text_field( $_POST['date_from'] ) : ''; + $date_to = isset( $_POST['date_to'] ) ? sanitize_text_field( $_POST['date_to'] ) : ''; + $term_taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_key( $_POST['taxonomy'] ) : ''; + $term_id = isset( $_POST['term'] ) ? intval( $_POST['term'] ) : 0; + $cat_filter = isset( $_POST['filter_cat'] ) ? intval( $_POST['filter_cat'] ) : 0; + $filter_autres = isset( $_POST['filter_autres'] ) ? intval( $_POST['filter_autres'] ) : 0; + $exclude_cats = isset( $_POST['exclude_cats'] ) ? sanitize_text_field( $_POST['exclude_cats'] ) : ''; + + $query_args = [ + 'post_type' => 'post', + 'posts_per_page' => 12, + 'paged' => $page, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'thalim_event_date_order' => true, + ]; + + $include_children = ! empty( $_POST['include_children'] ); + + $tax_clauses = []; + if ( $category ) { + $tax_clauses[] = [ 'taxonomy' => 'category', 'field' => 'term_id', 'terms' => [ $category ], 'include_children' => $include_children ]; + } + if ( $term_taxonomy && $term_id ) { + $tax_clauses[] = [ 'taxonomy' => $term_taxonomy, 'field' => 'term_id', 'terms' => [ $term_id ] ]; + $tax_clauses[] = [ 'taxonomy' => 'category', 'field' => 'term_id', 'terms' => [ 12 ], 'operator' => 'NOT IN' ]; + } + if ( $cat_filter ) { + $tax_clauses[] = [ 'taxonomy' => 'category', 'field' => 'term_id', 'terms' => [ $cat_filter ], 'include_children' => ! $filter_autres ]; + } + if ( $exclude_cats ) { + $ids = array_filter( array_map( 'intval', explode( ',', $exclude_cats ) ) ); + if ( ! empty( $ids ) ) { + $tax_clauses[] = [ 'taxonomy' => 'category', 'field' => 'term_id', 'terms' => $ids, 'operator' => 'NOT IN' ]; + } + } + if ( ! empty( $tax_clauses ) ) { + $query_args['tax_query'] = count( $tax_clauses ) > 1 + ? array_merge( [ 'relation' => 'AND' ], $tax_clauses ) + : $tax_clauses; + } + if ( $axe ) { + $query_args['meta_query'] = [[ 'key' => 'axes_thematiques', 'value' => $axe, 'type' => 'NUMERIC' ]]; + } + if ( $date_from || $date_to ) { + $query_args['thalim_event_date_filter'] = [ 'from' => $date_from, 'to' => $date_to ]; + } + + // On first page, count future events to find today's anchor position + $today_offset = 0; + if ( (int) $page === 1 ) { + $offset_args = $query_args; + $offset_args['posts_per_page'] = 1; + $offset_args['no_found_rows'] = false; + $offset_args['paged'] = 1; + $today_str = current_time( 'Y-m-d' ); + $existing_filter = $offset_args['thalim_event_date_filter'] ?? []; + $offset_args['thalim_event_date_filter'] = array_merge( + $existing_filter, + [ 'from' => $today_str ] + ); + $future_query = new \WP_Query( $offset_args ); + $today_offset = (int) $future_query->found_posts; + } + + $posts = Timber::get_posts( $query_args ); + if ( empty( $posts ) ) { + $response = [ 'html' => '' ]; + if ( (int) $page === 1 ) $response['today_offset'] = $today_offset; + wp_send_json_success( $response ); + return; + } + + $html = ''; + foreach ( $posts as $post ) { + $data = thalim_get_agenda_card_data( $post->ID, $lang ); + $html .= Timber::compile( 'partials/agenda-card.twig', array_merge( $data, [ 'post' => $post ] ) ); + } + $response = [ 'html' => $html ]; + if ( (int) $page === 1 ) $response['today_offset'] = $today_offset; + wp_send_json_success( $response ); +} +add_action( 'wp_ajax_load_more_agenda', 'thalim_load_more_agenda' ); +add_action( 'wp_ajax_nopriv_load_more_agenda', 'thalim_load_more_agenda' ); + +// Event date ordering: LEFT JOIN + COALESCE(date_de_debut, post_date) +// Activated by adding 'thalim_event_date_order' => true to WP_Query args. +// Event date ordering: COALESCE(date_de_debut, datetime, post_date) +// Activated by adding 'thalim_event_date_order' => true to WP_Query args. +add_filter('posts_join', function ($join, $query) { + if (!$query->get('thalim_event_date_order') && !$query->get('thalim_event_date_filter')) return $join; + global $wpdb; + $join .= " LEFT JOIN {$wpdb->postmeta} AS thalim_ed" + . " ON (thalim_ed.post_id = {$wpdb->posts}.ID" + . " AND thalim_ed.meta_key = 'date_de_debut') "; + $join .= " LEFT JOIN {$wpdb->postmeta} AS thalim_dt" + . " ON (thalim_dt.post_id = {$wpdb->posts}.ID" + . " AND thalim_dt.meta_key = 'datetime') "; + return $join; +}, 10, 2); + +add_filter('posts_orderby', function ($orderby, $query) { + if (!$query->get('thalim_event_date_order')) return $orderby; + global $wpdb; + $valid = "IS NOT NULL AND %s != '' AND %s NOT LIKE '0000-00-00%%'"; + return "CASE" + . " WHEN thalim_ed.meta_value " . sprintf($valid, 'thalim_ed.meta_value', 'thalim_ed.meta_value') . " THEN thalim_ed.meta_value" + . " WHEN thalim_dt.meta_value " . sprintf($valid, 'thalim_dt.meta_value', 'thalim_dt.meta_value') . " THEN thalim_dt.meta_value" + . " ELSE {$wpdb->posts}.post_date" + . " END DESC"; +}, 10, 2); + +// Event date range filter: uses same CASE logic as ordering so date_de_debut/datetime take priority over post_date. +// Activated by adding 'thalim_event_date_filter' => ['from' => $date_from, 'to' => $date_to] to WP_Query args. +add_filter('posts_where', function ($where, $query) { + $filter = $query->get('thalim_event_date_filter'); + if (empty($filter) || (!isset($filter['from']) && !isset($filter['to']))) return $where; + global $wpdb; + + $effective = "CASE" + . " WHEN thalim_ed.meta_value IS NOT NULL AND thalim_ed.meta_value != '' AND thalim_ed.meta_value NOT LIKE '0000-00-00%' THEN thalim_ed.meta_value" + . " WHEN thalim_dt.meta_value IS NOT NULL AND thalim_dt.meta_value != '' AND thalim_dt.meta_value NOT LIKE '0000-00-00%' THEN thalim_dt.meta_value" + . " ELSE {$wpdb->posts}.post_date" + . " END"; + + if (!empty($filter['from'])) { + $from = $wpdb->prepare('%s', $filter['from']); + $where .= " AND ({$effective}) >= {$from}"; + } + if (!empty($filter['to'])) { + $to = $wpdb->prepare('%s', $filter['to'] . ' 23:59:59'); + $where .= " AND ({$effective}) <= {$to}"; + } + + return $where; +}, 10, 2); + +// ── Axes thématiques groupés pour les filtres ────────────────── +// Retourne un tableau de groupes triés par période (plus récent en premier, +// "passés" toujours en dernier). Chaque terme contient id, name, ordre. +function thalim_get_axes_filter_groups() { + $terms = get_terms( [ 'taxonomy' => 'axe_thematique', 'hide_empty' => false ] ); + $axes_map = []; + + foreach ( $terms as $term ) { + $debut = trim( get_term_meta( $term->term_id, 'annee_debut', true ) ); + $fin = trim( get_term_meta( $term->term_id, 'annee_fin', true ) ); + + if ( $debut && $fin ) { + $key = $debut . '-' . $fin; + $label = $debut . ' – ' . $fin; + } else { + $key = 'passes'; + $label = 'Axes antérieurs'; + } + + if ( ! isset( $axes_map[ $key ] ) ) { + $axes_map[ $key ] = [ 'label' => $label, 'debut' => intval( $debut ), 'terms' => [] ]; + } + + $ordre = trim( get_term_meta( $term->term_id, 'ordre_daffichage', true ) ); + $axes_map[ $key ]['terms'][] = [ + 'id' => $term->term_id, + 'name' => $term->name, + 'ordre' => $ordre !== '' ? intval( $ordre ) : null, + 'href' => get_term_link( $term ), + ]; + } + + // Tri des groupes : plus récent en premier, passés toujours en dernier + uasort( $axes_map, function ( $a, $b ) { + if ( $a['label'] === 'Axes antérieurs' ) return 1; + if ( $b['label'] === 'Axes antérieurs' ) return -1; + return $b['debut'] - $a['debut']; + } ); + + // Tri des termes dans chaque groupe : ordre_daffichage d'abord, puis alphabétique + foreach ( $axes_map as &$group ) { + usort( $group['terms'], function ( $a, $b ) { + $a_has = $a['ordre'] !== null; + $b_has = $b['ordre'] !== null; + if ( $a_has && $b_has ) return $a['ordre'] - $b['ordre']; + if ( $a_has ) return -1; + if ( $b_has ) return 1; + return strcmp( $a['name'], $b['name'] ); + } ); + } + unset( $group ); + + return array_values( $axes_map ); +} + diff --git a/inc/admin-users-filter.php b/inc/admin-users-filter.php new file mode 100644 index 0000000..78c7577 --- /dev/null +++ b/inc/admin-users-filter.php @@ -0,0 +1,48 @@ + 'role', 'hide_empty' => true, 'orderby' => 'name' ] ); + if ( is_wp_error( $terms ) || empty( $terms ) ) return; + + $selected = isset( $_GET['thalim_statut'] ) ? intval( $_GET['thalim_statut'] ) : 0; + ?> + + + + set( 'meta_query', [ + 'relation' => 'OR', + [ 'key' => 'role_1', 'value' => $term_id, 'compare' => '=' ], + [ 'key' => 'role_2', 'value' => $term_id, 'compare' => '=' ], + [ 'key' => 'role_3', 'value' => $term_id, 'compare' => '=' ], + ] ); +} ); diff --git a/inc/author-helpers.php b/inc/author-helpers.php new file mode 100644 index 0000000..53ddbbb --- /dev/null +++ b/inc/author-helpers.php @@ -0,0 +1,250 @@ +name; + } + } + + // --- Direction title (read from "Le laboratoire" page) --- + $labo_page = get_page_by_path('le-laboratoire'); + $directeur_id = $labo_page ? intval(get_post_meta($labo_page->ID, 'directeur', true)) : 0; + $adjoint_id = $labo_page ? intval(get_post_meta($labo_page->ID, 'directeur_adjoint', true)) : 0; + if ($user_id === $directeur_id) { + $role_label = 'Directeur' . ($role_label ? ', ' . $role_label : ''); + } elseif ($user_id === $adjoint_id) { + $role_label = 'Directeur adjoint' . ($role_label ? ', ' . $role_label : ''); + } + + // --- Domaines de recherches (multiple usermeta rows with post_tag IDs) --- + $domaine_ids = get_user_meta($user_id, 'domaines_de_recherches', false); + $domaines_tags = []; + foreach ($domaine_ids as $tag_id) { + if (!$tag_id) continue; + $term = get_term(intval($tag_id), 'post_tag'); + if ($term && !is_wp_error($term)) { + $link = get_term_link($term); + if (!is_wp_error($link)) { + $domaines_tags[] = ['name' => thalim_bilingual($term->name, $lang), 'url' => $link]; + } + } + } + + // --- Axes thématiques (multiple usermeta rows) --- + $axe_ids = get_user_meta($user_id, 'axes_thematiques', false); + $axes = []; + foreach ($axe_ids as $axe_id) { + $term = get_term(intval($axe_id), 'axe_thematique'); + if ($term && !is_wp_error($term)) { + $axes[] = [ + 'name' => thalim_bilingual($term->name, $lang), + 'url' => get_term_link($term), + ]; + } + } + + // --- External links (up to 4) --- + $liens_externes = []; + for ($i = 1; $i <= 4; $i++) { + $url = get_user_meta($user_id, 'lien_externe_' . $i, true); + if ($url) { + $titre = thalim_bilingual(get_user_meta($user_id, 'titre_du_lien_' . $i, true) ?: '', $lang); + if (!$titre) { + $host = parse_url($url, PHP_URL_HOST) ?: $url; + $parts = explode('.', $host); + $titre = count($parts) >= 2 ? implode('.', array_slice($parts, -2)) : $host; + } + $liens_externes[] = ['url' => $url, 'titre' => $titre]; + } + } + + // --- Documents (multiple usermeta rows with attachment IDs) --- + $doc_ids = get_user_meta($user_id, 'documents', false); + $documents = []; + foreach ($doc_ids as $doc_id) { + $url = wp_get_attachment_url(intval($doc_id)); + if ($url) { + $documents[] = [ + 'url' => $url, + 'title' => get_the_title($doc_id) ?: basename(get_attached_file($doc_id)), + ]; + } + } + + // --- Thesis director (THALIM member — stored as user ID) --- + $directeur_id = get_user_meta($user_id, 'directeur_de_these_thalim', true); + $directeur_thalim = null; + if ($directeur_id) { + $dir_user = get_userdata(intval($directeur_id)); + if ($dir_user) { + $directeur_thalim = [ + 'name' => $dir_user->display_name, + 'url' => get_author_posts_url(intval($directeur_id)), + ]; + } + } + + // --- Email visibility --- + $is_ancien = isset($role_term) && $role_term && $role_term->slug === 'anciens-membres'; + $show_email = !$is_ancien && get_user_meta($user_id, 'afficher_ladresse_mail_sur_le_profil', true); + + return [ + 'display_name' => $user->display_name, + 'avatar_url' => $avatar_url, + 'role_label' => $role_label, + 'role_complement' => thalim_bilingual(get_user_meta($user_id, 'complement_de_role_1', true) ?: '', $lang), + 'affiliation' => (function() use ($user_id, $lang) { + $v = get_user_meta($user_id, 'affiliation', true) ?: ''; + return strtolower($v) === 'autre' + ? thalim_bilingual(get_user_meta($user_id, 'affiliation_autre', true) ?: '', $lang) + : $v; + })(), + 'bio' => wpautop( make_clickable( get_user_meta($user_id, 'biographie', true) ?: '' ) ), + 'bio_en' => wpautop( make_clickable( get_user_meta($user_id, 'biographie_en', true) ?: '' ) ), + 'domaines_tags' => $domaines_tags, + 'domaines' => wpautop( make_clickable( get_user_meta($user_id, 'autres_domaines_de_recherches', true) ?: '' ) ), + 'domaines_en' => wpautop( make_clickable( get_user_meta($user_id, 'autres_domaines_de_recherches_en', true) ?: '' ) ), + 'recherches' => wpautop( get_user_meta($user_id, 'recherches_en_cours', true) ?: '' ), + 'recherches_en' => wpautop( get_user_meta($user_id, 'recherches_en_cours_en', true) ?: '' ), + 'axes' => $axes, + 'titre_these' => thalim_bilingual(get_user_meta($user_id, 'titre_de_these', true) ?: '', $lang), + 'date_soutenance' => get_user_meta($user_id, 'date_de_soutenance', true) ?: '', + 'directeur_thalim'=> $directeur_thalim, + 'autre_directeur' => get_user_meta($user_id, 'autre_directeur_de_these', true) ?: '', + 'resume_these' => wpautop( get_user_meta($user_id, 'resume_de_la_these', true) ?: '' ), + 'resume_these_en' => wpautop( get_user_meta($user_id, 'resume_de_la_these_en', true) ?: '' ), + 'email' => $show_email ? $user->user_email : '', + 'liens_externes' => $liens_externes, + 'documents' => $documents, + 'hal_publications_url' => (function() use ($user_id) { + $hal_id = get_user_meta($user_id, 'identifiant_hal', true) ?: ''; + return $hal_id + ? 'https://hal.science/search/index/?qa[authIdHal_s][]=' . rawurlencode($hal_id) . '&sort=publicationDate_tdate+desc' + : ''; + })(), + 'user_since' => date_i18n('d/m/Y', strtotime($user->user_registered)), + ]; +} + +/** + * Query all posts linked to a member and group them by primary category. + * Returns an array sorted by post count (descending). + */ +function thalim_get_author_posts_by_category($user_id) { + $excluded_cats = [12, 31]; // séances de séminaire, etc. + $lang = thalim_current_language(); + + $posts = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => -1, + 'meta_query' => [ + 'relation' => 'OR', + [ + 'key' => 'membres', + 'value' => $user_id, + ], + [ + 'key' => 'autre_membres', + 'value' => $user_id, + ], + ], + 'thalim_event_date_order' => true, + 'lang' => '', + ]); + + $groups = []; + + foreach ($posts as $post) { + $categories = wp_get_post_categories($post->ID, ['fields' => 'all']); + $primary_cat = null; + + foreach ($categories as $cat) { + if (in_array($cat->term_id, $excluded_cats)) continue; + $primary_cat = $cat; + break; + } + + if (!$primary_cat) continue; + + $cat_id = $primary_cat->term_id; + if (!isset($groups[$cat_id])) { + // A top-level category with subcategories → these posts are "Autres" + $is_autres = false; + if ($primary_cat->parent == 0) { + $subcats = get_categories(['parent' => $cat_id, 'hide_empty' => true, 'exclude' => $excluded_cats]); + $is_autres = !empty($subcats); + } + $groups[$cat_id] = [ + 'cat_id' => $cat_id, + 'cat_name' => $is_autres + ? ($lang === 'en' ? 'Other ' : 'Autres ') . thalim_cat_name($primary_cat, $lang) + : thalim_cat_name($primary_cat, $lang), + 'cat_url' => $is_autres + ? trailingslashit(get_category_link($cat_id)) . 'autres/' + : get_category_link($cat_id), + 'posts' => [], + ]; + } + $groups[$cat_id]['posts'][] = $post; + } + + // Séances de séminaire — dedicated group. Posts in cat 12 where the member + // is listed in `membres`/`autre_membres`. Cards use the parent séminaire + // permalink with a #seance-{ID} hash (see thalim_get_card_data). + $seances = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => -1, + 'category__in' => [12], + 'meta_query' => [ + 'relation' => 'OR', + [ 'key' => 'membres', 'value' => $user_id ], + [ 'key' => 'autre_membres', 'value' => $user_id ], + ], + 'thalim_event_date_order' => true, + 'lang' => '', + ]); + if (count($seances) > 0) { + $seance_cat = get_term(12, 'category'); + $groups[12] = [ + 'cat_id' => 12, + 'cat_name' => $seance_cat && !is_wp_error($seance_cat) + ? thalim_cat_name($seance_cat, $lang) + : ($lang === 'en' ? 'Seminar sessions' : 'Séances de séminaire'), + 'cat_url' => get_category_link(12), + 'posts' => $seances, + ]; + } + + // Resolve card data and sort by count descending + foreach ($groups as &$group) { + $group['cards'] = thalim_get_cards_data($group['posts']); + } + unset($group); + + uasort($groups, function($a, $b) { + $oa = (int) get_term_meta($a['cat_id'], 'ordre_profil', true) ?: 999; + $ob = (int) get_term_meta($b['cat_id'], 'ordre_profil', true) ?: 999; + return $oa !== $ob + ? $oa <=> $ob + : count($b['posts']) <=> count($a['posts']); + }); + + return array_values($groups); +} diff --git a/inc/membres-helpers.php b/inc/membres-helpers.php new file mode 100644 index 0000000..f420e90 --- /dev/null +++ b/inc/membres-helpers.php @@ -0,0 +1,244 @@ + variants to \n and strips any remaining HTML tags. + */ +function thalim_sanitize_domaines( $raw ) { + // Normalise all
    variants (including \r before them) to a newline + $text = preg_replace( '/\r?/i', "\n", $raw ); + // Strip any remaining HTML tags + $text = strip_tags( $text ); + // Clean up excess blank lines / whitespace + $text = preg_replace( "/\n{3,}/", "\n\n", trim( $text ) ); + return $text; +} + +/** + * Build the display data array for a single user. + */ +function thalim_build_membre_data( $user ) { + $lang = thalim_current_language(); + $status_parts = []; + $role_names = []; + for ( $n = 1; $n <= 3; $n++ ) { + $role_id = get_user_meta( $user->ID, 'role_' . $n, true ); + if ( ! $role_id ) continue; + $term = get_term( intval( $role_id ), 'role' ); + if ( ! $term || is_wp_error( $term ) ) continue; + $role_names[] = $term->name; + $override = thalim_bilingual( get_user_meta( $user->ID, 'affichage_du_statut_' . $n, true ) ?: '', $lang ); + if ( $override ) { + $status_parts[] = $override; + } else { + $entry = $term->name; + $complement = thalim_bilingual( get_user_meta( $user->ID, 'complement_de_role_' . $n, true ) ?: '', $lang ); + if ( $complement ) $entry .= ' ' . $complement; + $status_parts[] = $entry; + } + } + + // Avatar (Simple Local Avatar with Gravatar fallback) + $avatar_url = thalim_get_user_avatar_url( $user->ID ); + + // Domaines de recherches: multiple usermeta rows, each is a post_tag term ID + $domaine_ids = get_user_meta( $user->ID, 'domaines_de_recherches', false ); + $domaines = []; + foreach ( $domaine_ids as $term_id ) { + $term = get_term( intval( $term_id ), 'post_tag' ); + if ( $term && ! is_wp_error( $term ) ) { + $domaines[] = $term->name; + } + } + + return [ + 'display_name' => $user->display_name, + 'sort_key' => thalim_get_sort_key( $user->ID, $user->display_name ), + 'url' => get_author_posts_url( $user->ID ), + 'status' => implode( ', ', $status_parts ), + 'affiliation' => (function() use ($user) { + $v = get_user_meta( $user->ID, 'affiliation', true ) ?: ''; + return strtolower( $v ) === 'autre' + ? ( get_user_meta( $user->ID, 'affiliation_autre', true ) ?: '' ) + : $v; + })(), + 'role_names' => $role_names, + 'avatar_url' => $avatar_url, + 'domaines' => $domaines, + 'autres_domaines' => thalim_sanitize_domaines( get_user_meta( $user->ID, 'autres_domaines_de_recherches', true ) ?: '' ), + ]; +} + +/** + * Return all role taxonomy terms that are in use, sorted by name. + */ +function thalim_get_role_terms() { + $terms = get_terms( [ 'taxonomy' => 'role', 'hide_empty' => true, 'orderby' => 'name' ] ); + if ( is_wp_error( $terms ) ) return []; + return array_values( array_map( + fn( $t ) => [ 'id' => $t->term_id, 'name' => $t->name ], + array_filter( $terms, fn( $t ) => ! in_array( mb_strtolower( $t->name, 'UTF-8' ), [ 'archive', 'à ranger' ], true ) ) + ) ); +} + +/** + * Return all role term_ids set for a user (role_1, role_2, role_3). + */ +function thalim_get_user_role_ids( $user_id ) { + $ids = []; + for ( $n = 1; $n <= 3; $n++ ) { + $role_id = get_user_meta( $user_id, 'role_' . $n, true ); + if ( $role_id ) $ids[] = intval( $role_id ); + } + return $ids; +} + +/** + * Sort key: first word of last_name user meta (handles compound last names like + * "Duclaux de l'Estoile" → "Duclaux"). Falls back to last word of display_name. + */ +function thalim_get_sort_key( $user_id, $display_name ) { + $last = get_user_meta( $user_id, 'last_name', true ); + if ( $last ) { + $parts = explode( ' ', trim( $last ) ); + return $parts[0]; + } + $parts = explode( ' ', trim( $display_name ) ); + return end( $parts ); +} + +/** + * Return all member groups for the /membres page. + * Each group: ['title' => string, 'members' => array of member data arrays]. + * Empty groups are omitted. + */ +function thalim_get_membres_groups() { + // Fetch all users that have role_1 set + $users = get_users( [ + 'meta_key' => 'role_1', + 'number' => -1, + ] ); + + // Direction: read directeur and directeur_adjoint from "Le laboratoire" page + $labo_page = get_page_by_path( 'le-laboratoire' ); + $directeur_id = $labo_page ? intval( get_post_meta( $labo_page->ID, 'directeur', true ) ) : 0; + $adjoint_id = $labo_page ? intval( get_post_meta( $labo_page->ID, 'directeur_adjoint', true ) ) : 0; + + $direction_users = []; + foreach ( [ $directeur_id, $adjoint_id ] as $uid ) { + if ( $uid ) { + $u = get_userdata( $uid ); + if ( $u ) $direction_users[] = $u; + } + } + + // Pre-build member data for all relevant users (cache by ID) + $member_cache = []; + $all_users = array_merge( $users, $direction_users ); + foreach ( $all_users as $user ) { + if ( ! isset( $member_cache[ $user->ID ] ) ) { + $member_cache[ $user->ID ] = thalim_build_membre_data( $user ); + } + } + + // Prepend direction title to status for director / deputy director + if ( $directeur_id && isset( $member_cache[ $directeur_id ] ) ) { + $existing = $member_cache[ $directeur_id ]['status']; + $member_cache[ $directeur_id ]['status'] = 'Directeur' . ( $existing ? ', ' . $existing : '' ); + } + if ( $adjoint_id && isset( $member_cache[ $adjoint_id ] ) ) { + $existing = $member_cache[ $adjoint_id ]['status']; + $member_cache[ $adjoint_id ]['status'] = 'Directeur adjoint' . ( $existing ? ', ' . $existing : '' ); + } + + // Build a slug→ID map for the 'role' taxonomy so group definitions survive + // database migrations where auto-incremented term IDs change. + $slug_to_id = []; + foreach ( get_terms( [ 'taxonomy' => 'role', 'hide_empty' => false ] ) as $term ) { + $slug_to_id[ $term->slug ] = $term->term_id; + } + $by_slug = fn( ...$slugs ) => array_values( + array_filter( array_map( fn( $s ) => $slug_to_id[ $s ] ?? null, $slugs ) ) + ); + + // Group definitions (title => role slugs that qualify a user for membership) + $group_definitions = [ + 'Chercheuses et chercheurs CNRS' => $by_slug( 'directeur-de-recherche', 'charge-de-recherche' ), + 'Enseignantes-chercheuses et enseignants-chercheurs' => $by_slug( 'professeur', 'maitre-de-conferences' ), + 'Doctorantes et doctorants' => $by_slug( 'doctorant' ), + 'Docteures et docteurs' => $by_slug( 'docteur' ), + 'Postdoctorantes et postdoctorants' => $by_slug( 'postdoctorant' ), + 'Personnel contractuel' => $by_slug( 'personnel-contractuel' ), + "Personnel d'accompagnement à la recherche" => $by_slug( 'personnel-technique' ), + 'Membres associées et membres associés' => $by_slug( 'membre-associe' ), + 'Anciennes et anciens membres' => $by_slug( 'anciens-membres' ), + ]; + + $groups = []; + + // Direction group first: directeur before directeur adjoint + $direction_members = []; + if ( $directeur_id && isset( $member_cache[ $directeur_id ] ) ) { + $direction_members[] = $member_cache[ $directeur_id ]; + } + if ( $adjoint_id && isset( $member_cache[ $adjoint_id ] ) ) { + $direction_members[] = $member_cache[ $adjoint_id ]; + } + if ( ! empty( $direction_members ) ) { + $groups[] = [ + 'title' => 'Direction', + 'members' => $direction_members, + 'fixed_order' => true, + ]; + } + + // Role-based groups (a user appears in every group that matches any of their roles) + foreach ( $group_definitions as $title => $term_ids ) { + $group_members = []; + foreach ( $users as $user ) { + $user_role_ids = thalim_get_user_role_ids( $user->ID ); + if ( array_intersect( $term_ids, $user_role_ids ) ) { + $group_members[] = $member_cache[ $user->ID ]; + } + } + + if ( empty( $group_members ) ) continue; + + // Sort alphabetically by last name, accent- and case-insensitive (fr locale) + static $collator = null; + if ( $collator === null ) { + $collator = class_exists( 'Collator' ) ? new Collator( 'fr_FR' ) : false; + if ( $collator ) $collator->setStrength( Collator::PRIMARY ); + } + usort( $group_members, function( $a, $b ) use ( $collator ) { + $la = $a['sort_key']; + $lb = $b['sort_key']; + if ( $collator ) return $collator->compare( $la, $lb ); + return strcmp( mb_strtolower( $la, 'UTF-8' ), mb_strtolower( $lb, 'UTF-8' ) ); + } ); + + // In "Personnel d'accompagnement", place "Gestion et pilotage" first + $fixed = false; + if ( $title === "Personnel d'accompagnement à la recherche" ) { + $priority = []; + $rest = []; + foreach ( $group_members as $m ) { + if ( stripos( $m['status'], 'Gestion et pilotage' ) !== false ) { + $priority[] = $m; + } else { + $rest[] = $m; + } + } + $group_members = array_merge( $priority, $rest ); + $fixed = true; + } + + $groups[] = [ + 'title' => $title, + 'members' => $group_members, + 'fixed_order' => $fixed, + ]; + } + + return $groups; +} diff --git a/inc/pods-conditional-required.php b/inc/pods-conditional-required.php new file mode 100644 index 0000000..ac3d121 --- /dev/null +++ b/inc/pods-conditional-required.php @@ -0,0 +1,94 @@ + $field ) { + $field_values[ $name ] = isset( $field['value'] ) ? $field['value'] : ''; + } + // Pour un post existant, si un champ n'a pas été soumis explicitement via + // pods_meta_* (ex. Pods DFV React en éditeur classique), remplir sa valeur + // depuis la BDD. Cela corrige à la fois : + // - la validation du champ lui-même (pieces['fields']['value']) + // - l'évaluation de la logique conditionnelle des autres champs ($field_values) + if ( ! $is_new_item && $id ) { + foreach ( $pieces['fields'] as $name => $field ) { + // Skip pick/file/avatar fields: their value format in $pieces is complex + // and get_post_meta returns a raw value that corrupts Pods' pick processing. + // These fields are always submitted via POST by Pods DFV React. + $field_type = pods_v( 'type', $field, '' ); + if ( in_array( $field_type, [ 'pick', 'file', 'avatar' ], true ) ) { + continue; + } + + $current_val = isset( $field['value'] ) ? $field['value'] : ''; + if ( ( '' === $current_val || null === $current_val ) && ! isset( $_POST[ 'pods_meta_' . $name ] ) ) { + $db_val = get_post_meta( (int) $id, $name, true ); + if ( '' !== $db_val && null !== $db_val ) { + $pieces['fields'][ $name ]['value'] = $db_val; + $field_values[ $name ] = $db_val; + } + } + } + } + + foreach ( $pieces['fields'] as $field_name => $field_data ) { + // Ne traiter que les champs required + $required = is_object( $field_data ) && method_exists( $field_data, 'get_field_object' ) + ? (int) $field_data->get_field_object()->get_arg( 'required', 0 ) + : (int) pods_v( 'required', $field_data, 0 ); + + if ( 1 !== $required ) { + continue; + } + + // Récupérer la logique conditionnelle + $conditional_logic = null; + + if ( is_object( $field_data ) && method_exists( $field_data, 'get_field_object' ) ) { + $conditional_logic = $field_data->get_field_object()->get_conditional_logic(); + } + + // Fallback : charger le champ via l'API Pods + if ( ! $conditional_logic ) { + $field_obj = pods_api()->load_field( [ + 'name' => $field_name, + 'pod' => 'post', + ] ); + + if ( $field_obj && method_exists( $field_obj, 'get_conditional_logic' ) ) { + $conditional_logic = $field_obj->get_conditional_logic(); + } + } + + if ( ! $conditional_logic ) { + continue; + } + + // Évaluer si le champ est visible avec les valeurs actuelles + if ( ! $conditional_logic->is_visible( $field_values ) ) { + // Le champ est masqué → désactiver le required + if ( is_object( $field_data ) && method_exists( $field_data, 'get_field_object' ) ) { + $field_data->get_field_object()->set_arg( 'required', 0 ); + } + $pieces['fields'][ $field_name ]['required'] = 0; + if ( isset( $pieces['fields'][ $field_name ]['options'] ) ) { + $pieces['fields'][ $field_name ]['options']['required'] = 0; + } + } + } + + return $pieces; +} diff --git a/inc/pods-save-error-handler.php b/inc/pods-save-error-handler.php new file mode 100644 index 0000000..f806770 --- /dev/null +++ b/inc/pods-save-error-handler.php @@ -0,0 +1,195 @@ + $val ) { + if ( str_starts_with( $key, 'pods_meta_' ) ) { + $restore[ $key ] = is_array( $val ) ? array_map( 'wp_unslash', $val ) : wp_unslash( $val ); + } + } + $error_text = is_wp_error( $error ) ? $error->get_error_message() : (string) $error; + $restore['_msg'] = wp_strip_all_tags( $error_text ); + $restore['_title'] = sanitize_text_field( wp_unslash( $_POST['post_title'] ?? '' ) ); + + set_transient( 'thalim_pods_restore_' . $post_id . '_' . $user_id, $restore, 10 * MINUTE_IN_SECONDS ); + + $GLOBALS['thalim_pods_error_post_id'] = $post_id; + + return false; // empêche wp_die() +}, 10, 2 ); + +// Après le save : rediriger vers la page d'édition + annuler le statut si besoin +add_filter( 'redirect_post_location', function ( $location ) { + $post_id = $GLOBALS['thalim_pods_error_post_id'] ?? 0; + if ( ! $post_id ) { + return $location; + } + + // Annuler le changement de statut vers publish si le post n'était pas encore publié + $original = isset( $_POST['original_post_status'] ) ? sanitize_key( $_POST['original_post_status'] ) : ''; + $post = get_post( $post_id ); + + if ( + $post && + in_array( $post->post_status, [ 'publish', 'future', 'pending' ], true ) && + ! in_array( $original, [ 'publish', 'future', 'pending' ], true ) + ) { + global $wpdb; + $wpdb->update( + $wpdb->posts, + [ 'post_status' => $original ?: 'draft' ], + [ 'ID' => $post_id ], + [ '%s' ], + [ '%d' ] + ); + clean_post_cache( $post_id ); + } + + return admin_url( 'post.php?post=' . $post_id . '&action=edit' ); +}, 10 ); + +// Sur la page d'édition (GET) : lire le transient une seule fois → global → supprimer +add_action( 'current_screen', function ( $screen ) { + if ( $screen->base !== 'post' ) { + return; + } + $post_id = isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0; + if ( ! $post_id ) { + return; + } + + $user_id = get_current_user_id(); + $key = 'thalim_pods_restore_' . $post_id . '_' . $user_id; + $data = get_transient( $key ); + if ( ! $data ) { + return; + } + + $GLOBALS['thalim_pods_restore'] = [ + 'post_id' => $post_id, + 'data' => $data, + ]; + delete_transient( $key ); +} ); + +// Injecter les valeurs dans get_post_meta → Pods DFV les embarque dans son JSON React +add_filter( 'get_post_metadata', function ( $value, $object_id, $meta_key, $single ) { + $restore = $GLOBALS['thalim_pods_restore'] ?? null; + if ( ! $restore || $restore['post_id'] !== (int) $object_id ) { + return $value; + } + + $pods_key = 'pods_meta_' . $meta_key; + if ( ! isset( $restore['data'][ $pods_key ] ) ) { + return $value; + } + + $val = $restore['data'][ $pods_key ]; + return $single ? $val : [ $val ]; +}, 10, 4 ); + +// Restauration JS : titre + champs Pods select/pick via PodsDFV (même pattern que les modales) +add_action( 'admin_footer', function () { + $restore = $GLOBALS['thalim_pods_restore'] ?? null; + if ( ! $restore ) { + return; + } + + $screen = get_current_screen(); + if ( ! $screen || $screen->base !== 'post' ) { + return; + } + + $post_id = intval( $restore['post_id'] ); + $title = $restore['data']['_title'] ?? ''; + + // Construire le map fieldName => value pour les champs Pods + $fields = []; + foreach ( $restore['data'] as $key => $val ) { + if ( str_starts_with( $key, 'pods_meta_' ) ) { + $fields[ substr( $key, 10 ) ] = $val; + } + } + + ?> + + base !== 'post' ) { + return; + } + + $msg = esc_html( $restore['data']['_msg'] ?? '' ); + if ( $msg ) { + echo '

    ' . $msg . '

    '; + } + echo '

    Votre contenu a été restauré. Vérifiez les champs obligatoires avant de republier.

    '; +} ); diff --git a/inc/post-card-helpers.php b/inc/post-card-helpers.php new file mode 100644 index 0000000..b85e61e --- /dev/null +++ b/inc/post-card-helpers.php @@ -0,0 +1,172 @@ + null, + 'card_membres' => [], + 'card_axes' => [], + 'card_etiquettes' => [], + 'parent_slug' => '', + 'card_category_name' => '', + 'card_category_url' => '', + 'card_type' => '', + 'card_event_date' => '', + 'card_event_date_iso' => '', + 'card_link' => '', + ]; + + // Event date — date_de_debut (events), fallback to datetime (communications) + // Used for display instead of post_date when set + foreach (['date_de_debut', 'datetime'] as $date_key) { + $event_raw = get_post_meta($post_id, $date_key, true) ?: ''; + if ($event_raw && !str_starts_with($event_raw, '0000-00-00')) { + $ts = strtotime($event_raw); + if ($ts) { + $data['card_event_date'] = date_i18n('d/m/Y', $ts); + $data['card_event_date_iso'] = date('Y-m-d', $ts); + break; + } + } + } + + // Resolve top-level parent category slug for color theming and direct category name for display + $categories = wp_get_post_categories($post_id, ['fields' => 'all']); + $excluded_ids = [12, 31]; + $is_seance = false; + foreach ($categories as $cat) { + if ($cat->term_id === 12) { $is_seance = true; } + } + foreach ($categories as $cat) { + if (in_array($cat->term_id, $excluded_ids)) continue; + $ancestor_ids = get_ancestors($cat->term_id, 'category'); + if (!empty($ancestor_ids)) { + $root = get_category(end($ancestor_ids)); + } else { + $root = $cat; + } + $data['parent_slug'] = $root->slug; + $data['card_category_name'] = thalim_cat_name($cat); + $data['card_category_url'] = get_category_link($cat->term_id); + break; + } + + // Séances de séminaire: link to parent séminaire with hash, derive color from parent's categories + if ($is_seance) { + // Always show the category label for séances even though cat 12 is excluded from color resolution + if (!$data['card_category_name']) { + $seance_cat = get_category(12); + if ($seance_cat) { + $data['card_category_name'] = thalim_cat_name($seance_cat); + $data['card_category_url'] = get_category_link(12); + } + } + global $wpdb; + $parent_id = $wpdb->get_var($wpdb->prepare( + "SELECT pm.post_id FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE pm.meta_key = 'seances' AND pm.meta_value = %s + AND p.post_status = 'publish' + LIMIT 1", + (string) $post_id + )); + if ($parent_id) { + $data['card_link'] = get_permalink((int) $parent_id) . '#seance-' . $post_id; + // Derive color from parent séminaire's categories if not already set + if (!$data['parent_slug']) { + foreach (wp_get_post_categories((int) $parent_id, ['fields' => 'all']) as $cat) { + if (in_array($cat->term_id, $excluded_ids)) continue; + $ancestor_ids = get_ancestors($cat->term_id, 'category'); + $root = !empty($ancestor_ids) ? get_category(end($ancestor_ids)) : $cat; + $data['parent_slug'] = $root->slug; + break; + } + } + } + } + + // Type label (first non-empty type_* field) + $type_fields = [ + 'type_colloque_journee_d_etude', + 'type_soutenance', + 'type_evenement_culturel', + 'type_media', + 'type_captation', + 'type_revue_collection', + 'type_autre', + ]; + foreach ($type_fields as $field) { + $val = get_post_meta($post_id, $field, true); + if ($val) { + $data['card_type'] = $val; + break; + } + } + + // First image from documents_joints + $doc_ids = get_post_meta($post_id, 'documents_joints', false); + foreach ($doc_ids as $doc_id) { + $mime = get_post_mime_type($doc_id); + if ($mime && str_starts_with($mime, 'image/')) { + $src = wp_get_attachment_image_url($doc_id, 'medium'); + if ($src) { + $data['card_image'] = $src; + break; + } + } + } + + // Members (user IDs → display names + profile URLs) + // Falls back to autre_membres if membres is empty + $membre_ids = get_post_meta($post_id, 'membres', false); + if (empty($membre_ids)) { + $membre_ids = get_post_meta($post_id, 'autre_membres', false); + } + foreach ($membre_ids as $uid) { + $user = get_userdata($uid); + if ($user) { + $data['card_membres'][] = [ + 'name' => $user->display_name, + 'url' => get_author_posts_url($uid), + ]; + } + } + + // Axes thématiques (post IDs → titles) + $axe_ids = get_post_meta($post_id, 'axes_thematiques', false); + foreach ($axe_ids as $axe_id) { + $axe = get_post($axe_id); + if ($axe) { + $data['card_axes'][] = $axe->post_title; + } + } + + // Etiquettes (post IDs → titles) + $tag_ids = get_post_meta($post_id, 'etiquettes', false); + foreach ($tag_ids as $tag_id) { + $tag_post = get_post($tag_id); + if ($tag_post) { + $data['card_etiquettes'][] = $tag_post->post_title; + } + } + + return $data; +} + +/** + * Build card data map for a collection of posts. + * Returns an array keyed by post ID. + */ +function thalim_get_cards_data($posts) { + $cards = []; + foreach ($posts as $post) { + $cards[$post->ID] = thalim_get_card_data($post->ID); + } + return $cards; +} diff --git a/inc/post-title-required.php b/inc/post-title-required.php new file mode 100644 index 0000000..07d01c5 --- /dev/null +++ b/inc/post-title-required.php @@ -0,0 +1,57 @@ +post_type, 'title' ) ) { + return; + } + + // Title was provided — nothing to do. + if ( trim( wp_unslash( $_POST['post_title'] ?? '' ) ) !== '' ) { + return; + } + + // Title is empty: store restore transient and signal the redirect handler + // (same keys as pods-save-error-handler.php so everything is shared). + $user_id = get_current_user_id(); + $restore = []; + + foreach ( $_POST as $key => $val ) { + if ( str_starts_with( $key, 'pods_meta_' ) ) { + $restore[ $key ] = is_array( $val ) + ? array_map( 'wp_unslash', $val ) + : wp_unslash( $val ); + } + } + + $restore['_msg'] = __( 'Le champ Titre est obligatoire.', 'thalim' ); + $restore['_title'] = ''; // intentionally empty — user must fill it + + set_transient( + 'thalim_pods_restore_' . $post_id . '_' . $user_id, + $restore, + 10 * MINUTE_IN_SECONDS + ); + + // Signal redirect_post_location (defined in pods-save-error-handler.php): + // it will revert the post status if needed and redirect to the edit screen. + $GLOBALS['thalim_pods_error_post_id'] = $post_id; +} diff --git a/inc/single-helpers.php b/inc/single-helpers.php new file mode 100644 index 0000000..bc2cf50 --- /dev/null +++ b/inc/single-helpers.php @@ -0,0 +1,447 @@ + thalim_bilingual( get_post_meta($post_id, 'sous-titre', true) ?: '', $lang ), + 'reference_bibliographique' => get_post_meta($post_id, 'reference_bibliographique', true) ?: '', + 'editeur' => get_post_meta($post_id, 'editeur', true) ?: '', + 'journal' => get_post_meta($post_id, 'journal', true) ?: '', + 'lieu' => thalim_bilingual( get_post_meta($post_id, 'lieu', true) ?: '', $lang ), + 'adresse' => get_post_meta($post_id, 'adresse', true) ?: '', + 'autrepersonnes' => get_post_meta($post_id, 'autrepersonnes', true) ?: '', + 'autre_autrepersonnes' => get_post_meta($post_id, 'autre_autrepersonnes', true) ?: '', + 'body_en' => apply_filters( 'the_content', get_post_meta($post_id, 'body_en', true) ?: '' ), + + // Dates (formatted for display) + 'datetime' => thalim_format_date(get_post_meta($post_id, 'datetime', true), $lang), + 'date_de_debut' => '', + 'date_de_fin' => '', + 'date_debut_ymd' => '', + 'date_fin_ymd' => '', + 'heure_de_debut' => substr( get_post_meta($post_id, 'heure_de_debut', true) ?: '', 0, 5 ), + 'heure_de_fin' => substr( get_post_meta($post_id, 'heure_de_fin', true) ?: '', 0, 5 ), + + // URLs + 'hal_url' => get_post_meta($post_id, 'hal_url', true) ?: '', + 'hal_file' => get_post_meta($post_id, 'hal_file', true) ?: '', + 'canal_u' => array_values( array_filter( array_map( function( $url ) { + if ( preg_match( '/(\d+)\/?$/', trim( $url ), $m ) ) { + return 'https://www.canal-u.tv/embed/' . $m[1] . '?t=0'; + } + return ''; + }, get_post_meta( $post_id, 'lien_canal_u', false ) ) ) ), + 'youtube' => array_values( array_filter( array_map( function( $url ) { + $url = trim( $url ); + // youtu.be/ID or youtube.com/embed/ID or youtube.com/watch?v=ID + if ( preg_match( '/(?:youtu\.be\/|youtube\.com\/(?:embed\/|watch\?.*v=|shorts\/))([A-Za-z0-9_-]{11})/', $url, $m ) ) { + return 'https://www.youtube-nocookie.com/embed/' . $m[1]; + } + return ''; + }, get_post_meta( $post_id, 'lien_youtube', false ) ) ) ), + + // Resolved below + 'liens_externes' => [], + 'membres' => [], + 'autre_membres' => [], + 'autre_fonction_label' => '', + 'axes' => [], + 'etiquettes' => [], + 'programmes' => [], + 'annonces_liees' => [], + 'seances_a_venir' => [], + 'seances_passees' => [], + 'show_image_titles' => (bool) get_post_meta($post_id, 'afficher_le_titre_des_images_en_legende', true), + 'images' => [], + 'documents' => [], + 'type_label' => '', + 'fonction_label' => '', + 'parent_slug' => '', + 'parent_name' => '', + 'parent_link' => '', + 'category_name' => '', + 'category_link' => '', + ]; + + // --- Dates --- + $raw_debut = get_post_meta($post_id, 'date_de_debut', true); + $raw_fin = get_post_meta($post_id, 'date_de_fin', true); + $ts_debut = ($raw_debut && !str_starts_with($raw_debut, '0000-00-00')) ? strtotime($raw_debut) : 0; + $ts_fin = ($raw_fin && !str_starts_with($raw_fin, '0000-00-00')) ? strtotime($raw_fin) : 0; + + $data['date_de_debut'] = thalim_format_date($raw_debut, $lang); + $data['date_de_fin'] = thalim_format_date($raw_fin, $lang); + if ($ts_debut) $data['date_debut_ymd'] = date('Y-m-d', $ts_debut); + if ($ts_fin) $data['date_fin_ymd'] = date('Y-m-d', $ts_fin); + + // --- External links (up to 3) --- + for ($i = 1; $i <= 3; $i++) { + $url = get_post_meta($post_id, 'lien_externe_' . $i, true); + if ($url) { + $titre = thalim_bilingual( get_post_meta($post_id, 'titre_du_lien_externe_' . $i, true) ?: '', $lang ); + if (!$titre) { + $host = parse_url($url, PHP_URL_HOST) ?: $url; + $parts = explode('.', $host); + $titre = count($parts) >= 2 ? implode('.', array_slice($parts, -2)) : $host; + } + $data['liens_externes'][] = [ + 'url' => $url, + 'titre' => $titre, + ]; + } + } + + // --- Category hierarchy for breadcrumb and color --- + $categories = wp_get_post_categories($post_id, ['fields' => 'all']); + $excluded_ids = [12, 31]; + foreach ($categories as $cat) { + if (in_array($cat->term_id, $excluded_ids)) continue; + $ancestor_ids = get_ancestors($cat->term_id, 'category'); + if (!empty($ancestor_ids)) { + $root = get_category(end($ancestor_ids)); + $data['parent_slug'] = $root->slug; + $data['parent_name'] = $root->name; + $data['parent_link'] = get_category_link($root->term_id); + $data['category_name'] = $cat->name; + } else { + $data['parent_slug'] = $cat->slug; + $data['parent_name'] = $cat->name; + $data['parent_link'] = get_category_link($cat->term_id); + $data['category_name'] = $lang === 'en' ? 'Other' : 'Autre'; + } + // category_link: for direct posts (no ancestors), point to the /autres index + $data['category_link'] = empty($ancestor_ids) + ? trailingslashit(get_category_link($cat->term_id)) . 'autres/' + : get_category_link($cat->term_id); + break; + } + + // --- Documents joints: split images vs files --- + $doc_ids = get_post_meta($post_id, 'documents_joints', false); + foreach ($doc_ids as $doc_id) { + $mime = get_post_mime_type($doc_id); + if (!$mime) continue; + if (str_starts_with($mime, 'image/')) { + $src = wp_get_attachment_image_url($doc_id, 'large'); + if ($src) { + $meta = wp_get_attachment_metadata($doc_id); + $w = isset($meta['width']) ? $meta['width'] : 0; + $h = isset($meta['height']) ? $meta['height'] : 0; + $data['images'][] = [ + 'url' => $src, + 'alt' => get_post_meta($doc_id, '_wp_attachment_image_alt', true) ?: '', + 'caption' => thalim_bilingual(wp_get_attachment_caption($doc_id) ?: '', $lang), + 'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang), + 'portrait' => ($h > $w), + ]; + } + } else { + $data['documents'][] = [ + 'url' => wp_get_attachment_url($doc_id), + 'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang) ?: basename(get_attached_file($doc_id)), + ]; + } + } + + // --- Members (user IDs → name + profile URL) --- + foreach (get_post_meta($post_id, 'membres', false) as $uid) { + $user = get_userdata($uid); + if ($user) { + $data['membres'][] = [ + 'name' => $user->display_name, + 'url' => get_author_posts_url($uid), + ]; + } + } + + // --- Autre membres (user IDs → name + profile URL) --- + foreach (get_post_meta($post_id, 'autre_membres', false) as $uid) { + $user = get_userdata($uid); + if ($user) { + $data['autre_membres'][] = [ + 'name' => $user->display_name, + 'url' => get_author_posts_url($uid), + ]; + } + } + + // --- Axes thématiques (taxonomy term IDs) --- + $axe_ids = get_post_meta($post_id, 'axes_thematiques', false); + foreach ($axe_ids as $axe_id) { + $term = get_term(intval($axe_id), 'axe_thematique'); + if ($term && !is_wp_error($term)) { + $data['axes'][] = [ + 'id' => $term->term_id, + 'name' => thalim_bilingual($term->name, $lang), + 'url' => get_term_link($term), + ]; + } + } + + // --- Étiquettes (taxonomy term IDs) --- + $tag_ids = get_post_meta($post_id, 'etiquettes', false); + foreach ($tag_ids as $tag_id) { + $term = get_term(intval($tag_id), 'post_tag'); + if ($term && !is_wp_error($term)) { + $data['etiquettes'][] = [ + 'id' => $term->term_id, + 'name' => thalim_bilingual($term->name, $lang), + 'url' => get_term_link($term), + ]; + } + } + + // --- Programmes de recherche (taxonomy term IDs) --- + $prog_ids = get_post_meta($post_id, 'programmes_de_recherche', false); + foreach ($prog_ids as $prog_id) { + $term = get_term(intval($prog_id), 'programme_de_recherche'); + if ($term && !is_wp_error($term)) { + $data['programmes'][] = [ + 'id' => $term->term_id, + 'name' => thalim_bilingual($term->name, $lang), + 'url' => get_term_link($term), + ]; + } + } + + // --- Annonces liées (related posts) --- + $related_ids = get_post_meta($post_id, 'annonces_liees', false); + if (!empty($related_ids)) { + $data['annonces_liees'] = Timber::get_posts([ + 'post_type' => 'post', + 'post__in' => array_map('intval', $related_ids), + 'posts_per_page' => -1, + 'lang' => '', + ]); + } + + // --- Séances (session posts) — split into upcoming / past --- + $seance_ids = get_post_meta($post_id, 'seances', false); + $data['seances_a_venir'] = []; + $data['seances_passees'] = []; + if (!empty($seance_ids)) { + $seance_posts = Timber::get_posts([ + 'post_type' => 'post', + 'post__in' => array_map('intval', $seance_ids), + 'posts_per_page' => -1, + 'orderby' => 'meta_value', + 'meta_key' => 'date_de_debut', + 'order' => 'ASC', + 'lang' => '', + 'post_status' => ['publish', 'future'], + ]); + $now = time(); + $current_year = date('Y'); + $months_fr = ['jan.', 'fév.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.']; + $months_en = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.']; + + foreach ($seance_posts as $seance) { + $raw_date = get_post_meta($seance->ID, 'date_de_debut', true); + $ts = $raw_date ? strtotime($raw_date) : strtotime($seance->post_date); + + // Only expose date_fin when it's a different day than date_de_debut + $raw_fin = get_post_meta($seance->ID, 'date_de_fin', true); + $ts_fin = $raw_fin && !str_starts_with($raw_fin, '0000-00-00') ? strtotime($raw_fin) : false; + $date_fin_display = ($ts_fin && date('Y-m-d', $ts_fin) !== date('Y-m-d', $ts)) + ? thalim_format_date($raw_fin, $lang) + : ''; + + $month_idx = intval(date('n', $ts)) - 1; + $seance_data = [ + 'post' => $seance, + 'day' => date('d', $ts), + 'month' => ($lang === 'en') ? $months_en[$month_idx] : $months_fr[$month_idx], + 'year' => (date('Y', $ts) !== $current_year) ? date('Y', $ts) : '', + 'date_full' => thalim_format_date($raw_date, $lang), + 'date_fin' => $date_fin_display, + 'heure_de_debut' => substr( get_post_meta($seance->ID, 'heure_de_debut', true) ?: '', 0, 5 ), + 'heure_de_fin' => substr( get_post_meta($seance->ID, 'heure_de_fin', true) ?: '', 0, 5 ), + 'lieu' => thalim_bilingual( get_post_meta($seance->ID, 'lieu', true) ?: '', $lang ), + 'adresse' => get_post_meta($seance->ID, 'adresse', true) ?: '', + 'body_en' => apply_filters( 'the_content', get_post_meta($seance->ID, 'body_en', true) ?: '' ), + 'intervenants' => [], + 'images' => [], + 'documents' => [], + 'liens_externes' => [], + 'annonces_liees' => [], + ]; + + // Resolve intervenants (membres or autrepersonnes) + $m_ids = get_post_meta($seance->ID, 'membres', false); + if (empty($m_ids)) { + $m_ids = get_post_meta($seance->ID, 'autre_membres', false); + } + foreach ($m_ids as $uid) { + $user = get_userdata($uid); + if ($user) { + $seance_data['intervenants'][] = [ + 'name' => $user->display_name, + 'url' => get_author_posts_url($uid), + ]; + } + } + $seance_data['autrepersonnes'] = get_post_meta($seance->ID, 'autrepersonnes', true) ?: ''; + $seance_data['show_image_titles'] = (bool) get_post_meta($seance->ID, 'afficher_le_titre_des_images_en_legende', true); + + // Documents joints: images and files + $s_doc_ids = get_post_meta($seance->ID, 'documents_joints', false); + foreach ($s_doc_ids as $doc_id) { + $mime = get_post_mime_type($doc_id); + if (!$mime) continue; + if (str_starts_with($mime, 'image/')) { + $src = wp_get_attachment_image_url($doc_id, 'large'); + if ($src) { + $seance_data['images'][] = [ + 'url' => $src, + 'alt' => get_post_meta($doc_id, '_wp_attachment_image_alt', true) ?: '', + 'caption' => thalim_bilingual(wp_get_attachment_caption($doc_id) ?: '', $lang), + 'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang), + ]; + } + } else { + $seance_data['documents'][] = [ + 'url' => wp_get_attachment_url($doc_id), + 'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang) ?: basename(get_attached_file($doc_id)), + ]; + } + } + + // External links (up to 3) + for ($i = 1; $i <= 3; $i++) { + $url = get_post_meta($seance->ID, 'lien_externe_' . $i, true); + if ($url) { + $titre = thalim_bilingual( get_post_meta($seance->ID, 'titre_du_lien_externe_' . $i, true) ?: '', $lang ); + if (!$titre) { + $host = parse_url($url, PHP_URL_HOST) ?: $url; + $parts = explode('.', $host); + $titre = count($parts) >= 2 ? implode('.', array_slice($parts, -2)) : $host; + } + $seance_data['liens_externes'][] = ['url' => $url, 'titre' => $titre]; + } + } + + // Annonces liées + $s_related_ids = get_post_meta($seance->ID, 'annonces_liees', false); + if (!empty($s_related_ids)) { + $seance_data['annonces_liees'] = Timber::get_posts([ + 'post_type' => 'post', + 'post__in' => array_map('intval', $s_related_ids), + 'posts_per_page' => -1, + 'lang' => '', + ]); + } + + if ($ts >= $now) { + $data['seances_a_venir'][] = $seance_data; + } else { + $data['seances_passees'][] = $seance_data; + } + } + // Past séances: most recent first + $data['seances_passees'] = array_reverse($data['seances_passees']); + } + + // --- Type label (category-conditional type fields) --- + $type_fields = [ + 'type_colloque_journee_d_etude', + 'type_soutenance', + 'type_evenement_culturel', + 'type_media', + 'type_captation', + 'type_revue_collection', + 'type_autre', + ]; + foreach ($type_fields as $field) { + $val = get_post_meta($post_id, $field, true); + if ($val) { + $data['type_label'] = thalim_bilingual( $val, $lang ); + break; + } + } + + // --- Fonction label (first non-empty fonction_* field) --- + $fonction_fields = [ + 'fonction_auteur', + 'fonction_organisation', + 'fonction_intervention', + 'fonction_redaction', + 'fonction_realisation', + 'fonction_dirige', + 'fonction_responsable', + 'fonction_candidat', + ]; + foreach ($fonction_fields as $field) { + $val = get_post_meta($post_id, $field, true); + if ($val) { + $data['fonction_label'] = thalim_bilingual( $val, $lang ); + break; + } + } + + // --- Autre fonction label (first non-empty autre_fonction_* field) --- + $autre_fonction_fields = [ + 'autre_fonction_autre', + 'autre_fonction_concerne', + 'autre_fonction_directeur', + 'autre_fonction_direction_d_ouvrage', + 'autre_fonction_intervenant', + 'autre_fonction_participants', + ]; + foreach ($autre_fonction_fields as $field) { + $val = get_post_meta($post_id, $field, true); + if ($val) { + $data['autre_fonction_label'] = thalim_bilingual( $val, $lang ); + break; + } + } + + // --- Fallback: derive labels from Pods categorie ID for older posts --- + if (!$data['fonction_label'] || !$data['autre_fonction_label']) { + $pods_cat = get_post_meta($post_id, '_pods_categorie', true); + $cat_id = (is_array($pods_cat) && !empty($pods_cat)) ? intval($pods_cat[0]) : 0; + + // Pods categorie ID → fonction label (main membres) + $cat_to_fonction = [ + 3 => 'Organisation', 4 => 'Auteur', 6 => 'Responsable', + 8 => 'Organisation', 9 => 'Responsable', 10 => 'Organisation', + 11 => 'Organisation', 12 => 'Intervention', 13 => 'Intervention', + 14 => 'Candidat', 15 => 'Auteur', 16 => 'Auteur', + 17 => 'Responsable', 18 => 'Organisation', 19 => 'Intervention', + 21 => 'Rédaction', 22 => 'Réalisation', 23 => 'Intervention', + 24 => 'Responsable', 25 => 'Responsable', 65 => 'Dirigé par', + ]; + + // Pods categorie ID → autre_fonction label (autre membres) + $cat_to_autre_fonction = [ + 3 => 'Participants', 4 => "Direction d'ouvrage", + 10 => 'Participants', 14 => 'Directeur de thèse', + 15 => "Direction d'ouvrage", 16 => "Direction d'ouvrage", + 19 => 'Membre concerné', 22 => 'Intervenant', + ]; + + if (!$data['fonction_label'] && isset($cat_to_fonction[$cat_id])) { + $data['fonction_label'] = $cat_to_fonction[$cat_id]; + } + if (!$data['autre_fonction_label'] && isset($cat_to_autre_fonction[$cat_id])) { + $data['autre_fonction_label'] = $cat_to_autre_fonction[$cat_id]; + } + } + + return $data; +} diff --git a/index.php b/index.php new file mode 100755 index 0000000..6c2c204 --- /dev/null +++ b/index.php @@ -0,0 +1,263 @@ +ID, $pin_field, true); + $fin = get_post_meta($post->ID, 'date_de_fin_depinglage', true); + $active = $epingle == '1' && (empty($fin) || $fin === '0000-00-00' || $fin >= $today); + if ($active) { $pinned[] = $post; } else { $normal[] = $post; } + } + return array_merge($pinned, array_slice($normal, 0, $max_normal)); +}; + +// --- Nombre d'items des diaporamas (depuis la page Le Laboratoire) --- +$labo_page = get_page_by_path('le-laboratoire'); +$max_swiper = $labo_page ? intval(get_post_meta($labo_page->ID, 'nombres_ditems_des_diaporamas', true)) : 0; +if ($max_swiper < 1) $max_swiper = 10; + +// --- Annonces diaporama --- +$annonces_raw = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => -1, + 'meta_query' => [[ + 'key' => 'afficher_dans_le_diaporama_dannonces_page_daccueil', + 'value' => '1', + ]], + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'thalim_event_date_order' => true, +]); +$context['annonces'] = $sort_with_pinned($annonces_raw, 'epingler_dans_le_diaporama_dannonces', $max_swiper); +$context['annonces_cards'] = thalim_get_cards_data($annonces_raw); + +// --- Publications et productions diaporama --- +$publications_raw = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => 30, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'tax_query' => [ + 'relation' => 'AND', + [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [4], + 'operator' => 'IN', + 'include_children' => true, + ], + [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [16], + 'operator' => 'NOT IN', + ], + ], + 'meta_query' => [ + 'relation' => 'OR', + [ + 'key' => 'type_autre', + 'value' => "Chapitre d'ouvrage", + 'compare' => '!=', + ], + [ + 'key' => 'type_autre', + 'compare' => 'NOT EXISTS', + ], + ], +]); +$context['publications'] = $sort_with_pinned($publications_raw, 'epingler_dans_le_diaporama_des_publications_et_productions', $max_swiper); +$context['publications_cards'] = thalim_get_cards_data($publications_raw); +$context['publications_link'] = thalim_en_url( get_category_link(4) ); +$context['annonces_link'] = thalim_en_url( get_permalink(29100) ); + +// --- Message du laboratoire --- +$messages_labo = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => 5, + 'cat' => 268, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', +]); +$context['messages_labo'] = $messages_labo ?: []; +$context['message_labo_link'] = thalim_en_url( get_category_link(268) ); + +// --- Agenda (médiation scientifique + séances de séminaire à venir) --- +$agenda_lang = thalim_current_language(); +$mediation_cat_ids = [5, 18, 19, 20, 21, 22, 23]; +$months_fr = ['jan.', 'fév.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.']; +$months_en = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.']; +$agenda_type_fields = [ + 'type_colloque_journee_d_etude', 'type_soutenance', 'type_evenement_culturel', + 'type_media', 'type_captation', 'type_revue_collection', 'type_autre', +]; +$now_str = date('Y-m-d H:i:s'); + +$make_agenda_item = function ($post, $raw_date, $type_label, $lieu, $link) use ($agenda_lang, $months_fr, $months_en) { + $ts = strtotime($raw_date); + if (!$ts) return null; + $month_idx = intval(date('n', $ts)) - 1; + return [ + 'post' => $post, + 'ts' => $ts, + 'day' => intval(date('j', $ts)), + 'month' => $agenda_lang === 'en' ? $months_en[$month_idx] : $months_fr[$month_idx], + 'type_label' => $type_label, + 'lieu' => $lieu, + 'link' => $link, + ]; +}; + +$agenda_items = []; + +// 1. Upcoming médiation scientifique posts +$mediation_upcoming = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => 8, + 'category__in' => $mediation_cat_ids, + 'orderby' => ['date_clause' => 'ASC'], + 'lang' => '', + 'meta_query' => [ + 'date_clause' => [ + 'key' => 'date_de_debut', + 'value' => $now_str, + 'compare' => '>=', + 'type' => 'DATETIME', + ], + ], +]); +foreach ($mediation_upcoming as $mpost) { + $raw_date = get_post_meta($mpost->ID, 'date_de_debut', true); + if (!$raw_date) continue; + $type_label = ''; + foreach ($agenda_type_fields as $field) { + $val = get_post_meta($mpost->ID, $field, true); + if ($val) { $type_label = $val; break; } + } + if (!$type_label) { + foreach (get_the_category($mpost->ID) as $cat) { + if (in_array($cat->term_id, $mediation_cat_ids)) { $type_label = thalim_cat_name($cat); break; } + } + } + $item = $make_agenda_item($mpost, $raw_date, $type_label, get_post_meta($mpost->ID, 'lieu', true) ?: '', get_permalink($mpost->ID)); + if ($item) $agenda_items[] = $item; +} + +// 2. Upcoming séances de séminaire (cat 12) +$seances_upcoming = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => 8, + 'category__in' => [12], + 'orderby' => ['date_clause' => 'ASC'], + 'lang' => '', + 'meta_query' => [ + 'date_clause' => [ + 'key' => 'date_de_debut', + 'value' => $now_str, + 'compare' => '>=', + 'type' => 'DATETIME', + ], + ], +]); +foreach ($seances_upcoming as $seance) { + $raw_date = get_post_meta($seance->ID, 'date_de_debut', true); + if (!$raw_date) continue; + // Direct DB lookup — bypasses Polylang and other hook filters + global $wpdb; + $parent_id = $wpdb->get_var($wpdb->prepare( + "SELECT pm.post_id FROM {$wpdb->postmeta} pm + JOIN {$wpdb->posts} p ON p.ID = pm.post_id + WHERE pm.meta_key = 'seances' AND pm.meta_value = %s + AND p.post_status = 'publish' + LIMIT 1", + (string) $seance->ID + )); + $link = $parent_id + ? get_permalink((int) $parent_id) . '#seance-' . $seance->ID + : get_permalink($seance->ID); + $label = $agenda_lang === 'en' ? 'Seminar session' : 'Séance de séminaire'; + $item = $make_agenda_item($seance, $raw_date, $label, get_post_meta($seance->ID, 'lieu', true) ?: '', $link); + if ($item) $agenda_items[] = $item; +} + +// Sort merged list by date, keep 5 soonest +usort($agenda_items, fn($a, $b) => $a['ts'] <=> $b['ts']); +$agenda_items = array_slice($agenda_items, 0, 5); + +// Fallback: if no upcoming events, show 5 most recent mediation events +if (empty($agenda_items)) { + $fallback = Timber::get_posts([ + 'post_type' => 'post', + 'posts_per_page' => 5, + 'category__in' => $mediation_cat_ids, + 'orderby' => ['date_clause' => 'DESC'], + 'lang' => '', + 'meta_query' => [ + 'date_clause' => ['key' => 'date_de_debut', 'type' => 'DATETIME'], + ], + ]); + foreach ($fallback as $fpost) { + $raw_date = get_post_meta($fpost->ID, 'date_de_debut', true); + if (!$raw_date) continue; + $type_label = ''; + foreach ($agenda_type_fields as $field) { + $val = get_post_meta($fpost->ID, $field, true); + if ($val) { $type_label = $val; break; } + } + if (!$type_label) { + foreach (get_the_category($fpost->ID) as $cat) { + if (in_array($cat->term_id, $mediation_cat_ids)) { $type_label = thalim_cat_name($cat); break; } + } + } + $item = $make_agenda_item($fpost, $raw_date, $type_label, get_post_meta($fpost->ID, 'lieu', true) ?: '', get_permalink($fpost->ID)); + if ($item) $agenda_items[] = $item; + } +} + +$context['agenda_items'] = $agenda_items; +$context['manifestations_link'] = thalim_en_url( add_query_arg( 'view', 'agenda', get_category_link(3) ) ); + +// --- Quick links --- +$newsletter_cat = get_category_by_slug('newsletter'); +$newsletter_url = ''; +if ($newsletter_cat) { + $nl_posts = get_posts([ + 'post_type' => 'post', + 'posts_per_page' => 1, + 'category__in' => [ $newsletter_cat->term_id ], + 'include_children' => false, + 'orderby' => 'date', + 'order' => 'DESC', + 'suppress_filters' => true, + ]); + if ( ! empty( $nl_posts ) ) { + $newsletter_url = thalim_en_url( get_permalink( $nl_posts[0]->ID ) ); + } +} +if ( ! $newsletter_url ) { + $newsletter_url = thalim_en_url( + $newsletter_cat ? get_category_link( $newsletter_cat->term_id ) : home_url( '/category/le-laboratoire/newsletter/' ) + ); +} +$context['quick_links'] = [ + 'agenda' => thalim_en_url(add_query_arg('view', 'agenda', get_category_link(3))), + 'contacts' => thalim_en_url(home_url('/contacts/')), + 'newsletter' => $newsletter_url, +]; + +// --- Tags (étiquettes) pour le nuage de mots-clés --- +$context['has_tags'] = !empty(get_terms([ + 'taxonomy' => 'post_tag', + 'hide_empty' => true, + 'number' => 1, + 'lang' => '', +])); + +Timber::render('index.twig', $context); \ No newline at end of file diff --git a/js/adminDashboardMods.js b/js/adminDashboardMods.js new file mode 100644 index 0000000..8470066 --- /dev/null +++ b/js/adminDashboardMods.js @@ -0,0 +1,883 @@ +(function($) { + 'use strict'; + + function isPostEditPage() { + return window.pagenow === 'post' + || window.pagenow === 'post-new' + // On CPTs, pagenow is the post_type slug — also catch them via the + // body classes WP sets for any post.php / post-new.php screen. + || document.body.classList.contains('post-php') + || document.body.classList.contains('post-new-php'); + } + + function isProfileEditPage() { + return window.pagenow === 'profile' || window.pagenow === 'user-edit' || window.pagenow === 'user-new'; + } + + function getProfileForm() { + return document.querySelector('#your-profile, #createuser'); + } + + function isPodsModal() { + return new URLSearchParams(window.location.search).has('pods_modal'); + } + + function renameArticlesToAnnonces() { + const replacements = [ + [/Tous les articles/g, 'Toutes les annonces'], + [/Ajouter un article/g, 'Ajouter une annonce'], + [/Modifier l.article/g, "Modifier l'annonce"], + [/Pr\u00e9visualiser l.article/g, "Pr\u00e9visualiser l'annonce"], + [/Afficher l.article/g, "Afficher l'annonce"], + [/Voir l.article/g, "Voir l'annonce"], + [/Article publi\u00e9/g, 'Annonce publi\u00e9e'], + [/Article mis \u00e0 jour/g, 'Annonce mise \u00e0 jour'], + [/Article planifi\u00e9/g, 'Annonce planifi\u00e9e'], + [/Articles par page/g, 'Annonces par page'], + [/Articles/g, 'Annonces'], + [/Article/g, 'Annonce'], + [/Rechercher des articles/g, 'Rechercher des annonces'], + ]; + + function applyReplacements(text) { + return replacements.reduce((t, [s, r]) => t.replace(s, r), text); + } + + function replaceInTextNodes(el) { + if (!el) return; + const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT); + const nodes = []; + while (walker.nextNode()) nodes.push(walker.currentNode); + nodes.forEach(function(node) { + const replaced = applyReplacements(node.textContent); + if (replaced !== node.textContent) node.textContent = replaced; + }); + } + + // Menu latéral + replaceInTextNodes(document.querySelector('#menu-posts')); + + // Titre de page (h1) et bouton d'ajout + document.querySelectorAll('.wp-heading-inline, .page-title-action').forEach(replaceInTextNodes); + + // Notifications après sauvegarde (Article publié, mis à jour…) + document.querySelectorAll('#message, .notice').forEach(replaceInTextNodes); + + // Boîte de publication — lien "Voir l'article" + replaceInTextNodes(document.querySelector('.submitbox')); + + // Options d'écran — "Articles par page" + replaceInTextNodes(document.querySelector('#screen-options-wrap')); + + // Bouton de recherche (attribut value + aria-label) + var searchSubmit = document.querySelector('#search-submit'); + if (searchSubmit) { + if (searchSubmit.value) { + searchSubmit.value = applyReplacements(searchSubmit.value); + } + var ariaLabel = searchSubmit.getAttribute('aria-label'); + if (ariaLabel) { + searchSubmit.setAttribute('aria-label', applyReplacements(ariaLabel)); + } + } + + // Titre de l'onglet du navigateur + document.title = applyReplacements(document.title); + } + + function updatePostboxVisibility() { + document.querySelectorAll('.postbox').forEach((postBox) => { + if (postBox.id.startsWith('pods')) { + // body-en is controlled by language tabs — never auto-hide it + if (postBox.id === 'pods-meta-body-en') return; + const fields = postBox.querySelectorAll('tr'); + const hasVisibleFields = Array.from(fields).some(field => field.style.display !== 'none'); + postBox.style.display = hasVisibleFields ? 'block' : 'none'; + } + }); + } + + // Force Visual (TinyMCE) mode on page load. + // WP stores the last-used editor mode in localStorage and restores it at document.ready. + // When Code mode is restored, TinyMCE is never initialised — tinymce.get() returns null. + // Instead, check the wrapper's CSS class: + // tmce-active = Visual mode (fine) + // html-active = Code mode (switch to Visual) + function ensureVisualMode(editorId, attempt) { + attempt = attempt || 0; + if (attempt > 15) return; + var wrap = document.getElementById('wp-' + editorId + '-wrap'); + if (!wrap) { + setTimeout(function() { ensureVisualMode(editorId, attempt + 1); }, 200); + return; + } + if (wrap.classList.contains('html-active')) { + var ed = window.tinymce && tinymce.get(editorId); + if (!ed || !ed.initialized) { + // TinyMCE not ready yet — retry rather than calling switchEditors.go() prematurely + setTimeout(function() { ensureVisualMode(editorId, attempt + 1); }, 200); + return; + } + if (typeof switchEditors !== 'undefined') { + switchEditors.go(editorId, 'tmce'); + } + return; + } + if (!wrap.classList.contains('tmce-active')) { + // Mode not yet determined — retry + setTimeout(function() { ensureVisualMode(editorId, attempt + 1); }, 200); + } + } + + // Phase 1: insert the tab bar and relocate #pods-meta-body-en. + // The DOM move breaks TinyMCE's iframe (browsers reset iframe content on detach), + // so we leave the container visible here and let Pods/TinyMCE initialise normally. + // The broken iframe is repaired by reinitEditor() on first EN tab open. + function setupBodyTabsDom() { + var nativeEditor = document.getElementById('postdivrich') || document.getElementById('postdiv'); + var bodyEnBox = document.getElementById('pods-meta-body-en'); + if (!nativeEditor || !bodyEnBox) return; + + var tabBar = document.createElement('div'); + tabBar.className = 'body-lang-tabs'; + tabBar.innerHTML = + '' + + ''; + nativeEditor.parentNode.insertBefore(tabBar, nativeEditor); + + // Move EN metabox to sit right after the native editor for correct visual layout. + // Do NOT hide it yet — Pods must init TinyMCE with the container visible so the + // iframe can measure its dimensions. Page is still opacity:0 so no flash. + nativeEditor.parentNode.insertBefore(bodyEnBox, nativeEditor.nextSibling); + } + + // Rebuild a TinyMCE editor whose iframe is broken (empty/non-interactive). + // This happens when TinyMCE is initialised on a hidden (display:none) element: + // the iframe can't measure dimensions and its document body stays empty. + // + // We reinit from tinyMCEPreInit.mceInit — first trying the editor's own config + // (registered by Pods server-side), falling back to 'content' (the native WP editor). + // + // Inline toolbar positioning fix: + // TinyMCE's 'wordpress' plugin captures document.getElementById(id+'_ifr') during + // 'preinit' — before the iframe is created — so mceIframe is always null. + // Fix: intercept getElementById during preinit so the 'wordpress' plugin captures + // a proxyIframe instead of null. After init, proxy delegates to the real iframe. + function reinitEditor(editorId) { + var ed = window.tinymce && tinymce.get(editorId); + // Preserve existing content before destroying the instance + var savedContent = ''; + if (ed) { + try { savedContent = ed.getContent(); } catch (e) {} + ed.remove(); + } + if (!savedContent) { + var ta = document.getElementById(editorId); + if (ta) savedContent = ta.value || ''; + } + + if (!window.tinyMCEPreInit || !window.tinymce) return; + + // Use the editor's own server-side config if available, else clone from 'content' + var baseInit = (tinyMCEPreInit.mceInit && tinyMCEPreInit.mceInit[editorId]) + || (tinyMCEPreInit.mceInit && tinyMCEPreInit.mceInit['content']); + if (!baseInit) return; + + // Proxy iframe: getBoundingClientRect() falls back to the editor wrap + var wrapId = 'wp-' + editorId + '-wrap'; + var proxyIframe = { + getBoundingClientRect: function() { + var el = document.getElementById(wrapId); + return el ? el.getBoundingClientRect() + : { top: 0, left: 0, right: window.innerWidth, + bottom: window.innerHeight, width: window.innerWidth, + height: window.innerHeight }; + } + }; + + var savedGetById = document.getElementById; + var origSetup = baseInit.setup; + var content = savedContent; + + tinymce.init($.extend({}, baseInit, { + selector: '#' + editorId, + setup: function(editor) { + if (typeof origSetup === 'function') origSetup(editor); + + editor.on('focus', function() { + window.wpActiveEditor = editorId; + }); + + editor.on('preinit', function() { + document.getElementById = function(id) { + if (id === editorId + '_ifr') return proxyIframe; + return savedGetById.call(document, id); + }; + setTimeout(function() { + document.getElementById = savedGetById; + }, 0); + }); + + editor.on('init', function() { + // Point proxy to real iframe + var realIframe = savedGetById.call(document, editorId + '_ifr'); + if (realIframe) { + proxyIframe.getBoundingClientRect = function() { + return realIframe.getBoundingClientRect(); + }; + } + // Restore content that was in the textarea + if (content) { + editor.setContent(content); + } + }); + } + })); + } + + // Phase 2: wire tab click handlers — runs at t=100ms after metabox reordering. + function initBodyLanguageTabs() { + var nativeEditor = document.getElementById('postdivrich') || document.getElementById('postdiv'); + var bodyEnBox = document.getElementById('pods-meta-body-en'); + var tabBar = document.querySelector('.body-lang-tabs'); + if (!nativeEditor || !bodyEnBox || !tabBar) { + // body_en not available (e.g. contributor role) — still force visual mode on main editor + if (nativeEditor) ensureVisualMode('content'); + return; + } + + var enEditorId = 'pods-form-ui-pods-meta-body-en'; + var enTmceReady = false; + + // Hide EN panel — page is still opacity:0, user won't see the switch + bodyEnBox.style.display = 'none'; + + tabBar.querySelectorAll('.body-lang-tab').forEach(function(btn) { + btn.addEventListener('click', function() { + tabBar.querySelectorAll('.body-lang-tab').forEach(function(b) { + b.classList.remove('is-active'); + }); + btn.classList.add('is-active'); + + var revealedPanel; + + if (btn.dataset.panel === 'fr') { + bodyEnBox.style.display = 'none'; + nativeEditor.style.opacity = '0'; + nativeEditor.style.display = ''; + revealedPanel = nativeEditor; + } else { + nativeEditor.style.display = 'none'; + bodyEnBox.style.opacity = '0'; + bodyEnBox.style.display = 'block'; + revealedPanel = bodyEnBox; + + if (!enTmceReady) { + enTmceReady = true; + // Reinit while container is visible so TinyMCE can measure dimensions + reinitEditor(enEditorId); + } + } + + // Notify TinyMCE to reflow, then fade in once layout is correct + setTimeout(function() { + window.dispatchEvent(new Event('resize')); + requestAnimationFrame(function() { + requestAnimationFrame(function() { + revealedPanel.style.opacity = ''; + }); + }); + }, 50); + }); + }); + + // Ensure both editors start in Visual (not Code) mode + ensureVisualMode('content'); + ensureVisualMode(enEditorId); + } + + function groupAxesCheckboxes() { + if (!window.thalimAxesGroups || !thalimAxesGroups.length) return; + + var row = document.querySelector('.pods-form-ui-row-name-axes-thematiques'); + if (!row) return; + + var list = row.querySelector('ul'); + if (!list) return; + + // Already grouped — nothing to do + if (list.querySelector('.axes-group-label')) return; + + // Map existing
  • by checkbox value; preserve "add new" button + var liMap = {}; + var addNewItem = null; + list.querySelectorAll('li').forEach(function(li) { + if (li.classList.contains('pods-pick-add-new')) { addNewItem = li; return; } + var cb = li.querySelector('input[type="checkbox"]'); + if (cb) liMap[cb.value] = li; + }); + + // Rebuild list in group order + list.innerHTML = ''; + thalimAxesGroups.forEach(function(group) { + var labelLi = document.createElement('li'); + labelLi.className = 'axes-group-label'; + labelLi.textContent = group.label; + list.appendChild(labelLi); + group.terms.forEach(function(term) { + var li = liMap[String(term.id)]; + if (li) list.appendChild(li); + }); + }); + + if (addNewItem) list.appendChild(addNewItem); + } + + var REF_BIB_EDITOR_ID = 'pods-form-ui-pods-meta-reference-bibliographique'; + var refBibReinited = false; + + // Reinit the référence bibliographique TinyMCE editor. + // Called at page load (if the field is already visible) and by the + // MutationObserver (when the field becomes visible after a category change). + function initRefBibEditor() { + if (refBibReinited) return; + var row = document.querySelector('.pods-form-ui-row-name-reference-bibliographique'); + if (!row || row.style.display === 'none') return; + refBibReinited = true; + reinitEditor(REF_BIB_EDITOR_ID); + ensureVisualMode(REF_BIB_EDITOR_ID); + } + + function initAxesGroupObserver() { + // Pods shows/hides conditional rows by removing inline style="display:none" + // Watch the entire Pods meta form for style changes on the axes row + var podsForm = document.querySelector('.pods-pick-values, #pods-meta-champs-contextuels, form#post'); + if (!podsForm) podsForm = document.body; + + var observer = new MutationObserver(function(mutations) { + for (var i = 0; i < mutations.length; i++) { + var target = mutations[i].target; + if (target.classList && target.classList.contains('pods-form-ui-row-name-axes-thematiques')) { + if (target.style.display !== 'none') { + setTimeout(groupAxesCheckboxes, 50); + } + } + // Reinit TinyMCE on the référence bibliographique field when its + // row becomes visible — Pods hides it with display:none which breaks + // the TinyMCE iframe. Only reinit once per page load. + if (!refBibReinited && target.classList && + target.classList.contains('pods-form-ui-row-name-reference-bibliographique')) { + if (target.style.display !== 'none') { + setTimeout(initRefBibEditor, 100); + } + } + } + }); + + observer.observe(podsForm, { attributes: true, attributeFilter: ['style'], subtree: true }); + } + + function initPostEditPage() { + // Disable category options (CSS handles the color) + const categorieSelect = document.querySelector('#pods-form-ui-pods-meta-categorie'); + if (categorieSelect) { + const categoriesToDisable = ['1', '12', '5', '20']; + categorieSelect.querySelectorAll('option').forEach(option => { + if (categoriesToDisable.includes(option.value)) { + option.disabled = true; + } + }); + } + + // Reorder meta boxes + const sideSortables = document.querySelector('#side-sortables'); + if (sideSortables) { + const typeDannonce = document.querySelector('#pods-meta-type-dannonce'); + const affichageAccueil = document.querySelector('#pods-meta-affichage-sur-laccueil'); + const thematique = document.querySelector('#pods-meta-thematique'); + if (typeDannonce) sideSortables.prepend(typeDannonce); + if (affichageAccueil) sideSortables.appendChild(affichageAccueil); + if (thematique) sideSortables.appendChild(thematique); + } + + const submitDiv = document.querySelector('#submitdiv'); + if (submitDiv && submitDiv.parentNode) { + submitDiv.parentNode.appendChild(submitDiv); + } + + const champsContextuels = document.querySelector('#pods-meta-champs-contextuels'); + if (champsContextuels && champsContextuels.parentNode) { + champsContextuels.parentNode.prepend(champsContextuels); + } + + initBodyLanguageTabs(); + initRefBibEditor(); + groupAxesCheckboxes(); + initAxesGroupObserver(); + updatePostboxVisibility(); + initDatePickerPopoverFix(); + initInfoPopovers(); + + // Place #pods-meta-documents-joints in #normal-sortables, right after + // #pods-meta-champs-contextuels. This keeps it out of #post-body-content + // (the body editor section) regardless of whether champsContextuels is + // currently visible. When champsContextuels is hidden it takes no space, + // so documentsJoints simply appears first in #normal-sortables. + const documentsJoints = document.querySelector('#pods-meta-documents-joints'); + if (documentsJoints) { + if (champsContextuels && champsContextuels.parentNode) { + champsContextuels.parentNode.insertBefore(documentsJoints, champsContextuels.nextSibling); + } else { + const normalSortables = document.querySelector('#normal-sortables'); + if (normalSortables) normalSortables.prepend(documentsJoints); + } + } + + // Inject separator row for the Membres grid layout + var membresTbody = document.querySelector('#pods-meta-membres .form-table tbody'); + if (membresTbody && !membresTbody.querySelector('.membres-grid-separator')) { + var sep = document.createElement('tr'); + sep.className = 'membres-grid-separator'; + membresTbody.appendChild(sep); + } + updateMembresGridSeparator(); + } + + var INFO_ICON = ''; + var TRANSLATE_ICON = ''; + + var TRANSLATE_LINES = [ + 'Traduction en anglais apr\u00e8s //', + 'ex\u00a0: Texte en fran\u00e7ais // English text' + ]; + + // Tips without a `page` key default to 'post'. + // type: 'translate' uses the globe icon + green button style. + var INFO_TIPS = [ + // --- post edit page: info --- + { + selector: '.wp-heading-inline', + lines: [ + 'Saisir le titre anglais apr\u00e8s //', + 'ex\u00a0: Titre de l\u2019annonce // Title of the announcement' + ] + }, + { + selector: '#pods-meta-documents-joints .postbox-header h2', + lines: [ + 'Ajouter les images dans les documents.', + 'Ajouter les l\u00e9gendes comme titre du document.' + ] + }, + { + selector: '#pods-meta-membres .postbox-header h2', + lines: [ + 'Le champ fonction change le libell\u00e9 de la liste de personnes cit\u00e9es.', + 'Le champ membre permet de lister les membres de Thalim li\u00e9s \u00e0 l\u2019annonce.', + 'Le champ autre personnes permet de lister des personnes ext\u00e9rieures \u00e0 Thalim.' + ] + }, + { + selector: '#pods-meta-dates .postbox-header h2', + lines: [ + 'Pour entrer une date sans l\u2019heure, r\u00e9gler l\u2019heure sur 00\u202f:00.' + ] + }, + { + selector: '#pods-meta-affichage-sur-laccueil .postbox-header h2', + lines: [ + '\u00c9pingler l\u2019annonce dans le diaporama la fait s\u2019afficher avant les autres.' + ] + }, + { + selector: '#pods-meta-medias .postbox-header h2', + lines: [ + 'Pour ajouter un m\u00e9dia Canal\u00a0U, copier le lien depuis \u00ab\u00a0Citer cette ressource\u00a0\u00bb.', + 'ex\u00a0: https://www.canal-u.tv/166564' + ] + }, + // --- post edit page: translate --- + { type: 'translate', selector: '#pods-meta-documents-joints .postbox-header h2', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-sous-titre th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-lieu th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-titre-du-lien-externe-1 th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-titre-du-lien-externe-2 th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-titre-du-lien-externe-3 th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-organisation th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-intervention th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-candidat th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-realisation th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-dirige th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-redaction th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-auteur th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-fonction-responsable th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-autre-fonction-autre th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-autre-fonction-concerne th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-autre-fonction-directeur th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-autre-fonction-direction-d-ouvrage th',lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-autre-fonction-intervenant th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-autre-fonction-participants th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-type-autre th', lines: TRANSLATE_LINES }, + // --- contenu_general edit page: translate --- + { type: 'translate', selector: '.pods-form-ui-row-name-umr th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-thalim th', lines: TRANSLATE_LINES }, + { type: 'translate', selector: '.pods-form-ui-row-name-siecles th', lines: TRANSLATE_LINES }, + // --- user/profile edit page: translate --- + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-titre-du-lien-1 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-titre-du-lien-2 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-titre-du-lien-3 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-titre-du-lien-4 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-complement-de-role-1 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-complement-de-role-2 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-complement-de-role-3 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-affichage-du-statut-1 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-affichage-du-statut-2 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-affichage-du-statut-3 th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-affiliation-autre th', lines: TRANSLATE_LINES }, + { type: 'translate', page: 'user', selector: '.pods-form-ui-row-name-titre-de-these th', lines: TRANSLATE_LINES }, + // --- taxonomy edit pages: translate --- + { type: 'translate', page: 'taxonomy', selector: 'label[for="name"]', lines: TRANSLATE_LINES }, + // --- user/profile edit page: info --- + { + page: 'user', + selector: '.pods-form-ui-label-pods-meta-identifiant-hal', + lines: [ + 'Renseigner votre idHAL (en lettres), pas votre PersonId (en chiffres).' + ] + }, + { + page: 'user', + selector: '.pods-form-ui-label-pods-meta-affichage-du-statut-1', + lines: [ + 'Texte de statut affiché sur le profil publique.' + ] + } + ]; + + var _popoverCloseHandlerRegistered = false; + + function initInfoPopovers(currentPage) { + currentPage = currentPage || 'post'; + + INFO_TIPS.forEach(function(tip) { + if ((tip.page || 'post') !== currentPage) return; + + var el = document.querySelector(tip.selector); + if (!el) return; + + var isTranslate = tip.type === 'translate'; + + var btn = document.createElement('button'); + btn.type = 'button'; + btn.className = isTranslate ? 'thalim-translate-btn' : 'thalim-info-btn'; + btn.setAttribute('aria-label', isTranslate ? 'Traduction bilingue' : 'Informations'); + btn.innerHTML = isTranslate ? TRANSLATE_ICON : INFO_ICON; + + var popover = document.createElement('div'); + popover.className = 'thalim-info-popover' + (isTranslate ? ' thalim-translate-popover' : ''); + popover.innerHTML = tip.lines.map(function(line) { + return '

    ' + line + '

    '; + }).join(''); + + var wrapper = document.createElement('span'); + wrapper.className = 'thalim-info-wrapper'; + wrapper.appendChild(btn); + wrapper.appendChild(popover); + + el.appendChild(wrapper); + + btn.addEventListener('click', function(e) { + e.stopPropagation(); + var isOpen = popover.classList.contains('is-open'); + document.querySelectorAll('.thalim-info-popover.is-open').forEach(function(p) { + p.classList.remove('is-open'); + }); + if (!isOpen) { + var rect = btn.getBoundingClientRect(); + popover.style.top = (rect.bottom + 6) + 'px'; + popover.style.left = (rect.left + rect.width / 2) + 'px'; + popover.classList.add('is-open'); + } + }); + + popover.addEventListener('click', function(e) { + e.stopPropagation(); + }); + }); + + if (!_popoverCloseHandlerRegistered) { + _popoverCloseHandlerRegistered = true; + document.addEventListener('click', function() { + document.querySelectorAll('.thalim-info-popover.is-open').forEach(function(p) { + p.classList.remove('is-open'); + }); + }); + } + } + + // Only native WP field sections — never touch Pods tables (they may contain TinyMCE editors) + var PROFILE_SECTION_KEYS = [ + 'user-language-wrap', + 'user-first-name-wrap', + 'user-email-wrap', + 'user-pass1-wrap', + 'upload-avatar-row', + ]; + + // Desired order. Groups of 2 are wrapped in a flex row and displayed side by side. + var PROFILE_ORDER = [ + ['user-first-name-wrap', 'upload-avatar-row'], + ['user-email-wrap'], + ['user-language-wrap', 'user-pass1-wrap'], + ]; + + function reorderProfileSections() { + var form = getProfileForm(); + if (!form) return; + + var pairMap = {}; + + form.querySelectorAll('table.form-table').forEach(function(table) { + PROFILE_SECTION_KEYS.forEach(function(key) { + if (pairMap[key] || !table.querySelector('.' + key)) return; + + // Find the associated heading: first try preceding sibling in same parent, + // then look for an h2/h3 inside the same wrapper element. + var h2 = null; + var el = table.previousElementSibling; + while (el) { + if (el.tagName === 'H2' || el.tagName === 'H3') { h2 = el; break; } + if (el.tagName === 'TABLE') break; + el = el.previousElementSibling; + } + if (!h2 && table.parentElement !== form) { + h2 = table.parentElement.querySelector('h2, h3'); + } + + // The unit to move: if h2 and table share a non-form wrapper, move the wrapper. + var wrapper = null; + if (h2 && h2.parentElement !== form && h2.parentElement === table.parentElement) { + wrapper = h2.parentElement; + } + + pairMap[key] = { h2: h2, table: table, wrapper: wrapper }; + }); + }); + + // Remove all matched units from DOM (dedup by actual element) + var removed = new Set(); + function removeEl(el) { + if (el && !removed.has(el)) { removed.add(el); el.remove(); } + } + Object.values(pairMap).forEach(function(unit) { + if (unit.wrapper) { removeEl(unit.wrapper); } + else { removeEl(unit.h2); removeEl(unit.table); } + }); + + // Re-insert in declared order before the submit button + var submitAnchor = form.querySelector('p.submit'); + function append(el) { + if (submitAnchor && submitAnchor.parentNode) form.insertBefore(el, submitAnchor); + else form.appendChild(el); + } + function appendUnit(unit) { + if (unit.wrapper) { append(unit.wrapper); } + else { if (unit.h2) append(unit.h2); append(unit.table); } + } + + PROFILE_ORDER.forEach(function(group) { + var available = group.filter(function(key) { return !!pairMap[key]; }); + if (!available.length) return; + + // Dedup: two keys may resolve to the same table/wrapper + var seen = new Set(); + var units = []; + available.forEach(function(key) { + var unit = pairMap[key]; + var id = unit.wrapper || unit.table; + if (!seen.has(id)) { seen.add(id); units.push(unit); } + }); + + if (units.length === 1) { + appendUnit(units[0]); + } else { + var row = document.createElement('div'); + row.className = 'profile-section-row'; + units.forEach(function(unit) { + var col = document.createElement('div'); + col.className = 'profile-section-col'; + if (unit.wrapper) { col.appendChild(unit.wrapper); } + else { if (unit.h2) col.appendChild(unit.h2); col.appendChild(unit.table); } + row.appendChild(col); + }); + append(row); + } + }); + } + + function initProfileEditors() { + reorderProfileSections(); + initInfoPopovers('user'); + + // Hide the "À propos du compte" section heading + document.querySelectorAll('#your-profile h2, #adduser h2, #createuser h2').forEach(function(h2) { + if (h2.textContent.trim() === '\u00c0 propos du compte') { + h2.style.display = 'none'; + } + }); + + // Rename "Rôle" label to "Rôle sur le site" + var roleLabel = document.querySelector('label[for="role"]'); + if (roleLabel && roleLabel.textContent.trim() === 'R\u00f4le') { + roleLabel.textContent = 'R\u00f4le sur le site'; + } + } + + // Gutenberg's Popover component closes on outside click via focusout detection. + // But if focus never enters the popover, focusout never fires and clicking outside + // does nothing. Fix: focus the popover container as soon as it appears in the DOM. + function initDatePickerPopoverFix() { + var observer = new MutationObserver(function(mutations) { + for (var i = 0; i < mutations.length; i++) { + var added = mutations[i].addedNodes; + for (var j = 0; j < added.length; j++) { + var node = added[j]; + if (node.nodeType !== 1) continue; + var content = node.classList.contains('components-popover__content') + ? node + : node.querySelector && node.querySelector('.components-popover__content'); + if (content) { + var c = content; + requestAnimationFrame(function() { + if (!c.hasAttribute('tabindex')) c.setAttribute('tabindex', '-1'); + c.focus(); + }); + } + } + } + }); + observer.observe(document.body, { childList: true, subtree: true }); + } + + function updateMembresGridSeparator() { + var sep = document.querySelector('#pods-meta-membres .membres-grid-separator'); + if (!sep) return; + var autreRows = document.querySelectorAll('#pods-meta-membres [class*="pods-form-ui-row-name-autre-"]'); + var anyVisible = Array.from(autreRows).some(function(row) { + return row.style.display !== 'none'; + }); + sep.style.display = anyVisible ? '' : 'none'; + } + + // Inject a "Type de programme" filter select into the taxonomy search form. + // The form already has hidden taxonomy/post_type fields so the select value + // is submitted with them and picked up by pre_get_terms server-side. + function initProgrammeFilter() { + var form = document.querySelector('form.search-form'); + if (!form) return; + + var types = [ + 'Programme subventionné', + 'Autre programme', + 'Ancien programme' + ]; + + // Read current filter value from the URL. + var params = new URLSearchParams(window.location.search); + var current = params.get('type_de_programme') || ''; + + var select = document.createElement('select'); + select.name = 'type_de_programme'; + select.id = 'filter-type-de-programme'; + select.style.cssText = 'margin-right:6px;'; + + var blank = document.createElement('option'); + blank.value = ''; + blank.textContent = 'Tous les types'; + select.appendChild(blank); + + types.forEach(function(type) { + var opt = document.createElement('option'); + opt.value = type; + opt.textContent = type; + if (type === current) opt.selected = true; + select.appendChild(opt); + }); + + // Insert before the first

    (search-box) inside the form. + var searchBox = form.querySelector('p.search-box'); + form.insertBefore(select, searchBox || null); + } + + $(document).ready(function() { + renameArticlesToAnnonces(); + + if (isPostEditPage()) { + setupBodyTabsDom(); + } + + setTimeout(() => { + if (isPostEditPage()) { + initPostEditPage(); + } + if (isProfileEditPage()) { + initProfileEditors(); + } + var TRANSLATE_TAXONOMIES = ['axe_thematique', 'programme_de_recherche', 'post_tag']; + var isTaxonomyListPage = TRANSLATE_TAXONOMIES.some(function(tax) { + return window.location.search.indexOf('taxonomy=' + tax) !== -1; + }); + if (isTaxonomyListPage) { + initInfoPopovers('taxonomy'); + } + if (window.location.search.indexOf('taxonomy=programme_de_recherche') !== -1) { + initProgrammeFilter(); + } + document.body.classList.add('admin-mods-ready'); + }, 100); + + // Fallback: force reveal after 2s in case the 100ms path failed (e.g. JS error mid-init) + setTimeout(() => { + document.body.classList.add('admin-mods-ready'); + }, 2000); + + $('#pods-form-ui-pods-meta-categorie').change(function() { + setTimeout(function() { + updatePostboxVisibility(); + updateMembresGridSeparator(); + }, 10); + }); + + if (isProfileEditPage() || window.pagenow === 'edit-tags' || window.pagenow === 'term') { + $(window).on('load', function() { + var scope = getProfileForm() || document; + scope.querySelectorAll('.pods-dfv-container-wysiwyg textarea').forEach(function(ta) { + if (!ta.id) return; + ensureVisualMode(ta.id); + }); + }); + } + + if (isPodsModal()) { + $(window).on('load', function() { + var itemId = $('#post_ID').val(); + if (window.PodsDFV && itemId) { + window.PodsDFV.setFieldValue('post', itemId, 'categorie', '12', 0); + } + + // Lock category select to 12 in iframe — delay to run after Pods React re-render + setTimeout(function() { + var $select = $('#pods-form-ui-pods-meta-categorie'); + if ($select.length) { + $select.find('option').each(function() { + this.disabled = this.value !== '12'; + }); + $select.val('12'); + } + updatePostboxVisibility(); + }, 200); + }); + } + }); + +})(jQuery); diff --git a/js/adminFormRestore.js b/js/adminFormRestore.js new file mode 100644 index 0000000..ece0ae9 --- /dev/null +++ b/js/adminFormRestore.js @@ -0,0 +1,119 @@ +(function($) { + 'use strict'; + + var STORAGE_PREFIX = 'thalim_form_restore_'; + var MAX_AGE_MS = 10 * 60 * 1000; // 10 min + + function getPostId() { + return $('#post_ID').val() || 'new'; + } + + function getStorageKey() { + return STORAGE_PREFIX + getPostId(); + } + + function saveFormData() { + var data = { timestamp: Date.now() }; + + var $title = $('#title'); + if ($title.length) data.title = $title.val(); + + var $content = $('#content'); + if ($content.length) { + if (typeof tinyMCE !== 'undefined' && tinyMCE.get('content')) { + data.content = tinyMCE.get('content').getContent(); + } else { + data.content = $content.val(); + } + } + + $('[name^="pods_meta_"]').each(function() { + data[this.name] = $(this).val(); + }); + + try { + sessionStorage.setItem(getStorageKey(), JSON.stringify(data)); + } catch (e) {} + } + + function restoreFormData() { + var navEntries = performance.getEntriesByType('navigation'); + if (!navEntries.length || navEntries[0].type !== 'back_forward') return; + + var key = getStorageKey(); + var stored, data; + try { + stored = sessionStorage.getItem(key); + if (!stored) return; + data = JSON.parse(stored); + } catch (e) { return; } + + if (!data.timestamp || Date.now() - data.timestamp > MAX_AGE_MS) { + try { sessionStorage.removeItem(key); } catch (e) {} + return; + } + + // Restaurer titre + if (data.title !== undefined) $('#title').val(data.title); + + // Restaurer contenu (TinyMCE ou textarea brut) + if (data.content !== undefined) { + $('#content').val(data.content); + if (typeof tinyMCE !== 'undefined') { + var ed = tinyMCE.get('content'); + if (ed) { + ed.setContent(data.content); + } else { + tinyMCE.on('AddEditor', function(e) { + if (e.editor.id === 'content') { + e.editor.on('init', function() { + e.editor.setContent(data.content); + }); + } + }); + } + } + } + + // Restaurer champs Pods après rendu React + var restorePods = function() { + setTimeout(function() { + var postId = getPostId(); + Object.keys(data).forEach(function(name) { + if (!name.startsWith('pods_meta_')) return; + var fieldName = name.replace(/^pods_meta_/, ''); + var value = data[name]; + var $el = $('[name="' + name + '"]'); + if ($el.length) $el.val(value).trigger('change'); + if (window.PodsDFV && postId) { + try { window.PodsDFV.setFieldValue('post', postId, fieldName, value, 0); } catch (e) {} + } + }); + }, 300); + }; + + if (document.readyState === 'complete') { + restorePods(); + } else { + $(window).on('load', restorePods); + } + + // Notice informative + var $notice = $('

    Votre contenu a \u00e9t\u00e9 restaur\u00e9 suite \u00e0 une erreur de validation. V\u00e9rifiez les champs obligatoires avant de publier.

    '); + $('#wpbody-content').find('.wrap').first().find('h1').after($notice); + } + + $(document).ready(function() { + if (window.pagenow !== 'post' && window.pagenow !== 'post-new') return; + + // Nettoyage si sauvegarde réussie + if ($('.notice-success, #message.updated').length) { + try { sessionStorage.removeItem(getStorageKey()); } catch (e) {} + return; + } + + $('#post').on('submit', saveFormData); + restoreFormData(); + }); + +})(jQuery); diff --git a/js/agendaView.js b/js/agendaView.js new file mode 100644 index 0000000..dba2fe0 --- /dev/null +++ b/js/agendaView.js @@ -0,0 +1,146 @@ +(function () { + 'use strict'; + + document.addEventListener('DOMContentLoaded', function () { + var toggleBtn = document.querySelector('.agenda-toggle-btn'); + var gridSections = document.getElementById('grid-sections'); + var agendaEl = document.getElementById('agenda-view'); + if (!toggleBtn || !agendaEl) return; + + var swiper = null; + var agendaPage = 0; + var agendaDone = false; + var agendaLoading = false; + var swiperWrapper = document.getElementById('agenda-swiper-wrapper'); + var agendaSpinner = document.getElementById('agenda-spinner'); + + var categoryId = agendaEl.dataset.category || ''; + var includeChildren = agendaEl.dataset.includeChildren || '0'; + var axe = agendaEl.dataset.axe || ''; + var dateFrom = agendaEl.dataset.dateFrom || ''; + var dateTo = agendaEl.dataset.dateTo || ''; + + function showAgenda() { + if (gridSections) gridSections.style.display = 'none'; + agendaEl.classList.add('is-active'); + toggleBtn.innerHTML = + '' + + (agendaViewData.lang === 'en' ? 'Switch to grid view' : 'Passer à la vue grille'); + if (!swiper) { + loadMoreAgenda(function (data) { + var todayOffset = Math.max(0, ((data && data.today_offset) || 0) - 1); + var anchorPage = Math.ceil((todayOffset + 1) / 12); + if (anchorPage > 1) { + chainLoadPages(2, anchorPage, function () { + initSwiper(todayOffset); + }); + } else { + initSwiper(todayOffset); + } + }); + } + } + + function showGrid() { + agendaEl.classList.remove('is-active'); + if (gridSections) gridSections.style.display = ''; + toggleBtn.innerHTML = + '' + + (agendaViewData.lang === 'en' ? 'Switch to agenda view' : 'Passer à la vue agenda'); + } + + function initSwiper(initialSlide) { + swiper = new Swiper(agendaEl.querySelector('.agenda-swiper'), { + slidesPerView: 1.2, + spaceBetween: 20, + initialSlide: initialSlide || 0, + navigation: { + nextEl: agendaEl.querySelector('.agenda-swiper-prev'), + prevEl: agendaEl.querySelector('.agenda-swiper-next'), + }, + breakpoints: { + 640: { slidesPerView: 2, spaceBetween: 24 }, + 1024: { slidesPerView: 3, spaceBetween: 32 }, + }, + on: { + reachEnd: function () { + if (!agendaDone) loadMoreAgenda(); + }, + }, + }); + swiper.changeLanguageDirection('rtl'); + } + + function chainLoadPages(fromPage, toPage, callback) { + if (fromPage > toPage) { callback(); return; } + loadMoreAgenda(function () { + chainLoadPages(fromPage + 1, toPage, callback); + }); + } + + function loadMoreAgenda(callback) { + if (agendaLoading || agendaDone) return; + agendaLoading = true; + agendaPage++; + agendaSpinner.style.display = 'flex'; + + var data = new FormData(); + data.append('action', 'load_more_agenda'); + data.append('page', agendaPage); + data.append('nonce', agendaViewData.nonce); + data.append('lang', agendaViewData.lang); + if (categoryId) data.append('category', categoryId); + if (includeChildren==='1') data.append('include_children', '1'); + if (axe) data.append('axe', axe); + if (dateFrom) data.append('date_from', dateFrom); + if (dateTo) data.append('date_to', dateTo); + + fetch(agendaViewData.ajaxUrl, { method: 'POST', body: data }) + .then(function (r) { return r.json(); }) + .then(function (result) { + agendaSpinner.style.display = 'none'; + agendaLoading = false; + if (result.success && result.data.html) { + var tmp = document.createElement('div'); + tmp.innerHTML = result.data.html; + Array.from(tmp.children).forEach(function (slide) { + if (swiper) { + swiper.appendSlide(slide.outerHTML); + } else { + swiperWrapper.appendChild(slide); + } + }); + if (swiper) swiper.update(); + if (callback) callback(result.data); + } else { + agendaDone = true; + if (callback) callback(result.data); + } + }) + .catch(function () { + agendaSpinner.style.display = 'none'; + agendaLoading = false; + }); + } + + toggleBtn.addEventListener('click', function (e) { + e.preventDefault(); + if (agendaEl.classList.contains('is-active')) { + var url = new URL(window.location.href); + url.searchParams.delete('view'); + history.pushState({}, '', url.toString()); + showGrid(); + } else { + var url = new URL(window.location.href); + url.searchParams.set('view', 'agenda'); + history.pushState({}, '', url.toString()); + showAgenda(); + } + }); + + // Initial state driven by server-side class + if (agendaEl.classList.contains('is-active')) { + showAgenda(); + } + }); +}()); diff --git a/js/animatedLogo.js b/js/animatedLogo.js new file mode 100644 index 0000000..78b78ba --- /dev/null +++ b/js/animatedLogo.js @@ -0,0 +1,577 @@ +const CURSOR_INFLUENCE_INNER = 150; +const CURSOR_INFLUENCE_OUTER = 300; +const EASING_FACTOR = 0.03; + +const COLORS = [ +'#e0775d', +'#7cc0c6', +'#e05680', +'#46ae51', +'#bb8dd9', +'#f7ff29', +]; + +class FloatingShape { +constructor(element, originalX, originalY, width, height) { + this.element = element; + this.width = width; + this.height = height; + this.originalX = originalX; + this.originalY = originalY; + this.posX = originalX; + this.posY = originalY; + this.targetX = originalX; + this.targetY = originalY; +} + +update(mouseX, mouseY) { + const shapeCenterX = this.originalX + this.width / 2; + const shapeCenterY = this.originalY + this.height / 2; + + const dx = mouseX - shapeCenterX; + const dy = mouseY - shapeCenterY; + const distance = Math.sqrt(dx * dx + dy * dy); + + const innerRadius = CURSOR_INFLUENCE_INNER / 2; + const outerRadius = CURSOR_INFLUENCE_OUTER / 2; + + if (distance < outerRadius && distance > 0) { + const dirX = dx / distance; + const dirY = dy / distance; + + let strength, maxDisplacement; + + if (distance < innerRadius) { + strength = (innerRadius - distance) / innerRadius; + maxDisplacement = innerRadius; + } else { + const outerZoneProgress = (outerRadius - distance) / (outerRadius - innerRadius); + strength = outerZoneProgress * 0.5; + maxDisplacement = innerRadius * 0.6; + } + + this.targetX = this.originalX - dirX * strength * maxDisplacement; + this.targetY = this.originalY - dirY * strength * maxDisplacement; + } else { + this.targetX = this.originalX; + this.targetY = this.originalY; + } + + this.posX += (this.targetX - this.posX) * EASING_FACTOR; + this.posY += (this.targetY - this.posY) * EASING_FACTOR; +} + +render() { + this.element.style.transform = `translate3d(${this.posX}px, ${this.posY}px, 0)`; +} +} + +class FloatingShapesManager { +constructor(containerId) { + this.container = document.getElementById(containerId); + if (!this.container) { + console.error(`Container #${containerId} not found`); + return; + } + + this.shapes = []; + this.strokePaths = []; // Store all stroke paths for fill control + this.fillShape = null; // Reference to the filled shape SVG for fade control + this.fillShapeReady = false; // Track if fillshape animation has completed + this.thalimText = null; // Reference to the THALIM text element + this.textReady = false; // Track if text animation has completed + this.mouseX = 0; + this.mouseY = 0; + this.animationId = null; + this.isTouching = false; // Track if currently in a touch interaction + + this.init(); +} + +init() { + const containerRect = this.container.getBoundingClientRect(); + const containerWidth = containerRect.width; + const containerHeight = containerRect.height; + + const shapeConfigs = [ + { + id: 'shape-1', + svgPath: `${themeDirURI}/assets/logo-shapes/shape1.svg`, + baseWidth: 53.564522, + baseHeight: 112.37409, + scale: 1.5, + posX: this.isMobile() ? 20: 35, + posY: -20, + strokeWidth: 2, + animationDuration: 1.9, + gradientStart: COLORS[0], // orange + gradientEnd: '#e0b7ad' + }, + { + id: 'shape-2', + svgPath: `${themeDirURI}/assets/logo-shapes/shape4.svg`, + baseWidth: 74.08564, + baseHeight: 121.90051, + scale: 1.5, + posX: 0, + posY: this.isMobile() ? -8 : -5, + strokeWidth: 2, + animationDuration: 2.5, + gradientStart: '#aec4c6', + gradientEnd: COLORS[1] // blue + }, + { + id: 'shape-3', + svgPath: `${themeDirURI}/assets/logo-shapes/shape3.svg`, + baseWidth: 159.16571, + baseHeight: 87.756729, + scale: 1.5, + posX: 0, + posY: -10, + strokeWidth: 2, + animationDuration: 3.1, + gradientStart: COLORS[2], // pink + gradientEnd: '#e0b0be' + }, + { + id: 'shape-4', + svgPath: `${themeDirURI}/assets/logo-shapes/shape2.svg`, + baseWidth: 143.26076, + baseHeight: 20.26207, + scale: 1.5, + posX: 0, + posY: this.isMobile() ? 20 : 45, + strokeWidth: 2, + animationDuration: 3.7, + gradientStart: '#8bc491', + gradientEnd: COLORS[3] // green + }, + { + id: 'shape-5', + svgPath: `${themeDirURI}/assets/logo-shapes/shape5.svg`, + baseWidth: 155.66518, + baseHeight: 87.785599, + scale: 1.5, + posX: this.isMobile() ? 13 : 19.5, + posY: this.isMobile() ? -18 : -23.5, + strokeWidth: 2, + animationDuration: 4.3, + gradientStart: COLORS[4], // purple + gradientEnd: '#c9b0d9' + }, + { + id: 'fillshape', + svgPath: `${themeDirURI}/assets/logo-shapes/fillshape.svg`, + baseWidth: 142.78297, + baseHeight: 72.903015, + scale: this.isMobile() ? 0.78 : 1.47, + posX: this.isMobile() ? 7.5 : 11, + posY: this.isMobile() ? -13 : -14.5, + isFilled: true, + targetOpacity: 1, + animationDuration: 0.9, + animationDelay: 3.7 // Start after longest stroke animation + } + ]; + + // Create shapes with center-based positioning + const centerX = containerWidth / 2; + const centerY = containerHeight / 2; + + // Apply mobile scale on smaller viewports + const mobileScale = 0.8; + shapeConfigs.forEach((config) => { + const scale = config.id === 'fillshape' ? config.scale : this.isMobile() ? mobileScale : config.scale; + const scaledWidth = config.baseWidth * scale; + const scaledHeight = config.baseHeight * scale; + + // Calculate position from center with offset, minus half dimensions to center the shape + const x = centerX + (config.posX || 0) - scaledWidth / 2; + const y = centerY + (config.posY || 0) - scaledHeight / 2; + + // Create animated stroke element + this.createAnimatedStrokeElement(config, scaledWidth, scaledHeight, x, y); + }); + + // Create THALIM text overlay + this.createThalimText(centerX, centerY); + + this.container.addEventListener('mousemove', this.handleMouseMove.bind(this)); + this.container.addEventListener('mouseleave', this.handleMouseLeave.bind(this)); + this.container.addEventListener('touchstart', this.handleTouchStart.bind(this)); + this.container.addEventListener('touchmove', this.handleTouchMove.bind(this), { passive: false }); + this.container.addEventListener('touchend', this.handleTouchEnd.bind(this)); + window.addEventListener('resize', this.handleResize.bind(this)); + + // Set sketch margin on mobile based on hero-logos height + this.setSketchMarginOnMobile(); + + this.animate(); +} + +isMobile() { + return window.innerWidth < 768; // tablet breakpoint from scss/_variables.scss +} + +setSketchMarginOnMobile() { + if (this.isMobile()) { + const heroLogos = document.querySelector('.hero-logos'); + if (heroLogos) { + const logoHeight = heroLogos.offsetHeight; + this.container.style.marginTop = `${logoHeight}px`; + } + } else { + // Reset margin-top on desktop (handled by CSS) + this.container.style.marginTop = ''; + } +} + +async createAnimatedStrokeElement(config, width, height, x, y) { + try { + const response = await fetch(config.svgPath); + const svgText = await response.text(); + + const parser = new DOMParser(); + const svgDoc = parser.parseFromString(svgText, 'image/svg+xml'); + const svgElement = svgDoc.querySelector('svg'); + + if (!svgElement) { + console.error(`Failed to parse SVG for ${config.id}`); + return; + } + + // Create shape div for physics + const shapeDiv = document.createElement('div'); + shapeDiv.className = 'floating-shape'; + shapeDiv.id = config.id; + shapeDiv.style.zIndex = config.isFilled ? '10' : '1'; // Fillshape above strokes + + // Set SVG dimensions + svgElement.setAttribute('width', width); + svgElement.setAttribute('height', height); + + if (config.isFilled) { + // Handle filled shapes (fade-in animation) + svgElement.style.opacity = '0'; + svgElement.style.transition = 'opacity 0.3s ease-out'; // Smooth fade on mouse interaction + const duration = config.animationDuration || 1.5; + const delay = config.animationDelay || 0; + const targetOpacity = config.targetOpacity || 0.5; + + svgElement.style.setProperty('--target-opacity', targetOpacity); + svgElement.style.animation = `fadeIn ${duration}s ease-in-out ${delay}s forwards`; + + // Remove animation after it completes so we can control opacity manually + setTimeout(() => { + svgElement.style.animation = 'none'; + svgElement.style.opacity = targetOpacity; + this.fillShapeReady = true; // Mark fillshape as ready for interaction + }, (delay + duration) * 1000); + + // Store reference for mouse interaction + this.fillShape = svgElement; + this.fillShapeOpacity = targetOpacity; + + // Position the fillshape div (since it's not in the physics system) + shapeDiv.style.transform = `translate3d(${x}px, ${y}px, 0)`; + } else { + // Handle stroked shapes (gradient + stroke drawing animation) + // Create gradient for stroke + const gradientId = `gradient-${config.id}`; + + // Create defs element if it doesn't exist + let defs = svgElement.querySelector('defs'); + if (!defs) { + defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); + svgElement.insertBefore(defs, svgElement.firstChild); + } + + // Create linear gradient + const gradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient'); + gradient.setAttribute('id', gradientId); + gradient.setAttribute('x1', '0%'); + gradient.setAttribute('y1', '0%'); + gradient.setAttribute('x2', '100%'); + gradient.setAttribute('y2', '100%'); + + // Create gradient stops + const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); + stop1.setAttribute('offset', '0%'); + stop1.setAttribute('stop-color', config.gradientStart); + + const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); + stop2.setAttribute('offset', '100%'); + stop2.setAttribute('stop-color', config.gradientEnd); + + gradient.appendChild(stop1); + gradient.appendChild(stop2); + defs.appendChild(gradient); + + // Apply stroke styling and animation to all paths in the SVG + const paths = svgElement.querySelectorAll('path, polyline, polygon, line, circle, ellipse, rect'); + paths.forEach(path => { + // Set stroke properties + path.style.fill = 'white'; + path.style.fillOpacity = '0'; // Start invisible + path.style.transition = 'fill-opacity 0.5s ease-in-out'; // Smooth fill changes + path.style.stroke = `url(#${gradientId})`; + // Don't override stroke-width - preserve SVG's compensated values + path.style.strokeLinecap = 'round'; + path.style.strokeLinejoin = 'round'; + + // Calculate path length for animation + const pathLength = path.getTotalLength ? path.getTotalLength() : 1000; + + // Set CSS variable for path length + path.style.setProperty('--path-length', pathLength); + + // Set up stroke dash animation + path.style.strokeDasharray = pathLength; + path.style.strokeDashoffset = pathLength; + + // Create CSS animation (plays once and stays completed) + const duration = config.animationDuration || 3; + path.style.animation = `drawStroke ${duration}s ease-in-out forwards`; + + // Store path reference for fill control + this.strokePaths.push(path); + }); + + // Fade in white fill when text starts appearing (at 4.3s) + setTimeout(() => { + paths.forEach(path => { + path.style.fillOpacity = '0.7'; + }); + }, 4300); + } + + // Append SVG to shape div + shapeDiv.appendChild(svgElement); + this.container.appendChild(shapeDiv); + + // Create FloatingShape instance (only for shapes with physics, not filled shapes) + if (!config.isFilled) { + const floatingShape = new FloatingShape(shapeDiv, x, y, width, height); + this.shapes.push(floatingShape); + } + + } catch (error) { + console.error(`Error loading SVG for ${config.id}:`, error); + } +} + +createThalimText(centerX, centerY) { + + // Create container for the text + const textContainer = document.createElement('div'); + textContainer.className = 'thalim-text'; + textContainer.style.top = '-20px'; + textContainer.style.left = '10px'; + textContainer.style.transform = `translate3d(${centerX}px, ${centerY}px, 0) translate(-50%, -50%)`; // Center the text + textContainer.style.color = '#000000cc'; + + // Create individual letter spans + const letters = 'thalim'.split(''); + letters.forEach((letter, index) => { + const span = document.createElement('span'); + span.textContent = letter; + span.style.opacity = '0'; + span.style.animation = `letterAppear 0.8s ease-in-out ${4.2 + index * 0.1}s forwards`; + textContainer.appendChild(span); + }); + + // Mark text as ready after last letter finishes animating + // Last letter: 4.2s + 0.5s (6th letter) + 0.8s (duration) = 5.5s + setTimeout(() => { + this.textReady = true; + }, 5500); + + this.container.appendChild(textContainer); + this.thalimText = textContainer; +} + +handleMouseMove(e) { + // Ignore mouse events during touch interactions (prevents interference from synthetic mouse events) + if (this.isTouching) { + return; + } + + const rect = this.container.getBoundingClientRect(); + this.mouseX = e.clientX - rect.left; + this.mouseY = e.clientY - rect.top; + + // Fade out fillshape and text on mouse move (only if they've finished appearing) + if (this.fillShape && this.fillShapeReady) { + this.fillShape.style.opacity = '0'; + } + if (this.thalimText && this.textReady) { + this.thalimText.style.opacity = '0'; + } + // Fade out white fills on stroke shapes + this.strokePaths.forEach(path => { + path.style.fillOpacity = '0'; + }); +} + +handleMouseLeave() { + // Move mouse far away to trigger return animation + this.mouseX = -10000; + this.mouseY = -10000; + + // Fade fillshape and text back in after delay (let strokes settle first) + // Only if they have completed their initial animations + if (this.fillShape && this.fillShapeReady) { + setTimeout(() => { + this.fillShape.style.opacity = this.fillShapeOpacity; + }, 800); // Delay to allow strokes to return to position + } + if (this.thalimText && this.textReady) { + setTimeout(() => { + this.thalimText.style.opacity = '1'; + }, 800); + } + // Fade white fills back in on stroke shapes + setTimeout(() => { + this.strokePaths.forEach(path => { + path.style.fillOpacity = '0.7'; + }); + }, 800); +} + +handleTouchStart() { + // Mark that we're in a touch interaction to prevent mouse event interference + this.isTouching = true; +} + +handleTouchMove(e) { + e.preventDefault(); // Prevent scrolling while interacting with sketch + const rect = this.container.getBoundingClientRect(); + const touch = e.touches[0]; + this.mouseX = touch.clientX - rect.left; + this.mouseY = touch.clientY - rect.top; + + // Fade out fillshape and text on touch move (only if they've finished appearing) + if (this.fillShape && this.fillShapeReady) { + this.fillShape.style.opacity = '0'; + } + if (this.thalimText && this.textReady) { + this.thalimText.style.opacity = '0'; + } + // Fade out white fills on stroke shapes + this.strokePaths.forEach(path => { + path.style.fillOpacity = '0'; + }); +} + +handleTouchEnd() { + // Reset shapes to original position + this.handleMouseLeave(); + + // Clear touch flag after a delay to ignore synthetic mouse events + setTimeout(() => { + this.isTouching = false; + }, 500); +} + +handleResize() { + // Don't refresh on mobile (prevents refresh during scroll when browser bar appears/disappears) + if (this.isMobile()) { + // Still update margin-top on mobile if needed + this.setSketchMarginOnMobile(); + return; + } + + // Debounced resize: only recreate after user stops resizing for 250ms + // This prevents page crashes from rapid resize events + if (this.resizeTimeout) { + clearTimeout(this.resizeTimeout); + } + + this.resizeTimeout = setTimeout(() => { + // Stop animation loop before destroying + if (this.animationId) { + cancelAnimationFrame(this.animationId); + this.animationId = null; + } + + // Clear shapes and container + this.shapes = []; + this.container.innerHTML = ''; + + // Reinitialize + this.init(); + }, 250); +} + +animate() { + // Update all shapes + this.shapes.forEach(shape => { + shape.update(this.mouseX, this.mouseY); + shape.render(); + }); + + // Continue loop + this.animationId = requestAnimationFrame(this.animate.bind(this)); +} + +destroy() { + if (this.animationId) { + cancelAnimationFrame(this.animationId); + } + if (this.resizeTimeout) { + clearTimeout(this.resizeTimeout); + } + this.container.removeEventListener('mousemove', this.handleMouseMove); + this.container.removeEventListener('mouseleave', this.handleMouseLeave); + this.container.removeEventListener('touchstart', this.handleTouchStart); + this.container.removeEventListener('touchmove', this.handleTouchMove); + this.container.removeEventListener('touchend', this.handleTouchEnd); + window.removeEventListener('resize', this.handleResize); +} +} + +// Inject CSS for stroke and fade animations +const style = document.createElement('style'); +style.textContent = ` +@keyframes drawStroke { + 0% { + stroke-dashoffset: var(--path-length); + opacity: 0; + } + 10% { + opacity: 1; + } + 100% { + stroke-dashoffset: 0; + opacity: 1; + } +} + +@keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: var(--target-opacity); + } +} + +@keyframes letterAppear { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +`; +document.head.appendChild(style); + +// Initialize when DOM is ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + window.floatingShapes = new FloatingShapesManager('sketch'); + }); +} else { + window.floatingShapes = new FloatingShapesManager('sketch'); +} diff --git a/js/annoncesSwiper.js b/js/annoncesSwiper.js new file mode 100644 index 0000000..1312b6f --- /dev/null +++ b/js/annoncesSwiper.js @@ -0,0 +1,28 @@ +document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('[data-swiper]').forEach(function (section) { + new Swiper(section.querySelector('.swiper'), { + navigation: { + addIcons: false, + nextEl: section.querySelector('.swiper-button-next'), + prevEl: section.querySelector('.swiper-button-prev'), + }, + autoplay: { + delay: 5000, + disableOnInteraction: false, + pauseOnMouseEnter: true, + }, + slidesPerView: 1, + spaceBetween: 30, + breakpoints: { + 768: { + slidesPerView: 2, + spaceBetween: 40, + }, + 1024: { + slidesPerView: 3, + spaceBetween: 50, + }, + }, + }); + }); +}); diff --git a/js/categoryFilters.js b/js/categoryFilters.js new file mode 100644 index 0000000..7b7c966 --- /dev/null +++ b/js/categoryFilters.js @@ -0,0 +1,138 @@ +document.addEventListener('DOMContentLoaded', function () { + // ── Filters toggle ──────────────────────────────────────── + var toggleBtn = document.getElementById('category-filters-toggle'); + var filtersEl = document.getElementById('category-filters'); + + if (toggleBtn && filtersEl) { + toggleBtn.addEventListener('click', function () { + var isOpen = filtersEl.classList.toggle('is-open'); + toggleBtn.classList.toggle('is-open', isOpen); + toggleBtn.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); + }); + } + + var dateBtn = document.getElementById('filter-date-btn'); + var datePopover = document.getElementById('filter-date-popover'); + var dateFrom = document.getElementById('filter-date-from'); + var dateTo = document.getElementById('filter-date-to'); + var dateApply = document.getElementById('filter-date-apply'); + var axeBtn = document.getElementById('filter-axe-btn'); + var axePopover = document.getElementById('filter-axe-popover'); + + if (!dateBtn) return; + + // Build URL with updated query params + function buildUrl(params) { + var url = new URL(window.location.href); + Object.keys(params).forEach(function (key) { + if (params[key]) { + url.searchParams.set(key, params[key]); + } else { + url.searchParams.delete(key); + } + }); + return url.toString(); + } + + function formatDate(d) { + var y = d.getFullYear(); + var m = String(d.getMonth() + 1).padStart(2, '0'); + var day = String(d.getDate()).padStart(2, '0'); + return y + '-' + m + '-' + day; + } + + // Dropdown toggle + function openDropdown(popover) { + popover.style.display = ''; + popover.closest('.filter-dd').classList.add('is-open'); + } + + function closeDropdown(popover) { + popover.style.display = 'none'; + popover.closest('.filter-dd').classList.remove('is-open'); + } + + function toggleDropdown(popover) { + if (popover.style.display === 'none') { + openDropdown(popover); + } else { + closeDropdown(popover); + } + } + + // Close dropdowns on outside click + document.addEventListener('click', function (e) { + if (!e.target.closest('#filter-date-dd')) { + closeDropdown(datePopover); + } + if (axePopover && !e.target.closest('#filter-axe-dd')) { + closeDropdown(axePopover); + } + }); + + // --- Date dropdown --- + dateBtn.addEventListener('click', function (e) { + e.stopPropagation(); + if (axePopover) closeDropdown(axePopover); + toggleDropdown(datePopover); + }); + + // Date presets + datePopover.addEventListener('click', function (e) { + var preset = e.target.dataset.preset; + if (!preset) return; + + var now = new Date(); + var from, to; + + if (preset === 'week') { + var day = now.getDay(); + var diff = day === 0 ? 6 : day - 1; + from = new Date(now); + from.setDate(now.getDate() - diff); + to = new Date(from); + to.setDate(from.getDate() + 6); + } else if (preset === 'month') { + from = new Date(now.getFullYear(), now.getMonth(), 1); + to = new Date(now.getFullYear(), now.getMonth() + 1, 0); + } else if (preset === 'lastmonth') { + from = new Date(now.getFullYear(), now.getMonth() - 1, 1); + to = new Date(now.getFullYear(), now.getMonth(), 0); + } else if (preset === 'upcoming') { + from = now; + to = null; + } + + window.location.href = buildUrl({ + date_from: formatDate(from), + date_to: to ? formatDate(to) : '' + }); + }); + + // Date apply → navigate + dateApply.addEventListener('click', function () { + window.location.href = buildUrl({ + date_from: dateFrom.value, + date_to: dateTo.value + }); + }); + + // --- Axe dropdown --- + if (axeBtn && axePopover) { + axeBtn.addEventListener('click', function (e) { + e.stopPropagation(); + closeDropdown(datePopover); + toggleDropdown(axePopover); + }); + + axePopover.addEventListener('click', function (e) { + var li = e.target.closest('[data-axe-id]'); + if (!li) return; + if (li.dataset.axeHref) { + window.location.href = li.dataset.axeHref; + } else { + window.location.href = buildUrl({ axe: li.dataset.axeId }); + } + }); + } +}); diff --git a/js/coloredWordsHero.js b/js/coloredWordsHero.js new file mode 100644 index 0000000..a57dbb9 --- /dev/null +++ b/js/coloredWordsHero.js @@ -0,0 +1,22 @@ +document.addEventListener('DOMContentLoaded', function() { + if (document.querySelector('body').classList.contains('home')) { + const colors = ['#e0775d', '#7cc0c6', '#e05680', '#46ae51', '#bb8dd9']; + const timeouts = new Map(); + + // Color changing on hover + document.querySelectorAll('.color-changer').forEach(element => { + element.addEventListener('mouseenter', (e) => { + if (timeouts.has(e.target)) { + clearTimeout(timeouts.get(e.target)); + } + + e.target.style.color = colors[Math.floor(Math.random() * colors.length)]; + const timeoutId = setTimeout(() => { + e.target.style.color = 'black'; + timeouts.delete(e.target); + }, 2000); + timeouts.set(e.target, timeoutId); + }); + }); + } +}); \ No newline at end of file diff --git a/js/fitPostCardTitle.js b/js/fitPostCardTitle.js new file mode 100644 index 0000000..81fa3b6 --- /dev/null +++ b/js/fitPostCardTitle.js @@ -0,0 +1,43 @@ +document.addEventListener('DOMContentLoaded', function () { + function fitTitles() { + var cards = document.querySelectorAll('.post-card'); + cards.forEach(function (card) { + var h2 = card.querySelector('.gradient-container h2'); + if (!h2) return; + + var container = h2.closest('.gradient-container'); + var maxHeight = container.clientHeight; + var fontSize = parseFloat(window.getComputedStyle(h2).fontSize); + var minSize = 12; + + h2.style.fontSize = ''; + fontSize = parseFloat(window.getComputedStyle(h2).fontSize); + + while (h2.scrollHeight > maxHeight && fontSize > minSize) { + fontSize -= 1; + h2.style.fontSize = fontSize + 'px'; + } + }); + } + + window.fitPostCardTitles = fitTitles; + + fitTitles(); + + var resizeTimer; + window.addEventListener('resize', function () { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(fitTitles, 200); + }); + + // Re-fit after infinite scroll loads new cards + var observer = new MutationObserver(function (mutations) { + mutations.forEach(function (m) { + if (m.addedNodes.length) fitTitles(); + }); + }); + var grid = document.getElementById('post-grid'); + if (grid) { + observer.observe(grid, { childList: true }); + } +}); diff --git a/js/frenchTypography.js b/js/frenchTypography.js new file mode 100644 index 0000000..64f16d4 --- /dev/null +++ b/js/frenchTypography.js @@ -0,0 +1,37 @@ +/** + * French typography: replaces normal spaces with non-breaking spaces + * where French typographic rules require them. + */ +function applyFrenchTypography(elements) { + elements.forEach(function (el) { + var html = el.innerHTML; + // Collapse multiple spaces into one (outside HTML tags) + html = html.replace(/([^<>]) {2,}(?=[^<>])/g, '$1 '); + // Space before ? ! : ; » + html = html.replace(/ ([?!:;»])/g, '\u00A0$1'); + // Space after « + html = html.replace(/(«) /g, '$1\u00A0'); + // "p. 42" → non-breaking space after "p." + html = html.replace(/\bp\. /g, 'p.\u00A0'); + // "n° 3" → non-breaking space after "n°" + html = html.replace(/\bn° /g, 'n°\u00A0'); + el.innerHTML = html; + }); +} + +document.addEventListener('DOMContentLoaded', function () { + applyFrenchTypography(document.querySelectorAll('.post-card h2, .post-card .post-card__title')); + + // Re-apply on any post-card dynamically added anywhere on the page + new MutationObserver(function (mutations) { + mutations.forEach(function (m) { + m.addedNodes.forEach(function (node) { + if (node.nodeType !== 1) return; + var targets = node.classList && node.classList.contains('post-card') + ? node.querySelectorAll('h2, .post-card__title') + : node.querySelectorAll('.post-card h2, .post-card .post-card__title'); + applyFrenchTypography(targets); + }); + }); + }).observe(document.body, { childList: true, subtree: true }); +}); diff --git a/js/imageSwiper.js b/js/imageSwiper.js new file mode 100644 index 0000000..20373a4 --- /dev/null +++ b/js/imageSwiper.js @@ -0,0 +1,16 @@ +document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('[data-image-swiper]').forEach(function (container) { + new Swiper(container.querySelector('.swiper'), { + slidesPerView: 1, + loop: true, + navigation: { + nextEl: container.querySelector('.swiper-button-next'), + prevEl: container.querySelector('.swiper-button-prev'), + }, + pagination: { + el: container.querySelector('.swiper-pagination'), + clickable: true, + }, + }); + }); +}); diff --git a/js/infiniteScroll.js b/js/infiniteScroll.js new file mode 100644 index 0000000..8d4e739 --- /dev/null +++ b/js/infiniteScroll.js @@ -0,0 +1,71 @@ +document.addEventListener('DOMContentLoaded', function () { + var sentinel = document.getElementById('scroll-sentinel'); + var grid = document.getElementById('post-grid'); + var spinner = document.getElementById('scroll-spinner'); + + if (!sentinel || !grid) return; + + var page = 1; + var loading = false; + var done = false; + + // Read filter params from sentinel data attributes (set server-side) + var categoryId = sentinel.dataset.category || ''; + var axe = sentinel.dataset.axe || ''; + var dateFrom = sentinel.dataset.dateFrom || ''; + var dateTo = sentinel.dataset.dateTo || ''; + var taxonomy = sentinel.dataset.taxonomy || ''; + var termId = sentinel.dataset.term || ''; + var catFilter = sentinel.dataset.filterCat || ''; + var filterAutres = sentinel.dataset.filterAutres || ''; + var excludeCats = sentinel.dataset.excludeCats || ''; + var searchQuery = sentinel.dataset.search || ''; + + function fetchPosts() { + if (loading || done) return; + loading = true; + page++; + spinner.style.display = 'flex'; + + var data = new FormData(); + data.append('action', 'load_more_posts'); + data.append('page', page); + data.append('nonce', infiniteScrollData.nonce); + data.append('lang', infiniteScrollData.lang || 'fr'); + if (categoryId) data.append('category', categoryId); + if (axe) data.append('axe', axe); + if (dateFrom) data.append('date_from', dateFrom); + if (dateTo) data.append('date_to', dateTo); + if (taxonomy) data.append('taxonomy', taxonomy); + if (termId) data.append('term', termId); + if (catFilter) data.append('filter_cat', catFilter); + if (filterAutres) data.append('filter_autres', filterAutres); + if (excludeCats) data.append('exclude_cats', excludeCats); + if (searchQuery) data.append('search', searchQuery); + + fetch(infiniteScrollData.ajaxUrl, { method: 'POST', body: data }) + .then(function (response) { return response.json(); }) + .then(function (result) { + spinner.style.display = 'none'; + if (result.success && result.data.html) { + grid.insertAdjacentHTML('beforeend', result.data.html); + loading = false; + } else { + done = true; + observer.disconnect(); + } + }) + .catch(function () { + spinner.style.display = 'none'; + loading = false; + }); + } + + var observer = new IntersectionObserver(function (entries) { + if (entries[0].isIntersecting) { + fetchPosts(); + } + }, { rootMargin: '200px' }); + + observer.observe(sentinel); +}); diff --git a/js/keywordCloud.js b/js/keywordCloud.js new file mode 100644 index 0000000..69aa409 --- /dev/null +++ b/js/keywordCloud.js @@ -0,0 +1,249 @@ +(function () { + 'use strict'; + + document.addEventListener('DOMContentLoaded', function () { + var container = document.getElementById('keyword-container'); + var tags = window.thalimTags; + + if (!container || !tags || !tags.length) return; + + // — init — + + var GAP = 14; + var COLORS = ['#e0775d', '#7cc0c6', '#e05680', '#46ae51', '#bb8dd9']; + var colorTimeouts = new Map(); + + // Mélange aléatoire pour varier la disposition à chaque chargement + var shuffled = tags.slice().sort(function () { return Math.random() - 0.5; }); + + // Crée les éléments une seule fois (réutilisés à chaque layout) + var items = shuffled.map(function (tag) { + var el = document.createElement('a'); + el.className = 'keyword'; + el.href = tag.url; + el.textContent = tag.name; + + el.addEventListener('mouseenter', function () { + if (colorTimeouts.has(el)) clearTimeout(colorTimeouts.get(el)); + el.style.color = COLORS[Math.floor(Math.random() * COLORS.length)]; + colorTimeouts.set(el, setTimeout(function () { + el.style.color = ''; + colorTimeouts.delete(el); + }, 2000)); + }); + + container.appendChild(el); + return { el: el, w: 0, h: 0 }; + }); + + var gradOverlay = null; + var lastLayoutWidth = 0; + + function layoutCloud() { + var cw = container.offsetWidth; + if (cw === lastLayoutWidth) return; + lastLayoutWidth = cw; + + var cx = cw / 2; + var isMobile = cw < 768; + var a = cx - GAP; // demi-grand axe horizontal + var b = Math.round(cw * (isMobile ? 0.45 : 0.20)); // demi-petit axe vertical (plus haut sur mobile) + var R_V = Math.round(b * (isMobile ? 0.45 : 0.70)); // demi-axe vertical de la zone d'exclusion (plus petit sur mobile) + var R_H = Math.round(b * (isMobile ? 0.70 : 1.15)); // demi-axe horizontal (plus petit sur mobile) + + // Re-mesurer les éléments (la taille peut changer avec le viewport) + // +1 compense les arrondis sub-pixel sur écrans haute densité + items.forEach(function (item) { + var rect = item.el.getBoundingClientRect(); + item.w = Math.ceil(rect.width) + 1; + item.h = Math.ceil(rect.height) + 1; + }); + + var placed = []; + + function hasOverlap(x, y, w, h) { + for (var i = 0; i < placed.length; i++) { + var p = placed[i]; + if (x < p.x + p.w + GAP && + x + w + GAP > p.x && + y < p.y + p.h + GAP && + y + h + GAP > p.y) return true; + } + return false; + } + + // Placement contraint à l'anneau elliptique : + // { intérieur ellipse (a, b) } ∩ { extérieur ellipse d'exclusion (R_H, R_V) } + // Les candidats sont triés par distance à l'ellipse d'exclusion (croissante) : + // les mots s'accumulent au plus près du centre avant de s'étendre. + function findPos(w, h) { + var candidates = []; + + for (var x = 0; x <= cw - w; x += 8) { + var dx = (x + w / 2) - cx; + var ratio = dx / a; + if (Math.abs(ratio) >= 1) continue; + + // Limite verticale du centre du mot imposée par l'ellipse externe (avec marge h/2) + var maxPcy = b * Math.sqrt(1 - ratio * ratio); + if (maxPcy < h / 2) continue; + var maxAbsY = maxPcy - h / 2; + + // Limite verticale minimale imposée par l'ellipse d'exclusion (R_H, R_V) + var minAbsY = (Math.abs(dx) >= R_H) ? 0 + : R_V * Math.sqrt(Math.max(0, 1 - (dx * dx) / (R_H * R_H))); + if (minAbsY > maxAbsY) continue; + + for (var absY = minAbsY; absY <= maxAbsY; absY += 4) { + // Distance normalisée à l'ellipse d'exclusion (0 = sur le bord) + var nx = dx / R_H, ny = absY / R_V; + var dist = Math.sqrt(nx * nx + ny * ny) - 1; + var yB = Math.round(absY - h / 2); + if (absY > 0) { + candidates.push({ x: x, y: Math.round(-absY - h / 2), dist: dist }); + } + candidates.push({ x: x, y: yB, dist: dist }); + } + } + + // Trier par distance à l'ellipse d'exclusion croissante → attraction vers le centre + candidates.sort(function (ca, cb) { return ca.dist - cb.dist; }); + + for (var i = 0; i < candidates.length; i++) { + var c = candidates[i]; + if (!hasOverlap(c.x, c.y, w, h)) return { x: c.x, y: c.y }; + } + + return null; // aucune position dans l'anneau + } + + // Place les mots dans l'anneau elliptique, collecte les débordements + var overflow = []; + items.forEach(function (item) { + var pos = findPos(item.w, item.h); + if (pos) { + item.pos = { x: pos.x, y: pos.y, w: item.w, h: item.h }; + placed.push(item.pos); + item.el.style.left = pos.x + 'px'; + item.el.style.top = pos.y + 'px'; + } else { + overflow.push(item); + } + }); + + // Placement des débordements en lignes centrées (style flex-wrap center) + if (overflow.length) { + var startY = placed.reduce(function (m, p) { return Math.max(m, p.y + p.h); }, 0) + GAP; + var rows = []; + var currentRow = []; + var currentRowW = 0; + + overflow.forEach(function (item) { + var needed = currentRowW > 0 ? item.w + GAP : item.w; + if (currentRowW + needed > cw && currentRow.length > 0) { + rows.push(currentRow); + currentRow = []; + currentRowW = 0; + } + currentRow.push(item); + currentRowW += (currentRowW > 0 ? GAP : 0) + item.w; + }); + if (currentRow.length) rows.push(currentRow); + + var curY = startY; + rows.forEach(function (row) { + var rowW = row.reduce(function (s, item) { return s + item.w; }, 0) + (row.length - 1) * GAP; + var rowH = row.reduce(function (m, item) { return Math.max(m, item.h); }, 0); + var offsetX = Math.round((cw - rowW) / 2); + + row.forEach(function (item) { + item.pos = { x: offsetX, y: curY, w: item.w, h: item.h }; + placed.push(item.pos); + item.el.style.left = offsetX + 'px'; + item.el.style.top = curY + 'px'; + offsetX += item.w + GAP; + }); + curY += rowH + GAP; + }); + } + + // Normalisation Y : cy=0 → shift px depuis le haut du conteneur + var minY = items.reduce(function (m, item) { return Math.min(m, item.pos.y); }, Infinity); + var shift = Math.max(0, GAP - minY); + + items.forEach(function (item) { + item.pos.y += shift; + item.el.style.top = item.pos.y + 'px'; + }); + + // Hauteur basée sur le contenu réel (évite le débordement sur mobile) + var maxPlacedY = items.reduce(function (m, item) { return Math.max(m, item.pos.y + item.pos.h); }, 0); + container.style.height = (maxPlacedY + GAP) + 'px'; + + // Dégradé sur un overlay séparé pour permettre l'animation scale au survol + if (gradOverlay) { + container.removeChild(gradOverlay); + } + gradOverlay = document.createElement('div'); + gradOverlay.style.cssText = + 'position:absolute;inset:0;pointer-events:none;' + + 'background:radial-gradient(circle ' + Math.round(R_V * 2) + 'px at ' + cx + 'px ' + shift + 'px,' + + 'rgba(247,255,41,1) 0%,rgba(247,255,41,0.6) 16%,rgba(247,255,41,0.15) 55%,transparent 70%);' + + 'transform-origin:' + cx + 'px ' + shift + 'px;' + + 'transition:transform 0.4s ease;'; + container.insertBefore(gradOverlay, container.firstChild); + } + + // Premier layout + layoutCloud(); + + // Événements du dégradé (référencent gradOverlay via closure) + container.addEventListener('mouseenter', function () { + if (!gradOverlay) return; + gradOverlay.style.transition = 'transform 0.4s ease'; + gradOverlay.style.transform = 'scale(0.82)'; + }); + container.addEventListener('mousemove', function (e) { + if (!gradOverlay) return; + var cw = container.offsetWidth; + var cx = cw / 2; + var rect = container.getBoundingClientRect(); + var dx = (e.clientX - rect.left) - cx; + var dy = (e.clientY - rect.top) - parseFloat(gradOverlay.style.transformOrigin.split(' ')[1]); + var tx = (-dx * 0.09).toFixed(2); + var ty = (-dy * 0.09).toFixed(2); + gradOverlay.style.transition = 'transform 0.15s ease-out'; + gradOverlay.style.transform = 'translate(' + tx + 'px,' + ty + 'px) scale(0.82)'; + }); + container.addEventListener('mouseleave', function () { + if (!gradOverlay) return; + gradOverlay.style.transition = 'transform 0.9s cubic-bezier(0.25, 0.46, 0.45, 0.94)'; + gradOverlay.style.transform = 'scale(1)'; + }); + + // Resize avec debounce + var resizeTimer; + window.addEventListener('resize', function () { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(function () { + lastLayoutWidth = 0; // forcer le recalcul + layoutCloud(); + }, 250); + }); + + // Animation d'apparition au scroll + var observer = new IntersectionObserver(function (entries) { + entries.forEach(function (entry) { + if (entry.isIntersecting) { + items.forEach(function (item, i) { + item.el.style.animationDelay = (i * 0.03) + 's'; + item.el.classList.add('keyword--visible'); + }); + observer.unobserve(container); + } + }); + }, { threshold: 0.05 }); + + observer.observe(container); + }); +})(); diff --git a/js/membresFilters.js b/js/membresFilters.js new file mode 100644 index 0000000..93b0d3c --- /dev/null +++ b/js/membresFilters.js @@ -0,0 +1,229 @@ +document.addEventListener('DOMContentLoaded', function () { + // ── Filters toggle ──────────────────────────────────────── + var membresToggleBtn = document.getElementById('membres-filters-toggle'); + var membresFiltersEl = document.getElementById('membres-filters'); + + if (membresToggleBtn && membresFiltersEl) { + membresToggleBtn.addEventListener('click', function () { + var isOpen = membresFiltersEl.classList.toggle('is-open'); + membresToggleBtn.classList.toggle('is-open', isOpen); + membresToggleBtn.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); + if (isOpen) { + var searchInput = document.getElementById('membres-search'); + if (searchInput) searchInput.focus(); + } + }); + } + + var activeRole = ''; + var searchQuery = ''; + + // ── Dropdown (role filter) ──────────────────────────────── + var roleBtn = document.getElementById('filter-role-btn'); + var rolePopover = document.getElementById('filter-role-popover'); + var roleLabel = document.getElementById('filter-role-label'); + var roleReset = document.getElementById('role-reset'); + var roleDd = document.getElementById('filter-role-dd'); + + function openRole() { rolePopover.style.display = ''; roleDd.classList.add('is-open'); } + function closeRole() { rolePopover.style.display = 'none'; roleDd.classList.remove('is-open'); } + + roleBtn.addEventListener('click', function () { + rolePopover.style.display === 'none' ? openRole() : closeRole(); + }); + + rolePopover.querySelectorAll('[data-role]').forEach(function (item) { + item.addEventListener('click', function () { + activeRole = item.dataset.role; + roleLabel.textContent = item.textContent.trim(); + roleReset.style.display = activeRole ? '' : 'none'; + roleDd.classList.toggle('is-active', !!activeRole); + closeRole(); + updateChips(); + applyFilters(); + }); + }); + + roleReset.addEventListener('click', function (e) { + e.preventDefault(); + activeRole = ''; + roleLabel.textContent = rolePopover.querySelector('[data-role=""]').textContent.trim(); + roleReset.style.display = 'none'; + roleDd.classList.remove('is-active'); + updateChips(); + applyFilters(); + }); + + document.addEventListener('click', function (e) { + if (!roleDd.contains(e.target)) closeRole(); + }); + + // ── Search input ────────────────────────────────────────── + var searchInput = document.getElementById('membres-search'); + var searchReset = document.getElementById('search-reset'); + + searchInput.addEventListener('input', function () { + searchQuery = searchInput.value.trim().toLowerCase(); + searchReset.style.display = searchQuery ? '' : 'none'; + searchInput.classList.toggle('is-active', !!searchQuery); + updateChips(); + applyFilters(); + }); + + searchReset.addEventListener('click', function (e) { + e.preventDefault(); + searchQuery = ''; + searchInput.value = ''; + searchReset.style.display = 'none'; + searchInput.classList.remove('is-active'); + updateChips(); + applyFilters(); + }); + + // ── Active chips ────────────────────────────────────────── + var chipsContainer = document.getElementById('membres-active-chips'); + + function makeChip(label, onRemove) { + var btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'filter-chip'; + btn.innerHTML = label + ''; + btn.addEventListener('click', onRemove); + return btn; + } + + function updateChips() { + if (!chipsContainer) return; + chipsContainer.innerHTML = ''; + if (activeRole) { + chipsContainer.appendChild(makeChip(activeRole, function () { + activeRole = ''; + roleLabel.textContent = rolePopover.querySelector('[data-role=""]').textContent.trim(); + roleReset.style.display = 'none'; + roleDd.classList.remove('is-active'); + updateChips(); + applyFilters(); + })); + } + if (searchQuery) { + chipsContainer.appendChild(makeChip(searchInput.value, function () { + searchQuery = ''; + searchInput.value = ''; + searchReset.style.display = 'none'; + searchInput.classList.remove('is-active'); + updateChips(); + applyFilters(); + })); + } + } + + // ── Column sort ─────────────────────────────────────────── + var sortKey = 'nom'; + var sortDir = 'asc'; + + function getSortValue(row, key) { + if (key === 'nom') { + return (row.dataset.sortName || '').trim().toLowerCase(); + } + if (key === 'statut') return (row.dataset.status || '').toLowerCase(); + if (key === 'affiliation') return (row.dataset.affiliation || '').toLowerCase(); + return ''; + } + + function applySortToTable(tbody) { + var rows = Array.from(tbody.querySelectorAll('tr')); + rows.sort(function (a, b) { + var va = getSortValue(a, sortKey); + var vb = getSortValue(b, sortKey); + var cmp = va.localeCompare(vb, 'fr', { sensitivity: 'base' }); + return sortDir === 'asc' ? cmp : -cmp; + }); + rows.forEach(function (row) { tbody.appendChild(row); }); + } + + function applySort() { + document.querySelectorAll('.membres-table tbody').forEach(function (tbody) { + if (!tbody.closest('table').hasAttribute('data-fixed-order')) { + applySortToTable(tbody); + } + restripe(tbody); + }); + document.querySelectorAll('.membres-table th[data-sort]').forEach(function (th) { + th.classList.remove('sort-asc', 'sort-desc'); + if (th.dataset.sort === sortKey) { + th.classList.add('sort-' + sortDir); + } + }); + } + + document.querySelectorAll('.membres-table th[data-sort]').forEach(function (th) { + th.addEventListener('click', function () { + var key = th.dataset.sort; + if (sortKey === key) { + sortDir = sortDir === 'asc' ? 'desc' : 'asc'; + } else { + sortKey = key; + sortDir = 'asc'; + } + applySort(); + }); + }); + + applySort(); + + // ── Row restriping (fixes alternating colors after filter) ── + function restripe(tbody) { + var n = 0; + tbody.querySelectorAll('tr').forEach(function (row) { + if (row.style.display === 'none') return; + row.classList.toggle('is-even-row', n % 2 === 1); + n++; + }); + } + + // ── Apply both filters ──────────────────────────────────── + function applyFilters() { + var isFiltering = activeRole || searchQuery; + + document.querySelectorAll('.membres-item').forEach(function (item) { + // Filter rows by role and/or name + var rows = item.querySelectorAll('tbody tr'); + var visible = 0; + rows.forEach(function (row) { + var name = (row.dataset.name || '').toLowerCase(); + var status = (row.dataset.status || '').toLowerCase(); + var affiliation = (row.dataset.affiliation || '').toLowerCase(); + var roles = (row.dataset.roles || '').split('|').map(function (r) { return r.trim().toLowerCase(); }); + + var matchesRole = !activeRole || roles.includes(activeRole.toLowerCase()); + var matchesName = !searchQuery || name.includes(searchQuery) + || status.includes(searchQuery) + || affiliation.includes(searchQuery); + + var show = matchesRole && matchesName; + row.style.display = show ? '' : 'none'; + if (show) visible++; + }); + item.querySelectorAll('tbody').forEach(restripe); + + // Hide group if no rows are visible + if (isFiltering && visible === 0) { + item.style.display = 'none'; + return; + } + + item.style.display = ''; + + var content = item.querySelector('.membres-content'); + if (isFiltering) { + // Auto-expand when a filter is active + content.style.display = ''; + item.classList.add('is-open'); + } else { + // Collapse back when all filters are cleared + content.style.display = 'none'; + item.classList.remove('is-open'); + } + }); + } +}); diff --git a/js/membresPopover.js b/js/membresPopover.js new file mode 100644 index 0000000..95c4c9e --- /dev/null +++ b/js/membresPopover.js @@ -0,0 +1,81 @@ +document.addEventListener('DOMContentLoaded', function () { + var section = document.querySelector('.membres-section'); + if (!section) return; + + // Build the popover element once and append to body + var popover = document.createElement('div'); + popover.id = 'membre-popover'; + popover.innerHTML = + '
    ' + + '' + + '
    ' + + '

    ' + + '

    ' + + '

    ' + + '

    ' + + '
    ' + + '
    '; + document.body.appendChild(popover); + + var pic = popover.querySelector('.membre-popover-pic'); + var elName = popover.querySelector('.membre-popover-name'); + var elStat = popover.querySelector('.membre-popover-status'); + var elDom = popover.querySelector('.membre-popover-domaines'); + var elAut = popover.querySelector('.membre-popover-autres'); + + var visible = false; + var currentRow = null; + + // ── Show/hide via event delegation on the section ──────── + section.addEventListener('mouseover', function (e) { + var row = e.target.closest('tbody tr'); + if (!row || row === currentRow) return; + currentRow = row; + + var avatar = row.dataset.avatar || ''; + if (avatar) { + pic.src = avatar; + pic.style.display = ''; + } else { + pic.src = ''; + pic.style.display = 'none'; + } + + elName.textContent = row.dataset.name || ''; + elStat.textContent = row.dataset.status || ''; + + var domVal = row.dataset.domaines || ''; + elDom.textContent = domVal; + elDom.style.display = domVal ? '' : 'none'; + + var autVal = row.dataset.autresDomaines || ''; + elAut.innerHTML = autVal.replace(/\n/g, '
    '); + elAut.style.display = autVal ? '' : 'none'; + + popover.classList.add('is-visible'); + visible = true; + }); + + section.addEventListener('mouseout', function (e) { + var row = e.target.closest('tbody tr'); + if (!row) return; + // only hide when leaving the row entirely (not moving to a child) + if (e.relatedTarget && row.contains(e.relatedTarget)) return; + currentRow = null; + popover.classList.remove('is-visible'); + visible = false; + }); + + // ── Follow the cursor ───────────────────────────────────── + document.addEventListener('mousemove', function (e) { + if (!visible) return; + var x = e.clientX + 18; + var y = e.clientY + 18; + var pw = popover.offsetWidth; + var ph = popover.offsetHeight; + if (x + pw > window.innerWidth) x = e.clientX - pw - 8; + if (y + ph > window.innerHeight) y = e.clientY - ph - 8; + popover.style.left = x + 'px'; + popover.style.top = y + 'px'; + }); +}); diff --git a/js/messageLabo.js b/js/messageLabo.js new file mode 100644 index 0000000..7802ef1 --- /dev/null +++ b/js/messageLabo.js @@ -0,0 +1,57 @@ +document.addEventListener('DOMContentLoaded', function () { + var messageList = document.querySelector('.messages-list'); + var agendaContent = document.querySelector('.agenda-content'); + var sectionTitle = document.querySelector('.message-du-labo .section-title'); + var buttonMessages = document.querySelector('.button-messages'); + if (!messageList || !agendaContent) return; + + var items = Array.from(messageList.querySelectorAll('.message-item')); + + function sync() { + items.forEach(function (item) { + item.style.display = ''; + var content = item.querySelector('.message-content'); + if (content) { content.style.maxHeight = ''; content.classList.remove('is-overflowing'); } + }); + + if (window.innerWidth < 768) { + // Mobile : afficher uniquement le premier message + items.forEach(function (item, i) { item.style.display = i === 0 ? '' : 'none'; }); + return; + } + + var budget = agendaContent.offsetHeight + - (sectionTitle ? sectionTitle.offsetHeight : 0) + - (buttonMessages ? buttonMessages.offsetHeight : 0); + var used = 0; + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + var itemHeight = item.offsetHeight; + if (used + itemHeight <= budget) { + used += itemHeight; + } else { + var remaining = budget - used; + var content = item.querySelector('.message-content'); + if (content && remaining > 100) { + var contentBudget = remaining - (itemHeight - content.offsetHeight); + if (contentBudget > 60) { + content.style.maxHeight = contentBudget + 'px'; + content.classList.add('is-overflowing'); + } else { + item.style.display = 'none'; + } + } else { + item.style.display = 'none'; + } + for (var j = i + 1; j < items.length; j++) { + items[j].style.display = 'none'; + } + break; + } + } + } + + sync(); + window.addEventListener('resize', sync); +}); diff --git a/js/navAxesToggle.js b/js/navAxesToggle.js new file mode 100644 index 0000000..56ad034 --- /dev/null +++ b/js/navAxesToggle.js @@ -0,0 +1,32 @@ +document.addEventListener('DOMContentLoaded', function () { + var item = document.querySelector('.nav-axes-item'); + if (!item) return; + + var trigger = item.querySelector('.nav-axes-trigger'); + var mainMenu = document.querySelector('.main-menu'); + + trigger.addEventListener('click', function (e) { + e.stopPropagation(); + var isOpen = item.classList.toggle('is-open'); + trigger.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); + }); + + // Close when clicking outside + document.addEventListener('click', function (e) { + if (!item.contains(e.target)) { + item.classList.remove('is-open'); + trigger.setAttribute('aria-expanded', 'false'); + } + }); + + // Reset when main menu closes + if (mainMenu) { + var observer = new MutationObserver(function () { + if (!mainMenu.classList.contains('active')) { + item.classList.remove('is-open'); + trigger.setAttribute('aria-expanded', 'false'); + } + }); + observer.observe(mainMenu, { attributes: true, attributeFilter: ['class'] }); + } +}); diff --git a/js/overlay.js b/js/overlay.js new file mode 100644 index 0000000..e892b2d --- /dev/null +++ b/js/overlay.js @@ -0,0 +1,133 @@ +document.addEventListener('DOMContentLoaded', function() { + const header = document.querySelector('header'); + const body = document.querySelector('body'); + const menuToggle = document.querySelector('.menu-toggle'); + const mainMenu = document.querySelector('.main-menu'); + const menuOverlay = document.querySelector('.overlay'); + const menuIcon = document.querySelector('.menu-toggle i'); + const wpAdminBar = document.querySelector('#wpadminbar'); + const stickyHeaderMobile = document.querySelector('.header-right'); + const searchButton = document.querySelector('.search-button'); + const searchPanel = document.querySelector('.search-panel'); + const searchIcon = document.querySelector('.search-button i'); + const searchInput = document.querySelector('.search-panel__input'); + + const breakpointTablet = 768; + + mainMenu.style.top = `${mainMenu.offsetHeight * -1}px`; + searchPanel.style.top = `${searchPanel.offsetHeight * -1}px`; + + // Compute the pixel offset at which panels should appear (just below the header) + function getHeaderBottom() { + const adminBarHeight = wpAdminBar ? wpAdminBar.offsetHeight : 0; + if (window.innerWidth < breakpointTablet) { + if (window.scrollY > header.getBoundingClientRect().bottom) { + return stickyHeaderMobile.getBoundingClientRect().bottom + window.scrollY; + } + return header.getBoundingClientRect().bottom + window.scrollY; + } + return header.offsetHeight + adminBarHeight; + } + + function updateOverlay() { + const anyOpen = mainMenu.classList.contains('active') || searchPanel.classList.contains('active'); + menuOverlay.classList.toggle('active', anyOpen); + if (anyOpen) { + body.style.overflow = 'hidden'; + } else { + body.style.removeProperty('overflow'); + } + } + + // --- Menu --- + function openMenu() { + mainMenu.scrollTo(0, 0); + if (window.innerWidth < breakpointTablet) { + const adminBarHeight = wpAdminBar ? wpAdminBar.offsetHeight : 0; + if (window.scrollY > header.getBoundingClientRect().bottom) { + mainMenu.style.height = `${window.innerHeight - adminBarHeight - stickyHeaderMobile.offsetHeight}px`; + } else { + mainMenu.style.height = `${window.innerHeight - header.getBoundingClientRect().bottom}px`; + } + } else { + mainMenu.style.removeProperty('height'); + } + mainMenu.style.top = `${getHeaderBottom()}px`; + mainMenu.classList.add('active'); + menuIcon.classList.remove('iconoir-menu'); + menuIcon.classList.add('iconoir-xmark'); + } + + function closeMenu() { + mainMenu.style.top = `${mainMenu.offsetHeight * -1}px`; + mainMenu.classList.remove('active'); + menuIcon.classList.remove('iconoir-xmark'); + menuIcon.classList.add('iconoir-menu'); + } + + function toggleMenu() { + if (searchPanel.classList.contains('active')) closeSearch(); + if (mainMenu.classList.contains('active')) { + closeMenu(); + } else { + openMenu(); + } + updateOverlay(); + } + + // --- Search --- + function openSearch() { + searchPanel.style.top = `${getHeaderBottom()}px`; + searchPanel.classList.add('active'); + searchIcon.classList.remove('iconoir-search'); + searchIcon.classList.add('iconoir-xmark'); + setTimeout(function() { if (searchInput) searchInput.focus(); }, 400); + } + + function closeSearch() { + searchPanel.style.top = `${searchPanel.offsetHeight * -1}px`; + searchPanel.classList.remove('active'); + searchIcon.classList.remove('iconoir-xmark'); + searchIcon.classList.add('iconoir-search'); + } + + function toggleSearch() { + if (mainMenu.classList.contains('active')) closeMenu(); + if (searchPanel.classList.contains('active')) { + closeSearch(); + } else { + openSearch(); + } + updateOverlay(); + } + + // --- Event listeners --- + menuToggle.addEventListener('click', toggleMenu); + searchButton.addEventListener('click', toggleSearch); + + menuOverlay.addEventListener('click', function() { + if (mainMenu.classList.contains('active')) closeMenu(); + if (searchPanel.classList.contains('active')) closeSearch(); + updateOverlay(); + }); + + document.addEventListener('keydown', function(e) { + if (e.key === 'Escape') { + if (mainMenu.classList.contains('active')) closeMenu(); + if (searchPanel.classList.contains('active')) closeSearch(); + updateOverlay(); + } + }); + + let resizeTimeout; + window.addEventListener('resize', function() { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(function() { + if (mainMenu.classList.contains('active')) closeMenu(); + if (searchPanel.classList.contains('active')) closeSearch(); + updateOverlay(); + mainMenu.style.top = `${mainMenu.offsetHeight * -1}px`; + searchPanel.style.top = `${searchPanel.offsetHeight * -1}px`; + }, 150); + }); +}); diff --git a/js/quickLinks.js b/js/quickLinks.js new file mode 100644 index 0000000..aef72a4 --- /dev/null +++ b/js/quickLinks.js @@ -0,0 +1,24 @@ +document.addEventListener('DOMContentLoaded', function () { + var quickLinks = document.querySelector('.quick-links'); + if (!quickLinks) return; + + // Last section: keyword cloud if present, otherwise last swiper section + var lastSection = document.querySelector('.keyword-cloud'); + if (!lastSection) { + var swiperSections = document.querySelectorAll('.swiper-section'); + lastSection = swiperSections[swiperSections.length - 1] || null; + } + if (!lastSection) return; + + var initialTop = quickLinks.getBoundingClientRect().top + window.scrollY; + var quickLinksHeight = quickLinks.offsetHeight; + + window.addEventListener('scroll', function () { + var sectionBottom = lastSection.getBoundingClientRect().bottom; + if (initialTop - sectionBottom > 0) { + quickLinks.style.top = (initialTop - (initialTop - sectionBottom) - quickLinksHeight) + 'px'; + } else { + quickLinks.style.top = initialTop + 'px'; + } + }); +}); diff --git a/js/seanceToggle.js b/js/seanceToggle.js new file mode 100644 index 0000000..d862658 --- /dev/null +++ b/js/seanceToggle.js @@ -0,0 +1,39 @@ +document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('[data-seance-toggle]').forEach(function (header) { + header.addEventListener('click', function (e) { + // Don't toggle when clicking a link + if (e.target.closest('a')) return; + + var item = header.closest('.seance-item, .membres-item, .author-posts-item, .labo-dropdown-item'); + var content = item.querySelector('.seance-content, .membres-content, .author-posts-content, .labo-dropdown-content'); + var isOpen = item.classList.contains('is-open'); + + if (isOpen) { + content.style.display = 'none'; + item.classList.remove('is-open'); + } else { + content.style.display = ''; + item.classList.add('is-open'); + if (window.fitPostCardTitles) window.fitPostCardTitles(); + } + }); + }); + + // Auto-expand and scroll to a séance targeted by URL hash (#seance-{ID}) + var hash = window.location.hash; + if (hash && hash.startsWith('#seance-')) { + var target = document.querySelector(hash + '.seance-item'); + if (target) { + var content = target.querySelector('.seance-content'); + if (content) { + content.style.display = ''; + target.classList.add('is-open'); + if (window.fitPostCardTitles) window.fitPostCardTitles(); + setTimeout(function () { + var top = target.getBoundingClientRect().top + window.scrollY - 150; + window.scrollTo({ top: top, behavior: 'smooth' }); + }, 150); + } + } + } +}); diff --git a/js/stickyHeader.js b/js/stickyHeader.js new file mode 100644 index 0000000..735e2c7 --- /dev/null +++ b/js/stickyHeader.js @@ -0,0 +1,91 @@ +document.addEventListener('DOMContentLoaded', function() { + const header = document.querySelector('header'); + const wpAdminBar = document.querySelector('#wpadminbar'); + + const stickyHeaderMobile = document.querySelector('.header-right'); + const relativeHeaderMobile = document.querySelector('.header-left'); + + const mainLogo = document.querySelector('.main-logo'); + const description = document.querySelector('.description'); + const burgerContainer = document.querySelector('.menu-toggle > div'); + const menuIconContainer = document.querySelector('.menu-toggle > div > div'); + const menuText = document.querySelector('.menu-toggle > div > p'); + + const breakpointTablet = 768; + + function checkMobile() { + if (window.innerWidth < breakpointTablet) { + stickyHeaderMobile.style.top = wpAdminBar ? `${wpAdminBar.offsetHeight}px` : '0px'; + } else { + stickyHeaderMobile.style.top = 'unset'; + } + } + + function resetStyles() { + header.style.removeProperty("height"); + mainLogo.style.removeProperty("padding"); + description.style.removeProperty("opacity"); + burgerContainer.style.removeProperty("padding"); + burgerContainer.style.removeProperty("justify-content"); + menuIconContainer.style.removeProperty("font-size"); + menuText.style.removeProperty("display"); + } + + window.addEventListener('scroll', () => { + const isScrolledTop = window.scrollY === 0; + if (window.innerWidth < breakpointTablet) { + // mobile + if (window.scrollY > header.getBoundingClientRect().bottom) { + // déployer petit logo à gauche + if(!stickyHeaderMobile.classList.contains('scrolled')) { + stickyHeaderMobile.classList.add('scrolled') + } + } else { + // rétracter petit logo à gauche + if(stickyHeaderMobile.classList.contains('scrolled')) { + stickyHeaderMobile.classList.remove('scrolled'); + } + } + } else { + // desktop + header.style.height = isScrolledTop ? '12vh' : '6vh'; + header.style.minHeight = isScrolledTop ? '100px' : 'unset'; + mainLogo.style.padding = isScrolledTop ? '1.5rem 2rem' : '0.2rem 0.4rem'; + description.style.opacity = isScrolledTop ? '1' : '0'; + burgerContainer.style.padding = isScrolledTop ? '2rem' : '0.6rem 1rem'; + burgerContainer.style.justifyContent = isScrolledTop ? 'space-between' : 'center'; + menuIconContainer.style.fontSize = isScrolledTop ? '2rem' : '1.5rem'; + menuText.style.display = isScrolledTop ? '' : 'none'; + + + + if (window.scrollY === 0) { + // agrandir le header + } else { + // diminuer le header + } + } + + + }); + +let resizeTimeout; +let previousWidth = window.innerWidth; + +window.addEventListener('resize', () => { + let currentWidth = window.innerWidth; + + if (currentWidth !== previousWidth) { + window.scrollTo(0, 0); + previousWidth = currentWidth; + } + + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(function() { + resetStyles(); + checkMobile(); + }, 150); +}); + + checkMobile(); +}); \ No newline at end of file diff --git a/page-annonces.php b/page-annonces.php new file mode 100644 index 0000000..419af66 --- /dev/null +++ b/page-annonces.php @@ -0,0 +1,165 @@ + $active_axe ?: null, + 'date_from' => $active_date_from ?: null, + 'date_to' => $active_date_to ?: null, + ]); + $redir_url = $redir_params + ? add_query_arg( $redir_params, get_category_link( $cat_obj->term_id ) ) + : get_category_link( $cat_obj->term_id ); + wp_redirect( $redir_url, 301 ); + exit; + } +} + +// Determine active rubrique +$active_rubrique_id = 0; +if ($active_cat_id) { + $active_cat_obj = get_category($active_cat_id); + $active_rubrique_id = ($active_cat_obj && $active_cat_obj->parent) + ? $active_cat_obj->parent + : $active_cat_id; +} +$context['active_rubrique'] = $active_rubrique_id; + +// Base filter params preserved across all filter links +$base_filter_params = array_filter([ + 'axe' => $active_axe ?: null, + 'date_from' => $active_date_from ?: null, + 'date_to' => $active_date_to ?: null, +]); + +// Build tax_query: exclude séances + optional category filter +$tax_query = [ + 'relation' => 'AND', + [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => $excluded_cat_ids, + 'operator' => 'NOT IN', + ], +]; +if ($active_cat_id) { + $tax_query[] = [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$active_cat_id], + 'include_children' => !$filter_autres, + ]; +} + +$query_args = [ + 'post_type' => 'post', + 'posts_per_page' => 12, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'tax_query' => $tax_query, + 'thalim_event_date_order' => true, +]; +if ($active_axe) { + $query_args['meta_query'] = [[ + 'key' => 'axes_thematiques', + 'value' => $active_axe, + 'type' => 'NUMERIC', + ]]; +} +if ($active_date_from || $active_date_to) { + $query_args['thalim_event_date_filter'] = ['from' => $active_date_from, 'to' => $active_date_to]; +} + +// Axes thématiques for filter dropdown +$axes_groups = thalim_get_axes_filter_groups(); +$current_axes = $axes_groups[0]['terms'] ?? []; +$context['filter_axes'] = $current_axes; + +// Rubrique/catégorie filter links (stay on this page with filter_cat param) +$page_url = get_permalink(); +$all_cats = get_categories(['taxonomy' => 'category', 'hide_empty' => false, 'exclude' => $excluded_cat_ids]); + +$filter_parents = []; +foreach ($all_cats as $cat) { + if ($cat->parent == 0) { + $filter_parents[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => $base_filter_params + ? add_query_arg($base_filter_params, get_category_link($cat->term_id)) + : get_category_link($cat->term_id), + ]; + } +} +$context['filter_parents'] = $filter_parents; + +$filter_categories = []; +if ($active_rubrique_id) { + foreach ($all_cats as $cat) { + if ($cat->parent == $active_rubrique_id) { + $filter_categories[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => $base_filter_params + ? add_query_arg($base_filter_params, get_category_link($cat->term_id)) + : get_category_link($cat->term_id), + ]; + } + } +} +// Add "Autres" entry if active rubrique has posts directly assigned to it +if ($active_rubrique_id && !empty($filter_categories)) { + $lang = thalim_current_language(); + $direct_check = new WP_Query([ + 'post_type' => 'post', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'lang' => '', + 'tax_query' => [[ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$active_rubrique_id], + 'include_children' => false, + ]], + ]); + if ($direct_check->have_posts()) { + $params = array_filter(array_merge($base_filter_params, ['filter_cat' => $active_rubrique_id, 'filter_autres' => 1])); + $filter_categories[] = [ + 'id' => 'autres', + 'name' => $lang === 'en' ? 'Other' : 'Autres', + 'slug' => 'autres', + 'link' => add_query_arg($params, $page_url), + ]; + } +} +$context['filter_categories'] = $filter_categories; + +$posts = Timber::get_posts($query_args); +$context['cards'] = thalim_get_cards_data($posts); +$context['posts'] = $posts; + +Timber::render('page-annonces.twig', $context); diff --git a/page-announcements.php b/page-announcements.php new file mode 100644 index 0000000..c1f77ce --- /dev/null +++ b/page-announcements.php @@ -0,0 +1,2 @@ +ID; + +// ── Liens (internal page links) ────────────────────────────── +$lien_ids = get_post_meta( $page_id, 'liens', false ); +$liens = []; +foreach ( $lien_ids as $lid ) { + $lid = intval( $lid ); + if ( ! $lid ) continue; + $liens[] = [ + 'title' => get_the_title( $lid ), + 'url' => get_permalink( $lid ), + ]; +} +$context['liens'] = $liens; + +$labo_lang = thalim_current_language(); + +// ── Images (two side-by-side slots) ────────────────────────── +$images = []; +foreach ( [ 'image_labo_1', 'image_labo_2' ] as $field ) { + $img_id = intval( get_post_meta( $page_id, $field, true ) ); + if ( ! $img_id ) continue; + $src = wp_get_attachment_image_url( $img_id, 'large' ); + if ( ! $src ) continue; + $images[] = [ + 'url' => $src, + 'alt' => get_post_meta( $img_id, '_wp_attachment_image_alt', true ) ?: '', + 'title' => thalim_bilingual( get_the_title( $img_id ), $labo_lang ), + ]; +} +$context['images'] = $images; + +// ── Axes thématiques grouped by period ─────────────────────── +$terms = get_terms( [ 'taxonomy' => 'axe_thematique', 'hide_empty' => false ] ); +$axes_map = []; + +$label_prefix = $labo_lang === 'en' ? 'Research areas ' : 'Axes thématiques '; +$label_passes = $labo_lang === 'en' ? 'Past research areas' : 'Axes thématiques antérieurs'; + +foreach ( $terms as $term ) { + $debut = trim( get_term_meta( $term->term_id, 'annee_debut', true ) ); + $fin = trim( get_term_meta( $term->term_id, 'annee_fin', true ) ); + + if ( $debut && $fin ) { + $key = $debut . '-' . $fin; + $label = $label_prefix . $debut . ' – ' . $fin; + } else { + $key = 'passes'; + $label = $label_passes; + } + + if ( ! isset( $axes_map[ $key ] ) ) { + $axes_map[ $key ] = [ + 'label' => $label, + 'debut' => intval( $debut ), + 'terms' => [], + ]; + } + $ordre = trim( get_term_meta( $term->term_id, 'ordre_daffichage', true ) ); + $axes_map[ $key ]['terms'][] = [ + 'name' => $term->name, + 'url' => get_term_link( $term ), + 'ordre' => $ordre !== '' ? intval( $ordre ) : null, + ]; +} + +// Sort: newest first by annee_debut, 'passes' always last (debut === 0) +uasort( $axes_map, function ( $a, $b ) { + if ( $a['debut'] === 0 ) return 1; + if ( $b['debut'] === 0 ) return -1; + return $b['debut'] - $a['debut']; +} ); + +// Within each group: numbered items first (ascending), then unnumbered alphabetically +foreach ( $axes_map as &$group ) { + usort( $group['terms'], function ( $a, $b ) { + $a_has = $a['ordre'] !== null; + $b_has = $b['ordre'] !== null; + if ( $a_has && $b_has ) return $a['ordre'] - $b['ordre']; + if ( $a_has ) return -1; + if ( $b_has ) return 1; + return strcmp( $a['name'], $b['name'] ); + } ); +} +unset( $group ); + +$context['axes_groups'] = array_values( $axes_map ); + +// ── Body (English override) ────────────────────────────────── +$context['body_en'] = apply_filters( 'the_content', get_post_meta( $page_id, 'body_en', true ) ?: '' ); + +// ── WYSIWYG fields ──────────────────────────────────────────── +$context['partenaires_internationaux'] = wpautop( ( $labo_lang === 'en' && get_post_meta( $page_id, 'partenaires_internationaux_en', true ) ) + ? get_post_meta( $page_id, 'partenaires_internationaux_en', true ) + : ( get_post_meta( $page_id, 'partenaires_internationaux', true ) ?: '' ) ); +$context['partenaires_nationaux'] = wpautop( ( $labo_lang === 'en' && get_post_meta( $page_id, 'partenaires_nationaux_en', true ) ) + ? get_post_meta( $page_id, 'partenaires_nationaux_en', true ) + : ( get_post_meta( $page_id, 'partenaires_nationaux', true ) ?: '' ) ); +$context['bibliotheques'] = wpautop( ( $labo_lang === 'en' && get_post_meta( $page_id, 'bibliotheques_en', true ) ) + ? get_post_meta( $page_id, 'bibliotheques_en', true ) + : ( get_post_meta( $page_id, 'bibliotheques', true ) ?: '' ) ); + +// ── Edit link ───────────────────────────────────────────────── +$context['page_edit_link'] = current_user_can( 'edit_page', $page_id ) ? get_edit_post_link( $page_id ) : ''; + +Timber::render( 'page-le-laboratoire.twig', $context ); diff --git a/page-membres.php b/page-membres.php new file mode 100644 index 0000000..5f4e5f2 --- /dev/null +++ b/page-membres.php @@ -0,0 +1,8 @@ + 'programme_de_recherche', 'hide_empty' => false ] ); + +$sections = [ + 'subventionne' => [ 'label' => 'Programmes subventionnés', 'items' => [] ], + 'autre' => [ 'label' => 'Autres programmes', 'items' => [] ], + 'ancien' => [ 'label' => 'Anciens programmes', 'items' => [] ], +]; + +foreach ( $terms as $term ) { + $type = get_term_meta( $term->term_id, 'type_de_programme', true ); + + $item = [ + 'name' => $term->name, + 'description' => wpautop( $term->description ), + 'url' => get_term_link( $term ), + 'annee_fin' => (int) get_term_meta( $term->term_id, 'annee_fin', true ), + ]; + + if ( $type === 'Programme subventionné' ) { + $sections['subventionne']['items'][] = $item; + } elseif ( $type === 'Ancien programme' ) { + $sections['ancien']['items'][] = $item; + } else { + // "Autre programme" or no type set + $sections['autre']['items'][] = $item; + } +} + +// Sort by annee_fin descending (most recent end year first); items without a year go last +foreach ( $sections as &$section ) { + usort( $section['items'], function( $a, $b ) { + if ( $a['annee_fin'] === $b['annee_fin'] ) return strcmp( $a['name'], $b['name'] ); + if ( ! $a['annee_fin'] ) return 1; + if ( ! $b['annee_fin'] ) return -1; + return $b['annee_fin'] - $a['annee_fin']; + } ); +} +unset( $section ); + +$context['sections'] = array_values( $sections ); +$context['page_edit_link'] = current_user_can( 'edit_page', $post->ID ) ? get_edit_post_link( $post->ID ) : ''; + +Timber::render( 'page-programmes-de-recherche.twig', $context ); diff --git a/page.php b/page.php new file mode 100644 index 0000000..851a4fe --- /dev/null +++ b/page.php @@ -0,0 +1,8 @@ +ID ) ? get_edit_post_link( $post->ID ) : ''; + +Timber::render( 'page.twig', $context ); diff --git a/scss/_author.scss b/scss/_author.scss new file mode 100644 index 0000000..8debf33 --- /dev/null +++ b/scss/_author.scss @@ -0,0 +1,164 @@ +// ==================================== +// AUTHOR PROFILE PAGE +// ==================================== + +.author-header { + display: flex; + gap: 2rem; + align-items: flex-start; + margin: 2rem 0; +} + +.author-avatar { + flex-shrink: 0; + + img { + width: 140px; + height: 140px; + object-fit: cover; + + @media ($tablet) { + width: 180px; + height: 180px; + } + } +} + +.author-identity { + h2 { + margin-top: 0; + } + & + .author-bio { + margin-top: 2rem; + } +} + +.author-role { + font-family: $font-primary; + margin-top: 0.4rem; + opacity: 0.85; + line-height: 1.4; +} + +.author-bio { + margin-bottom: 1.5rem; + line-height: 1.6; + + > p { + margin: 0.8rem 0; + } + + hr { + display: none; + } + + h3 { + font-family: $font-primary; + text-transform: uppercase; + } +} + +.author-resume-these { + margin-top: 1rem; + line-height: 1.6; + + > p { + margin: 0.8rem 0; + } +} + +.author-cat-footer { + display: flex; + justify-content: center; + margin-top: 1.5rem; + padding-top: 1rem; + border-top: 1px solid $light-gray; +} + +.author-titre-these { + font-family: $font-heading; + font-size: 1.4rem; + line-height: 1.3 !important; + margin-bottom: 0.8rem; +} + +.these-inline-title { + text-transform: uppercase; +} + +// WYSIWYG article-fields (domaines de recherches, recherches en cours): +// the wpautop'd

    follows the inline title — give it a small breathing space. +.domaines-autres, +.recherches-en-cours { + > p:first-of-type { + margin-top: 0.4rem; + } +} + +// ── Author posts dropdowns ──────────────────────────────────── +.author-posts-section { + display: flex; + flex-direction: column; + gap: 1rem; + margin-top: 5rem; +} + +.author-posts-header { + display: flex; + align-items: center; + gap: 1.2rem; + padding: 0.8rem; + background-color: $light-gray; + cursor: pointer; + transition: background-color 0.15s; + font-family: $font-primary; + text-transform: uppercase; + + &:hover { + background-color: $less-light-gray; + } +} + +.author-posts-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + margin-left: auto; + + .author-posts-item.is-open & { + transform: rotate(180deg); + } +} + +.author-posts-content { + padding: 1rem; + background-color: $light-light-gray; + position: relative; + padding-bottom: 3rem; + &::after { + @include yellow-gradient-after; + } + + article a { + text-decoration: none !important; + h2 { + margin-top: 0 !important; + } + + } +} + +.author-post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + width: 100%; + + h2::after { + display: none; + } + + @media ($tablet) { + grid-template-columns: repeat(2, 1fr); + } +} diff --git a/scss/_base.scss b/scss/_base.scss new file mode 100644 index 0000000..a9b9812 --- /dev/null +++ b/scss/_base.scss @@ -0,0 +1,3 @@ +#wpadminbar { + position: fixed !important; +} \ No newline at end of file diff --git a/scss/_category.scss b/scss/_category.scss new file mode 100644 index 0000000..361262a --- /dev/null +++ b/scss/_category.scss @@ -0,0 +1,557 @@ +// Category archive pages + +.category-archive { + margin-top: 0; + .category-header-top { + display: flex; + justify-content: space-between; + flex-direction: column; + align-items: start; + .breadcrumb { + font-size: 0.85rem; + margin-bottom: 1.5rem; + text-transform: uppercase; + line-height: 1.3; + + &__separator { + margin: 0 0.4rem; + } + } + @media ($tablet) { + flex-direction: row; + } + } + + h1 { + font-family: Gelasio; + font-weight: normal; + font-size: 1.8rem; + position: relative; + margin-top: 2rem; + display: inline-block; + &::after { + content: ''; + display: block; + position: absolute; + height: 6px; + width: 100%; + bottom: -1.2rem; + left: 0; + z-index: 2; + } + @media ($tablet) { + font-size: 2.6rem; + &::after { + bottom: -0.4rem; + } + } + } + + .category--le-laboratoire h1::after { + background: linear-gradient(to bottom, transparent 0%, $laboratoire 50%); + } + + .category--manifestations-scientifiques h1::after { + background: linear-gradient(to bottom, transparent 0%, $manifestations 50%); + } + + .category--publications-et-productions h1::after { + background: linear-gradient(to bottom, transparent 0%, $publications 50%); + } + + .category--mediation-scientifique h1::after { + background: linear-gradient(to bottom, transparent 0%, $mediations 50%); + } + + .category--ressources h1::after { + background: linear-gradient(to bottom, transparent 0%, $ressources 50%); + } + +} + +// Breadcrumb: non-category links (Accueil, etc.) underlined in muted gray +.breadcrumb a:not(.breadcrumb__cat) { text-decoration: underline; text-decoration-color: $less-light-gray; text-underline-offset: 3px; } + +// Breadcrumb category color underlines — links and current page indicator +.category--le-laboratoire .breadcrumb__cat, +.category--le-laboratoire .breadcrumb__current { text-decoration: underline; text-decoration-color: $laboratoire; text-underline-offset: 3px; } +.category--manifestations-scientifiques .breadcrumb__cat, +.category--manifestations-scientifiques .breadcrumb__current { text-decoration: underline; text-decoration-color: $manifestations; text-underline-offset: 3px; } +.category--publications-et-productions .breadcrumb__cat, +.category--publications-et-productions .breadcrumb__current { text-decoration: underline; text-decoration-color: $publications; text-underline-offset: 3px; } +.category--mediation-scientifique .breadcrumb__cat, +.category--mediation-scientifique .breadcrumb__current { text-decoration: underline; text-decoration-color: $mediations; text-underline-offset: 3px; } +.category--ressources .breadcrumb__cat, +.category--ressources .breadcrumb__current { text-decoration: underline; text-decoration-color: $ressources; text-underline-offset: 3px; } + +.category-archive { + + .taxonomy-description { + font-family: $font-primary; + margin-top: 2rem; + line-height: 1.6; + max-width: 70ch; + + + a { + text-decoration: underline; + } + + p { + margin-bottom: 1rem; + line-height: 1.6; + strong { + font-weight: bold; + } + em { + font-style: italic; + } + &:first-child { + margin-top: 0 !important; + } + } + + ul, ol { + line-height: 1.6; + padding-left: 0.8rem; + } + + ul { + list-style: inside "· "; + } + + ol { + list-style: inside decimal; + } + + blockquote{ + padding-left: 1rem; + margin-left: 1.5rem; + border-left: solid 1px $light-gray; + } + } + + .subcategory-section { + margin-top: 5rem; + &:first-of-type { + margin-top: 2rem; + } + .subcategory-section__title { + font-family: NewsCycle; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + font-size: 1.2rem; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } + } + + .post-grid { + display: grid; + align-items: start; + grid-template-columns: 1fr; + gap: 2rem; + width: 100%; + + .post-card { + min-width: 0; + } + + @media ($tablet) { + grid-template-columns: repeat(2, 1fr); + } + + @media ($desktop) { + grid-template-columns: repeat(3, 1fr); + } + } + + .category-section-footer { + margin-top: 3rem; + display: flex; + justify-content: center; + } + } + +} + +/* +.category-header { + width: 100%; + margin-bottom: 3vh; + + &__back { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-size: 0.9rem; + text-decoration: none; + margin-bottom: 1rem; + + i { + font-size: 1.1rem; + } + } + + &__title { + font-size: 2rem; + margin-bottom: 0.5rem; + + @media ($tablet) { + font-size: 2.5rem; + } + } + + &__description { + color: $less-dark-gray; + } +} + +// Sub-category sections on parent category page +.subcategory-section { + width: 100%; + margin-bottom: 4vh; + + &__header { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 2vh; + border-bottom: 2px solid $publications; + padding-bottom: 0.5rem; + } + + &__title { + font-size: 1.5rem; + + a { + text-decoration: none; + } + } + + &__see-all { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-size: 0.9rem; + text-decoration: none; + white-space: nowrap; + } + + &__empty { + color: $less-less-light-gray; + font-style: italic; + } +} + +// Post grid +.post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + width: 100%; + + @media ($tablet) { + grid-template-columns: repeat(2, 1fr); + } + + @media ($desktop) { + grid-template-columns: repeat(3, 1fr); + } +} + +// Post card +.post-card { + background-color: white; + display: flex; + flex-direction: column; + overflow: hidden; + + &__image { + width: 100%; + aspect-ratio: 4 / 3; + overflow: hidden; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + } + + &__content { + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.4rem; + } + + &__title { + font-size: 1.1rem; + line-height: 1.3; + + a { + text-decoration: none; + } + } + + &__subtitle { + font-size: 0.9rem; + color: $less-dark-gray; + font-style: italic; + } + + &__date { + font-size: 0.8rem; + color: $less-less-light-gray; + } + + &__authors { + font-size: 0.9rem; + display: flex; + flex-wrap: wrap; + gap: 0.2rem; + + .post-card__author { + &:not(:last-child)::after { + content: ','; + } + } + + &--external { + color: $less-dark-gray; + } + } + + &__role, + &__publisher, + &__journal { + font-size: 0.85rem; + color: $less-dark-gray; + } + + &__axes, + &__tags { + display: flex; + flex-wrap: wrap; + gap: 0.3rem; + } + + &__axe, + &__tag { + font-size: 0.75rem; + background-color: $light-gray; + padding: 0.15rem 0.5rem; + } + + &__axe { + border-left: 2px solid $publications; + } + + &__links { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.3rem; + } + + &__link { + font-size: 0.8rem; + + &--hal { + color: $publications; + } + } +} + +// Infinite scroll spinner +.scroll-spinner { + display: flex; + justify-content: center; + align-items: center; + gap: 0.4rem; + padding: 2rem 0; + + &__dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: $publications; + animation: scroll-spinner-bounce 1.2s infinite ease-in-out; + + &:nth-child(2) { + animation-delay: 0.2s; + } + + &:nth-child(3) { + animation-delay: 0.4s; + } + } +} + +@keyframes scroll-spinner-bounce { + 0%, 80%, 100% { + transform: scale(0.6); + opacity: 0.4; + } + 40% { + transform: scale(1); + opacity: 1; + } +} +*/ + +// ── Agenda slider view ──────────────────────────────────────────────────────── + +.agenda-view-container { + display: none; + margin-top: 3rem; + &.is-active { display: block; } +} + +.agenda-view-title { + font-family: NewsCycle; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + font-size: 1.2rem; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } +} + +.agenda-swiper-wrap { + display: flex; + align-items: center; + gap: 0.8rem; + margin-top: 2rem; +} + +.agenda-swiper { + flex: 1; + overflow: hidden; +} + +.agenda-swiper-prev, +.agenda-swiper-next { + flex-shrink: 0; + background: none; + border: none; + cursor: pointer; + font-size: 1.2rem; + padding: 0.4rem; + color: $dark-gray; + line-height: 1; + + &:hover { color: black; } + + &.swiper-button-disabled { + opacity: 0.25; + pointer-events: none; + } +} + +.agenda-card { + display: flex; + flex-direction: column; + text-decoration: none; + color: inherit; + padding-bottom: 1.5rem; + border-bottom: 1px solid $light-gray; + height: 100%; + position: relative; + transition: transform 0.2s ease-out; + margin-right: 0 !important; + + // Timeline line running through the center of the date boxes + &::before { + content: ''; + position: absolute; + top: 1.7rem; + left: 0; + right: 0; + height: 1px; + background-color: $less-light-gray; + z-index: 0; + width: calc(100% + 20px); + @media ($tablet) { + width: calc(100% + 24px); + } + @media ($desktop) { + width: calc(100% + 32px); + } + } + + &:hover { transform: scale(0.98); } + + &__dates { + display: flex; + align-items: flex-start; + gap: 0.5rem; + margin-bottom: 1rem; + } + + &__body { flex: 1; } + + &__meta { + display: flex; + flex-wrap: wrap; + gap: 0.3rem 0.8rem; + font-family: $font-primary; + font-size: 0.75rem; + text-transform: uppercase; + opacity: 0.65; + margin-bottom: 0.5rem; + } + + &__title { + font-family: $font-heading; + font-size: 1rem; + line-height: 1.35; + } +} + +// Category-specific accent color on agenda card border + date box gradient +.category--manifestations-scientifiques .agenda-card { border-bottom-color: $manifestations; } +.category--le-laboratoire .agenda-card { border-bottom-color: $laboratoire; } +.category--publications-et-productions .agenda-card { border-bottom-color: $publications; } +.category--mediation-scientifique .agenda-card { border-bottom-color: $mediations; } +.category--ressources .agenda-card { border-bottom-color: $ressources; } + +.category--manifestations-scientifiques .agenda-date-box { @include category-gradient($manifestations); } +.category--le-laboratoire .agenda-date-box { @include category-gradient($laboratoire); } +.category--publications-et-productions .agenda-date-box { @include category-gradient($publications); } +.category--mediation-scientifique .agenda-date-box { @include category-gradient($mediations); } +.category--ressources .agenda-date-box { @include category-gradient($ressources); } + +.agenda-date-box { + position: relative; + z-index: 1; + background-color: $light-gray; + font-family: $font-primary; + text-transform: uppercase; + text-align: center; + padding: 0.4rem 0.55rem; + flex-shrink: 0; + min-width: 2.6rem; + + .agenda-date-day { display: block; font-size: 1.3rem; line-height: 1; } + .agenda-date-month { display: block; font-size: 0.65rem; margin-top: 2px; } + .agenda-date-year { display: block; font-size: 0.6rem; opacity: 0.65; } + + sup { + font-size: 0.7rem; + } +} + +.agenda-date-arrow { + font-size: 0.85rem; + opacity: 0.5; +} \ No newline at end of file diff --git a/scss/_filters.scss b/scss/_filters.scss new file mode 100644 index 0000000..e4b7b00 --- /dev/null +++ b/scss/_filters.scss @@ -0,0 +1,344 @@ +.filters-bar { + margin-top: 2.5rem; + display: flex; + align-items: center; + gap: 0.8rem; + flex-wrap: wrap; + position: relative; + padding-bottom: 0.6rem; + + &::after { + content: ''; + position: absolute; + bottom: 0; + left: -5vw; + width: calc(100% + 10vw); + height: 1px; + background-color: $light-gray; + } + + @media ($tablet) { + &::after { + left: -3vw; + width: calc(100% + 6vw); + } + } +} + +.filters-toggle-btn { + display: flex; + align-items: center; + gap: 0.5rem; + font-family: $font-primary; + font-size: 0.9rem; + text-transform: uppercase; + background-color: $light-gray; + border: unset; + cursor: pointer; + padding: 0.4rem 0.8rem; + color: $dark-gray; + flex-shrink: 0; + + .filters-chevron { + transition: transform 0.2s; + } + + &.is-open .filters-chevron { + transform: rotate(180deg); + } + + &:hover { + background-color: $less-light-gray; + } +} + +.filters-active-chips { + display: flex; + align-items: center; + gap: 0.4rem; + flex-wrap: wrap; +} + +.filter-chip { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-family: $font-primary; + font-size: 0.8rem; + text-transform: uppercase; + background-color: $light-gray; + padding: 0.3rem 0.5rem; + text-decoration: none; + color: $dark-gray; + border: none; + cursor: pointer; + + i { + font-size: 0.7rem; + opacity: 0.5; + } + + &:hover { + background-color: $less-light-gray; + + i { opacity: 1; } + } +} + +.category-filters { + font-size: 0.9rem; + font-family: $font-primary; + margin-top: 0; + display: none; + padding: 2rem 5vw; + padding-bottom: 0.5rem !important; + margin-left: -5vw; + width: calc(100% + 10vw); + justify-content: space-between; + background-color: $light-light-gray; + border-top: solid 1px $light-gray; + border-bottom: solid 1px $light-gray; + flex-direction: column; + + &.is-open { + display: flex; + } + > div { + margin-top: 1.2rem; + margin-bottom: 1.2rem; + flex: auto; + + &.filtre-rubrique { + flex: 2; + } + + &.filtre-categorie { + flex: 1; + } + + &.filtre-date { + flex: 1; + } + + &.filtre-axe { + flex: 1; + } + + &:first-of-type { + margin-top: 0; + padding-left: 0; + } + + &:last-of-type { + margin-bottom: 0; + } + + &:not(:last-of-type) { + padding-bottom: 1.8rem; + border-bottom: solid 1px $less-light-gray; + } + + .filter-section-header { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 2rem; + } + + .section-title { + text-transform: uppercase; + position: relative; + display: inline-block; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } + } + + ul { + display: flex; + justify-content: start; + flex-wrap: wrap; + align-items: start; + gap: 0.5rem 0.2rem; + list-style: none; + margin: 0; + padding: 0; + + li { + font-size: 1rem; + cursor: pointer; + padding: 0.4rem 0.6rem; + + a { + text-decoration: none; + } + + &:hover, + &.is-active { + background-color: $less-light-gray; + } + } + } + } + + .filter-dd { + position: relative; + margin-top: 0; + display: inline-block; + + .dd-title { + padding: 0.3rem 0.5rem; + display: flex; + align-items: center; + gap: 0.4rem; + cursor: pointer; + + > p, + > i { + font-size: 0.9rem; + } + + > i { + transition: transform 0.2s; + } + } + + &.is-open .dd-title > i { + transform: rotate(180deg); + } + + &.is-active .dd-title { + background-color: $less-light-gray; + } + + .dd-content { + position: absolute; + padding: 0; + z-index: 5; + background: white; + border: 1px solid $less-light-gray; + min-width: 100%; + + ul { + gap: 0.3rem; + padding: 0.5rem; + + li { + padding: 0.4rem; + white-space: nowrap; + background-color: $light-gray; + } + } + &#filter-axe-popover { + right: auto; + left: 0; + min-width: 280px; + max-width: min(420px, 90vw); + + ul { + flex-direction: column; + padding: 0; + gap: 0; + li { + width: 100%; + margin: 0; + white-space: normal; + &:nth-of-type(odd) { + background-color: white; + } + &:first-of-type { + background-color: $less-light-gray; + } + &.dd-axe-group-label { + font-size: 0.72rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: $less-dark-gray; + background-color: $light-gray !important; + padding: 0.5rem 0.4rem; + cursor: default; + pointer-events: none; + border-top: solid 1px $less-light-gray; + &:first-child { + margin-top: 0; + } + } + } + } + } + } + + .dd-date-fields { + padding: 0.6rem; + padding-top: 1rem; + display: flex; + flex-direction: column; + gap: 0.4rem; + border-top: 1px solid $less-light-gray; + + label { + display: flex; + align-items: center; + gap: 0.4rem; + font-size: 0.85rem; + text-transform: uppercase; + } + + input[type="date"] { + padding: 0.2rem 0.4rem; + border: 1px solid $less-light-gray; + font-size: 0.85rem; + font-family: $font-primary; + } + } + + .dd-date-apply { + margin-top: 0.6rem; + width: 100%; + padding: 0.3rem; + border: none; + font-size: 0.85rem; + font-family: $font-primary; + text-transform: uppercase; + cursor: pointer; + } + } + + .membres-search-input.is-active { + background-color: $less-light-gray; + } + + .date-reset-link { + display: block; + font-size: 0.75rem; + text-transform: uppercase; + text-decoration: none; + color: inherit; + opacity: 0.6; + padding: 0 0.5rem 0.4rem; + + &:hover { + opacity: 1; + } + } + + @media ($tablet) { + padding: 2rem 3vw; + margin-left: -3vw; + width: calc(100% + 6vw); + flex-direction: row; + > div { + margin-top: 0; + margin-bottom: 0; + padding-left: 1.5rem; + &:not(:last-of-type) { + padding-right: 0.8rem; + border-bottom: unset; + border-right: solid 1px $less-light-gray; + } + } + } +} diff --git a/scss/_footer.scss b/scss/_footer.scss new file mode 100644 index 0000000..0408881 --- /dev/null +++ b/scss/_footer.scss @@ -0,0 +1,55 @@ +footer { + background-color: white; + width: 100%; + padding: 1rem 2rem; + margin-top: auto; +} + +.footer-content { + margin: 0 auto; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + gap: 2rem; + @media ($tablet) { + gap: unset; + flex-direction: row; + } +} + +.footer-nav { + ul#menu-footer, + ul#menu-footer-en { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 0.5rem 2rem; + @media ($tablet) { + justify-content: start; + } + + li { + a { + text-transform: uppercase; + font-size: 0.75rem; + } + } + } +} + +.footer-logos { + display: inline-flex; + height: 3rem; + gap: 1rem; + padding: 0.5rem; + align-items: start; + + > a { + height: 100%; + > img { + height: 100%; + } + } +} \ No newline at end of file diff --git a/scss/_header.scss b/scss/_header.scss new file mode 100644 index 0000000..6547672 --- /dev/null +++ b/scss/_header.scss @@ -0,0 +1,330 @@ +body > +header { + display: flex; + flex-direction: column-reverse; + justify-content: space-between; + transition: height 0.3s ease-out; + background-color: white; + width: 100%; + position: relative; + z-index: 5; + @media ($tablet) { + position: fixed; + height: 12vh; + min-height: 100px; + max-height: 130px; + flex-direction: row; + } +} + +.header-left { + display: flex; + height: 6rem; + margin-top: 3rem; + @media ($tablet) { + margin-top: unset; + height: 100%; + } +} + +.main-logo-container { + display: inline-block; + background-color: $light_gray; + height: 100%; + @media ($tablet) { + flex-direction: row; + } +} + +.main-logo { + display: inline-block; + padding: 0.3rem 0.6rem; + height: 100%; + @include hover-gradient-background; + @media ($tablet) { + padding: 1.5rem 2rem; + } + + > img, + > a > img { + height: 100%; + transform: scale(1); + transition: transform 0.2s ease-out; + @media ($tablet) { + } + } + + &:hover > img, + &:hover > a > img { + transform: scale(1.05); + } +} + +.description { + display: flex; + flex-direction: column; + align-items: start; + justify-content: center; + gap: 0.3rem; + margin-left: 1.2rem; + opacity: 1; + transition: opacity 0.2s ease-out; + + > div:first-of-type { + text-transform: uppercase; + font-size: 0.8rem; + background-color: $light_gray; + padding: 0.2rem; + } + + > div:last-of-type { + font-size: 0.8rem; + > sup { + font-size: 0.6rem; + vertical-align: super; + } + } +} + +.header-right { + display: flex; + align-items: center; + gap: 2rem; + justify-content: space-between; + position: fixed; + width: 100%; + background-color: white; + height: 3rem; + @media ($tablet) { + padding-left: unset; + height: unset; + position: relative; + width: unset; + } + &.scrolled { + .secondary-logo-container { + max-width: 40vw; + .main-logo:hover { + background-position: unset; + img { + transform: unset; + } + } + } + } +} + +.secondary-logo-container { + height: 100%; + max-width: 0; + overflow: hidden; + transition: max-width 0.6s ease-out; + @media ($tablet) { + display: none; + } +} + +.lang-switch { + text-transform: uppercase; + font-size: 0.8rem; + transition: font-size 0.2s ease-out; + > ul { + display: flex; + gap: 1rem; + > li:not(.active) { + > a { + text-decoration: none; + } + } + } +} + +.search-button { + margin-left: auto; + > div { + background-color: $light_gray; + padding: 0.5rem; + border-radius: 5rem; + min-width: 2rem; + aspect-ratio: 1 / 1; + display: flex; + justify-content: center; + align-items: center; + transition: background-color 0.3s ease-out; + cursor: pointer; + + &:hover { + background-color: $less_light-gray; + } + } +} + +.menu-toggle { + display: inline-block; + background-color: $light_gray; + width: fit-content; + height: 100%; + + > div { + height: 100%; + display: flex; + padding: 0.5rem; + flex-direction: column; + align-items: center; + justify-content: space-between; + gap: 0.3rem; + @include hover-gradient-background; + @media ($tablet) { + gap: unset; + padding: 2rem; + } + + > div { + font-size: 1rem; + transform: scale(1); + transition: transform 0.2s ease-out, font-size 0.2s ease-out; + @media ($tablet) { + font-size: 2rem; + } + } + + > p { + font-family: NewsCycle; + text-transform: uppercase; + transform: scale(1); + font-size: 0.8rem; + transition: transform 0.2s ease-out, font-size 0.2s ease-out; + @media ($tablet) { + font-size: 1rem; + } + } + } + + &:hover > div > div, + &:hover > div > p { + transform: scale(0.9); + } +} + +.search-panel { + position: absolute; + right: 0; + width: 100%; + background-color: white; + z-index: 4; + border-top: 2px solid $light-gray; + transition: top 0.4s ease-out, opacity 0.2s ease-out; + opacity: 0; + pointer-events: none; + + @media ($tablet) { + position: fixed; + width: 33.333%; + } + + &.active { + opacity: 1; + pointer-events: all; + } + + &__inner { + padding: 2rem; + display: flex; + flex-direction: column; + gap: 1rem; + } + + &__title { + font-family: $font-primary; + font-size: 1.1rem; + text-transform: uppercase; + display: inline-block; + align-self: start; + position: relative; + line-height: 1.6; + &::after { + @include yellow-gradient-after; + bottom: -10px; + } + } + + &__desc { + font-family: $font-primary; + color: $less-dark-gray; + } + + &__input-wrap { + position: relative; + } + + &__icon-btn { + position: absolute; + right: 0.8rem; + top: 50%; + transform: translateY(-50%); + color: $less-dark-gray; + background: none; + border: none; + padding: 0; + cursor: pointer; + line-height: 1; + + &:hover { + color: $dark-gray; + } + } + + &__input { + width: 100%; + border: none; + padding: 0.6rem 2.5rem 0.6rem 0.8rem; + font-family: $font-primary; + font-size: 0.85rem; + outline: none; + background-color: $light-gray; + + &::placeholder { + color: $less-dark-gray; + text-transform: uppercase; + } + + &:focus { + border-color: $less-light-gray; + } + } + + &__submit { + display: block; + margin-left: auto; + background-color: $light-gray; + padding: 0.5rem 1rem; + font-family: $font-primary; + font-size: 0.85rem; + text-transform: uppercase; + cursor: pointer; + margin-top: 1rem; + border: none; + + &:hover { + background-color: $less-light-gray; + } + } +} + +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: $less_light-gray; + opacity: 0; + z-index: 3; + pointer-events: none; + transition: opacity 0.3s ease-out; + + &.active { + opacity: 0.6; + pointer-events: all; + } +} \ No newline at end of file diff --git a/scss/_index.scss b/scss/_index.scss new file mode 100644 index 0000000..ccab7eb --- /dev/null +++ b/scss/_index.scss @@ -0,0 +1,562 @@ +.hero-header { + display: flex; + flex-direction: column-reverse; + position: relative; + @media ($tablet) { + flex-direction: row; + } +} + +.hero-logos { + position: absolute; + top: 2.5vh; + display: inline-flex; + height: 3rem; + gap: 1rem; + padding: 0.5rem; + align-items: start; + background-color: $light_gray; + @media ($tablet) { + position: unset; + top: unset; + } + + > a { + height: 100%; + > img { + height: 100%; + + } + &:nth-of-type(2) { + mix-blend-mode: darken; + } + } +} + +.color-changer { + transition: color 0.3s ease-out; +} + +.hero-presentation { + font-family: $font-heading; + font-size: 1.6rem; + line-height: 1.1; + @media ($tablet) { + margin-top: 2rem; + font-size: 2.6rem; + } +} + +.hero-presentation-detail { + margin-top: 1.5rem; + width: 90%; +} + +.hero-content > .link-button { + margin-top: 2.5rem; +} + +.hero-content { + @media ($tablet) { + width: 75%; + } +} + +// ==================================== +// SKETCH +// ==================================== + +#sketch { + z-index: 0; + position: relative; + display: block; + height: 200px; + // background-color: white; + @media ($tablet) { + height: unset; + width: 25%; + margin-top: 0 !important; + } +} + +// Floating shapes (DOM-based implementation) +.floating-shape { + position: absolute; + top: 0; + left: 0; + transform-origin: top left; + will-change: transform; + pointer-events: none; + + svg { + overflow: visible; + } + + path, polyline, polygon, line, circle, ellipse, rect { + transition: fill-opacity 0.5s ease-in-out; + } +} + +.thalim-text { + position: absolute; + pointer-events: none; + transition: opacity 0.3s ease-out; + display: flex; + gap: 0; + z-index: 20; + font-family: 'NewsCycle', sans-serif; + font-size: 26px; + @media ($tablet) { + font-size: 48px; + } +} +// ==================================== +// END SKETCH +// ==================================== + +// ==================================== +// SWIPER SECTIONS (annonces, publications…) +// ==================================== + +.swiper-section { + margin-top: 3rem; + position: relative; + + .section-title { + font-family: NewsCycle; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } + } + + .swiper_content_controls { + display: flex; + gap: 0.8rem; + align-items: center; + margin-top: 2rem; + + .swiper { + overflow: hidden; + flex: 1; + } + + .swiper-button-prev, + .swiper-button-next { + position: static; + width: 2.4rem; + height: 2.4rem; + aspect-ratio: 1; + margin: 0; + background-color: $light-gray; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: $dark-gray; + transition: background-color 0.2s ease-out; + cursor: pointer; + flex-shrink: 0; + + &::after { + display: none; // hide Swiper default arrow + } + + &:hover { + background-color: $less-light-gray; + } + + i { + font-size: 1.1rem; + line-height: 1; + } + } + } + + .button-annonces { + display: flex; + justify-content: center; + margin-top: 1.2rem; + } +} + +// ==================================== +// END SWIPER SECTIONS +// ==================================== + +// ==================================== +// MESSAGE DU LABORATOIRE + AGENDA +// ==================================== + +.message-agenda-section { + display: flex; + flex-direction: column; + width: 100%; + gap: 2rem; + margin-top: 3rem; + + @media ($tablet) { + flex-direction: row; + } +} + +.message-du-labo, +.agenda { + position: relative; + padding: 2.5vh 5vw; + padding-bottom: 6vh; + background-color: white; + display: flex; + flex-direction: column; + align-items: flex-start; + + &::after { + @include yellow-gradient-after; + } + + .section-title { + font-family: $font-primary; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 3rem; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } + } + + @media ($tablet) { + padding: 3vh 3vw; + padding-bottom: 8vh; + } +} + +.message-du-labo { + @media ($tablet) { + flex: 4; + } +} + +.messages-list { + width: 100%; + + @media ($tablet) { + flex: 1; + overflow: hidden; + } +} + +.agenda { + @media ($tablet) { + flex: 3; + // border-left: 1px solid $light-gray; + } +} + +.message-date { + font-size: 0.75rem; + color: $less-dark-gray; + display: block; + margin-bottom: 0.3rem; +} + +.message-item { + & + .message-item { + margin-top: 1.5rem; + padding-top: 1.5rem; + border-top: 1px solid $light-gray; + } +} + +.message-content { + margin-bottom: 1.5rem; + padding-right: 2rem; + position: relative; + + > p { + margin: 0.7rem 0; + } + + + p { + margin-bottom: 1rem; + line-height: 1.6; + strong { + font-weight: bold; + } + em { + font-style: italic; + } + } + + ul, ol { + line-height: 1.6; + padding-left: 0.8rem; + } + + ul { + list-style: inside "· "; + } + + ol { + list-style: inside decimal; + } + + blockquote{ + padding-left: 1rem; + margin-left: 1.5rem; + border-left: solid 1px $light-gray; + } + + + @media ($tablet) { + overflow: hidden; + + &.is-overflowing::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 8rem; + background: linear-gradient(to bottom, transparent 0%, white 70%); + pointer-events: none; + } + } +} + +.button-messages, +.button-agenda { + align-self: center; + margin-top: auto; +} + +.message-read-more { + display: none; + position: absolute; + bottom: 1rem; + left: 0; + z-index: 1; + font-size: 0.85rem; + text-decoration: none; + font-family: $font-primary; + text-transform: uppercase; + + .is-overflowing & { + display: inline-block; + } +} + +.agenda-content { + width: 100%; + + .agenda-item { + display: flex; + align-items: center; + gap: 1.5rem; + transform: scale(1); + transition: transform 0.2s ease-out; + margin-bottom: 2rem; + text-decoration: none; + color: inherit; + + &:hover { + transform: scale(0.97); + } + } + + .date-container { + background-color: $light-gray; + text-transform: uppercase; + font-family: $font-primary; + text-align: center; + padding: 0.5rem; + position: relative; + flex-shrink: 0; + min-width: 3rem; + + > p { + position: relative; + z-index: 1; + + &:first-of-type { + font-size: 1.3rem; + } + + &:last-of-type { + margin-bottom: 3px; + } + } + + &::after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, $light-gray 0%, $manifestations 100%); + z-index: 0; + } + } + + .event-content { + padding-bottom: 1rem; + border-bottom: 1px solid $manifestations; + flex: 1; + + .meta { + font-family: $font-primary; + display: flex; + gap: 1rem; + padding-bottom: 0.5rem; + text-transform: uppercase; + font-size: 0.8rem; + flex-wrap: wrap; + opacity: 0.7; + } + + .event-title { + font-family: $font-heading; + font-size: 1.1rem; + } + } +} + +// ==================================== +// END MESSAGE DU LABORATOIRE + AGENDA +// ==================================== + +// ==================================== +// NUAGE DE MOTS-CLÉS +// ==================================== + +.keyword-cloud { + margin-top: 4rem; + margin-bottom: 3rem; + width: 100%; + + @media ($tablet) { + margin-bottom: 0; + } + .section-title { + font-family: $font-primary; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } + } +} + +#keyword-container { + position: relative; + width: 100%; + min-height: 120px; +} + +.keyword { + position: absolute; + font-family: $font-primary; + font-size: 0.7rem; + text-transform: uppercase; + white-space: nowrap; + text-decoration: none; + color: $dark-gray; + opacity: 0; + cursor: pointer; + + @media ($tablet) { + font-size: 0.95rem; + } + + &.keyword--visible { + animation: keywordFadeIn 0.7s ease-out forwards; + } +} + +@keyframes keywordFadeIn { + from { + opacity: 0; + transform: scale(0.92); + } + to { + opacity: 1; + transform: scale(1); + } +} + +// ==================================== +// END NUAGE DE MOTS-CLÉS +// ==================================== +// Quick links widget +.quick-links { + position: fixed; + display: block; + background-color: $light-light-gray; + right: 0; + top: 35vh; + z-index: 3; + font-family: $font-primary; + max-width: 2.2rem; + overflow: hidden; + transition: max-width 0.9s ease-out, top 0.2s ease; + text-decoration: none; + z-index: 10; + + @media ($tablet) { + right: 2vw; + } + + @media ($desktop) { + right: 4vw; + } + + > ul { + display: flex; + flex-direction: column; + align-items: end; + gap: 0.8rem; + padding: 0.8rem 0.6rem; + + > li > a { + display: flex; + align-items: center; + gap: 0.8rem; + text-decoration: none; + white-space: nowrap; + + &:hover { + font-weight: bold; + } + } + } + + &:hover { + max-width: 40vw; + } + + &::after { + content: ""; + display: block; + position: absolute; + height: 10px; + width: 100%; + bottom: 0; + left: 0; + background: linear-gradient(to bottom, transparent 0%, $yellow 100%); + z-index: 2; + } +} diff --git a/scss/_layout.scss b/scss/_layout.scss new file mode 100644 index 0000000..15abab5 --- /dev/null +++ b/scss/_layout.scss @@ -0,0 +1,44 @@ +body { + display: flex; + flex-direction: column; + height: 100vh; + background-color: $light_gray; +} + +main { + width: 100vw; + display: flex; + flex-direction: column; + align-items: center; + @media ($tablet) { + margin-top: 12vh; + } +} + +.container { + display: flex; + align-items: center; + justify-content: start; + flex-direction: column; + position: relative; + z-index: 1; + padding: 4vh 5vw; + max-width: 1640px; + width: 100vw; +} + +.full-block { + width: 100%; + background-color: white; + padding: 2.5vh 5vw; + position: relative; + padding-bottom: 6vh; + @media ($tablet) { + padding: 3vh 3vw; + padding-bottom: 8vh; + } + + &::after { + @include yellow-gradient-after; + } +} \ No newline at end of file diff --git a/scss/_membres.scss b/scss/_membres.scss new file mode 100644 index 0000000..cf3c500 --- /dev/null +++ b/scss/_membres.scss @@ -0,0 +1,211 @@ +.membres-section { + margin-top: 4rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.membres-item.is-open { + position: relative; + padding-bottom: 1rem; + &::after { + @include yellow-gradient-after; + } +} + +.membres-header { + display: flex; + align-items: center; + gap: 1.2rem; + padding: 0.8rem; + background-color: $light-gray; + cursor: pointer; + transition: background-color 0.15s; + font-family: $font-primary; + text-transform: uppercase; + + &:hover { + background-color: $less-light-gray; + } +} + +.membres-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + margin-left: auto; + + .membres-item.is-open & { + transform: rotate(180deg); + } +} + +.membres-sort-chevron { + font-size: 0.8rem; + opacity: 0.3; + transition: transform 0.2s, opacity 0.15s; + vertical-align: middle; + margin-left: 0.3rem; +} + +.membres-content { + padding: 1rem; + background-color: $light-light-gray; + font-family: $font-primary; + font-size: 0.9rem; + line-height: 1.6; +} + +.membres-table { + width: 100%; + table-layout: fixed; + border-collapse: collapse; + font-size: 0.9rem; + + th { + width: 33.333%; + text-align: left; + text-transform: uppercase; + font-family: $font-primary; + font-weight: normal; + font-size: 0.8rem; + padding: 0.5rem 1.5rem 0.5rem 0.5rem; + background-color: $light-gray; + cursor: pointer; + user-select: none; + white-space: nowrap; + + &:hover .membres-sort-chevron { + opacity: 0.7; + } + + &.sort-asc .membres-sort-chevron { + opacity: 1; + transform: rotate(180deg); + } + + &.sort-desc .membres-sort-chevron { + opacity: 1; + transform: rotate(0deg); + } + } + + tbody tr { + cursor: pointer; + transition: background-color 0.15s; + background-color: white; + + &.is-even-row { + background-color: $light-gray; + } + + &:last-child { + border-bottom: none; + } + + &:hover { + background-color: $less-light-gray; + } + } + + td { + padding: 0.6rem 1.5rem 0.6rem 0.5rem; + vertical-align: top; + line-height: 1.4; + + // Name column + &:first-child { + white-space: nowrap; + } + + a { + text-decoration: none; + } + } +} + +// Member hover popover +#membre-popover { + position: fixed; + z-index: 9999; + background: white; + pointer-events: none; + max-width: 380px; + min-width: 180px; + padding: 1rem; + font-family: $font-primary; + font-size: 0.85rem; + opacity: 0; + transition: opacity 0.1s; + border: solid 1px $light-gray; + + &.is-visible { + opacity: 1; + } + + .membre-popover-inner { + display: flex; + gap: 1rem; + align-items: flex-start; + } + + .membre-popover-pic { + width: 80px; + height: 80px; + object-fit: cover; + object-position: top; + flex-shrink: 0; + } + + .membre-popover-name { + font-size: 1.1rem; + font-weight: normal; + margin: 0 0 0.3rem; + } + + .membre-popover-status { + text-transform: uppercase; + color: $less-dark-gray; + margin: 0 0 0.6rem; + } + + .membre-popover-domaines { + font-size: 0.8rem; + margin: 0 0 0.3rem; + line-height: 1.5; + } + + .membre-popover-autres { + font-size: 0.8rem; + margin: 0; + line-height: 1.5; + color: $less-dark-gray; + } +} + +// Filter bar sizing +.filtre-role { flex: 2; } +.filtre-recherche { flex: 1; } + +// Member search input +.membres-search-input { + width: 100%; + border: none; + padding: 0.6rem 0.8rem; + font-family: $font-primary; + font-size: 0.85rem; + background-color: $light-gray; + outline: none; + + &::placeholder { + color: $less-dark-gray; + } + + &:focus { + background-color: $less-light-gray; + } + + @media ($desktop) { + width: 50%; + } +} diff --git a/scss/_mixins.scss b/scss/_mixins.scss new file mode 100644 index 0000000..aa93556 --- /dev/null +++ b/scss/_mixins.scss @@ -0,0 +1,32 @@ +@mixin hover-gradient-background { + background: linear-gradient(to bottom, $light_gray 60%, $yellow 100%); + background-position: bottom 0px left 0px; + background-repeat: no-repeat; + cursor: pointer; + transition: background 0.3s ease-out, padding 0.2s ease-out; + + &:hover { + background-position: bottom -10px left 0px; + } +} + +@mixin yellow-gradient { + background: linear-gradient(to bottom, transparent 50%, $yellow 100%); +} + +@mixin yellow-gradient-after($height: 30px) { + content: ''; + display: block; + position: absolute; + height: $height; + width: 100%; + bottom: 0; + left: 0; + @include yellow-gradient; + z-index: 2; + pointer-events: none; +} + +@mixin category-gradient($color) { + background: linear-gradient(to bottom, $light_gray 60%, $color); +} \ No newline at end of file diff --git a/scss/_navigation.scss b/scss/_navigation.scss new file mode 100644 index 0000000..90e4d0e --- /dev/null +++ b/scss/_navigation.scss @@ -0,0 +1,205 @@ +.main-menu { + overflow-y: scroll; + left: 0; + width: 100%; + background-color: white; + z-index: 4; + padding-bottom: 4vh; + transition: top 0.4s ease-out, opacity 0.2s ease-out; + border-top: 2px solid $light_gray; + position: absolute; + opacity: 0; + @media ($tablet) { + overflow-y: unset; + position: fixed; + } + + &::after { + @include yellow-gradient-after(); + opacity: 0; + transition: opacity 0.2s ease-out 0.3s; + @media ($tablet) { + opacity: 1; + } + } + &.active { + opacity: 1; + &::after { + position: fixed; + opacity: 1; + @media ($tablet) { + position: absolute; + } + } + } +} + +.menu-navigation-container, +.menu-navigation-en-container { + > ul { + display: grid; + gap: 3rem; + padding: 2rem 1.5rem; + max-width: 1400px; + margin: 0 auto; + @media ($tablet) { + padding: 3rem 4rem; + grid-template-columns: repeat(3, auto); + } + @media ($desktop) { + grid-template-columns: repeat(5, auto); + } + > li { + display: flex; + flex-direction: column; + gap: 0.8rem; + align-items: start; + > a { + text-transform: uppercase; + text-decoration: none; + position: relative; + padding-bottom: 0.8rem; + margin-bottom: 0.5rem; + &::after { + content: ''; + display: block; + position: absolute; + height: 8px; + width: 100%; + bottom: 0; + left: 0; + z-index: 2; + } + } + &:nth-of-type(1) { + a:hover { + color: $laboratoire; + } + > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, $laboratoire 100%); + } + > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid $laboratoire; + padding-bottom: 0.8rem; + } + + } + + &:nth-of-type(2) { + a:hover { + color: $manifestations; + } + > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, $manifestations 100%); + } + > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid $manifestations; + padding-bottom: 0.8rem; + } + } + + &:nth-of-type(3) { + a:hover { + color: $publications; + } + > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, $publications 100%); + } + > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid $publications; + padding-bottom: 0.8rem; + } + + } + + &:nth-of-type(4) { + a:hover { + color: $mediations; + } + > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, $mediations 100%); + } + > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid $mediations; + padding-bottom: 0.8rem; + } + + } + + &:nth-of-type(5) { + a:hover { + color: $ressources; + } + > a::after { + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, $ressources 100%); + } + > ul.sub-menu > li:first-of-type { + border-bottom: 1px solid $ressources; + padding-bottom: 0.8rem; + } + + } + + > ul.sub-menu { + display: flex; + flex-direction: column; + gap: 0.8rem; + > li { + > a { + text-decoration: none; + } + } + } + } + } +} + +// Axes thématiques dropdown inside nav first column +.nav-axes-item { + .nav-axes-trigger { + background: none; + border: none; + padding: 0; + cursor: pointer; + font-family: $font-primary; + font-size: inherit; + color: inherit; + display: flex; + align-items: center; + gap: 0.3rem; + + i { + transition: transform 0.2s ease; + } + } + + &.is-open .nav-axes-trigger i { + transform: rotate(180deg); + } + + .nav-axes-list { + display: none; + flex-direction: column; + gap: 0.8rem; + padding-top: 0.8rem; + + li a { + font-size: 0.8rem; + padding-left: 0.5rem; + text-decoration: none; + } + } + + &.is-open .nav-axes-list { + display: flex; + } +} + +.menu-navigation-container, +.menu-navigation-en-container { + > ul > li:nth-of-type(1) { + .nav-axes-trigger:hover { + color: $laboratoire; + } + } +} \ No newline at end of file diff --git a/scss/_page-laboratoire.scss b/scss/_page-laboratoire.scss new file mode 100644 index 0000000..b522329 --- /dev/null +++ b/scss/_page-laboratoire.scss @@ -0,0 +1,183 @@ +// ==================================== +// PAGE LE LABORATOIRE +// ==================================== + +// ── Images ──────────────────────────────────────────────────── +.labo-images { + display: flex; + flex-wrap: wrap; + gap: 1.5rem; + margin-bottom: 3rem; +} + +.labo-image { + flex: 0 0 auto; + width: 100%; + margin-top: 2rem; + + img { + width: 100%; + height: auto; + display: block; + } + + figcaption { + font-family: $font-primary; + font-size: 0.85rem; + color: $less-dark-gray; + margin-top: 0.5rem; + } + + @media ($tablet) { + width: calc(50% - 0.75rem); + } +} + +// ── Section titles ──────────────────────────────────────────── +.labo-section { + margin-top: 5rem; + + > h3 { + font-family: $font-primary; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } + } +} + +// ── Dropdown wrapper ────────────────────────────────────────── +.labo-dropdowns { + display: flex; + flex-direction: column; + gap: 1rem; + margin-top: 5rem; + + .labo-section & { + margin-top: 0; + } +} + +// ── Dropdown item ───────────────────────────────────────────── +.labo-dropdown-item.is-open { + position: relative; + padding-bottom: 1rem; + + &::after { + @include yellow-gradient-after; + } +} + +.labo-dropdown-header { + display: flex; + align-items: center; + gap: 1.2rem; + padding: 0.8rem; + background-color: $light-gray; + cursor: pointer; + transition: background-color 0.15s; + font-family: $font-primary; + font-size: inherit; + font-weight: normal; + text-transform: uppercase; + margin: 0; + + &:hover { + background-color: $less-light-gray; + } +} + +.labo-dropdown-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + margin-left: auto; + + .labo-dropdown-item.is-open & { + transform: rotate(180deg); + } +} + +.labo-dropdown-content { + padding: 1rem 1rem 1rem 1rem; + background-color: $light-light-gray; + font-family: $font-primary; + font-size: 0.9rem; + line-height: 1.6; + + p { + margin-bottom: 0.6rem; + } + + p + ul { + margin-top: -1rem; + } + + ul, ol { + padding-left: 1.2rem; + margin-bottom: 0.6rem; + } +} + +// ── Axes list ───────────────────────────────────────────────── +.labo-axes-list { + list-style: none; + padding: 0; + margin: 0; + + li { + padding: 0.5rem 0; + border-bottom: 1px solid $light-gray; + + &:last-child { + border-bottom: none; + } + + a { + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + } +} + +// ── Bibliothèques ───────────────────────────────────────────── +.labo-bibliotheques { + font-family: $font-primary; + font-size: 0.9rem; + line-height: 1.6; + + p { + margin-bottom: 0.8rem; + } + + p + ul { + margin-top: -1rem; + margin-bottom: 0.6rem; + } + + a { + text-decoration: underline; + } +} + +// ── Programme de recherche ──────────────────────────────────── +.programme-description { + margin-bottom: 1.5rem; + + p { margin-bottom: 0.6rem; } + ul, ol { padding-left: 1.2rem; } + a { text-decoration: underline; } +} + +.programme-link { + padding-top: 0.5rem; + border-top: 1px solid $light-gray; +} diff --git a/scss/_postcard.scss b/scss/_postcard.scss new file mode 100644 index 0000000..dbc539b --- /dev/null +++ b/scss/_postcard.scss @@ -0,0 +1,114 @@ +.post-card { + padding-bottom: 0.8rem; + border-bottom: solid 1px; + + // Category-specific gradients + &.gradient--le-laboratoire { + .gradient-container { + @include category-gradient($laboratoire); + } + border-color: $laboratoire + } + &.gradient--manifestations-scientifiques { + .gradient-container { + @include category-gradient($manifestations); + } + border-color: $manifestations; + } + &.gradient--publications-et-productions { + .gradient-container { + @include category-gradient($publications); + } + border-color: $publications; + } + &.gradient--mediation-scientifique { + .gradient-container { + @include category-gradient($mediations); + } + border-color: $mediations + } + &.gradient--ressources { + .gradient-container { + @include category-gradient($ressources); + } + border-color: $ressources + } + + &:hover { + .gradient-container { + img, h2 { + transform: scale(0.98); + } + } + } + .gradient-container { + height: 25vh; + padding: 0.7rem; + display: flex; + align-items: center; + justify-content: center; + text-decoration: unset; + + img { + max-height: 100%; + transition: transform 0.2s ease-out; + transform: scale(1); + max-width: 100%; + } + + h2 { + font-family: Gelasio; + font-size: 1.7rem; + line-height: 1.1; + padding: 1.5rem; + transition: transform 0.2s ease-out; + transform: scale(1); + text-decoration: unset; + } + + + &.text-only { + font-family: Gelasio; + font-size: 1.7rem; + padding: 1.5rem; + + p { + transition: transform 0.2s ease-out; + transform: scale(1); + } + } + } + .contextual-infos { + text-transform: uppercase; + font-size: 0.8rem; + display: flex; + justify-content: space-between; + margin-top: 0.6rem; + line-height: 1.3; + .authors { + a { + text-decoration: none; + } + > span:not(:last-of-type)::after { + content: ", "; + } + } + .date-category { + display: flex; + flex-direction: column; + align-items: flex-end; + text-align: right; + a { + text-decoration: none; + } + } + } + .title-bottom { + font-size: 1.2rem; + margin-top: 0.8rem; + line-height: 1.2; + a { + text-decoration: none; + } + } +} diff --git a/scss/_reset.scss b/scss/_reset.scss new file mode 100644 index 0000000..1854ca1 --- /dev/null +++ b/scss/_reset.scss @@ -0,0 +1,46 @@ +*, *:before, *:after{ + box-sizing: border-box; +} + +html, body, div, span, object, iframe, figure, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, code, em, img, small, strike, strong, sub, sup, tt, b, u, i, ol, ul, li, fieldset, form, label, table, caption, tbody, tfoot, thead, tr, th, td, main, canvas, embed, footer, header, nav, section, video{ + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + text-size-adjust: none; +} + +footer, header, nav, section, main{ + display: block; +} + +body{ + line-height: 1; +} + +ol, ul{ + list-style: none; +} + +blockquote, q{ + quotes: none; +} + +blockquote:before, blockquote:after, q:before, q:after{ + content: ''; + content: none; +} + +table{ + border-collapse: collapse; + border-spacing: 0; +} + +input{ + -webkit-appearance: none; + border-radius: 0; +} \ No newline at end of file diff --git a/scss/_search.scss b/scss/_search.scss new file mode 100644 index 0000000..1bbcbcf --- /dev/null +++ b/scss/_search.scss @@ -0,0 +1,153 @@ +.search-page-form { + margin-top: 2rem; + margin-bottom: 3rem; + @media ($desktop) { + width: 50%; + } + .search-panel__desc { + margin-bottom: 1.3rem; + } +} + +.search-page-form + #category-filters { + margin-top: 0; +} + +// Author search results +.author-results { + margin-bottom: 3rem; + + &__title { + font-family: $font-primary; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1.2rem; + color: $less-dark-gray; + } +} + +// Taxonomy search results (axes & programmes) +.taxonomy-results { + margin-bottom: 3rem; + + &__title { + font-family: $font-primary; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1.2rem; + color: $less-dark-gray; + } + + &__list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-wrap: wrap; + gap: 0.6rem; + } + + &__link { + display: block; + padding: 0.5rem 1rem; + border: 1px solid $light-gray; + text-decoration: none; + transition: border-color 0.15s; + + &:hover { + border-color: $dark-gray; + } + } + + &__name { + font-family: $font-heading; + font-size: 0.95rem; + line-height: 1.3; + } + + &__meta { + display: block; + font-family: $font-primary; + font-size: 0.7rem; + text-transform: uppercase; + color: $less-dark-gray; + margin-top: 0.15rem; + } +} + +.author-cards-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.5rem; + + @media ($tablet) { + grid-template-columns: repeat(3, 1fr); + } + + @media ($desktop) { + grid-template-columns: repeat(6, 1fr); + } +} + +.author-card { + border-bottom: solid 1px $laboratoire; + padding-bottom: 0.4rem; + + &__visual { + display: flex; + align-items: center; + justify-content: center; + height: 14vh; + padding: 0.7rem; + background-color: lighten($laboratoire, 28%); + overflow: hidden; + text-decoration: none; + + img { + max-height: 100%; + max-width: 100%; + width: auto; + height: auto; + transition: transform 0.2s ease-out; + } + + &:hover img { + transform: scale(0.98); + } + } + + &__initials { + font-family: $font-heading; + font-size: 2rem; + color: $laboratoire; + user-select: none; + } + + &__info { + padding-top: 0.5rem; + } + + &__name { + font-family: $font-heading; + font-size: 1rem; + font-weight: normal; + line-height: 1.2; + margin-bottom: 0.3rem; + + a { + text-decoration: none; + } + } + + &__role, + &__affiliation { + font-family: $font-primary; + font-size: 0.75rem; + text-transform: uppercase; + line-height: 1.3; + color: $less-dark-gray; + margin: 0; + } +} \ No newline at end of file diff --git a/scss/_single.scss b/scss/_single.scss new file mode 100644 index 0000000..a45a0e9 --- /dev/null +++ b/scss/_single.scss @@ -0,0 +1,584 @@ +.article { + margin-top: 0; + width: 100%; + + .category-header-top { + display: flex; + flex-direction: column; + align-items: start; + justify-content: space-between; + .breadcrumb { + font-size: 0.85rem; + margin-bottom: 1.5rem; + text-transform: uppercase; + line-height: 1.3; + + &__separator { + margin: 0 0.4rem; + } + } + @media ($tablet) { + flex-direction: row; + } + } + + + h2 { + font-family: Gelasio; + font-weight: normal; + font-size: 1.8rem; + position: relative; + display: inline-block; + margin-top: 2rem; + margin-bottom: 2rem; + + &::after { + content: ''; + display: block; + position: absolute; + height: 5px; + width: 100%; + // bottom: -1.1rem; + left: 0; + z-index: 2; + } + + p { + line-height: 1.3; + &:last-of-type { + margin-top: 0.3rem; + margin-bottom: 0.3rem; + } + } + + p + p { + font-size: 1.6rem; + } + + @media ($tablet) { + p { + font-size: 2.2rem !important; + } + + p + p { + font-size: 1.6rem !important; + } + &::after { + bottom: -0.4rem; + } + } + } + + // Category color gradients on h2::after + &.category--le-laboratoire h2::after { + background: linear-gradient(to bottom, transparent 0%, $laboratoire 30%); + } + &.category--manifestations-scientifiques h2::after { + background: linear-gradient(to bottom, transparent 0%, $manifestations 30%); + } + &.category--publications-et-productions h2::after { + background: linear-gradient(to bottom, transparent 0%, $publications 30%); + } + &.category--mediation-scientifique h2::after { + background: linear-gradient(to bottom, transparent 0%, $mediations 30%); + } + &.category--ressources h2::after { + background: linear-gradient(to bottom, transparent 0%, $ressources 30%); + } + + .article-type { + display: inline-block; + margin-top: 2rem; + font-family: $font-primary; + font-size: 0.85rem; + text-transform: uppercase; + background-color: $light-gray; + padding: 0.2rem 0.6rem; + } + + .maj { + font-family: $font-primary; + font-size: 0.85rem !important; + color: $less-dark-gray; + margin-top: 1rem; + margin-bottom: 2.5rem; + text-transform: uppercase; + } + + .imgs { + width: 100%; + margin-bottom: 3rem; + + figure { + width: 100%; + + img { + width: 100%; + height: auto; + } + + figcaption { + font-family: $font-primary; + font-size: 0.85rem; + color: $less-dark-gray; + margin-top: 0.5rem; + font-style: italic; + } + + @media ($tablet) { + width: 50%; + } + } + + &--swiper { + display: flex; + align-items: center; + gap: 0.5rem; + + @media ($tablet) { + width: 50%; + } + + .swiper { + flex: 1; + min-width: 0; + } + + figure { + width: 100%; + } + + .swiper-pagination { + position: static; + margin-top: 0.5rem; + text-align: center; + } + + .swiper-pagination-bullet-active { + background-color: $less-dark-gray !important; + } + } + } + + .article-content { + display: flex; + flex-direction: column-reverse; + gap: 3rem; + + @media ($tablet) { + flex-direction: row; + } + } + + .sidebar { + background-color: $light-light-gray; + @media ($tablet) { + width: 25%; + padding: 0.8rem; + flex-shrink: 0; + } + + .sidebar-container { + position: sticky; + top: 6rem; + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .sidebar-section { + display: flex; + flex-direction: column; + gap: 0.8rem; + line-height: 1.4; + + p { + margin: 0; + } + + &.reference-bibliographique { + display: inline-block; + em, i { + font-style: italic; + } + strong { + font-weight: bold; + } + } + } + + p { + font-family: $font-primary; + } + + .link-button { + width: fit-content; + } + + .imgs--portrait { + @media ($tablet) { + width: calc(100% + 1.6rem); + margin-left: -0.8rem; + margin-right: -0.8rem; + } + + .sidebar-portrait { + width: 100%; + } + } + + .sidebar-portrait { + width: 100%; + + img { + width: 100%; + height: auto; + } + + figcaption { + font-family: $font-primary; + font-size: 0.85rem; + color: $less-dark-gray; + margin-top: 0.5rem; + font-style: italic; + } + } + } + + .main-content-text { + margin-top: 1rem; + flex: 1; + min-height: unset; + + /* Affichage posts newsletter */ + &:has(table[role=presentation]) { + p:not(table[role=presentation] p):not(.maj) { + display: none; + } + table { + &[role=presentation] p { + margin: unset; + } + td { + vertical-align: top; + } + br { + display: none; + } + } + } + + > *:not(.article-field) { + font-size: 1.25rem; + } + + a { + text-decoration: underline; + } + + p { + margin-bottom: 1rem; + line-height: 1.4; + strong { + font-weight: bold; + } + em { + font-style: italic; + } + &:first-child { + margin-top: 0 !important; + } + } + + ul, ol { + line-height: 1.4; + padding-left: 0.8rem; + } + + ul { + list-style: inside "· "; + } + + ol { + list-style: inside decimal; + } + + blockquote{ + padding-left: 1rem; + margin-left: 1.5rem; + border-left: solid 1px $light-gray; + } + + p:first-of-type + .mots-cles { + margin-top: 1rem; + } + + .article-field { + font-size: 0.9rem !important; + line-height: 1.4 !important; + margin-bottom: 1.5rem !important; + i { + font-style: italic; + } + } + + .mots-cles { + margin-top: 4rem; // style par défaut = style du premier + } + .mots-cles ~ .mots-cles { + margin-top: 1rem; // les 2e et 3e ont moins d'espace + } + + .canal-u-embeds, + .youtube-embeds { + margin-top: 2rem; + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .canal-u-embed, + .video-embed { + position: relative; + width: 100%; + padding-bottom: 56.25%; // 16:9 + height: 0; + + iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; + } + } + + } + + + .inline-title { + text-transform: uppercase; + position: relative; + + &::after { + content: ''; + width: 100%; + height: 10px; + bottom: -1px; + left: 0; + position: absolute; + @include yellow-gradient; + } + } + + .related-posts, + .seances-section { + margin-top: 5rem; + + h3 { + font-family: $font-primary; + text-transform: uppercase; + position: relative; + display: inline-block; + margin-bottom: 2rem; + + &::after { + @include yellow-gradient-after(10px); + bottom: -10px; + } + } + } + + .related-posts { + .post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + + .post-card { + min-width: 0; + a { + text-decoration: none; + h2::after { + display: none; + } + } + } + + @media ($tablet) { + grid-template-columns: repeat(2, 1fr); + } + } + } + + .seances-list { + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .seance-header { + display: flex; + align-items: stretch; + gap: 1.2rem; + padding: 0.1rem; + background-color: $light-gray; + cursor: pointer; + transition: background-color 0.15s; + + &:hover { + background-color: $less-light-gray; + } + @media ($tablet) { + padding: 0.8rem; + } + } + + .seance-date { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 3rem; + font-family: $font-primary; + text-transform: uppercase; + line-height: 1.2; + padding: 0.4rem 0; + background: linear-gradient(to bottom, $light_light_gray 60%, $manifestations); + + &__day { + font-size: 1.4rem; + } + + &__month { + font-size: 0.85rem; + } + + &__year { + font-size: 0.75rem; + } + } + + .seance-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.2rem; + + .seance-title { + font-family: $font-heading; + font-size: 1.15rem; + line-height: 1.2; + text-decoration: none; + padding: 0.6rem 0; + @media ($tablet) { + padding: unset; + } + } + + .seance-intervenants { + font-family: $font-primary; + font-size: 0.85rem; + margin-top: 0.1rem; + color: black; + + a { + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + } + } + + .seance-chevron { + font-size: 1.2rem; + transition: transform 0.2s; + flex-shrink: 0; + align-self: center; + margin-right: 0.5rem; + } + + .seance-item.is-open .seance-chevron { + transform: rotate(180deg); + } + + .seance-content { + padding: 1rem 1rem 1rem 1rem; + background-color: $light-light-gray; + font-family: $font-primary; + font-size: 0.9rem; + line-height: 1.4; + + .seance-content-infos { + display: flex; + width: 100%; + justify-content: space-between; + margin-bottom: 1rem; + > .seance-content-lieu { + text-align: right; + > p { + margin: 0; + } + } + } + + p { + margin-bottom: 0.6rem; + } + .seance-images { + margin-top: 1.5rem; + margin-bottom: 0; + + figure { + @media ($tablet) { + width: 50%; + } + } + } + + .seance-extras { + display: flex; + flex-direction: column; + align-items: start; + gap: 0.8rem; + margin-top: 1.5rem; + } + + .seance-related { + margin-top: 1.5rem; + + h4 { + font-family: $font-primary; + text-transform: uppercase; + margin-bottom: 1rem; + } + + .post-grid { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + + .post-card { + min-width: 0; + a { + text-decoration: none; + h2::after { + display: none; + } + } + } + + @media ($tablet) { + grid-template-columns: repeat(2, 1fr); + } + } + } + + @media ($tablet) { + padding: 1rem 1rem 1rem 5.5rem; + } + } +} diff --git a/scss/_typography.scss b/scss/_typography.scss new file mode 100644 index 0000000..0bfd60e --- /dev/null +++ b/scss/_typography.scss @@ -0,0 +1,69 @@ +@font-face { + font-family: 'Gelasio'; + src: url('../assets/fonts/Gelasio-Regular.woff2') format('woff2'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'NewsCycle'; + src: url('../assets/fonts/NewsCycle-Regular.woff2') format('woff2'); + font-weight: normal; + font-style: normal; +} + +body { + font-family: $font-primary; +} + +h1, h2, h3, h4, h5, h6 { + font-family: $font-heading; +} + +a, +a:active { + color: $dark-gray; + transition: color 0.2s ease-out; +} + +a:hover { + color: $less-dark-gray; +} + +p { + line-height: 1.2; +} + +.link-button { + display: inline-flex; + background-color: $light_gray; + color: $dark_gray; + padding: 0.6rem 0.7rem; + font-size: 0.9rem; + transition: background-color 0.3s ease-out; + text-decoration: none; + justify-content: center; + align-items: center; + word-break: break-all; + max-width: 100%; + + > i { + margin-right: 0.6rem; + } + + &:hover { + background-color: $less_light-gray; + } + + @media ($tablet) { + font-size: unset; + padding: 0.6rem 1rem; + } + + // Multi-word titles: break at word boundaries; only split a word mid-letter + // as a last resort when it overflows the container. + &--wrap-word { + word-break: normal; + overflow-wrap: break-word; + } +} \ No newline at end of file diff --git a/scss/_variables.scss b/scss/_variables.scss new file mode 100644 index 0000000..3db6af4 --- /dev/null +++ b/scss/_variables.scss @@ -0,0 +1,31 @@ +// Neutral colors +$light-light-gray: #fcfcfc; +$light-gray: #eeeeee; +$less-light-gray: #cccccc; +$less-less-light-gray: #bbbbbb; +$yellow: #f7ff29; +$dark-gray: #1a1a1a; +$less-dark-gray: #3e3e3e; + +// Theme colors +$laboratoire: #e0775d; +$manifestations: #7cc0c6; +$mediations: #e05680; +$publications: #46ae51; +$ressources: #bb8dd9; + +// Fonts +$font-primary: 'NewsCycle', sans-serif; +$font-heading: 'Gelasio', serif; + +// Breakpoints +$breakpoint-tablet: 768px; +$breakpoint-desktop: 1024px; +$breakpoint-large: 1440px; + +// Media queries (mobile first) +$tablet: 'min-width: #{$breakpoint-tablet}'; +$desktop: 'min-width: #{$breakpoint-desktop}'; +$large: 'min-width: #{$breakpoint-large}'; + +// Fonts sizes diff --git a/scss/style.scss b/scss/style.scss new file mode 100644 index 0000000..88bd0fd --- /dev/null +++ b/scss/style.scss @@ -0,0 +1,25 @@ +@import 'reset'; +@import 'variables'; +@import 'mixins'; +@import 'base'; +@import 'typography'; +@import 'layout'; +@import 'header'; +@import 'navigation'; +@import 'footer'; +@import 'index'; +@import 'postcard'; +@import 'category'; +@import 'filters'; +@import 'single'; +@import 'author'; +@import 'membres'; +@import 'page-laboratoire'; +@import 'search'; + +/* +Theme Name: Thalim +Author: Valentin Le Moign +Version: 1.0 +*/ + diff --git a/search.php b/search.php new file mode 100644 index 0000000..a772475 --- /dev/null +++ b/search.php @@ -0,0 +1,248 @@ +parent) + ? $active_cat_obj->parent + : $active_cat_id; +} +$context['active_rubrique'] = $active_rubrique_id; + +// Base URL for search filter links (language-aware) +$search_base = thalim_en_url( home_url('/') ); + +// Override annonces_url: rubrique reset stays on search page (no filter_cat) +$context['annonces_url'] = add_query_arg(['s' => $search_query], $search_base); + +// Base params preserved across filter links (preserves search term) +$base_filter_params = array_filter([ + 's' => $search_query, + 'axe' => $active_axe ?: null, + 'date_from' => $active_date_from ?: null, + 'date_to' => $active_date_to ?: null, +]); + +// Build tax_query +$tax_query = [ + 'relation' => 'AND', + [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => $excluded_cat_ids, + 'operator' => 'NOT IN', + ], +]; +if ($active_cat_id) { + $tax_query[] = [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$active_cat_id], + 'include_children' => !$filter_autres, + ]; +} + +$query_args = [ + 'post_type' => 'post', + 's' => $search_query, + 'relevanssi' => true, + 'posts_per_page' => 12, + 'orderby' => 'relevance', + 'order' => 'DESC', + 'lang' => '', + 'tax_query' => $tax_query, +]; +if ($active_axe) { + $query_args['meta_query'] = [[ + 'key' => 'axes_thematiques', + 'value' => $active_axe, + 'type' => 'NUMERIC', + ]]; +} +if ($active_date_from || $active_date_to) { + $date_query = ['inclusive' => true]; + if ($active_date_from) $date_query['after'] = $active_date_from; + if ($active_date_to) $date_query['before'] = $active_date_to; + $query_args['date_query'] = [$date_query]; +} + +// Axes thématiques for filter dropdown +$axes_groups = thalim_get_axes_filter_groups(); +$current_axes = $axes_groups[0]['terms'] ?? []; +$context['filter_axes'] = $current_axes; +$context['axe_stay_on_page'] = true; + +// Rubrique/catégorie filter links (all preserve search term) +$all_cats = get_categories(['taxonomy' => 'category', 'hide_empty' => false, 'exclude' => $excluded_cat_ids]); + +$filter_parents = []; +foreach ($all_cats as $cat) { + if ($cat->parent == 0) { + $params = array_filter(array_merge($base_filter_params, ['filter_cat' => $cat->term_id])); + $filter_parents[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => add_query_arg($params, $search_base), + ]; + } +} +$context['filter_parents'] = $filter_parents; + +$filter_categories = []; +if ($active_rubrique_id) { + foreach ($all_cats as $cat) { + if ($cat->parent == $active_rubrique_id) { + $params = array_filter(array_merge($base_filter_params, ['filter_cat' => $cat->term_id])); + $filter_categories[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => add_query_arg($params, $search_base), + ]; + } + } +} +// Add "Autres" entry if active rubrique has posts directly assigned to it +if ($active_rubrique_id && !empty($filter_categories)) { + $lang = thalim_current_language(); + $direct_check = new WP_Query([ + 'post_type' => 'post', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'lang' => '', + 'tax_query' => [[ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$active_rubrique_id], + 'include_children' => false, + ]], + ]); + if ($direct_check->have_posts()) { + $params = array_filter(array_merge($base_filter_params, ['filter_cat' => $active_rubrique_id, 'filter_autres' => 1])); + $filter_categories[] = [ + 'id' => 'autres', + 'name' => $lang === 'en' ? 'Other' : 'Autres', + 'slug' => 'autres', + 'link' => add_query_arg($params, $search_base), + ]; + } +} +$context['filter_categories'] = $filter_categories; + +$posts = Timber::get_posts($query_args); +$context['cards'] = thalim_get_cards_data($posts); +$context['posts'] = $posts; + +// Search users (members) by display_name +$author_cards = []; +if ( $search_query ) { + $excluded_role_ids = [ 600, 598 ]; // "À ranger", "Archive" + $user_query = new WP_User_Query([ + 'search' => '*' . $search_query . '*', + 'search_columns' => ['display_name'], + 'number' => 6, + 'orderby' => 'display_name', + 'order' => 'ASC', + 'meta_query' => [ + [ + 'key' => 'role_1', + 'value' => $excluded_role_ids, + 'compare' => 'NOT IN', + ], + ], + ]); + $lang = thalim_current_language(); + + // Direction IDs (same source as membres page and author page) + $labo_page = get_page_by_path( 'le-laboratoire' ); + $labo_directeur_id = $labo_page ? intval( get_post_meta( $labo_page->ID, 'directeur', true ) ) : 0; + $labo_adjoint_id = $labo_page ? intval( get_post_meta( $labo_page->ID, 'directeur_adjoint', true ) ) : 0; + + foreach ( $user_query->get_results() as $user ) { + $avatar_url = thalim_get_user_avatar_url( $user->ID ); + + $role_id = get_user_meta( $user->ID, 'role_1', true ); + $role_label = ''; + if ( $role_id ) { + $role_term = get_term( intval( $role_id ), 'role' ); + if ( $role_term && ! is_wp_error( $role_term ) ) { + $override = thalim_bilingual( get_user_meta( $user->ID, 'affichage_du_statut_1', true ) ?: '', $lang ); + $role_label = $override ?: $role_term->name; + } + } + + if ( $user->ID === $labo_directeur_id ) { + $role_label = 'Directeur' . ( $role_label ? ', ' . $role_label : '' ); + } elseif ( $user->ID === $labo_adjoint_id ) { + $role_label = 'Directeur adjoint' . ( $role_label ? ', ' . $role_label : '' ); + } + + $affiliation = get_user_meta( $user->ID, 'affiliation', true ) ?: ''; + if ( strtolower( $affiliation ) === 'autre' ) { + $affiliation = thalim_bilingual( get_user_meta( $user->ID, 'affiliation_autre', true ) ?: '', $lang ); + } + + $words = preg_split( '/\s+/', trim( $user->display_name ) ); + $initials = implode( '', array_map( fn( $w ) => mb_substr( $w, 0, 1 ), $words ) ); + + $author_cards[] = [ + 'id' => $user->ID, + 'name' => $user->display_name, + 'url' => get_author_posts_url( $user->ID ), + 'avatar_url' => $avatar_url, + 'initials' => mb_strtoupper( $initials ), + 'role_label' => $role_label, + 'affiliation' => $affiliation, + ]; + } +} +$context['author_cards'] = $author_cards; + +// Search taxonomy terms (axes thématiques + programmes de recherche) +$taxonomy_cards = []; +if ( $search_query ) { + $matching_terms = get_terms([ + 'taxonomy' => [ 'axe_thematique', 'programme_de_recherche' ], + 'hide_empty' => false, + 'name__like' => $search_query, + ]); + if ( ! is_wp_error( $matching_terms ) ) { + foreach ( $matching_terms as $term ) { + $tax_obj = get_taxonomy( $term->taxonomy ); + $taxonomy_cards[] = [ + 'name' => $term->name, + 'url' => get_term_link( $term ), + 'taxonomy_label' => $tax_obj ? $tax_obj->labels->singular_name : $term->taxonomy, + 'count' => $term->count, + ]; + } + } +} +$context['taxonomy_cards'] = $taxonomy_cards; + +Timber::render('search.twig', $context); diff --git a/single.php b/single.php new file mode 100644 index 0000000..5fd0a92 --- /dev/null +++ b/single.php @@ -0,0 +1,21 @@ +ID); + +// Card data for related posts (main + séances) +$related_cards = []; +if (!empty($context['article']['annonces_liees'])) { + $related_cards += thalim_get_cards_data($context['article']['annonces_liees']); +} +foreach (['seances_a_venir', 'seances_passees'] as $seance_group) { + foreach ($context['article'][$seance_group] as $s) { + if (!empty($s['annonces_liees'])) { + $related_cards += thalim_get_cards_data($s['annonces_liees']); + } + } +} +$context['related_cards'] = $related_cards; + +Timber::render('single.twig', $context); diff --git a/style.css b/style.css new file mode 100644 index 0000000..1f983da --- /dev/null +++ b/style.css @@ -0,0 +1,9 @@ +/* +Theme Name: Thalim +Author: THALIM — Théorie et Histoire des Arts et des Littératures de la Modernité +Description: Thème personnalisé pour le laboratoire THALIM (UMR 7172). Basé sur Timber/Twig. +Version: 1.0.0 +Requires at least: 6.0 +Requires PHP: 7.4 +Text Domain: thalim +*/ diff --git a/tag.php b/tag.php new file mode 100644 index 0000000..83f8a5f --- /dev/null +++ b/tag.php @@ -0,0 +1,2 @@ +taxonomy; + +$context['term'] = Timber::get_term($term); +$context['taxonomy_slug'] = $taxonomy; +$context['term_id'] = $term->term_id; +$context['parent_slug'] = ''; +$tax_object = get_taxonomy($taxonomy); +$context['taxonomy_label'] = $tax_object ? $tax_object->labels->singular_name : $taxonomy; + +$excluded_ids = [12, 31]; // Séance de séminaire, Non classé +if ( ! is_user_logged_in() ) $excluded_ids[] = 9; // Vie du labo + +// Read filter query params +$active_axe = isset($_GET['axe']) ? intval($_GET['axe']) : 0; +$active_date_from = isset($_GET['date_from']) ? sanitize_text_field($_GET['date_from']) : ''; +$active_date_to = isset($_GET['date_to']) ? sanitize_text_field($_GET['date_to']) : ''; +$active_cat_id = isset($_GET['filter_cat']) ? intval($_GET['filter_cat']) : 0; +$filter_autres = isset($_GET['filter_autres']) ? 1 : 0; + +$context['active_axe'] = $active_axe; +$context['active_date_from'] = $active_date_from; +$context['active_date_to'] = $active_date_to; +$context['active_category_id'] = $filter_autres ? 'autres' : $active_cat_id; +$context['active_cat_id'] = $active_cat_id; +$context['filter_autres'] = $filter_autres; + +// Determine active rubrique from active category (parent if subcategory, itself if top-level) +$active_rubrique_id = 0; +if ($active_cat_id) { + $active_cat_obj = get_category($active_cat_id); + $active_rubrique_id = ($active_cat_obj && $active_cat_obj->parent) + ? $active_cat_obj->parent + : $active_cat_id; +} +$context['active_rubrique'] = $active_rubrique_id; + +// Base params shared across all filter links (preserves active filters when navigating) +$base_filter_params = array_filter([ + 'axe' => $active_axe ?: null, + 'date_from' => $active_date_from ?: null, + 'date_to' => $active_date_to ?: null, +]); + +// Build tax_query — combine all clauses with AND +$tax_query = [ + 'relation' => 'AND', + // Terme de la taxonomie courante + [ + 'taxonomy' => $taxonomy, + 'field' => 'term_id', + 'terms' => [$term->term_id], + ], + // Exclure les séances de séminaire (catégorie 12) + [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [12], + 'operator' => 'NOT IN', + ], +]; +if ($active_cat_id) { + $tax_query[] = [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$active_cat_id], + 'include_children' => !$filter_autres, + ]; +} + +// On axe_thematique pages, the current term IS the active axe (for display only — taxonomy query handles filtering) +$axe_taxonomy_mode = ($taxonomy === 'axe_thematique'); +if ($axe_taxonomy_mode) { + $active_axe = $term->term_id; + $context['active_axe'] = $active_axe; +} + +// Build remaining query args (meta/date) +$extra_query_args = []; +if ($active_axe && !$axe_taxonomy_mode) { + $extra_query_args['meta_query'] = [[ + 'key' => 'axes_thematiques', + 'value' => $active_axe, + 'type' => 'NUMERIC', + ]]; +} +if ($active_date_from || $active_date_to) { + $extra_query_args['thalim_event_date_filter'] = ['from' => $active_date_from, 'to' => $active_date_to]; +} + +// Axes thématiques filter +$axes_groups = thalim_get_axes_filter_groups(); +$current_axes = $axes_groups[0]['terms'] ?? []; +$context['filter_axes'] = $current_axes; +$context['axe_taxonomy_mode'] = $axe_taxonomy_mode; +$context['axe_stay_on_page'] = !$axe_taxonomy_mode; + +// Build rubrique/catégorie filter links pointing back to the current taxonomy URL +$current_term_url = get_term_link($term); +$all_cats = get_categories(['taxonomy' => 'category', 'hide_empty' => false, 'exclude' => $excluded_ids]); + +$filter_parents = []; +foreach ($all_cats as $cat) { + if ($cat->parent == 0) { + $params = array_filter(array_merge($base_filter_params, ['filter_cat' => $cat->term_id])); + $filter_parents[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => add_query_arg($params, $current_term_url), + ]; + } +} +$context['filter_parents'] = $filter_parents; + +$filter_categories = []; +if ($active_rubrique_id) { + foreach ($all_cats as $cat) { + if ($cat->parent == $active_rubrique_id) { + $params = array_filter(array_merge($base_filter_params, ['filter_cat' => $cat->term_id])); + $filter_categories[] = [ + 'id' => $cat->term_id, + 'name' => thalim_cat_name($cat), + 'slug' => $cat->slug, + 'link' => add_query_arg($params, $current_term_url), + ]; + } + } +} +// Add "Autres" entry if active rubrique has posts directly assigned to it +if ($active_rubrique_id && !empty($filter_categories)) { + $lang = thalim_current_language(); + $direct_check = new WP_Query([ + 'post_type' => 'post', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'lang' => '', + 'tax_query' => [ + 'relation' => 'AND', + [ + 'taxonomy' => $taxonomy, + 'field' => 'term_id', + 'terms' => [$term->term_id], + ], + [ + 'taxonomy' => 'category', + 'field' => 'term_id', + 'terms' => [$active_rubrique_id], + 'include_children' => false, + ], + ], + ]); + if ($direct_check->have_posts()) { + $params = array_filter(array_merge($base_filter_params, ['filter_cat' => $active_rubrique_id, 'filter_autres' => 1])); + $filter_categories[] = [ + 'id' => 'autres', + 'name' => $lang === 'en' ? 'Other' : 'Autres', + 'slug' => 'autres', + 'link' => add_query_arg($params, $current_term_url), + ]; + } +} +$context['filter_categories'] = $filter_categories; + +$posts = Timber::get_posts(array_merge([ + 'post_type' => 'post', + 'tax_query' => $tax_query, + 'posts_per_page' => 12, + 'orderby' => 'date', + 'order' => 'DESC', + 'lang' => '', + 'thalim_event_date_order' => true, +], $extra_query_args)); +$context['cards'] = thalim_get_cards_data($posts); +$context['posts'] = $posts; + +// Custom Pods presentation fields (not the WP built-in description) +$tax_lang = thalim_current_language(); +$pres_fr = get_term_meta($term->term_id, 'presentation', true) ?: ''; +$pres_en = get_term_meta($term->term_id, 'presentation_en', true) ?: ''; +$context['term_presentation'] = wpautop( ( $tax_lang === 'en' && $pres_en ) ? $pres_en : $pres_fr ); + +Timber::render('taxonomy.twig', $context); diff --git a/templates/404.twig b/templates/404.twig new file mode 100644 index 0000000..3390f93 --- /dev/null +++ b/templates/404.twig @@ -0,0 +1,21 @@ +{% extends "base.twig" %} + +{% block content %} +

    +
    +

    404

    +

    + {{ current_language == 'en' ? 'Page not found' : 'Page introuvable' }} +

    +

    + {{ current_language == 'en' + ? 'The page you are looking for does not exist or has been moved.' + : "La page que vous cherchez n'existe pas ou a été déplacée." }} +

    + + {{ current_language == 'en' ? '← Back to home' : "← Retour à l'accueil" }} + +
    +
    +
    +{% endblock %} diff --git a/templates/author.twig b/templates/author.twig new file mode 100644 index 0000000..a5ab085 --- /dev/null +++ b/templates/author.twig @@ -0,0 +1,181 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    + + + + {% if author.avatar_url %} +
    +
    + {{ author.display_name }} +
    +
    +

    {{ author.display_name }}

    + {% if author.role_label or author.role_complement or author.affiliation %} +

    + {{ author.role_label }}{% if author.role_complement %} {{ author.role_complement }}{% if author.affiliation %},{% endif %}{% endif %}{% if author.affiliation %} {{ author.affiliation }}{% endif %} +

    + {% endif %} +

    {{ current_language == 'en' ? 'Updated on' : 'Mis à jour le' }} {{ author.user_since }}

    +
    +
    + {% endif %} + +
    + + {% if author.email or author.liens_externes or author.documents or author.hal_publications_url %} + + {% endif %} + +
    + + {% if not author.avatar_url %} +
    +

    {{ author.display_name }}

    + {% if author.role_label or author.role_complement or author.affiliation %} +

    + {{ author.role_label }}{% if author.role_complement %} {{ author.role_complement }}{% if author.affiliation %},{% endif %}{% endif %}{% if author.affiliation %} {{ author.affiliation }}{% endif %} +

    + {% endif %} +

    {{ current_language == 'en' ? 'Updated on' : 'Mis à jour le' }} {{ author.user_since }}

    +
    + {% endif %} + + {% if current_language == 'en' and author.bio_en %} +
    {{ author.bio_en|raw }}
    + {% elseif author.bio %} +
    {{ author.bio|raw }}
    + {% endif %} + + {% if author.domaines_tags %} +

    + {{ current_language == 'en' ? 'Research areas' : 'Domaines de recherches' }} : + {% for tag in author.domaines_tags %} + {{ tag.name }}{% if not loop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} + {% if current_language == 'en' and author.domaines_en %} +
    + {% if not author.domaines_tags %}Research areas :{% endif %} + {{ author.domaines_en|raw }} +
    + {% elseif author.domaines %} +
    + {% if not author.domaines_tags %}{{ current_language == 'en' ? 'Research areas' : 'Domaines de recherches' }} :{% endif %} + {{ author.domaines|raw }} +
    + {% endif %} + + {% if current_language == 'en' and author.recherches_en %} +
    + Current research : + {{ author.recherches_en|raw }} +
    + {% elseif author.recherches %} +
    + {{ current_language == 'en' ? 'Current research' : 'Recherches en cours' }} : + {{ author.recherches|raw }} +
    + {% endif %} + + {% if author.axes %} +

    + {{ current_language == 'en' ? 'Thematic axes' : 'Axes thématiques' }} : + {% for axe in author.axes %} + {{ axe.name }}{% if not loop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} + + {% if author.titre_these or author.resume_these or author.resume_these_en %} +
    +

    {{ current_language == 'en' ? 'Thesis' : 'Thèse' }}

    + {% if author.titre_these %} +

    {{ author.titre_these }}

    + {% endif %} + {% if author.date_soutenance %} +

    + {{ current_language == 'en' ? 'Defended in' : 'Soutenue en' }} + {{ author.date_soutenance }} +

    + {% endif %} + {% if author.directeur_thalim or author.autre_directeur %} +

    + {{ current_language == 'en' ? 'Supervisor' : 'Direction' }} : + {% if author.directeur_thalim %} + {{ author.directeur_thalim.name }}{% if author.autre_directeur %}, {% endif %} + {% endif %} + {% if author.autre_directeur %}{{ author.autre_directeur }}{% endif %} +

    + {% endif %} + {% if current_language == 'en' and author.resume_these_en %} +
    {{ author.resume_these_en|raw }}
    + {% elseif author.resume_these %} +
    {{ author.resume_these|raw }}
    + {% endif %} +
    + {% endif %} + + {% if author_posts %} +
    + {% for group in author_posts %} +
    +

    + {{ group.cat_name }} + +

    + +
    + {% endfor %} +
    + {% endif %} + +
    +
    + +
    +
    +{% endblock %} diff --git a/templates/base.twig b/templates/base.twig new file mode 100644 index 0000000..40a7623 --- /dev/null +++ b/templates/base.twig @@ -0,0 +1,21 @@ + + + + + + {{ function('wp_head') }} + + + {% include 'partials/header.twig' %} + +
    +
    + {% block content %}{% endblock %} +
    +
    + + {% include 'partials/footer.twig' %} + + {{ function('wp_footer') }} + + \ No newline at end of file diff --git a/templates/category.twig b/templates/category.twig new file mode 100644 index 0000000..f12510b --- /dev/null +++ b/templates/category.twig @@ -0,0 +1,132 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    +
    +
    + +

    + {%- if is_direct -%} + {{ current_language == 'en' ? 'Other' : 'Autres' }} {{ (category|cat_name)|lower }} + {%- else -%} + {{ category|cat_name }} + {%- endif -%} +

    + {% if term_presentation %} +
    {{ term_presentation|raw }}
    + {% endif %} +
    + {% include 'partials/category-filters.twig' %} + + + {# Agenda view — shared by parent and leaf categories #} +
    +

    Agenda

    +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +{% endblock %} diff --git a/templates/index.twig b/templates/index.twig new file mode 100644 index 0000000..bf703d3 --- /dev/null +++ b/templates/index.twig @@ -0,0 +1,135 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    + +

    + {{ gc.presentation }} +

    +

    + {{ gc.presentation_detail }} +

    + + {{ current_language == 'fr' ? 'En savoir plus' : 'Learn more' }} + +
    +
    +
    + + {% include 'partials/swiper-section.twig' with { + section_posts: annonces, + section_cards: annonces_cards, + section_title: current_language == 'en' ? 'Announcements' : 'Annonces', + all_link: annonces_link, + all_label: current_language == 'en' ? 'All announcements' : 'Toutes les annonces' + } %} + + {% if messages_labo or agenda_items %} +
    + {% if messages_labo %} +
    +
    +

    {{ current_language == 'en' ? 'Laboratory messages' : 'Messages du laboratoire' }}

    +
    +
    + {% for message in messages_labo %} +
    + + +
    + {% endfor %} +
    + +
    + {% endif %} + {% if agenda_items %} + + {% endif %} +
    + {% endif %} + + {% include 'partials/swiper-section.twig' with { + section_posts: publications, + section_cards: publications_cards, + section_title: current_language == 'en' ? 'Books & journals' : 'Ouvrages et Revues', + all_link: publications_link, + all_label: current_language == 'en' ? 'All publications' : 'Toutes les publications' + } %} + + {% if has_tags %} +
    +
    +
    + {% endif %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/page-annonces.twig b/templates/page-annonces.twig new file mode 100644 index 0000000..cf3096a --- /dev/null +++ b/templates/page-annonces.twig @@ -0,0 +1,43 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    +
    +
    +
    + +
    +

    {{ current_language == 'en' ? 'Announcements' : 'Annonces' }}

    +
    + + {% include 'partials/category-filters.twig' %} + +
    +
    + {% for post in posts %} + {% include 'partials/post-card.twig' with { post: post, card: cards[post.ID], show_category: true } %} + {% endfor %} +
    + +
    + +
    +
    +
    +
    +{% endblock %} diff --git a/templates/page-le-laboratoire.twig b/templates/page-le-laboratoire.twig new file mode 100644 index 0000000..fc9ab40 --- /dev/null +++ b/templates/page-le-laboratoire.twig @@ -0,0 +1,123 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    + + + +

    {{ post.title | bilingual(current_language) }}

    + + {% if images %} +
    + {% for img in images %} +
    + {{ img.alt }} + {% if img.title %} +
    {{ img.title }}
    + {% endif %} +
    + {% endfor %} +
    + {% endif %} + +
    + + {% if liens %} + + {% endif %} + +
    + + {% if current_language == 'en' and body_en %} + {{ body_en|raw }} + {% else %} + {{ post.content }} + {% endif %} + + {% if axes_groups %} +
    + {% for group in axes_groups %} +
    +

    + {{ group.label }} + +

    + +
    + {% endfor %} +
    + {% endif %} + + {% if partenaires_internationaux or partenaires_nationaux %} +
    +

    {{ current_language == 'en' ? 'Partner institutions' : 'Institutions partenaires' }}

    +
    + {% if partenaires_internationaux %} +
    +

    + {{ current_language == 'en' ? 'International partners' : 'Partenaires internationaux' }} + +

    + +
    + {% endif %} + {% if partenaires_nationaux %} +
    +

    + {{ current_language == 'en' ? 'National partners' : 'Partenaires nationaux' }} + +

    + +
    + {% endif %} +
    +
    + {% endif %} + + {% if bibliotheques %} +
    +

    {{ current_language == 'en' ? 'Libraries' : 'Bibliothèques' }}

    +
    + {{ bibliotheques|raw }} +
    +
    + {% endif %} + +
    +
    + +
    +
    +{% endblock %} diff --git a/templates/page-membres.twig b/templates/page-membres.twig new file mode 100644 index 0000000..1a95561 --- /dev/null +++ b/templates/page-membres.twig @@ -0,0 +1,107 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    +
    +
    +
    + +
    +

    {{ current_language == 'en' ? 'Lab members' : 'Membres du laboratoire' }}

    +
    + +
    + +
    +
    +
    + +
    +
    +

    {{ current_language == 'en' ? 'Filter by status' : 'Filtrer par statut' }}

    + +
    +
    +
    +

    {{ current_language == 'en' ? 'All statuses' : 'Tous les statuts' }}

    + +
    + +
    +
    + +
    +
    +

    {{ current_language == 'en' ? 'Search a member' : 'Rechercher un membre' }}

    + +
    + +
    + +
    + +
    + {% for group in groups %} +
    +

    + {{ group.title }} + +

    + +
    + {% endfor %} +
    +
    +
    +
    +{% endblock %} diff --git a/templates/page-programmes-de-recherche.twig b/templates/page-programmes-de-recherche.twig new file mode 100644 index 0000000..390c32d --- /dev/null +++ b/templates/page-programmes-de-recherche.twig @@ -0,0 +1,60 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    + +
    + + {% if page_edit_link %} + + {{ current_language == 'en' ? 'Edit page' : 'Éditer la page' }} + + {% endif %} +
    + +

    {{ post.title | bilingual(current_language) }}

    + +
    +
    + + {% for section in sections %} + {% if section.items %} +
    +

    {{ section.label }}

    +
    + {% for programme in section.items %} +
    +

    + {{ programme.name }} + +

    + +
    + {% endfor %} +
    +
    + {% endif %} + {% endfor %} + +
    +
    + +
    +
    +{% endblock %} diff --git a/templates/page.twig b/templates/page.twig new file mode 100644 index 0000000..7121cd7 --- /dev/null +++ b/templates/page.twig @@ -0,0 +1,30 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    + + + +

    {{ post.title | bilingual(current_language) }}

    + +
    +
    + {{ post.content }} +
    +
    + +
    +
    +{% endblock %} diff --git a/templates/partials/agenda-card.twig b/templates/partials/agenda-card.twig new file mode 100644 index 0000000..9bd8bb3 --- /dev/null +++ b/templates/partials/agenda-card.twig @@ -0,0 +1,25 @@ + +
    +
    + {{ day }}{% if day == 1 %}er{% endif %} + {{ month }} + {{ year }} +
    + {% if end_day %} + +
    + {{ end_day }} + {{ end_month }} + {{ end_year }} +
    + {% endif %} +
    +
    +
    + {% if date_label %}{{ date_label }}{% endif %} + {% if type_label %}{{ type_label }}{% endif %} + {% if lieu %}{{ lieu }}{% endif %} +
    +

    {{ post.title }}

    +
    +
    diff --git a/templates/partials/author-card.twig b/templates/partials/author-card.twig new file mode 100644 index 0000000..072c6ce --- /dev/null +++ b/templates/partials/author-card.twig @@ -0,0 +1,18 @@ + diff --git a/templates/partials/category-filters.twig b/templates/partials/category-filters.twig new file mode 100644 index 0000000..701248b --- /dev/null +++ b/templates/partials/category-filters.twig @@ -0,0 +1,171 @@ +
    + +
    + {% if filter_parents is defined and active_rubrique %} + {% for parent in filter_parents %} + {% if parent.id == active_rubrique %} + {{ parent.name }} + {% endif %} + {% endfor %} + {% endif %} + {% if filter_categories is defined and active_category_id and active_category_id != active_rubrique %} + {% for cat in filter_categories %} + {% if cat.id == active_category_id %} + {% set cat_reset_url = annonces_url %} + {% if filter_parents is defined %} + {% for parent in filter_parents %} + {% if parent.id == active_rubrique %}{% set cat_reset_url = parent.link %}{% endif %} + {% endfor %} + {% endif %} + {{ cat.name }} + {% endif %} + {% endfor %} + {% endif %} + {% if active_date_from or active_date_to %} + + {%- if active_date_from %}{{ active_date_from|date('d/m/Y') }}{% endif -%} + {{- active_date_from and active_date_to ? ' → ' : '' -}} + {%- if active_date_to %}{{ active_date_to|date('d/m/Y') }}{% endif -%} + + + {% endif %} + {% if active_axe is defined and active_axe and filter_axes is defined %} + {% for axe in filter_axes %} + {% if axe.id == active_axe %} + {{ axe.name }} + {% endif %} + {% endfor %} + {% endif %} +
    +
    +
    + {% if filter_parents is defined and filter_parents %} +
    +
    +

    {{ current_language == 'en' ? 'Filter by section' : 'Filtrer par rubrique' }}

    + {% if active_rubrique %} + + {{ current_language == 'en' ? 'Reset' : 'Réinitialiser' }} + + {% endif %} +
    + +
    + {% endif %} + + {% if filter_categories is defined and filter_categories %} +
    +
    +

    {{ current_language == 'en' ? 'Filter by category' : 'Filtrer par catégorie' }}

    + {% if active_category_id and active_category_id != active_rubrique %} + {% for parent in filter_parents %} + {% if parent.id == active_rubrique %} + + {{ current_language == 'en' ? 'Reset' : 'Réinitialiser' }} + + {% endif %} + {% endfor %} + {% endif %} +
    +
      + {% for cat in filter_categories %} + + {{ cat.name }} + + {% endfor %} +
    +
    + {% endif %} + +
    +
    +

    {{ current_language == 'en' ? 'Filter by date' : 'Filtrer par date' }}

    + {% if active_date_from or active_date_to %} + + {{ current_language == 'en' ? 'Reset' : 'Réinitialiser' }} + + {% endif %} +
    +
    +
    +

    + {%- if active_date_from or active_date_to -%} + {%- if active_date_from -%}{{ active_date_from|date('d/m/Y') }}{%- endif -%} + {{- active_date_from and active_date_to ? ' → ' : '' -}} + {%- if active_date_to -%}{{ active_date_to|date('d/m/Y') }}{%- endif -%} + {%- else -%} + {{ current_language == 'en' ? 'Show all' : 'Tout afficher' }} + {%- endif -%} +

    + +
    + +
    +
    + + {% if filter_axes is defined and filter_axes %} +
    +
    +

    {{ current_language == 'en' ? 'Filter by thematic axis' : 'Filtrer par axe thématique' }}

    + {% if active_axe and not axe_taxonomy_mode %} + + {{ current_language == 'en' ? 'Reset' : 'Réinitialiser' }} + + {% endif %} +
    +
    +
    +

    + {%- set axe_label = current_language == 'en' ? 'All axes' : 'Tous les axes' -%} + {%- for axe in filter_axes -%} + {%- if axe.id == active_axe -%} + {%- set axe_label = axe.name -%} + {%- endif -%} + {%- endfor -%} + {{ axe_label }} +

    + +
    + +
    +
    + {% endif %} +
    diff --git a/templates/partials/footer.twig b/templates/partials/footer.twig new file mode 100644 index 0000000..0aafd36 --- /dev/null +++ b/templates/partials/footer.twig @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/templates/partials/header.twig b/templates/partials/header.twig new file mode 100644 index 0000000..e157dc4 --- /dev/null +++ b/templates/partials/header.twig @@ -0,0 +1,57 @@ +
    +
    +
    + +
    +
    +
    {{ gc.umr }}
    +
    {{ gc.thalim }}
    +
    {{ gc.siecles }}
    +
    +
    +
    +
    + +
    +
    + {% if languages %} + + {% endif %} +
    +
    +
    + +
    +
    + +
    +
    + +
    + +{% include 'partials/search-panel.twig' %} + +{% include 'partials/navigation.twig' %} \ No newline at end of file diff --git a/templates/partials/navigation.twig b/templates/partials/navigation.twig new file mode 100644 index 0000000..3470fec --- /dev/null +++ b/templates/partials/navigation.twig @@ -0,0 +1,34 @@ + diff --git a/templates/partials/post-card.twig b/templates/partials/post-card.twig new file mode 100644 index 0000000..f163a83 --- /dev/null +++ b/templates/partials/post-card.twig @@ -0,0 +1,116 @@ +
    + + {% if card.card_image %} + {{ post.title|bilingual(current_language) }} + {% else %} +

    {{ post.title|bilingual(current_language) }}

    + {% endif %} +
    +
    +
    + {% set autres = post.meta('autrepersonnes') %} + {% set autres_list = autres ? autres|split(', ') : [] %} + {% set membres_count = card.card_membres|length %} + {% set total = membres_count + autres_list|length %} + {% set slots_left = 3 - membres_count %} + {% for membre in card.card_membres|slice(0, 3) %} + {{ membre.name }} + {% endfor %} + {% if slots_left > 0 and autres_list|length > 0 %} + {{ autres_list|slice(0, slots_left)|join(', ') }} + {% endif %} + {% if total > 3 %}…{% endif %} +
    + {% if show_category %} +
    + + {% if card.card_type %} + {{ card.card_type }} + {% elseif card.card_category_name and not type_only %} + {{ card.card_category_name }} + {% endif %} +
    + {% else %} + + {% endif %} +
    + {% if card.card_image %} +

    + {{ post.title|bilingual(current_language) }} +

    + {% endif %} +
    +{# +
    + {% if card.card_image %} +
    + {{ post.title }} +
    + {% endif %} + +
    +

    + {{ post.title }} +

    + + {% if post.meta('sous-titre') %} +

    {{ post.meta('sous-titre') }}

    + {% endif %} + + + + {% if card.card_membres is not empty or post.meta('autrepersonnes') %} +
    + {% for name in card.card_membres %} + {{ name }} + {% endfor %} + {% if post.meta('autrepersonnes') %} + {{ post.meta('autrepersonnes') }} + {% endif %} +
    + {% endif %} + + {% if post.meta('fonction_auteur') %} + {{ post.meta('fonction_auteur') }} + {% endif %} + + {% if post.meta('editeur') %} + {{ post.meta('editeur') }} + {% endif %} + + {% if post.meta('journal') %} + {{ post.meta('journal') }} + {% endif %} + + {% if card.card_axes is not empty %} +
    + {% for axe in card.card_axes %} + {{ axe }} + {% endfor %} +
    + {% endif %} + + {% if card.card_etiquettes is not empty %} +
    + {% for tag in card.card_etiquettes %} + {{ tag }} + {% endfor %} +
    + {% endif %} + +
    + {% if post.meta('lien_externe_1') %} + + {{ post.meta('titre_du_lien_externe_1') ?: post.meta('lien_externe_1') }} + + {% endif %} + + {% if post.meta('hal_url') %} + + HAL + + {% endif %} +
    +
    +
    + #} \ No newline at end of file diff --git a/templates/partials/search-panel.twig b/templates/partials/search-panel.twig new file mode 100644 index 0000000..c126254 --- /dev/null +++ b/templates/partials/search-panel.twig @@ -0,0 +1,17 @@ +
    +
    +

    {{ current_language == 'en' ? 'Search the site' : 'Rechercher sur le site' }}

    +

    {{ current_language == 'en' ? 'Search for an event, a lab member, a publication…' : 'Rechercher un événement, un membre du laboratoire, un ouvrage…' }}

    + +
    +
    diff --git a/templates/partials/swiper-section.twig b/templates/partials/swiper-section.twig new file mode 100644 index 0000000..096eddd --- /dev/null +++ b/templates/partials/swiper-section.twig @@ -0,0 +1,27 @@ +{% if section_posts %} +
    +
    +

    {{ section_title }}

    +
    +
    +
    + +
    +
    +
    + {% for post in section_posts %} +
    + {% include 'partials/post-card.twig' with { post: post, card: section_cards[post.ID], show_category: true } %} +
    + {% endfor %} +
    +
    +
    + +
    +
    + +
    +{% endif %} diff --git a/templates/search.twig b/templates/search.twig new file mode 100644 index 0000000..f075089 --- /dev/null +++ b/templates/search.twig @@ -0,0 +1,92 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    +
    +
    +
    + +
    +

    {{ current_language == 'en' ? 'Search the site' : 'Recherche sur le site' }}

    +
    + +
    +

    {{ current_language == 'en' ? 'Search for an event, a lab member, a publication…' : 'Rechercher un événement, un membre du laboratoire, un ouvrage…' }}

    + +
    + + {% if author_cards is not empty %} +
    +

    {{ current_language == 'en' ? 'Lab members' : 'Membres du laboratoire' }}

    +
    + {% for author in author_cards %} + {% include 'partials/author-card.twig' %} + {% endfor %} +
    +
    + {% endif %} + + {% if taxonomy_cards is not empty %} +
    +

    {{ current_language == 'en' ? 'Axes & research programs' : 'Axes & programmes de recherche' }}

    + +
    + {% endif %} + + {% include 'partials/category-filters.twig' %} + +
    + {% if posts is not empty %} +
    + {% for post in posts %} + {% include 'partials/post-card.twig' with { post: post, card: cards[post.ID], show_category: true } %} + {% endfor %} +
    +
    + + {% else %} +

    + {{ current_language == 'en' ? 'No results for' : 'Aucun résultat pour' }} « {{ search_query }} ». +

    + {% endif %} +
    +
    +
    +
    +{% endblock %} diff --git a/templates/single.twig b/templates/single.twig new file mode 100644 index 0000000..c37fb8c --- /dev/null +++ b/templates/single.twig @@ -0,0 +1,487 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    +
    + + {% if post.edit_link %} + + {{ current_language == 'en' ? 'Edit post' : 'Éditer l\'annonce' }} + + {% endif %} +
    + +

    +

    {{ post.title | bilingual(current_language) }}

    + {% if article.sous_titre %} +

    {{ article.sous_titre }}

    + {% endif %} +

    + + {% if article.type_label %} + {{ article.type_label }} + {% endif %} + + {% set landscape_images = [] %} + {% set portrait_images = [] %} + {% for img in article.images %} + {% if img.portrait %} + {% set portrait_images = portrait_images|merge([img]) %} + {% else %} + {% set landscape_images = landscape_images|merge([img]) %} + {% endif %} + {% endfor %} + {% if landscape_images %} + {% if landscape_images|length > 1 %} +
    +
    +
    + {% for img in landscape_images %} +
    +
    + {{ img.alt }} + {% set legend = article.show_image_titles and img.title ? img.title : img.caption %} + {% if legend %} +
    {{ legend }}
    + {% endif %} +
    +
    + {% endfor %} +
    +
    +
    +
    + {% else %} +
    + {% for img in landscape_images %} +
    + {{ img.alt }} + {% set legend = article.show_image_titles and img.title ? img.title : img.caption %} + {% if legend %} +
    {{ legend }}
    + {% endif %} +
    + {% endfor %} +
    + {% endif %} + {% endif %} + +
    + {% if portrait_images or article.date_de_debut or article.date_de_fin or article.datetime + or article.lieu|trim or article.adresse|trim + or article.documents or article.liens_externes or article.hal_url or article.hal_file + or article.reference_bibliographique %} + + {% endif %} + +
    + {% if article.membres is not empty or article.autrepersonnes %} +

    + {% if article.fonction_label %} + {{ article.fonction_label }} : + {% endif %} + {% for m in article.membres %} + {{ m.name }}{% if not loop.last %}, {% endif %} + {% endfor %} + {% if article.autrepersonnes %}{% if article.membres is not empty %}, {% endif %}{{ article.autrepersonnes }}{% endif %} +

    + {% endif %} + + {% if article.autre_membres is not empty or article.autre_autrepersonnes %} +

    + {% if article.autre_fonction_label %} + {{ article.autre_fonction_label }} : + {% endif %} + {% for m in article.autre_membres %} + {{ m.name }}{% if not loop.last %}, {% endif %} + {% endfor %} + {% if article.autre_autrepersonnes %}{% if article.autre_membres is not empty %}, {% endif %}{{ article.autre_autrepersonnes }}{% endif %} +

    + {% endif %} + + {% if article.editeur %} +

    + {{ current_language == 'en' ? 'Publisher' : 'Éditeur' }} : + {{ article.editeur }} +

    + {% endif %} + + {% if article.journal %} +

    + Journal : + {{ article.journal }} +

    + {% endif %} + + {% if current_language == 'en' and article.body_en %} + {{ article.body_en|raw }} + {% else %} + {{ post.content }} + {% endif %} + + {% if article.canal_u %} +
    + {% for embed_url in article.canal_u %} +
    + +
    + {% endfor %} +
    + {% endif %} + + {% if article.youtube %} +
    + {% for embed_url in article.youtube %} +
    + +
    + {% endfor %} +
    + {% endif %} + + {% if article.axes %} +

    + {{ current_language == 'en' ? 'Thematic axes' : 'Axes thématiques' }} : + {% for kw in article.axes %} + {{ kw.name }}{% if not loop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} + + {% if article.programmes %} +

    + {{ current_language == 'en' ? 'Research programmes' : 'Programmes de recherche' }} : + {% for kw in article.programmes %} + {{ kw.name }}{% if not loop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} + + {% if article.etiquettes %} +

    + {{ current_language == 'en' ? 'Keywords' : 'Mots-clés' }} : + {% for kw in article.etiquettes %} + {{ kw.name }}{% if not loop.last %}, {% endif %} + {% endfor %} +

    + {% endif %} + + {% if article.annonces_liees is not empty %} + + {% endif %} + + {% if article.seances_a_venir is not empty %} +
    +

    {{ current_language == 'en' ? 'Upcoming sessions' : 'Séances à venir' }}

    +
    + {% for s in article.seances_a_venir %} +
    +
    +
    + {{ s.day }} + {{ s.month }} + {% if s.year %}{{ s.year }}{% endif %} +
    +
    +

    {{ s.post.title | bilingual(current_language) }}

    + {% if s.intervenants is not empty or s.autrepersonnes %} + + {% for i in s.intervenants %} + {{ i.name }}{% if not loop.last %}, {% endif %} + {% endfor %} + {% if s.autrepersonnes %}{% if s.intervenants is not empty %}, {% endif %}{{ s.autrepersonnes }}{% endif %} + + {% endif %} +
    + +
    + +
    + {% endfor %} +
    +
    + {% endif %} + + {% if article.seances_passees is not empty %} +
    +

    {{ current_language == 'en' ? 'Past sessions' : 'Séances passées' }}

    +
    + {% for s in article.seances_passees %} +
    +
    +
    + {{ s.day }} + {{ s.month }} + {% if s.year %}{{ s.year }}{% endif %} +
    +
    +

    {{ s.post.title | bilingual(current_language) }}

    + {% if s.intervenants is not empty or s.autrepersonnes %} + + {% for i in s.intervenants %} + {{ i.name }}{% if not loop.last %}, {% endif %}{% endfor %}{% if s.autrepersonnes %}{% if s.intervenants is not empty %}, {% endif %}{{ s.autrepersonnes }}{% endif %} + + {% endif %} +
    + +
    + +
    + {% endfor %} +
    +
    + {% endif %} + +

    {{ current_language == 'en' ? 'Updated on' : 'Mis à jour le' }} {{ post.modified_date('d/m/Y') ?: post.date('d/m/Y') }}

    +
    +
    + +
    +
    +{% endblock %} diff --git a/templates/taxonomy.twig b/templates/taxonomy.twig new file mode 100644 index 0000000..5ccc053 --- /dev/null +++ b/templates/taxonomy.twig @@ -0,0 +1,49 @@ +{% extends "base.twig" %} + +{% block content %} +
    +
    +
    +
    +
    + +
    +

    {{ term.name | bilingual(current_language) }}

    + {% if term_presentation %} +
    {{ term_presentation|raw }}
    + {% endif %} +
    + + {% include 'partials/category-filters.twig' %} + +
    +
    + {% for post in posts %} + {% include 'partials/post-card.twig' with { post: post, card: cards[post.ID], show_category: true } %} + {% endfor %} +
    + +
    + +
    +
    +
    +
    +{% endblock %}