XXX_Controller.php
if (strpos($relative_class, 'controllers\\CRVI_') === 0) {
$class_name = substr($relative_class, strlen('controllers\\CRVI_'));
$possible_files[] = plugin_dir_path(__FILE__) . $class_name . '.php';
}
// Pattern 2: controllers\XXX_Controller -> XXX_Controller.php (sans CRVI_)
if (strpos($relative_class, 'controllers\\') === 0) {
$class_name = substr($relative_class, strlen('controllers\\'));
$possible_files[] = plugin_dir_path(__FILE__) . $class_name . '.php';
}
// Essayer de charger le premier fichier qui existe
foreach ($possible_files as $file) {
if (file_exists($file)) {
try {
require_once $file;
$file_loaded = true;
break;
} catch (\Throwable $e) {
error_log("Erreur lors du chargement du fichier {$file}: " . $e->getMessage());
}
}
}
// Si le fichier n'a pas pu être chargé, logger les détails
if (!$file_loaded && !class_exists($controller)) {
$debug_info = [
'controller' => $controller,
'relative_class' => $relative_class,
'files_tried' => $possible_files,
'files_exist' => array_map('file_exists', $possible_files)
];
error_log("Classe {$controller} non trouvée lors de l'enregistrement des routes. Détails: " . print_r($debug_info, true));
continue; // Passer au contrôleur suivant
}
}
if (class_exists($controller)) {
try {
// Vérifier que la méthode register_routes existe avant de l'appeler
if (method_exists($controller, 'register_routes')) {
$controller::register_routes();
} else {
error_log("La méthode register_routes() n'existe pas pour la classe {$controller}");
}
} catch (\Throwable $e) {
// Capturer toutes les exceptions et erreurs (Exception, Error, etc.)
$error_details = [
'controller' => $controller,
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
];
error_log("Erreur lors de l'enregistrement des routes pour {$controller}: " . print_r($error_details, true));
}
} else {
// Cette ligne ne devrait plus être atteinte si le chargement a réussi
// Mais on la garde pour sécurité
error_log("Classe {$controller} non trouvée après tentative de chargement manuel");
}
}
}
public function load_actions() {
add_action('init', [self::class, 'register_cpt']);
add_action('rest_api_init', [self::class, 'register_routes']); // Enregistrer les routes REST API
add_action('admin_menu', [self::class, 'add_admin_menu']);
// Initialiser le contrôleur de notifications pour enregistrer les hooks AJAX
new \ESI_CRVI_AGENDA\controllers\CRVI_Notifications_Controller();
add_action('admin_post_crvi_import_beneficiaire', [\ESI_CRVI_AGENDA\controllers\CRVI_Beneficiaire_Controller::class, 'import_csv_admin']);
add_action('admin_post_crvi_import_local', [\ESI_CRVI_AGENDA\controllers\CRVI_Local_Controller::class, 'import_csv_admin']);
add_action('admin_post_crvi_import_traducteur', [\ESI_CRVI_AGENDA\controllers\CRVI_Traducteur_Controller::class, 'import_csv_admin']);
add_action('admin_post_crvi_import_intervenant', [\ESI_CRVI_AGENDA\controllers\CRVI_Intervenant_Controller::class, 'import_csv_admin']);
add_action('admin_post_crvi_import_departement', [\ESI_CRVI_AGENDA\controllers\CRVI_Departement_Controller::class, 'import_csv_admin']);
add_action('admin_post_crvi_import_type_intervention', [\ESI_CRVI_AGENDA\controllers\CRVI_Type_Intervention_Controller::class, 'import_csv_admin']);
// Enqueue assets conditionnellement
add_action('admin_enqueue_scripts', [self::class, 'enqueue_admin_assets']);
add_action('wp_enqueue_scripts', [self::class, 'enqueue_front_assets']);
// Localisation des scripts après que toutes les fonctions WordPress soient disponibles
add_action('wp_loaded', [self::class, 'localize_scripts']);
// Ajouter type="module" aux scripts ES6
add_filter('script_loader_tag', [self::class, 'add_module_type_to_scripts'], 10, 3);
// Hook pour vérifier les indisponibilités lors de la sauvegarde d'un intervenant
add_action('acf/save_post', [self::class, 'check_intervenant_availability_on_save'], 20);
// Hook pour afficher les messages de conflit d'indisponibilités
add_action('admin_notices', [self::class, 'display_intervenant_conflicts_notice']);
// Hook pour vider le cache via paramètre URL
add_action('admin_init', [self::class, 'handle_clear_cache_request']);
}
/**
* Ajoute la page d'admin Agenda CRVI avec sous-menus Hub, Stats, Agenda
*/
public static function add_admin_menu() {
// Menu principal : Agenda CRVI (affiche le Hub)
add_menu_page(
__('Agenda CRVI', 'esi_crvi_agenda'),
__('Agenda CRVI', 'esi_crvi_agenda'),
'manage_options',
'crvi_agenda',
[CRVI_Main_View::class, 'render_hub_admin_page'],
'dashicons-calendar-alt',
6
);
// Sous-menu : Hub (même callback que le menu principal)
add_submenu_page(
'crvi_agenda',
__('Hub', 'esi_crvi_agenda'),
__('Hub', 'esi_crvi_agenda'),
'manage_options',
'crvi_agenda',
[CRVI_Main_View::class, 'render_hub_admin_page']
);
// Sous-menu : Stats
add_submenu_page(
'crvi_agenda',
__('Stats', 'esi_crvi_agenda'),
__('Stats', 'esi_crvi_agenda'),
'manage_options',
'crvi_agenda_stats',
[self::class, 'render_stats_page']
);
// Sous-menu : Agenda
add_submenu_page(
'crvi_agenda',
__('Agenda', 'esi_crvi_agenda'),
__('Agenda', 'esi_crvi_agenda'),
'manage_options',
'crvi_agenda_hub',
[CRVI_Agenda_View::class, 'render_agenda_page']
);
// Sous-menu : Gestion des permanences
add_submenu_page(
'crvi_agenda',
__('Permanences', 'esi_crvi_agenda'),
__('Permanences', 'esi_crvi_agenda'),
'manage_options',
'crvi_agenda_permanences',
[self::class, 'render_permanences_page']
);
// Menu : Capacités de traduction
CRVI_TraductionLangue_Controller::register_admin_page();
}
/**
* Affiche la page d'admin des stats
*/
public static function render_stats_page() {
// Charger les mêmes données que pour la page agenda
$locals = \ESI_CRVI_AGENDA\models\CRVI_Local_Model::get_locals([], true);
$intervenants = \ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model::get_intervenants([], true);
$departements = \ESI_CRVI_AGENDA\models\CRVI_Departement_Model::all(true);
$types_intervention = \ESI_CRVI_AGENDA\models\CRVI_Type_Intervention_Model::all(true);
$traducteurs = \ESI_CRVI_AGENDA\models\CRVI_Traducteur_Model::get_traducteurs([], true);
$langues_beneficiaire = \ESI_CRVI_AGENDA\helpers\Api_Helper::get_languages(true);
// Récupérer tous les bénéficiaires
$beneficiaire_model = new \ESI_CRVI_AGENDA\models\CRVI_Beneficiaire_Model();
$beneficiaires_objects = $beneficiaire_model->get_all_beneficiaires();
$beneficiaires = [];
foreach ($beneficiaires_objects as $beneficiaire) {
$beneficiaires[] = [
'id' => $beneficiaire->id,
'nom' => $beneficiaire->nom . ' ' . $beneficiaire->prenom,
];
}
$template = plugin_dir_path(__FILE__) . '../../templates/admin/agenda-stats-form.php';
if (file_exists($template)) {
include $template;
} else {
echo '
Template agenda-stats-form.php introuvable.
';
}
}
/**
* Affiche la page d'admin de gestion des permanences
*/
public static function render_permanences_page() {
echo '' . esc_html__('Gestion des Permanences', 'esi_crvi_agenda') . '
';
$template = plugin_dir_path(__FILE__) . '../../templates/admin/permanences-admin.php';
if (file_exists($template)) {
include $template;
} else {
echo '
Template permanences-admin.php introuvable.
';
}
echo '
';
}
public static function load_filters() {
/* add_filter('rest_endpoints', [self::class, 'register_routes']); */
add_filter('wp_script_attributes', [self::class, 'custom_script_tag'], 10, 2);
}
public function load_shortcodes() {
/* add_shortcode('crvi_agenda', [self::class, 'crvi_agenda_shortcode']); */
// Shortcodes pour l'espace intervenant
add_shortcode('crvi_intervenant_hub', [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'shortcode_hub']);
add_shortcode('crvi_intervenant_agenda', [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'shortcode_agenda']);
add_shortcode('crvi_intervenant_profil', [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'shortcode_profil']);
add_shortcode('crvi_intervenant_permanences', [\ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller::class, 'shortcode_permanences']);
}
public function load_frontend_assets() {
self::enqueue_admin_assets();
self::enqueue_front_assets();
}
public function load_admin_assets() {
}
public function add_pages() {
//hub admin
}
/**
* Charge les assets uniquement sur les pages admin CRVI
*/
public static function enqueue_admin_assets() {
// Les slugs des pages admin CRVI
$crvi_pages = [
'crvi_agenda',
'crvi_agenda_stats',
'crvi_agenda_hub',
'crvi_agenda_permanences',
];
$page = $_GET['page'] ?? '';
if (in_array($page, $crvi_pages, true)) {
// Charger Select2 via WordPress
wp_enqueue_style('select2', 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css', [], '4.0.13');
wp_enqueue_script('select2', 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.full.min.js', ['jquery'], '4.0.13', true);
wp_enqueue_script('crvi_fa', 'https://kit.fontawesome.com/08d831faac.js', [], '6.0.0', true);
// CSS des librairies (Bootstrap, Toastr, Select2) - compilé par Vite
wp_enqueue_style('crvi_libraries_css', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_libraries.min.css', [], '1.0.0');
// CSS principal du plugin - compilé par Vite (inclut déjà agenda-visual-filters.css)
wp_enqueue_style('crvi_main_css', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_main.min.css', ['crvi_libraries_css'], '1.0.0');
// Librairies externes (Bootstrap, Toastr, FullCalendar) - chargées en premier
wp_enqueue_script('crvi_libraries', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_libraries.min.js', ['jquery', 'select2'], '1.0.0', true);
// Script principal du plugin (modules locaux, inclut déjà agenda-visual-filters.js) - dépend des librairies
wp_enqueue_script('crvi_main_agenda', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_main.min.js', ['crvi_libraries'], '1.0.0', true);
self::localize_acf_data('crvi_main_agenda');
}
}
/**
* Charge les assets uniquement sur la page publique crvi_agenda_interne
* et sur les pages contenant les shortcodes de l'espace intervenant
*/
public static function enqueue_front_assets() {
global $post;
// Vérifier si on est sur la page crvi_agenda_interne
$is_agenda_page = is_page('crvi_agenda_interne');
// Vérifier si on est sur une page de l'espace intervenant (par slug)
$is_intervenant_page = false;
$intervenant_page_slugs = [
'espace-intervenant',
'espace-intervenant-profil',
'espace-intervenant-agenda',
'encodage-permanences'
];
// Vérifier si la page actuelle correspond à un slug intervenant
if ($post && $post->post_type === 'page') {
$page_slug = $post->post_name;
// Vérifier directement le slug de la page
if (in_array($page_slug, $intervenant_page_slugs, true)) {
$is_intervenant_page = true;
}
// Vérifier si c'est une page enfant d'espace-intervenant
if (!$is_intervenant_page && $post->post_parent) {
$parent = get_post($post->post_parent);
if ($parent && $parent->post_name === 'espace-intervenant') {
$is_intervenant_page = true;
}
}
// Vérifier aussi avec is_page() pour plus de robustesse
if (!$is_intervenant_page) {
foreach ($intervenant_page_slugs as $slug) {
if (is_page($slug)) {
$is_intervenant_page = true;
break;
}
}
}
}
// Vérifier aussi par le chemin de l'URL (pour les pages avec permalink hiérarchique)
if (!$is_intervenant_page) {
$current_url = $_SERVER['REQUEST_URI'] ?? '';
$current_path = parse_url($current_url, PHP_URL_PATH);
if ($current_path && (
strpos($current_path, '/espace-intervenant') !== false ||
strpos($current_path, '/encodage-permanences') !== false
)) {
$is_intervenant_page = true;
}
}
// Vérifier si la page contient un shortcode intervenant
$has_intervenant_shortcode = false;
if ($post && isset($post->post_content)) {
$shortcodes = [
'crvi_intervenant_hub',
'crvi_intervenant_agenda',
'crvi_intervenant_profil',
'crvi_intervenant_permanences'
];
foreach ($shortcodes as $shortcode) {
if (has_shortcode($post->post_content, $shortcode)) {
$has_intervenant_shortcode = true;
break;
}
}
}
if ($is_agenda_page || $is_intervenant_page || $has_intervenant_shortcode) {
wp_enqueue_script('crvi_fa', 'https://kit.fontawesome.com/08d831faac.js', [], '6.0.0', true);
// CSS des librairies (Bootstrap, Toastr, Select2) - compilé par Vite
wp_enqueue_style('crvi_libraries_css', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_libraries.min.css', [], '1.0.0');
// CSS principal du plugin - compilé par Vite
wp_enqueue_style('crvi_main_css', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_main.min.css', ['crvi_libraries_css'], '1.0.0');
// CSS spécifique pour la page de profil intervenant (compilé par Vite)
if ($is_intervenant_page && $post && $post->post_name === 'espace-intervenant-profil') {
wp_enqueue_style('crvi_intervenant_profile_css', ESI_CRVI_AGENDA_URL . 'assets/js/dist/intervenant-profile.min.css', ['crvi_main_css'], '1.0.0');
}
// Select2 JS via CDN (utilisé par crvi_libraries)
wp_enqueue_script('select2', 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.full.min.js', ['jquery'], '4.0.13', true);
// Librairies externes (Bootstrap, Toastr, Select2) - compilées par Vite
wp_enqueue_script('crvi_libraries', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_libraries.min.js', ['jquery', 'select2'], '1.0.0', true);
// Script principal du plugin (modules locaux) - dépend des librairies
wp_enqueue_script('crvi_main_agenda', ESI_CRVI_AGENDA_URL . 'assets/js/dist/crvi_main.min.js', ['crvi_libraries'], '1.0.0', true);
// Localiser les données si c'est une page intervenant
if ($is_intervenant_page || $has_intervenant_shortcode) {
self::localize_acf_data('crvi_main_agenda');
}
}
}
/**
* Récupère toutes les données ACF pour les couleurs et icônes
* et les passe au JavaScript via wp_localize_script
*/
public static function localize_acf_data($script_handle) {
// Vérifier que ACF est actif
if (!function_exists('get_field')) {
return;
}
$acf_data = [
'departements' => self::get_departements_acf_data(),
'statuts' => self::get_statuts_acf_data(),
'types_local' => self::get_types_local_acf_data(),
'types_intervention' => self::get_types_intervention_acf_data(),
'intervenants' => self::get_intervenants_acf_data(),
'permissions' => self::get_user_permissions(),
'couleurs_rdv' => self::get_couleurs_rdv_acf_data(),
'couleurs_permanence' => self::get_couleurs_permanence_acf_data(),
'indisponibilites_intervenants' => self::get_intervenants_disponibilites(),
'beneficiaires' => self::get_beneficiaires_acf_data(),
'traductions_capacites' => self::get_traductions_capacites_data()
];
wp_localize_script($script_handle, 'crviACFData', $acf_data);
}
/**
* Récupère les données des départements (icône et couleur)
*/
private static function get_departements_acf_data() {
$departements = [];
// Récupérer tous les départements
$departements_posts = get_posts([
'post_type' => 'departement',
'numberposts' => -1,
'post_status' => 'publish'
]);
foreach ($departements_posts as $post) {
$nom = get_field('nom', $post->ID);
$icone = get_field('icone_departement', $post->ID);
$couleur = get_field('couleur_departement', $post->ID);
if ($nom) {
$departements[strtolower($nom)] = [
'id' => $post->ID,
'nom' => $nom,
'icone' => $icone ?: '📋',
'couleur' => $couleur ?: '#6c757d'
];
}
}
return $departements;
}
/**
* Récupère les données des statuts de RDV
*/
private static function get_statuts_acf_data() {
$statuts = [];
// Récupérer les statuts depuis les options ACF
$statuts_rdv = get_field('statut_rdv', 'option');
if (is_array($statuts_rdv)) {
foreach ($statuts_rdv as $statut) {
$nom = $statut['statut'] ?? '';
$couleur = $statut['couleur'] ?? '';
if ($nom) {
$statuts[sanitize_title($nom)] = [
'nom' => $nom,
'couleur' => $couleur ?: '#3788d8'
];
}
}
}
// Valeurs par défaut si aucun statut configuré
if (empty($statuts)) {
$statuts = [
'prevu' => ['nom' => 'Prévu', 'couleur' => '#28a745'],
'annule' => ['nom' => 'Annulé', 'couleur' => '#dc3545'],
'non_tenu' => ['nom' => 'Non tenu', 'couleur' => '#ffc107'],
'cloture' => ['nom' => 'Clôturé', 'couleur' => '#6c757d'],
'absence' => ['nom' => 'Absence', 'couleur' => '#fd7e14']
];
}
return $statuts;
}
/**
* Récupère les données des types de local
*/
private static function get_types_local_acf_data() {
$types_local = [];
// Récupérer les types de local depuis les options ACF
$types_local_config = get_field('type_de_local', 'option');
if (is_array($types_local_config)) {
foreach ($types_local_config as $type) {
$nom = $type['type'] ?? '';
$couleur = $type['couleur'] ?? '';
$icone = $type['icone'] ?? '';
if ($nom) {
$types_local[strtolower($nom)] = [
'nom' => $nom,
'couleur' => $couleur ?: '#6c757d',
'icone' => $icone ?: '🏢'
];
}
}
}
return $types_local;
}
/**
* Récupère les données des types d'intervention
*/
private static function get_types_intervention_acf_data() {
$types_intervention = [];
// Récupérer tous les types d'intervention (CPT)
$types_intervention_posts = get_posts([
'post_type' => 'type_intervention',
'numberposts' => -1,
'post_status' => 'publish',
]);
foreach ($types_intervention_posts as $post) {
$couleur = get_field('couleur', $post->ID);
$icone = get_field('icone', $post->ID);
$types_intervention[$post->ID] = [
'id' => $post->ID,
'nom' => $post->post_title,
'couleur' => $couleur ?: '#6c757d',
'icone' => $icone ?: '📋'
];
}
return $types_intervention;
}
/**
* Récupère les données des intervennats (user role intervenant)
*/
private static function get_intervenants_acf_data() {
$intervenants_data = [];
$intervenants = get_users([
'role' => 'intervenant'
]);
//champs acf 'couleur'
foreach ($intervenants as $intervenant) {
$couleur = get_field('couleur', 'user_'.$intervenant->ID);
$intervenants_data[$intervenant->ID] = [
'id' => $intervenant->ID,
'nom' => $intervenant->display_name,
'couleur' => $couleur ?: '#6c757d'
];
}
return $intervenants_data;
}
/**
* Récupère les permissions de l'utilisateur actuel
*/
private static function get_user_permissions() {
$user = wp_get_current_user();
return [
'can_view' => current_user_can('read'),
'can_create' => current_user_can('edit_posts'),
'can_edit' => current_user_can('edit_posts'),
'can_delete' => current_user_can('delete_posts'),
'user_id' => $user->ID,
'user_roles' => $user->roles
];
}
/**
* Récupère les couleurs des RDV selon le type de local depuis les options ACF
*/
private static function get_couleurs_rdv_acf_data() {
return [
'bureau' => get_field('couleur_rdv_bureau', 'option') ?: '#6c757d',
'salle' => get_field('couleur_rdv_salle', 'option') ?: '#6c757d'
];
}
/**
* Récupère les couleurs des permanences depuis les options ACF
*/
private static function get_couleurs_permanence_acf_data() {
return [
'permanence' => get_field('couleur_permanence', 'option') ?: '#9e9e9e',
'permanence_non_attribuee' => get_field('couleur_permanence_non_attribuee', 'option') ?: '#9e9e9e',
'permanence_non_disponible' => get_field('couleur_permanence_non_disponible', 'option') ?: '#9e9e9e'
];
}
/**
* Localise les scripts après que toutes les fonctions WordPress soient disponibles
*/
public static function localize_scripts() {
// Vérifier si le script crvi_main_agenda est enregistré
if (!wp_script_is('crvi_main_agenda', 'registered')) {
return;
}
// Ajouter le nonce pour l'authentification API
wp_localize_script('crvi_main_agenda', 'wpApiSettings', [
'nonce' => \wp_create_nonce('wp_rest'),
'root' => \esc_url_raw(\rest_url())
]);
// Transmettre les permissions utilisateur au JavaScript
if (is_user_logged_in()) {
$permissions = \ESI_CRVI_AGENDA\helpers\Api_Helper::get_user_permissions();
wp_localize_script('crvi_main_agenda', 'crviPermissions', $permissions);
} else {
// Permissions par défaut si l'utilisateur n'est pas connecté
wp_localize_script('crvi_main_agenda', 'crviPermissions', [
'can_create' => false,
'can_edit' => false,
'can_delete' => false,
'can_close' => false,
'can_view' => false,
'user_roles' => [],
'user_id' => 0,
]);
}
// Fournir l'URL AJAX et un nonce pour les appels admin-ajax (ex: debug SMS)
// + données statiques avec cache (couleurs, permanences, disponibilités intervenants)
wp_localize_script('crvi_main_agenda', 'crviAjax', [
'url' => \admin_url('admin-ajax.php'),
'nonce' => \wp_create_nonce('crvi_sms'),
'couleurs_types_intervention' => self::get_couleurs_types_intervention(),
'couleur_permanence_non_attribuee' => self::get_couleur_permanence_non_attribuee(),
'couleur_permanence_non_disponible' => self::get_couleur_permanence_non_disponible(),
'intervenants_disponibilites' => self::get_intervenants_disponibilites()
]);
}
/**
* Récupère les couleurs de tous les types d'intervention (id => couleur)
* Utilise un transient de 12h pour le cache
*/
private static function get_couleurs_types_intervention() {
$transient_key = 'crvi_couleurs_types_intervention';
$couleurs = get_transient($transient_key);
if (false === $couleurs) {
$couleurs = [];
// Vérifier que ACF est actif
if (!function_exists('get_field')) {
return $couleurs;
}
// Récupérer tous les types d'intervention (CPT)
$types_intervention_posts = get_posts([
'post_type' => 'type_intervention',
'numberposts' => -1,
'post_status' => 'publish',
]);
foreach ($types_intervention_posts as $post) {
$couleur = get_field('couleur', $post->ID);
$couleurs[$post->ID] = $couleur ?: '#6c757d';
}
// Mettre en cache pour 12 heures (43200 secondes)
set_transient($transient_key, $couleurs, 12 * HOUR_IN_SECONDS);
}
return $couleurs;
}
/**
* Récupère la couleur de permanence non attribuée depuis les options ACF
* Utilise un transient de 12h pour le cache
*/
private static function get_couleur_permanence_non_attribuee() {
$transient_key = 'crvi_permanence_non_attribuee';
$couleur = get_transient($transient_key);
if (false === $couleur) {
// Vérifier que ACF est actif
if (!function_exists('get_field')) {
return '#9e9e9e';
}
$couleur = get_field('couleur_permanence_non_attribuee', 'option') ?: '#9e9e9e';
// Mettre en cache pour 12 heures
set_transient($transient_key, $couleur, 12 * HOUR_IN_SECONDS);
}
return $couleur;
}
/**
* Récupère la couleur de permanence non disponible depuis les options ACF
* Utilise un transient de 12h pour le cache
*/
private static function get_couleur_permanence_non_disponible() {
$transient_key = 'crvi_permanence_non_disponible';
$couleur = get_transient($transient_key);
if (false === $couleur) {
// Vérifier que ACF est actif
if (!function_exists('get_field')) {
return '#9e9e9e';
}
$couleur = get_field('couleur_permanence_non_disponible', 'option') ?: '#9e9e9e';
// Mettre en cache pour 12 heures
set_transient($transient_key, $couleur, 12 * HOUR_IN_SECONDS);
}
return $couleur;
}
/**
* Récupère les disponibilités de tous les intervenants
* Structure: array de tous les intervenants avec jours_dispo => array, conges => array
* Utilise un transient de 12h pour le cache
*/
private static function get_intervenants_disponibilites() {
$transient_key = 'crvi_intervenants_disponibilites';
$intervenants_data = get_transient($transient_key);
if (false === $intervenants_data) {
$intervenants_data = [];
// Vérifier que ACF est actif
if (!function_exists('get_field')) {
return $intervenants_data;
}
// Récupérer tous les intervenants (users avec role 'intervenant')
$intervenants = get_users([
'role' => 'intervenant'
]);
foreach ($intervenants as $intervenant) {
// Récupérer les jours de disponibilité (checkbox)
$jours_dispo = get_field('jours_de_disponibilite', 'user_' . $intervenant->ID);
if (!is_array($jours_dispo)) {
$jours_dispo = [];
}
// Récupérer les indisponibilités ponctuelles (repeater)
$indisponibilites = get_field('indisponibilitee_ponctuelle', 'user_' . $intervenant->ID);
$conges = [];
if (is_array($indisponibilites)) {
foreach ($indisponibilites as $indispo) {
$conges[] = [
'debut' => $indispo['debut'] ?? '',
'fin' => $indispo['fin'] ?? '',
'type' => $indispo['type'] ?? '',
'commentaire' => $indispo['commentaire'] ?? ''
];
}
}
$intervenants_data[$intervenant->ID] = [
'id' => $intervenant->ID,
'jours_dispo' => $jours_dispo,
'conges' => $conges
];
}
// Mettre en cache pour 12 heures
set_transient($transient_key, $intervenants_data, 3 * HOUR_IN_SECONDS);
}
return $intervenants_data;
}
/**
* Récupère les données des bénéficiaires (notamment leur statut liste rouge)
*/
private static function get_beneficiaires_acf_data() {
$transient_key = 'crvi_beneficiaires_liste_rouge';
$beneficiaires_data = get_transient($transient_key);
if (false === $beneficiaires_data) {
$beneficiaires_data = [];
// Vérifier que ACF est actif
if (!function_exists('get_field')) {
return $beneficiaires_data;
}
// Récupérer tous les bénéficiaires
$beneficiaires = get_posts([
'post_type' => 'beneficiaire',
'numberposts' => -1,
'post_status' => 'publish'
]);
foreach ($beneficiaires as $beneficiaire) {
// Récupérer le champ ACF personne_en_liste_rouge
$personne_en_liste_rouge = get_field('field_69495826ac495', $beneficiaire->ID);
$beneficiaires_data[$beneficiaire->ID] = [
'id' => $beneficiaire->ID,
'personne_en_liste_rouge' => (bool) $personne_en_liste_rouge
];
}
// Mettre en cache pour 3 heures
set_transient($transient_key, $beneficiaires_data, 3 * HOUR_IN_SECONDS);
}
return $beneficiaires_data;
}
/**
* Récupère les capacités de traduction par langue avec ventilation par période et par jour
* Format : langue_slug => {id, name, slug, total, remaining, by_periode: {matin/apres_midi: {total, remaining}}, by_jour: {lundi->dimanche: {total, remaining}}}
*
* @return array Capacités de traduction par langue
*/
private static function get_traductions_capacites_data() {
// Vérifier que la classe du modèle existe
if (!class_exists('\ESI_CRVI_AGENDA\models\CRVI_TraductionLangue_Model')) {
return [];
}
// Récupérer la date actuelle
$date = current_time('Y-m-d');
// Utiliser la méthode du modèle qui gère le cache
$capacites = \ESI_CRVI_AGENDA\models\CRVI_TraductionLangue_Model::getAllLanguesCapacitesForACF($date, true);
return $capacites;
}
/**
* crvi+script loader
*/
public static function custom_script_tag(array $attr, $handle = null) {
if (!isset($attr['id']) || 'crvi_main_agenda-js' !== $attr['id']) {
return $attr;
}
// Le script est maintenant compilé avec toutes les dépendances, pas besoin de type module
// $attr['type'] = 'module';
return $attr;
}
public static function write_log($message, $level = 'INFO') {
$message = self::format_message_for_log($message);
$log_entry = sprintf("[%s] [%s] %s\n", date('Y-m-d H:i:s'), $level, $message);
error_log($log_entry);
}
/**
* Écrit un message dans un fichier debug.txt sous le dossier uploads
* Chemin: wp-content/uploads/crvi-debug.txt
*/
public static function write_debug_file($message, $level = 'INFO') {
$message = self::format_message_for_log($message);
$log_entry = sprintf("[%s] [%s] %s\n", date('Y-m-d H:i:s'), $level, $message);
$upload_dir = wp_upload_dir();
$file_path = trailingslashit($upload_dir['basedir']) . 'crvi-debug.txt';
// Tente d'écrire le log. En cas d'échec, fallback sur error_log
@file_put_contents($file_path, $log_entry, FILE_APPEND);
// Écrire également dans le log PHP (wp-content/debug.log si WP_DEBUG_LOG actif, sinon error_log du serveur)
error_log($log_entry);
}
private static function format_message_for_log($message) {
if (is_array($message)) {
return print_r($message, true);
} elseif (is_object($message)) {
return print_r($message, true);
} elseif (is_bool($message)) {
return $message ? 'TRUE' : 'FALSE';
} elseif (is_numeric($message)) {
return (string) $message;
} elseif (is_null($message)) {
return 'NULL';
} else {
return var_export($message, true);
}
}
/**
* Ajoute type="module" aux scripts ES6 pour permettre les imports/exports
*/
public static function add_module_type_to_scripts($tag, $handle, $src) {
// Scripts qui utilisent ES modules
$module_scripts = ['crvi_libraries', 'crvi_main_agenda'];
if (in_array($handle, $module_scripts)) {
// Remplacer type='text/javascript' par type='module'
$tag = str_replace("type='text/javascript'", "type='module'", $tag);
$tag = str_replace('type="text/javascript"', 'type="module"', $tag);
// Si pas de type existant, l'ajouter
if (strpos($tag, "type=") === false) {
$tag = str_replace('