feat: first working version

This commit is contained in:
MrRaph_
2025-05-23 09:54:50 +00:00
parent acbd9bbad5
commit 20f9c31008
3 changed files with 468 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
jQuery( document ).ready( function( $ ) {
var frame;
// Quand on clique sur "Choisir une image"
$( '.ccgw-upload-image' ).on( 'click', function( e ) {
e.preventDefault();
// Si le frame existe déjà, on louvre
if ( frame ) {
frame.open();
return;
}
// Créer le frame
frame = wp.media({
title: ccgw_data.title || 'Choisir une image de catégorie',
button: { text: ccgw_data.button || 'Sélectionner' },
multiple: false
});
// Quand on sélectionne une image
frame.on( 'select', function() {
var attachment = frame.state().get('selection').first().toJSON();
$( '#category-image-id' ).val( attachment.id );
$( '#category-image-wrapper' ).html( '<img src="' + attachment.sizes.thumbnail.url + '" style="max-width:100px;height:auto;" />' );
});
frame.open();
});
// Quand on clique sur "Supprimer limage"
$( '.ccgw-remove-image' ).on( 'click', function( e ) {
e.preventDefault();
$( '#category-image-id' ).val('');
$( '#category-image-wrapper' ).html('');
});
});

View File

@@ -0,0 +1,164 @@
<?php
/**
* Plugin Name: Elementor Category Grid Widget
* Description: Custom Elementor widget to display a grid of post categories with images.
* Version: 1.0.0
* Author: MrRaph_
* Text Domain: category-grid-widget
* Requires Plugins: elementor
* Elementor tested up to: 3.25.0
* Elementor Pro tested up to: 3.25.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Sécurité : empêche l'accès direct.
}
/**
* Charger les scripts WordPress Media Uploader pour nos écrans de taxonomie.
*/
// function ccgw_enqueue_media_uploader( $hook_suffix ) {
// // On ne charge que sur l'écran de gestion des catégories
// if ( 'edit-tags.php' !== $hook_suffix
// || ! isset( $_GET['taxonomy'] )
// || 'category' !== $_GET['taxonomy']
// ) {
// return;
// }
// // 1. Charger la librairie media
// wp_enqueue_media();
// // 2. Enqueue de ton script JS pour l'image de catégorie
// wp_enqueue_script(
// 'ccgw-category-image', // handle
// plugin_dir_url( __FILE__ ) . 'admin/js/category-image.js', // chemin
// [ 'jquery' ], // dépendances
// '1.0', // version
// true // in_footer
// );
// // 3. Localiser la variable ccgw_data pour ton JS
// wp_localize_script(
// 'ccgw-category-image', // même handle que ci-dessus
// 'ccgw_data', // nom de lobjet JS
// [
// 'title' => esc_js( __( 'Sélectionner une image de catégorie', 'category-grid-widget' ) ),
// 'button' => esc_js( __( 'Sélectionner', 'category-grid-widget' ) ),
// ]
// );
// }
// add_action( 'admin_enqueue_scripts', 'ccgw_enqueue_media_uploader' );
/**
* Charger les scripts Media Uploader et localiser les données JS
* sur les écrans d'ajout ET d'édition de catégorie.
*/
function ccgw_enqueue_media_uploader( $hook_suffix ) {
// On ne charge que sur edit-tags.php (création) OU term.php (édition)
if ( ! in_array( $hook_suffix, [ 'edit-tags.php', 'term.php' ], true ) ) {
return;
}
// Et seulement pour la taxonomie 'category'
if ( empty( $_GET['taxonomy'] ) || 'category' !== $_GET['taxonomy'] ) {
return;
}
// Charge la librairie media de WP
wp_enqueue_media();
// Ton script JS
wp_enqueue_script(
'ccgw-category-image',
plugin_dir_url( __FILE__ ) . 'admin/js/category-image.js',
[ 'jquery' ],
'1.0',
true
);
// Variable JS
wp_localize_script(
'ccgw-category-image',
'ccgw_data',
[
'title' => esc_js( __( 'Sélectionner une image de catégorie', 'category-grid-widget' ) ),
'button' => esc_js( __( 'Sélectionner', 'category-grid-widget' ) ),
]
);
}
add_action( 'admin_enqueue_scripts', 'ccgw_enqueue_media_uploader' );
/**
* Affiche le champ d'upload dans le formulaire de création de catégorie.
*/
function ccgw_category_image_field( $taxonomy ) { ?>
<div class="form-field term-group">
<label for="category-image-id"><?php esc_html_e( 'Image de la catégorie', 'category-grid-widget' ); ?></label>
<input type="hidden" id="category-image-id" name="category-image-id" value="">
<div id="category-image-wrapper"></div>
<p>
<button type="button" class="button button-secondary ccgw-upload-image"><?php esc_html_e( 'Choisir une image', 'category-grid-widget' ); ?></button>
<button type="button" class="button button-secondary ccgw-remove-image"><?php esc_html_e( 'Supprimer limage', 'category-grid-widget' ); ?></button>
</p>
</div>
<?php }
add_action( 'category_add_form_fields', 'ccgw_category_image_field', 10, 2 );
/**
* Affiche le champ d'upload dans le formulaire d'édition de catégorie.
*/
function ccgw_category_image_field_edit( $term, $taxonomy ) {
$image_id = get_term_meta( $term->term_id, 'thumbnail_id', true );
$image_url = $image_id ? wp_get_attachment_thumb_url( $image_id ) : '';
?>
<tr class="form-field term-group-wrap">
<th scope="row">
<label for="category-image-id"><?php esc_html_e( 'Image de la catégorie', 'category-grid-widget' ); ?></label>
</th>
<td>
<input type="hidden" id="category-image-id" name="category-image-id" value="<?php echo esc_attr( $image_id ); ?>">
<div id="category-image-wrapper">
<?php if ( $image_url ) : ?>
<img src="<?php echo esc_url( $image_url ); ?>" style="max-width:100px; height:auto;">
<?php endif; ?>
</div>
<p>
<button type="button" class="button button-secondary ccgw-upload-image"><?php esc_html_e( 'Choisir une image', 'category-grid-widget' ); ?></button>
<button type="button" class="button button-secondary ccgw-remove-image"><?php esc_html_e( 'Supprimer limage', 'category-grid-widget' ); ?></button>
</p>
</td>
</tr>
<?php }
add_action( 'category_edit_form_fields', 'ccgw_category_image_field_edit', 10, 2 );
/**
* Sauvegarde le term meta 'thumbnail_id' pour la catégorie.
*/
function ccgw_save_category_image( $term_id ) {
if ( isset( $_POST['category-image-id'] ) ) {
$image_id = intval( $_POST['category-image-id'] );
if ( $image_id ) {
update_term_meta( $term_id, 'thumbnail_id', $image_id );
} else {
delete_term_meta( $term_id, 'thumbnail_id' );
}
}
}
add_action( 'created_category', 'ccgw_save_category_image', 10, 2 );
add_action( 'edited_category', 'ccgw_save_category_image', 10, 2 );
/**
* Enregistrer le widget "Grille de Catégories" pour Elementor.
*
* Inclut le fichier de la classe du widget et enregistre le widget auprès du manager d'Elementor.
*
* @param \Elementor\Widgets_Manager $widgets_manager Le gestionnaire de widgets d'Elementor.
*/
function register_category_grid_widget( $widgets_manager ) {
// Inclure le fichier de la classe du widget.
require_once __DIR__ . '/widgets/category-grid-widget.php';
// Enregistrer la classe du widget auprès d'Elementor.
$widgets_manager->register( new \Elementor_Category_Grid_Widget() );
}
add_action( 'elementor/widgets/register', 'register_category_grid_widget' );

View File

@@ -0,0 +1,272 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Empêche l'accès direct.
}
/**
* Classe Elementor_Category_Grid_Widget.
*
* Widget Elementor personnalisé qui affiche une grille de catégories d'articles.
* Chaque catégorie est affichée sous forme de carte avec son nom, son image et un lien vers son archive.
*/
class Elementor_Category_Grid_Widget extends \Elementor\Widget_Base {
/**
* Identifiant unique du widget.
*
* @return string Slug du widget.
*/
public function get_name(): string {
return 'category-grid';
}
/**
* Titre du widget affiché dans l'interface Elementor.
*
* @return string Titre du widget.
*/
public function get_title(): string {
return esc_html__( 'Grille de Catégories', 'category-grid-widget' );
}
/**
* Icône du widget (bibliothèque d'icônes Elementor).
*
* @return string Classe de l'icône du widget.
*/
public function get_icon(): string {
return 'eicon-posts-grid';
}
/**
* Catégories Elementor dans lesquelles le widget apparaîtra.
*
* @return array Catégories du panneau Elementor.
*/
public function get_categories(): array {
return [ 'general' ];
}
/**
* Mots-clés facilitant la recherche du widget.
*
* @return array Mots-clés du widget.
*/
public function get_keywords(): array {
return [ 'category', 'catégorie', 'grid', 'grille' ];
}
/**
* Enregistrer les contrôles du widget (champs configurables dans Elementor).
*/
protected function register_controls(): void {
/* Section "Contenu" pour le choix des catégories et options de requête */
$this->start_controls_section(
'section_content',
[
'label' => esc_html__( 'Catégories', 'category-grid-widget' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
// Contrôle : Sélection des catégories à afficher (multisélection).
$options = [];
$categories = get_categories( [ 'hide_empty' => false ] );
foreach ( $categories as $cat ) {
$options[ $cat->term_id ] = $cat->name;
}
$this->add_control(
'categories',
[
'label' => esc_html__( 'Catégories à afficher', 'category-grid-widget' ),
'type' => \Elementor\Controls_Manager::SELECT2,
'multiple' => true,
'label_block' => true,
'options' => $options,
'description' => esc_html__( 'Sélectionnez les catégories darticles à afficher.', 'category-grid-widget' ),
]
);
// Contrôle : Inclure les sous-catégories.
$this->add_control(
'show_subcategories',
[
'label' => esc_html__( 'Afficher les sous-catégories', 'category-grid-widget' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Oui', 'category-grid-widget' ),
'label_off' => esc_html__( 'Non', 'category-grid-widget' ),
'return_value' => 'yes',
'default' => 'yes',
'description' => esc_html__( 'Inclure aussi les sous-catégories des catégories sélectionnées.', 'category-grid-widget' ),
]
);
// Contrôle : Trier par (critère de tri des catégories).
$this->add_control(
'order_by',
[
'label' => esc_html__( 'Trier par', 'category-grid-widget' ),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
'name' => esc_html__( 'Nom (alphabétique)', 'category-grid-widget' ),
'count' => esc_html__( 'Nombre darticles', 'category-grid-widget' ),
'id' => esc_html__( 'ID (date de création)', 'category-grid-widget' ),
],
'default' => 'name',
]
);
// Contrôle : Ordre croissant/décroissant.
$this->add_control(
'order',
[
'label' => esc_html__( 'Ordre', 'category-grid-widget' ),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
'ASC' => esc_html__( 'Croissant (A-Z / 0-9)', 'category-grid-widget' ),
'DESC' => esc_html__( 'Décroissant (Z-A / 9-0)', 'category-grid-widget' ),
],
'default' => 'ASC',
]
);
$this->end_controls_section();
/* Section "Mise en page" pour les options d'affichage (colonnes, taille d'image) */
$this->start_controls_section(
'section_layout',
[
'label' => esc_html__( 'Mise en page', 'category-grid-widget' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
// Contrôle : Nombre de colonnes de la grille.
$this->add_control(
'columns',
[
'label' => esc_html__( 'Nombre de colonnes', 'category-grid-widget' ),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
1 => '1',
2 => '2',
3 => '3',
4 => '4',
5 => '5',
6 => '6',
],
'default' => 3,
]
);
// Contrôle : Taille des images affichées.
$this->add_control(
'image_size',
[
'label' => esc_html__( 'Taille des images', 'category-grid-widget' ),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
'thumbnail' => esc_html__( 'Miniature (thumbnail)', 'category-grid-widget' ),
'medium' => esc_html__( 'Moyen (medium)', 'category-grid-widget' ),
'large' => esc_html__( 'Grand (large)', 'category-grid-widget' ),
'full' => esc_html__( 'Plein (full)', 'category-grid-widget' ),
],
'default' => 'medium',
]
);
$this->end_controls_section();
}
/**
* Générer l'affichage du widget côté front-end.
*/
protected function render(): void {
$settings = $this->get_settings_for_display();
// Récupérer les ID des catégories sélectionnées.
$selected_ids = [];
if ( ! empty( $settings['categories'] ) ) {
// S'assurer d'avoir un tableau d'ID (entiers).
$selected_ids = is_array( $settings['categories'] ) ? $settings['categories'] : [ $settings['categories'] ];
$selected_ids = array_map( 'intval', $selected_ids );
}
if ( empty( $selected_ids ) ) {
// Aucune catégorie sélectionnée : ne rien afficher.
return;
}
// Déterminer les catégories à afficher (inclure les sous-catégories si demandé).
$category_ids_to_show = $selected_ids;
if ( ! empty( $settings['show_subcategories'] ) && $settings['show_subcategories'] === 'yes' ) {
// Ajouter les sous-catégories de chaque catégorie sélectionnée.
foreach ( $selected_ids as $cat_id ) {
$child_ids = get_term_children( $cat_id, 'category' );
if ( is_array( $child_ids ) && ! empty( $child_ids ) ) {
$category_ids_to_show = array_merge( $category_ids_to_show, $child_ids );
}
}
// Éliminer les doublons d'ID au cas où.
$category_ids_to_show = array_unique( $category_ids_to_show );
}
// Préparer les arguments de requête pour récupérer les termes.
$term_args = [
'taxonomy' => 'category',
'include' => $category_ids_to_show,
'hide_empty' => false,
];
// Appliquer les options de tri.
if ( ! empty( $settings['order_by'] ) ) {
$term_args['orderby'] = $settings['order_by'];
}
if ( ! empty( $settings['order'] ) ) {
$term_args['order'] = $settings['order'];
}
// Obtenir les termes de catégories à afficher.
$categories = get_terms( $term_args );
if ( is_wp_error( $categories ) || empty( $categories ) ) {
return;
}
// Calculer la classe CSS pour le nombre de colonnes (pour usage dans le CSS).
$columns = ! empty( $settings['columns'] ) ? (int) $settings['columns'] : 3;
$grid_class = 'columns-' . $columns;
// Début du markup HTML de la grille.
echo '<div class="elementor-category-grid ' . esc_attr( $grid_class ) . '">';
foreach ( $categories as $term ) {
$term_name = $term->name;
$term_link = get_term_link( $term );
if ( is_wp_error( $term_link ) ) {
continue; // Ignorer si le lien d'archive pose un problème.
}
// Récupérer l'image de la catégorie via sa méta 'thumbnail_id' (image mise en avant de la catégorie).
$image_url = '';
$thumbnail_id = get_term_meta( $term->term_id, 'thumbnail_id', true );
if ( $thumbnail_id ) {
$size = ! empty( $settings['image_size'] ) ? $settings['image_size'] : 'medium';
$image_url = wp_get_attachment_image_url( $thumbnail_id, $size );
}
echo '<div class="category-card">';
// Rendre la carte cliquable vers l'archive de la catégorie.
echo '<a href="' . esc_url( $term_link ) . '">';
// Image de la catégorie (si disponible).
if ( $image_url ) {
echo '<div class="category-card-image">';
echo '<img src="' . esc_url( $image_url ) . '" alt="' . esc_attr( $term_name ) . '" />';
echo '</div>';
}
// Nom de la catégorie.
echo '<div class="category-card-name">' . esc_html( $term_name ) . '</div>';
echo '</a>';
echo '</div>'; // .category-card
}
echo '</div>'; // .elementor-category-grid
// Astuce : ajouter du CSS (feuille de style ou balise <style>) pour styliser la grille.
// Par exemple, .elementor-category-grid.columns-3 .category-card { width: 33.33%; float: left; } pour disposer 3 colonnes.
}
}