1266 lines
51 KiB
PHP
1266 lines
51 KiB
PHP
<?php
|
|
|
|
namespace ESI_CRVI_AGENDA\controllers;
|
|
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Local_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Beneficiaire_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Intervenant_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Traducteur_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Departement_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Type_Intervention_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_TraductionLangue_Controller;
|
|
use ESI_CRVI_AGENDA\models\CRVI_Event_Model;
|
|
/* use ESI_CRVI_AGENDA\models\Event_Permission_Model; */
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Event_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Entity_Controller;
|
|
use ESI_CRVI_AGENDA\controllers\CRVI_Incident_Controller;
|
|
use ESI_CRVI_AGENDA\views\CRVI_Main_View;
|
|
use ESI_CRVI_AGENDA\views\CRVI_Agenda_View;
|
|
|
|
class CRVI_Plugin {
|
|
public static function init() {
|
|
// Initialisation du plugin (CPT, hooks, etc.)
|
|
/* add_action('init', [self::class, 'register_cpt']); */
|
|
/* self::register_cpt(); */
|
|
self::load_actions();
|
|
self::load_filters();
|
|
self::load_shortcodes();
|
|
self::load_frontend_assets();
|
|
self::load_admin_assets();
|
|
}
|
|
public static function activate() {
|
|
// Code d'activation (création de tables, etc.)
|
|
|
|
// Créer les tables
|
|
CRVI_Event_Model::create_table();
|
|
/* Event_Permission_Model::create_table(); */
|
|
// Tables pour les présences
|
|
\ESI_CRVI_AGENDA\models\CRVI_Presence_Model::create_tables();
|
|
// Table de log des SMS
|
|
\ESI_CRVI_AGENDA\controllers\CRVI_Notifications_Controller::create_sms_log_table();
|
|
}
|
|
public static function deactivate() {
|
|
// Code de désactivation (nettoyage, etc.)
|
|
}
|
|
public static function register_cpt() {
|
|
CRVI_Local_Controller::register_cpt();
|
|
CRVI_Beneficiaire_Controller::register_cpt();
|
|
CRVI_Intervenant_Controller::register_cpt();
|
|
CRVI_Traducteur_Controller::register_cpt();
|
|
|
|
|
|
//register departement et type_intervention
|
|
CRVI_Departement_Controller::register_cpt();
|
|
CRVI_Type_Intervention_Controller::register_cpt();
|
|
|
|
//register traduction_langue (capacités de traduction)
|
|
CRVI_TraductionLangue_Controller::register_cpt();
|
|
}
|
|
|
|
public static function register_routes() {
|
|
// Vérification et enregistrement des routes avec gestion d'erreur
|
|
$controllers = [
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Local_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Beneficiaire_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Intervenant_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Traducteur_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Departement_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Event_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Entity_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\Intervenant_Space_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_Incident_Controller',
|
|
'ESI_CRVI_AGENDA\controllers\CRVI_TraductionLangue_Controller'
|
|
];
|
|
|
|
foreach ($controllers as $controller) {
|
|
// Forcer le chargement de la classe via l'autoloader si elle n'existe pas encore
|
|
if (!class_exists($controller)) {
|
|
// Essayer de charger le fichier manuellement si l'autoloader a échoué
|
|
$relative_class = str_replace('ESI_CRVI_AGENDA\\', '', $controller);
|
|
$file_loaded = false;
|
|
|
|
// Essayer différents patterns de nommage
|
|
$possible_files = [];
|
|
|
|
// Pattern 1: controllers\CRVI_XXX_Controller -> 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']);
|
|
}
|
|
|
|
/**
|
|
* 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 '<p style="color:red">Template agenda-stats-form.php introuvable.</p>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Affiche la page d'admin de gestion des permanences
|
|
*/
|
|
public static function render_permanences_page() {
|
|
echo '<div class="wrap"><h1>' . esc_html__('Gestion des Permanences', 'esi_crvi_agenda') . '</h1>';
|
|
$template = plugin_dir_path(__FILE__) . '../../templates/admin/permanences-admin.php';
|
|
if (file_exists($template)) {
|
|
include $template;
|
|
} else {
|
|
echo '<p style="color:red">Template permanences-admin.php introuvable.</p>';
|
|
}
|
|
echo '</div>';
|
|
}
|
|
|
|
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
|
|
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) - 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()
|
|
];
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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('<script ', '<script type="module" ', $tag);
|
|
}
|
|
}
|
|
|
|
return $tag;
|
|
}
|
|
|
|
/**
|
|
* Vérifie les indisponibilités lors de la sauvegarde d'un utilisateur intervenant
|
|
* et clear le transient des disponibilités
|
|
*
|
|
* @param int|string $post_id L'ID du post/user sauvegardé
|
|
*/
|
|
public static function check_intervenant_availability_on_save($post_id) {
|
|
// Vérifier que ACF est actif
|
|
if (!function_exists('get_field')) {
|
|
return;
|
|
}
|
|
|
|
// Vérifier si c'est un utilisateur (les IDs utilisateurs sont préfixés par 'user_')
|
|
if (!is_string($post_id) || strpos($post_id, 'user_') !== 0) {
|
|
return;
|
|
}
|
|
|
|
// Extraire l'ID utilisateur
|
|
$user_id = (int) str_replace('user_', '', $post_id);
|
|
if ($user_id <= 0) {
|
|
return;
|
|
}
|
|
|
|
// Vérifier que l'utilisateur existe et a le rôle 'intervenant'
|
|
$user = get_user_by('id', $user_id);
|
|
if (!$user || !in_array('intervenant', $user->roles)) {
|
|
return;
|
|
}
|
|
|
|
// Clear le transient pour forcer le rechargement des disponibilités
|
|
delete_transient('crvi_intervenants_disponibilites');
|
|
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'crvi_agenda';
|
|
$conflicts = [];
|
|
|
|
// 1. Vérifier les jours de disponibilité
|
|
$jours_dispo = get_field('jours_de_disponibilite', 'user_' . $user_id);
|
|
|
|
if (is_array($jours_dispo) && !empty($jours_dispo)) {
|
|
// Mapping des jours français vers les numéros de jours (0 = dimanche, 1 = lundi, etc.)
|
|
$jours_mapping = [
|
|
'dimanche' => 0,
|
|
'lundi' => 1,
|
|
'mardi' => 2,
|
|
'mercredi' => 3,
|
|
'jeudi' => 4,
|
|
'vendredi' => 5,
|
|
'samedi' => 6
|
|
];
|
|
|
|
// Créer un array des numéros de jours disponibles
|
|
$jours_dispo_numbers = [];
|
|
foreach ($jours_dispo as $jour) {
|
|
if (isset($jours_mapping[$jour])) {
|
|
$jours_dispo_numbers[] = $jours_mapping[$jour];
|
|
}
|
|
}
|
|
|
|
// Si des jours sont définis, vérifier les événements futurs sur les jours non disponibles
|
|
if (!empty($jours_dispo_numbers)) {
|
|
// Récupérer tous les événements futurs de cet intervenant
|
|
// Exclure les permanences non attribuées (assign = 0)
|
|
$query = $wpdb->prepare(
|
|
"SELECT id, date_rdv, heure_rdv, type, assign
|
|
FROM {$table_name}
|
|
WHERE id_intervenant = %d
|
|
AND date_rdv >= CURDATE()
|
|
AND statut NOT IN ('annule', 'non_tenu')
|
|
AND (type != 'permanence' OR assign != 0)
|
|
ORDER BY date_rdv, heure_rdv",
|
|
$user_id
|
|
);
|
|
|
|
$events = $wpdb->get_results($query);
|
|
|
|
foreach ($events as $event) {
|
|
$timestamp = strtotime($event->date_rdv);
|
|
$day_of_week = (int) date('w', $timestamp);
|
|
|
|
// Si l'événement est sur un jour non disponible
|
|
if (!in_array($day_of_week, $jours_dispo_numbers)) {
|
|
$day_name = array_search($day_of_week, $jours_mapping);
|
|
|
|
if (!isset($conflicts['jours_indisponibles'])) {
|
|
$conflicts['jours_indisponibles'] = [
|
|
'type' => 'Jours non disponibles',
|
|
'events' => []
|
|
];
|
|
}
|
|
|
|
$conflicts['jours_indisponibles']['events'][] = $event;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Vérifier les indisponibilités ponctuelles
|
|
$indisponibilites = get_field('indisponibilitee_ponctuelle', 'user_' . $user_id);
|
|
|
|
if (!is_array($indisponibilites)) {
|
|
$indisponibilites = [];
|
|
}
|
|
|
|
// Vérifier chaque période d'indisponibilité
|
|
foreach ($indisponibilites as $indispo) {
|
|
if (!isset($indispo['debut']) || !isset($indispo['fin'])) {
|
|
continue;
|
|
}
|
|
|
|
// Convertir les dates au format YYYY-MM-DD
|
|
$debut = self::convert_date_to_mysql($indispo['debut']);
|
|
$fin = self::convert_date_to_mysql($indispo['fin']);
|
|
|
|
if (!$debut || !$fin) {
|
|
continue;
|
|
}
|
|
|
|
// Requête pour vérifier les événements de cet intervenant pendant cette période
|
|
// Exclure les permanences non attribuées (assign = 0)
|
|
$query = $wpdb->prepare(
|
|
"SELECT id, date_rdv, heure_rdv, type, assign
|
|
FROM {$table_name}
|
|
WHERE id_intervenant = %d
|
|
AND date_rdv BETWEEN %s AND %s
|
|
AND statut NOT IN ('annule', 'non_tenu')
|
|
AND (type != 'permanence' OR assign != 0)
|
|
ORDER BY date_rdv, heure_rdv",
|
|
$user_id,
|
|
$debut,
|
|
$fin
|
|
);
|
|
|
|
$events = $wpdb->get_results($query);
|
|
|
|
if (!empty($events)) {
|
|
$type_indispo = $indispo['type'] ?? 'Indisponibilité';
|
|
$conflicts[] = [
|
|
'periode' => [
|
|
'debut' => $debut,
|
|
'fin' => $fin,
|
|
'type' => $type_indispo
|
|
],
|
|
'events' => $events
|
|
];
|
|
}
|
|
}
|
|
|
|
// Si des conflits sont détectés, créer un message d'erreur
|
|
if (!empty($conflicts)) {
|
|
$message = '<div class="notice notice-error is-dismissible"><p><strong>Attention :</strong> Des événements sont planifiés pendant les périodes d\'indisponibilité :</p><ul>';
|
|
|
|
foreach ($conflicts as $key => $conflict) {
|
|
// Gérer les conflits de jours indisponibles
|
|
if ($key === 'jours_indisponibles') {
|
|
$message .= sprintf(
|
|
'<li><strong>%s</strong> : %d événement(s) planifié(s)<ul>',
|
|
esc_html($conflict['type']),
|
|
count($conflict['events'])
|
|
);
|
|
|
|
foreach ($conflict['events'] as $event) {
|
|
$date_formatted = date_i18n('d/m/Y', strtotime($event->date_rdv));
|
|
$day_name = date_i18n('l', strtotime($event->date_rdv));
|
|
$message .= sprintf(
|
|
'<li>%s (%s) à %s - %s (ID: %d)</li>',
|
|
$date_formatted,
|
|
$day_name,
|
|
substr($event->heure_rdv, 0, 5),
|
|
esc_html(ucfirst($event->type)),
|
|
$event->id
|
|
);
|
|
}
|
|
|
|
$message .= '</ul></li>';
|
|
} else {
|
|
// Gérer les conflits de périodes d'indisponibilité
|
|
$periode = $conflict['periode'];
|
|
$debut_formatted = date_i18n('d/m/Y', strtotime($periode['debut']));
|
|
$fin_formatted = date_i18n('d/m/Y', strtotime($periode['fin']));
|
|
|
|
$message .= sprintf(
|
|
'<li><strong>%s du %s au %s</strong> : %d événement(s) planifié(s)<ul>',
|
|
esc_html($periode['type']),
|
|
$debut_formatted,
|
|
$fin_formatted,
|
|
count($conflict['events'])
|
|
);
|
|
|
|
foreach ($conflict['events'] as $event) {
|
|
$date_formatted = date_i18n('d/m/Y', strtotime($event->date_rdv));
|
|
$message .= sprintf(
|
|
'<li>%s à %s - %s (ID: %d)</li>',
|
|
$date_formatted,
|
|
substr($event->heure_rdv, 0, 5),
|
|
esc_html(ucfirst($event->type)),
|
|
$event->id
|
|
);
|
|
}
|
|
|
|
$message .= '</ul></li>';
|
|
}
|
|
}
|
|
|
|
$message .= '</ul><p>Veuillez annuler ou déplacer ces événements avant de définir ces périodes comme indisponibles.</p></div>';
|
|
|
|
// Stocker le message dans un transient pour l'afficher après la redirection
|
|
set_transient('crvi_intervenant_conflicts_' . $user_id, $message, 30);
|
|
|
|
// Envoyer un email de notification aux administrateurs via le contrôleur de notifications
|
|
CRVI_Notifications_Controller::send_conflict_notification($user_id, $conflicts);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convertit une date ACF (format d/m/Y ou Y-m-d) en format MySQL (Y-m-d)
|
|
*
|
|
* @param string $date La date à convertir
|
|
* @return string|false La date au format Y-m-d ou false en cas d'échec
|
|
*/
|
|
private static function convert_date_to_mysql($date) {
|
|
if (empty($date)) {
|
|
return false;
|
|
}
|
|
|
|
// Si la date contient des slashes, c'est le format d/m/Y
|
|
if (strpos($date, '/') !== false) {
|
|
$parts = explode('/', $date);
|
|
if (count($parts) === 3) {
|
|
// Format d/m/Y
|
|
return sprintf('%04d-%02d-%02d', (int)$parts[2], (int)$parts[1], (int)$parts[0]);
|
|
}
|
|
}
|
|
|
|
// Sinon, supposer que c'est déjà au format Y-m-d ou un format valide
|
|
$timestamp = strtotime($date);
|
|
if ($timestamp === false) {
|
|
return false;
|
|
}
|
|
|
|
return date('Y-m-d', $timestamp);
|
|
}
|
|
|
|
/**
|
|
* Affiche les messages de conflit d'indisponibilités stockés dans les transients (admin)
|
|
*/
|
|
public static function display_intervenant_conflicts_notice() {
|
|
// DEBUG: Vérifier que l'action est bien appelée
|
|
// error_log('CRVI DEBUG: display_intervenant_conflicts_notice appelé');
|
|
|
|
// Récupérer l'ID utilisateur courant si on est sur une page de profil
|
|
$user_id = 0;
|
|
|
|
// Cas 1: Page de profil utilisateur (admin)
|
|
if (isset($_GET['user_id'])) {
|
|
$user_id = (int) $_GET['user_id'];
|
|
}
|
|
// Cas 2: Page de profil personnel
|
|
elseif (get_current_screen() && get_current_screen()->id === 'profile') {
|
|
$user_id = get_current_user_id();
|
|
}
|
|
|
|
if ($user_id <= 0) {
|
|
return;
|
|
}
|
|
|
|
// Récupérer le message de conflit s'il existe
|
|
$message = get_transient('crvi_intervenant_conflicts_' . $user_id);
|
|
|
|
if ($message) {
|
|
echo $message;
|
|
// Supprimer le transient après affichage
|
|
delete_transient('crvi_intervenant_conflicts_' . $user_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Affiche les messages de conflit d'indisponibilités en front-end
|
|
* À utiliser dans les templates front-end
|
|
*/
|
|
public static function display_intervenant_conflicts_frontend() {
|
|
if (!is_user_logged_in()) {
|
|
return;
|
|
}
|
|
|
|
$user_id = get_current_user_id();
|
|
|
|
// Récupérer le message de conflit s'il existe
|
|
$message = get_transient('crvi_intervenant_conflicts_' . $user_id);
|
|
|
|
if ($message) {
|
|
// Convertir le message admin en message front-end (Bootstrap alert)
|
|
$frontend_message = str_replace(
|
|
'<div class="notice notice-error is-dismissible">',
|
|
'<div class="alert alert-danger alert-dismissible fade show" role="alert">',
|
|
$message
|
|
);
|
|
$frontend_message = str_replace(
|
|
'</div>',
|
|
'<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>',
|
|
$frontend_message
|
|
);
|
|
|
|
echo $frontend_message;
|
|
// Supprimer le transient après affichage
|
|
delete_transient('crvi_intervenant_conflicts_' . $user_id);
|
|
}
|
|
}
|
|
}
|