466 lines
17 KiB
PHP
466 lines
17 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ESI_CRVI_AGENDA\controllers;
|
|
|
|
use ESI_CRVI_AGENDA\models\CRVI_Intervenant_Model;
|
|
use ESI_CRVI_AGENDA\helpers\Api_Helper;
|
|
|
|
class CRVI_Intervenant_Controller {
|
|
public static function register_cpt() {
|
|
\register_post_type('intervenant', [
|
|
'label' => 'Intervenants',
|
|
'labels' => [
|
|
'name' => 'Intervenants',
|
|
'singular_name' => 'Intervenant',
|
|
'add_new' => 'Ajouter un intervenant',
|
|
'add_new_item' => 'Ajouter un nouvel intervenant',
|
|
'edit_item' => 'Modifier l\'intervenant',
|
|
'new_item' => 'Nouvel intervenant',
|
|
'view_item' => 'Voir l\'intervenant',
|
|
'search_items' => 'Rechercher un intervenant',
|
|
'not_found' => 'Aucun intervenant trouvé',
|
|
'not_found_in_trash' => 'Aucun intervenant dans la corbeille',
|
|
],
|
|
'public' => true,
|
|
'show_in_menu' => true,
|
|
'menu_position' => 22,
|
|
'menu_icon' => 'dashicons-businessman',
|
|
'supports' => ['title'],
|
|
'has_archive' => false,
|
|
'show_in_rest' => true,
|
|
]);
|
|
|
|
self::register_taxonomy();
|
|
|
|
\register_taxonomy_for_object_type('departement', 'intervenant');
|
|
\register_taxonomy_for_object_type('type_intervention', 'intervenant');
|
|
}
|
|
|
|
public static function register_taxonomy() {
|
|
\register_taxonomy('departement', 'intervenant', [
|
|
'label' => 'Départements',
|
|
'labels' => [
|
|
'name' => 'Départements',
|
|
'singular_name' => 'Département',
|
|
],
|
|
'show_in_rest' => true,
|
|
'hierarchical' => true,
|
|
'rewrite' => ['slug' => 'departement'],
|
|
'show_admin_column' => true,
|
|
'query_var' => true,
|
|
'public' => true,
|
|
'show_in_menu' => true,
|
|
]);
|
|
|
|
\register_taxonomy('type_intervention', 'intervenant', [
|
|
'label' => 'Types d\'intervention',
|
|
'labels' => [
|
|
'name' => 'Types d\'intervention',
|
|
'singular_name' => 'Type d\'intervention',
|
|
],
|
|
'show_in_rest' => true,
|
|
'hierarchical' => true,
|
|
'rewrite' => ['slug' => 'type-intervention'],
|
|
'show_admin_column' => true,
|
|
'query_var' => true,
|
|
'public' => true,
|
|
'show_in_menu' => true,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Retourne les intervenants disponibles à une date donnée.
|
|
* @param string $date au format Y-m-d
|
|
* @param string|null $departement (optionnel)
|
|
* @param string|null $specialisation (optionnel)
|
|
* @return array Liste des intervenants disponibles (WP_Post)
|
|
*/
|
|
public static function filtrer_disponibles($date, $departement = null, $specialisation = null) {
|
|
if (empty($date)) {
|
|
return [];
|
|
}
|
|
$timestamp = strtotime($date);
|
|
$jour = strtolower(date('l', $timestamp)); // ex: 'monday'
|
|
$jours_fr = [
|
|
'monday' => 'lundi',
|
|
'tuesday' => 'mardi',
|
|
'wednesday' => 'mercredi',
|
|
'thursday' => 'jeudi',
|
|
'friday' => 'vendredi',
|
|
'saturday' => 'samedi',
|
|
'sunday' => 'dimanche',
|
|
];
|
|
$jour_semaine = $jours_fr[$jour] ?? '';
|
|
if (!$jour_semaine) return [];
|
|
|
|
// 1. Récupérer les intervenants (filtrage taxonomie si besoin)
|
|
$tax_query = [];
|
|
if ($departement) {
|
|
$tax_query[] = [
|
|
'taxonomy' => 'departement',
|
|
'field' => 'slug',
|
|
'terms' => $departement,
|
|
];
|
|
}
|
|
if ($specialisation) {
|
|
$tax_query[] = [
|
|
'taxonomy' => 'type_intervention',
|
|
'field' => 'slug',
|
|
'terms' => $specialisation,
|
|
];
|
|
}
|
|
$args = [
|
|
'post_type' => 'intervenant',
|
|
'numberposts' => -1,
|
|
];
|
|
if (!empty($tax_query)) {
|
|
$args['tax_query'] = $tax_query;
|
|
}
|
|
$intervenants = get_posts($args);
|
|
$disponibles = [];
|
|
foreach ($intervenants as $intervenant) {
|
|
// Vérifier jours de disponibilité
|
|
$jours = get_field('jours_de_disponibilite', $intervenant->ID);
|
|
if (!is_array($jours) || !in_array($jour_semaine, $jours, true)) {
|
|
continue;
|
|
}
|
|
// Vérifier absences ponctuelles
|
|
$absences = get_field('indisponibilitee_ponctuelle', $intervenant->ID);
|
|
$est_absent = false;
|
|
if (is_array($absences)) {
|
|
foreach ($absences as $absence) {
|
|
$debut = isset($absence['debut']) ? strtotime($absence['debut']) : null;
|
|
$fin = isset($absence['fin']) ? strtotime($absence['fin']) : null;
|
|
if ($debut && $fin && $timestamp >= $debut && $timestamp <= $fin) {
|
|
$est_absent = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ($est_absent) continue;
|
|
$disponibles[] = $intervenant;
|
|
}
|
|
return $disponibles;
|
|
}
|
|
|
|
public static function register_routes() {
|
|
register_rest_route('crvi/v1', '/intervenants', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_items'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'create_item'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
]);
|
|
|
|
register_rest_route('crvi/v1', '/intervenants/import', [
|
|
[
|
|
'methods' => 'POST',
|
|
'callback' => [self::class, 'import_csv'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
'args' => [
|
|
'file' => [
|
|
'required' => true,
|
|
'description' => 'Fichier CSV à importer',
|
|
'type' => 'file',
|
|
],
|
|
],
|
|
],
|
|
]);
|
|
|
|
register_rest_route('crvi/v1', '/intervenants/(?P<id>\\d+)', [
|
|
[
|
|
'methods' => 'GET',
|
|
'callback' => [self::class, 'get_item'],
|
|
'permission_callback' => '__return_true',
|
|
],
|
|
[
|
|
'methods' => 'PUT,PATCH',
|
|
'callback' => [self::class, 'update_item'],
|
|
'permission_callback' => [self::class, 'can_edit'],
|
|
],
|
|
[
|
|
'methods' => 'DELETE',
|
|
'callback' => [self::class, 'delete_item'],
|
|
'permission_callback' => [self::class, 'can_delete'],
|
|
],
|
|
]);
|
|
}
|
|
|
|
public static function get_items($request) {
|
|
$items = CRVI_Intervenant_Model::all();
|
|
return Api_Helper::json_success($items);
|
|
}
|
|
|
|
public static function get_item($request) {
|
|
$id = (int) $request['id'];
|
|
$item = CRVI_Intervenant_Model::load($id);
|
|
if (!$item) {
|
|
return Api_Helper::json_error('Intervenant introuvable', 404);
|
|
}
|
|
return Api_Helper::json_success($item);
|
|
}
|
|
|
|
public static function create_item($request) {
|
|
|
|
$data = $request->get_json_params();
|
|
|
|
$validation = self::validate_intervenant_data($data);
|
|
if (is_wp_error($validation)) {
|
|
return Api_Helper::json_error($validation->get_error_message(), 400);
|
|
}
|
|
|
|
$model = new CRVI_Intervenant_Model();
|
|
$result = $model->create($data);
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
return Api_Helper::json_success([
|
|
'id' => $result,
|
|
'nom' => $data['nom'],
|
|
'prenom' => $data['prenom'] ?? '',
|
|
'message' => 'Intervenant créé avec succès'
|
|
]);
|
|
}
|
|
|
|
public static function update_item($request) {
|
|
$id = (int) $request['id'];
|
|
$model = new CRVI_Intervenant_Model();
|
|
$result = $model->update($id, $request->get_json_params());
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
return Api_Helper::json_success(['id' => $id]);
|
|
}
|
|
|
|
public static function delete_item($request) {
|
|
$id = (int) $request['id'];
|
|
$model = new CRVI_Intervenant_Model();
|
|
$result = $model->delete($id);
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
return Api_Helper::json_success(['id' => $id, 'deleted' => true]);
|
|
}
|
|
|
|
/**
|
|
* Import CSV d'intervenants via REST (aligné sur Traducteur_Controller)
|
|
* @param WP_REST_Request $request
|
|
* @return array
|
|
*/
|
|
public static function import_csv($request) {
|
|
if (!current_user_can('edit_posts')) {
|
|
return Api_Helper::json_error('Non autorisé', 403);
|
|
}
|
|
$file = $request->get_file_params()['file'] ?? null;
|
|
if (!$file || !is_uploaded_file($file['tmp_name'])) {
|
|
return Api_Helper::json_error('Fichier CSV manquant ou invalide', 400);
|
|
}
|
|
$handle = fopen($file['tmp_name'], 'r');
|
|
if (!$handle) {
|
|
return Api_Helper::json_error('Impossible d\'ouvrir le fichier', 400);
|
|
}
|
|
$header = fgetcsv($handle, 0, ',');
|
|
$created = $updated = $errors = 0;
|
|
while (($row = fgetcsv($handle, 0, ',')) !== false) {
|
|
$data = array_combine($header, $row);
|
|
$data = array_combine(
|
|
array_map(function($k) { return sanitize_title($k); }, array_keys($data)),
|
|
array_map('trim', array_values($data))
|
|
);
|
|
// Conversion titres -> IDs pour departements_ids et types_intervention_ids
|
|
foreach ([
|
|
'departements_ids' => 'departement',
|
|
'types_intervention_ids' => 'type_intervention',
|
|
] as $csv_key => $cpt) {
|
|
if (!empty($data[$csv_key])) {
|
|
$titles = explode('|', $data[$csv_key]);
|
|
$ids = [];
|
|
foreach ($titles as $title) {
|
|
$title = trim($title);
|
|
if (!$title) continue;
|
|
$slug = sanitize_title($title);
|
|
$posts = get_posts([
|
|
'post_type' => $cpt,
|
|
'name' => $slug,
|
|
'posts_per_page' => 1,
|
|
'post_status' => 'publish',
|
|
'fields' => 'ids',
|
|
]);
|
|
if (!empty($posts)) {
|
|
$ids[] = $posts[0];
|
|
}
|
|
}
|
|
// Adapter la clé pour le modèle (departements, specialisations)
|
|
if ($csv_key === 'departements_ids') {
|
|
$data['departements'] = $ids;
|
|
} elseif ($csv_key === 'types_intervention_ids') {
|
|
$data['specialisations'] = $ids;
|
|
}
|
|
}
|
|
}
|
|
$result = CRVI_Intervenant_Model::create($data, false);
|
|
if (is_numeric($result)) $created++;
|
|
else $errors++;
|
|
}
|
|
fclose($handle);
|
|
$msg = "Créés: $created, Modifiés: $updated, Erreurs: $errors";
|
|
return Api_Helper::json_success(['message' => $msg]);
|
|
}
|
|
|
|
// Permissions personnalisées
|
|
public static function can_edit() {
|
|
$user = wp_get_current_user();
|
|
// Intervenant : lecture seule (GET autorisé via __return_true)
|
|
if (in_array('intervenant', (array)$user->roles, true)) {
|
|
return false;
|
|
}
|
|
// Admins ou autres rôles ayant edit_posts : accès autorisé
|
|
return current_user_can('edit_posts');
|
|
}
|
|
public static function can_delete() {
|
|
return current_user_can('delete_posts');
|
|
}
|
|
|
|
// Handler pour l'import CSV via formulaire admin
|
|
public static function import_csv_admin() {
|
|
if (!current_user_can('edit_users')) {
|
|
wp_die('Non autorisé');
|
|
}
|
|
check_admin_referer('crvi_import_intervenant');
|
|
if (empty($_FILES['import_csv']['tmp_name'])) {
|
|
wp_redirect(admin_url('admin.php?page=crvi_agenda&import=error&msg=Fichier manquant'));
|
|
exit;
|
|
}
|
|
$file = $_FILES['import_csv']['tmp_name'];
|
|
$handle = fopen($file, 'r');
|
|
if (!$handle) {
|
|
wp_redirect(admin_url('admin.php?page=crvi_agenda&import=error&msg=Impossible d\'ouvrir le fichier'));
|
|
exit;
|
|
}
|
|
$header = fgetcsv($handle, 0, ',');
|
|
$created = $updated = $errors = 0;
|
|
while (($row = fgetcsv($handle, 0, ',')) !== false) {
|
|
$data = array_combine($header, $row);
|
|
$data = array_combine(
|
|
array_map(function($k) { return sanitize_title($k); }, array_keys($data)),
|
|
array_map('trim', array_values($data))
|
|
);
|
|
// Conversion titres -> IDs pour departements_ids et types_intervention_ids
|
|
foreach ([
|
|
'departements_ids' => 'departement',
|
|
'types_intervention_ids' => 'type_intervention',
|
|
] as $csv_key => $cpt) {
|
|
if (!empty($data[$csv_key])) {
|
|
$titles = explode('|', $data[$csv_key]);
|
|
$ids = [];
|
|
foreach ($titles as $title) {
|
|
$title = trim($title);
|
|
if (!$title) continue;
|
|
$slug = sanitize_title($title);
|
|
$posts = get_posts([
|
|
'post_type' => $cpt,
|
|
'name' => $slug,
|
|
'posts_per_page' => 1,
|
|
'post_status' => 'publish',
|
|
'fields' => 'ids',
|
|
]);
|
|
if (!empty($posts)) {
|
|
$ids[] = $posts[0];
|
|
} else {
|
|
// Fallback LIKE sur le slug
|
|
global $wpdb;
|
|
$like = '%' . $wpdb->esc_like($slug) . '%';
|
|
$sql = $wpdb->prepare(
|
|
"SELECT ID FROM $wpdb->posts WHERE post_type = %s AND post_status = 'publish' AND post_name LIKE %s LIMIT 1",
|
|
$cpt, $like
|
|
);
|
|
$like_id = $wpdb->get_var($sql);
|
|
if ($like_id) {
|
|
$ids[] = (int)$like_id;
|
|
}
|
|
}
|
|
}
|
|
if ($csv_key === 'departements_ids') {
|
|
$data['departements'] = $ids;
|
|
} elseif ($csv_key === 'types_intervention_ids') {
|
|
$data['specialisations'] = $ids;
|
|
}
|
|
}
|
|
}
|
|
$result = CRVI_Intervenant_Model::create($data, false);
|
|
if (is_numeric($result)) $created++;
|
|
else $errors++;
|
|
}
|
|
fclose($handle);
|
|
$msg = "Créés: $created, Modifiés: $updated, Erreurs: $errors";
|
|
wp_redirect(admin_url('admin.php?page=crvi_agenda&import=success&msg=' . urlencode($msg)));
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Parse un champ répéteur au format pipe/point-virgule/deux-points.
|
|
* @param string $value
|
|
* @return array
|
|
*/
|
|
function parse_repeater_field(string $value): array {
|
|
if (empty($value)) return [];
|
|
$items = explode('|', $value);
|
|
$result = [];
|
|
foreach ($items as $item) {
|
|
$fields = explode(';', $item);
|
|
$assoc = [];
|
|
foreach ($fields as $field) {
|
|
[$key, $val] = array_pad(explode(':', $field, 2), 2, null);
|
|
if ($key !== null) {
|
|
$assoc[trim($key)] = trim($val ?? '');
|
|
}
|
|
}
|
|
if ($assoc) $result[] = $assoc;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Permet à un intervenant de modifier uniquement ses propres événements (liés à son intervenant_id).
|
|
* @param int|null $event_intervenant_id L'ID de l'intervenant dans l'événement
|
|
* @return bool
|
|
*/
|
|
public static function can_edit_own_event($event_intervenant_id = null) {
|
|
$user = wp_get_current_user();
|
|
|
|
// Admins ou rôles ayant edit_posts : accès total
|
|
if (current_user_can('edit_posts')) {
|
|
return true;
|
|
}
|
|
|
|
// Intervenant : peut modifier uniquement ses propres événements
|
|
if (in_array('intervenant', (array)$user->roles, true)) {
|
|
// L'ID de l'intervenant dans l'événement doit correspondre à l'ID de l'utilisateur connecté
|
|
if ($event_intervenant_id && (int)$event_intervenant_id === (int)$user->ID) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static function validate_intervenant_data($data) {
|
|
if (empty($data['nom'])) {
|
|
return new \WP_Error('missing_nom', 'Le nom est obligatoire');
|
|
}
|
|
|
|
if (!empty($data['email']) && !is_email($data['email'])) {
|
|
return new \WP_Error('invalid_email', 'L\'email n\'est pas valide');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} |