<?php
/**
* Functions du thème enfant Twenty Twenty-Five
*/

if ( ! defined( 'ABSPATH' ) ) {

  exit;

}

============================================================================
1) Charger les styles du thème parent + enfant
============================================================================
function dp_child_enqueue_styles() {
$parent_style = 'twentytwentyfive-style';

Style du parent

  wp_enqueue_style(
      $parent_style,
      get_template_directory_uri() . '/style.css'
  );
  // Style du thème enfant
  wp_enqueue_style(
      'twentytwentyfive-child-style',
      get_stylesheet_directory_uri() . '/style.css',
      array( $parent_style ),
      time() // version dynamique pour casser les caches
  );

}
add_action( 'wp_enqueue_scripts', 'dp_child_enqueue_styles' );

============================================================================
2) Création des rôles personnalisés : Visiteur et Animateur
============================================================================
function dp_creer_roles_personnalises() {

— Rôle VISITEUR —

  if ( ! get_role( 'visiteur' ) ) {
      add_role(
          'visiteur',
          'Visiteur',
          array(
              'read'              => true,
              'edit_posts'        => false,
              'edit_pages'        => false,
              'publish_posts'     => false,
              'upload_files'      => false,
              'delete_posts'      => false,
              'moderate_comments' => false,
          )
      );
  }
  // --- Rôle ANIMATEUR ---
  if ( ! get_role( 'animateur' ) ) {
      add_role(
          'animateur',
          'Animateur',
          array(
              'read'                   => true,
              'edit_posts'             => true,
              'edit_published_posts'   => true,
              'delete_posts'           => true,
              'delete_published_posts' => true,
              'publish_posts'          => true,
              'edit_pages'             => true,
              'edit_published_pages'   => true,
              'publish_pages'          => true,
              'delete_pages'           => true,
              'delete_published_pages' => true,
              'upload_files'           => true,
          )
      );
  }

}
add_action( 'init', 'dp_creer_roles_personnalises' );

============================================================================
3) Shortcode [page_discussion] : zone de discussion liée à la page
============================================================================
function dp_page_discussion_shortcode( $atts ) {
if ( ! is_singular() ) {
return ;
}

$post_id = get_the_ID();

if ( ! comments_open( $post_id ) ) {
return '<p>La discussion n’est pas ouverte pour cette page.</p>';
}

ob_start();
?>

      <h2 class="dp-discussion-titre">Discussion</h2>
      <div class="dp-discussion-liste">
          <?php
          $comments = get_comments( array(
              'post_id' => $post_id,
              'status'  => 'approve',
              'order'   => 'ASC',
          ) );
          if ( ! empty( $comments ) ) :
              ?>
              <ul class="dp-commentaires">
                  <?php
                  wp_list_comments( array(
                      'style'       => 'ul',
                      'short_ping'  => true,
                      'avatar_size' => 32,
                  ), $comments );
                  ?>
              </ul>
              <?php
          else :
              ?>
              <p>Aucun message pour le moment. Lancez la discussion&nbsp;!</p>
              <?php
          endif;
          ?>
      </div>
      <div class="dp-discussion-formulaire">
          <?php
          comment_form( array(
              'title_reply'          => 'Ajouter un message',
              'title_reply_to'       => 'Répondre à %s',
              'label_submit'         => 'Envoyer',
              'comment_notes_before' => '',
              'comment_notes_after'  => '',
          ), $post_id );
          ?>
      </div>
  </div>
  <?php
  return ob_get_clean();

}
add_shortcode( 'page_discussion', 'dp_page_discussion_shortcode' );

============================================================================
4) Taxonomie “Sources” pour les articles (post)
============================================================================
function journal_register_taxonomy_sources() {
$labels = array(
'name' ⇒ 'Sources',
'singular_name' ⇒ 'Source',
'search_items' ⇒ 'Rechercher des sources',
'all_items' ⇒ 'Toutes les sources',
'parent_item' ⇒ 'Source parente',
'parent_item_colon' ⇒ 'Source parente :',
'edit_item' ⇒ 'Modifier la source',
'update_item' ⇒ 'Mettre à jour la source',
'add_new_item' ⇒ 'Ajouter une nouvelle source',
'new_item_name' ⇒ 'Nom de la nouvelle source',
'menu_name' ⇒ 'Sources',
);

$args = array(
'labels' ⇒ $labels,
'public' ⇒ true,
'hierarchical' ⇒ true,
'show_ui' ⇒ true,
'show_admin_column' ⇒ true,
'show_in_rest' ⇒ true,
'rewrite' ⇒ array( 'slug' ⇒ 'sources' ),
);

register_taxonomy( 'sources', array( 'post' ), $args );
}
add_action( 'init', 'journal_register_taxonomy_sources' );

5) Fonction utilitaire : afficher un arbre hiérarchique de taxonomie

function journal_afficher_arbre_taxonomie( $taxonomy, $title ) {

  $terms = get_terms( array(
      'taxonomy'   => $taxonomy,
      'hide_empty' => false,
  ));
  if ( empty( $terms ) || is_wp_error( $terms ) ) {
      return '<p>Aucun terme trouvé pour : ' . esc_html( $taxonomy ) . '</p>';
  }
  // Hiérarchie parent -> enfants
  $hierarchie = array();
  foreach ( $terms as $term ) {
      $hierarchie[ $term->parent ][] = $term;
  }
  $selected = array();
  if ( isset( $_GET['tax_query'][ $taxonomy ] ) && is_array( $_GET['tax_query'][ $taxonomy ] ) ) {
      $selected = array_map( 'intval', $_GET['tax_query'][ $taxonomy ] );
  }
  $build_tree = function( $parent_id ) use ( &$build_tree, $hierarchie, $selected, $taxonomy ) {
      if ( ! isset( $hierarchie[ $parent_id ] ) ) {
          return '';
      }
      $html = '<ul class="journal-arbre-taxo">';
      foreach ( $hierarchie[ $parent_id ] as $term ) {
          $has_children = isset( $hierarchie[ $term->term_id ] );
          $checked      = in_array( $term->term_id, $selected, true ) ? 'checked' : '';
          $html .= '<li>';
          $html .= $has_children
              ? '<span class="toggle-node">►</span>'
              : '<span class="toggle-placeholder"></span>';
          $html .= '<label><input type="checkbox" name="tax_query[' . esc_attr( $taxonomy ) . '][]" value="' . intval( $term->term_id ) . '" ' . $checked . '> ' . esc_html( $term->name ) . '</label>';
          if ( $has_children ) {
              $html .= '<div class="sub-tree" style="display:none;">' . $build_tree( $term->term_id ) . '</div>';
          }
          $html .= '</li>';
      }
      $html .= '</ul>';
      return $html;
  };
  return '<h3>' . esc_html( $title ) . '</h3>' . $build_tree( 0 );

}

============================================================================
6) Shortcode [blog_filtrable_relevanssi]
============================================================================
function journal_shortcode_blog_filtrable_relevanssi( $atts = array() ) {

$atts = shortcode_atts(
array(
'post_type' ⇒ 'post',
'taxonomies' ⇒ 'category,sources',
'per_page' ⇒ 10,
'order_by' ⇒ 'date',
'order' ⇒ 'DESC',
'intro' ⇒ ,
),
$atts,
'blog_filtrable_relevanssi'
);

$post_type = sanitize_text_field( $atts['post_type'] );
$per_page = max( 1, intval( $atts['per_page'] ) );
$order_by = sanitize_key( $atts['order_by'] );
$order = ( strtoupper( $atts['order'] ) === 'ASC' ) ? 'ASC' : 'DESC';
$intro = wp_kses_post( $atts['intro'] );

$taxonomies = array();
foreach ( explode( ',', $atts['taxonomies'] ) as $tx ) {
$tx = trim( $tx );
if ( $tx !==
) {
$taxonomies[] = $tx;
}
}

$search_term = isset( $_GET['bfrr_s'] ) ? sanitize_text_field( $_GET['bfrr_s'] ) : ;

$tax_query = array();
if ( isset( $_GET['tax_query'] ) && is_array( $_GET['tax_query'] ) ) {
foreach ( $_GET['tax_query'] as $taxonomy ⇒ $terms ) {
$terms = array_filter( array_map( 'intval', (array) $terms ) );
if ( ! empty( $terms ) ) {
$tax_query[] = array(
'taxonomy' ⇒ $taxonomy,
'field' ⇒ 'term_id',
'terms' ⇒ $terms,
'operator' ⇒ 'IN',
);
}
}
}

if ( count( $tax_query ) > 1 ) {
$tax_query = array_merge( array( 'relation' ⇒ 'AND' ), $tax_query );
}

Texte “Filtre appliqué”
$filtre_applique = array();

foreach ( $taxonomies as $tx ) {
if ( isset( $_GET['tax_query'][ $tx ] ) && is_array( $_GET['tax_query'][ $tx ] ) ) {
$ids = array_filter( array_map( 'intval', $_GET['tax_query'][ $tx ] ) );
if ( ! empty( $ids ) ) {
$terms_tx = get_terms( array(
'taxonomy' ⇒ $tx,
'include' ⇒ $ids,
) );
if ( ! is_wp_error( $terms_tx ) && ! empty( $terms_tx ) ) {
$noms = wp_list_pluck( $terms_tx, 'name' );
$tax_obj = get_taxonomy( $tx );
$label = ( $tax_obj && ! empty( $tax_obj→labels→name ) )
? $tax_obj→labels→name
: ucfirst( $tx );

$filtre_applique[] = $label . ' : ' . implode( ', ', $noms );
}
}
}
}

if ( empty( $filtre_applique ) && empty( $search_term ) ) {
$filtre_applique_texte = 'Aucun filtre : tous les articles';
} else {
$parts = array();
if ( ! empty( $search_term ) ) {
$parts[] = 'Texte : « ' . $search_term . ' »';
}
if ( ! empty( $filtre_applique ) ) {
$parts = array_merge( $parts, $filtre_applique );
}
$filtre_applique_texte = 'Filtre appliqué : ' . implode( ' | ', $parts );
}

ob_start();

$page_obj = get_post();
if ( ! $page_obj ) {
return ;
}

$form_action = get_permalink( $page_obj→ID );
$form_action = strtok( $form_action, '?' );

if ( ! empty( $intro ) ) {
echo '<p class=“journal-intro-formulaire”>' . $intro . '</p>';
}

echo '<form method=“get” action=“' . esc_url( $form_action ) . '” class=“journal-arbre-form”>';

echo '<p><input type=“text” name=“bfrr_s” placeholder=“Recherche texte (optionnel)” value=“' . esc_attr( $search_term ) . '” style=“width:100%; padding:6px;”></p>';

echo '

';

  foreach ( $taxonomies as $tx ) {
      $tax_obj = get_taxonomy( $tx );
      $title   = ( $tax_obj && ! empty( $tax_obj->labels->name ) ) ? $tax_obj->labels->name : ucfirst( $tx );
      echo '<div class="journal-arbre-colonne">';
      echo journal_afficher_arbre_taxonomie( $tx, $title );
      echo '</div>';
  }
  echo '</div>';
  echo '<p><input type="submit" value="Rechercher"></p>';
  echo '</form>';
  $paged = max(
      1,
      get_query_var( 'paged' )
          ? get_query_var( 'paged' )
          : ( isset( $_GET['paged'] ) ? intval( $_GET['paged'] ) : 1 )
  );
  $args = array(
      'post_type'           => $post_type,
      'post_status'         => 'publish',
      'ignore_sticky_posts' => true,
      's'                   => $search_term,
      'paged'               => $paged,
      'posts_per_page'      => $per_page,
      'orderby'             => $order_by,
      'order'               => $order,
  );
  if ( ! empty( $tax_query ) ) {
      $args['tax_query'] = $tax_query;
  }
  $query = new WP_Query( $args );
  if ( function_exists( 'relevanssi_do_query' ) && ! empty( $search_term ) ) {
      relevanssi_do_query( $query );
  }
  echo '<div class="journal-resultats">';
  echo '<p class="journal-filtre-applique">' . esc_html( $filtre_applique_texte ) . '</p>';
  if ( $query->have_posts() ) {
      while ( $query->have_posts() ) {
          $query->the_post();
          echo '<article class="journal-resultat-article">';
          echo '<h3 class="journal-resultat-titre"><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></h3>';
          echo '<div class="journal-resultat-extrait">' . wp_kses_post( wp_trim_words( get_the_excerpt(), 30, '...' ) ) . '</div>';
          echo '</article>';
      }
      echo paginate_links( array(
          'total'   => $query->max_num_pages,
          'current' => $paged,
      ) );
  } else {
      echo '<p>Aucun article trouvé.</p>';
  }
  echo '</div>';
  wp_reset_postdata();
  ?>
  <script>
  document.addEventListener("DOMContentLoaded", function() {
      document.querySelectorAll(".toggle-node").forEach(function(node) {
          node.addEventListener("click", function() {
              const subtree = this.parentNode.querySelector(".sub-tree");
              if (!subtree) return;
              if (subtree.style.display === 'none') {
                  subtree.style.display = 'block';
                  this.textContent = '▼';
              } else {
                  subtree.style.display = 'none';
                  this.textContent = '►';
              }
          });
      });
  });
  </script>
  <?php
  return ob_get_clean();

}
add_shortcode( 'blog_filtrable_relevanssi', 'journal_shortcode_blog_filtrable_relevanssi' );

============================================================================
7) Shortcode [dernier_article] : N derniers articles
============================================================================
if ( ! function_exists( 'bfrr_dernier_article_shortcode' ) ) {
function bfrr_dernier_article_shortcode( $atts = array() ) {

$atts = shortcode_atts(
array(
'nb' ⇒ 1,
),
$atts,
'dernier_article'
);

$nb = max( 1, intval( $atts['nb'] ) );

$args = array(
'post_type' ⇒ 'post',
'post_status' ⇒ 'publish',
'posts_per_page' ⇒ $nb,
);
$query = new WP_Query( $args );

if ( ! $query→have_posts() ) {
return '<p>Aucun article trouvé.</p>';
}

ob_start();

while ( $query→have_posts() ) {
$query→the_post();

$post_id = get_the_ID();
$permalink = get_permalink();

echo '<article class=“bfrr-dernier-article”>';
echo '<h2 class=“bfrr-da-titre”><a href=“' . esc_url( $permalink ) . '”>' . esc_html( get_the_title() ) . '</a></h2>';
echo '<p class=“bfrr-da-meta”>Publié le ' . esc_html( get_the_date() ) . '</p>';

$cats_list = get_the_category_list( ', ' );
if ( $cats_list ) {
echo '<p class=“bfrr-da-taxo bfrr-da-cats”><strong>Catégories : </strong>' . wp_kses_post( $cats_list ) . '</p>';
}

$sources = get_the_terms( $post_id, 'sources' );
if ( $sources && ! is_wp_error( $sources ) ) {
$sources_names = wp_list_pluck( $sources, 'name' );
echo '<p class=“bfrr-da-taxo bfrr-da-sources”><strong>Sources : </strong>' . esc_html( implode( ', ', $sources_names ) ) . '</p>';
}

$tags_list = get_the_tag_list( , ', ' );
if ( $tags_list ) {
echo '<p class=“bfrr-da-taxo bfrr-da-tags”><strong>Étiquettes : </strong>' . wp_kses_post( $tags_list ) . '</p>';
}

echo '

';

          echo wp_kses_post( wp_trim_words( get_the_excerpt(), 55, '...' ) );
          echo '</div>';
          echo '<p class="bfrr-da-bouton-wrapper">';
          echo '<a class="bfrr-da-bouton" href="' . esc_url( $permalink ) . '">Lire l’article complet</a>';
          echo '</p>';
          echo '</article>';
      }
      wp_reset_postdata();
      return ob_get_clean();
  }

}
add_shortcode( 'dernier_article', 'bfrr_dernier_article_shortcode' );

============================================================================
8) Injection du bouton “Vider la requête” en JS
============================================================================
add_action( 'wp_footer', function() {
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
const interval = setInterval(() ⇒ {

const arbre = document.querySelector('.journal-arbre-taxo');
if (!arbre) return;

const formContainer = arbre.closest('form');
if (!formContainer) return;

clearInterval(interval);

const submitInput = formContainer.querySelector('p input[type=“submit”]');
if (!submitInput) return;

const submitP = submitInput.parentNode;

let clearBtn = document.getElementById('journal-clear-filters');
if (!clearBtn) {
clearBtn = document.createElement('button');
clearBtn.type = 'button';
clearBtn.id = 'journal-clear-filters';
clearBtn.className = 'journal-clear-btn';
clearBtn.textContent = 'Vider la requête';
}

submitP.appendChild(clearBtn);

clearBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();

formContainer.querySelectorAll('input[type=“text”], input[type=“search”], textarea')
.forEach(el ⇒ el.value = '');
formContainer.querySelectorAll('input[type=“checkbox”], input[type=“radio”]')
.forEach(el ⇒ el.checked = false);
formContainer.querySelectorAll('select')
.forEach(sel ⇒ sel.selectedIndex = 0);

var url = window.location.protocol + '
' + window.location.host + window.location.pathname;

              window.location.href = url;
          });
      }, 200);
  });
  </script>
  <?php

});

============================================================================
9) CPT “atelier” + taxonomies “atelier_sujet” et “atelier_fiche”
============================================================================
function dp_register_atelier_cpt() {

$labels = array(
'name' ⇒ 'atelier',
'singular_name' ⇒ 'Article atelier',
'menu_name' ⇒ 'Ateliers',
'name_admin_bar' ⇒ 'Atelier',
'add_new' ⇒ 'Ajouter',
'add_new_item' ⇒ 'Ajouter un article',
'new_item' ⇒ 'Nouvel article',
'edit_item' ⇒ 'Modifier l’article',
'view_item' ⇒ 'Voir l’article',
'all_items' ⇒ 'Tous les articles atelier',
'search_items' ⇒ 'Rechercher dans atelier',
'parent_item_colon' ⇒ 'Article parent :',
'not_found' ⇒ 'Aucun article trouvé.',
'not_found_in_trash' ⇒ 'Aucun article trouvé dans la corbeille.',
);

$args = array(
'labels' ⇒ $labels,
'public' ⇒ true,
'has_archive' ⇒ true,
'show_in_rest' ⇒ true,
'menu_position' ⇒ 21,
'menu_icon' ⇒ 'dashicons-welcome-write-blog',
'supports' ⇒ array(
'title',
'editor',
'excerpt',
'thumbnail',
'author',
'comments',
),
'rewrite' ⇒ array(
'slug' ⇒ 'atelier',
'with_front' ⇒ true,
),
);

register_post_type( 'atelier', $args );
}
add_action( 'init', 'dp_register_atelier_cpt' );

function dp_register_atelier_sujet_taxonomy() {

$labels = array(
'name' ⇒ 'Sujets',
'singular_name' ⇒ 'Sujet',
'search_items' ⇒ 'Rechercher des sujets',
'all_items' ⇒ 'Tous les sujets',
'parent_item' ⇒ 'Sujet parent',
'parent_item_colon' ⇒ 'Sujet parent :',
'edit_item' ⇒ 'Modifier le sujet',
'update_item' ⇒ 'Mettre à jour le sujet',
'add_new_item' ⇒ 'Ajouter un nouveau sujet',
'new_item_name' ⇒ 'Nom du nouveau sujet',
'menu_name' ⇒ 'Sujet',
);

$args = array(
'hierarchical' ⇒ true,
'labels' ⇒ $labels,
'show_ui' ⇒ true,
'show_admin_column' ⇒ true,
'public' ⇒ true,
'show_in_rest' ⇒ true,
'rewrite' ⇒ array(
'slug' ⇒ 'atelier-sujet',
),
);

register_taxonomy( 'atelier_sujet', array( 'atelier' ), $args );
}
add_action( 'init', 'dp_register_atelier_sujet_taxonomy' );

function dp_register_atelier_fiche_taxonomy() {

$labels = array(
'name' ⇒ 'fiche',
'singular_name' ⇒ 'fiche atelier',
'search_items' ⇒ 'Rechercher des fiches',
'all_items' ⇒ 'Tous les fiches',
'parent_item' ⇒ 'fiche parent',
'parent_item_colon' ⇒ 'fiche parent :',
'edit_item' ⇒ 'Modifier le fiche',
'update_item' ⇒ 'Mettre à jour le fiche',
'add_new_item' ⇒ 'Ajouter un nouveau fiche',
'new_item_name' ⇒ 'Nom du nouveau fiche',
'menu_name' ⇒ 'Fiche',
);

$args = array(
'hierarchical' ⇒ true,
'labels' ⇒ $labels,
'show_ui' ⇒ true,
'show_admin_column' ⇒ true,
'public' ⇒ true,
'show_in_rest' ⇒ true,
'rewrite' ⇒ array(
'slug' ⇒ 'atelier-fiche',
),
);

register_taxonomy( 'atelier_fiche', array( 'atelier' ), $args );
}
add_action( 'init', 'dp_register_atelier_fiche_taxonomy' );