'Locaux', 'labels' => [ 'name' => 'Locaux', 'singular_name' => 'Local', 'add_new' => 'Ajouter un local', 'add_new_item' => 'Ajouter un nouveau local', 'edit_item' => 'Modifier le local', 'new_item' => 'Nouveau local', 'view_item' => 'Voir le local', 'search_items' => 'Rechercher un local', 'not_found' => 'Aucun local trouvé', 'not_found_in_trash' => 'Aucun local dans la corbeille', ], 'public' => true, 'show_in_menu' => true, 'menu_position' => 23, 'menu_icon' => 'dashicons-building', 'supports' => ['title'], 'has_archive' => false, 'show_in_rest' => true, ]); } public static function register_routes() { register_rest_route('crvi/v1', '/locaux', [ [ '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', '/locaux/disponibles', [ [ 'methods' => 'GET', 'callback' => [self::class, 'filtrer_disponibles'], 'permission_callback' => '__return_true', 'args' => [ 'date_debut' => [ 'required' => false, 'type' => 'string', 'description' => 'Date de début (YYYY-MM-DD)', ], 'date_fin' => [ 'required' => false, 'type' => 'string', 'description' => 'Date de fin (YYYY-MM-DD)', ], 'heure_debut' => [ 'required' => false, 'type' => 'string', 'description' => 'Heure de début (HH:MM)', ], 'heure_fin' => [ 'required' => false, 'type' => 'string', 'description' => 'Heure de fin (HH:MM)', ], ], ], ]); register_rest_route('crvi/v1', '/locaux/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', '/locaux/(?P\\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_Local_Model::all(); return Api_Helper::json_success($items); } public static function get_item($request) { $id = (int) $request['id']; $item = CRVI_Local_Model::load($id); if (!$item) { return Api_Helper::json_error('Local introuvable', 404); } return Api_Helper::json_success($item); } /** * Filtre les locaux disponibles selon les critères fournis * @param WP_REST_Request $request * @return WP_REST_Response */ public static function filtrer_disponibles($request) { $date_debut = $request->get_param('date_debut'); $date_fin = $request->get_param('date_fin'); $heure_debut = $request->get_param('heure_debut'); $heure_fin = $request->get_param('heure_fin'); // Récupérer tous les locaux $tous_locaux = CRVI_Local_Model::all(); // Si aucun critère de temps n'est fourni, retourner tous les locaux if (empty($date_debut) && empty($date_fin) && empty($heure_debut) && empty($heure_fin)) { return Api_Helper::json_success($tous_locaux); } // Filtrer les locaux disponibles selon les critères $locaux_disponibles = []; foreach ($tous_locaux as $local) { $disponible = true; // Vérifier la disponibilité selon les critères fournis if ($date_debut && $date_fin) { // Vérifier s'il y a des conflits d'événements dans cette période $conflits = self::verifier_conflits_local($local['id'], $date_debut, $date_fin, $heure_debut, $heure_fin); if (!empty($conflits)) { $disponible = false; } } if ($disponible) { $locaux_disponibles[] = $local; } } return Api_Helper::json_success($locaux_disponibles); } /** * Vérifie s'il y a des conflits d'événements pour un local donné * @param int $local_id * @param string $date_debut * @param string $date_fin * @param string $heure_debut * @param string $heure_fin * @return array */ private static function verifier_conflits_local($local_id, $date_debut, $date_fin, $heure_debut = null, $heure_fin = null) { global $wpdb; $table_events = $wpdb->prefix . 'crvi_agenda'; $where_conditions = ['local_id = %d']; $where_values = [$local_id]; // Ajouter les conditions de date if ($date_debut && $date_fin) { $where_conditions[] = '( (date_rdv BETWEEN %s AND %s) OR (date_fin BETWEEN %s AND %s) OR (%s BETWEEN date_rdv AND date_fin) OR (%s BETWEEN date_rdv AND date_fin) )'; $where_values = array_merge($where_values, [$date_debut, $date_fin, $date_debut, $date_fin, $date_debut, $date_fin]); } // Ajouter les conditions d'heure si fournies if ($heure_debut && $heure_fin) { $where_conditions[] = '( (heure_rdv BETWEEN %s AND %s) OR (heure_fin BETWEEN %s AND %s) OR (%s BETWEEN heure_rdv AND heure_fin) OR (%s BETWEEN heure_rdv AND heure_fin) )'; $where_values = array_merge($where_values, [$heure_debut, $heure_fin, $heure_debut, $heure_fin, $heure_debut, $heure_fin]); } $where_clause = implode(' AND ', $where_conditions); $query = $wpdb->prepare( "SELECT * FROM {$table_events} WHERE {$where_clause}", $where_values ); return $wpdb->get_results($query, ARRAY_A); } public static function create($request) { $data = $request->get_json_params(); $model = new CRVI_Local_Model(); $validation = self::validate_local_data($data); if (is_wp_error($validation)) { return Api_Helper::json_error($validation->get_error_message(), 400); } $result = $model->create($data); if (is_wp_error($result)) { return $result; } return Api_Helper::json_success([ 'id' => $result, 'nom' => $data['nom'], 'message' => 'Local créé avec succès' ]); } public static function update_item($request) { $id = (int) $request['id']; $model = new CRVI_Local_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_Local_Model(); $result = $model->delete($id); if (is_wp_error($result)) { return $result; } return Api_Helper::json_success(['id' => $id, 'deleted' => true]); } /** * Import CSV de locaux via REST * @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, ','); $results = []; $row_num = 1; while (($row = fgetcsv($handle, 0, ',')) !== false) { $row_num++; $data = array_combine($header, $row); $model = new CRVI_Local_Model(); $result = $model->create($data); $results[] = [ 'ligne' => $row_num, 'data' => $data, 'resultat' => $result ]; } fclose($handle); return Api_Helper::json_success(['import' => $results]); } // Permissions personnalisées public static function can_edit() { 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_posts')) { wp_die('Non autorisé'); } check_admin_referer('crvi_import_local'); 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)) ); $model = new CRVI_Local_Model(); $result = $model->create($data); if (is_numeric($result)) { $created++; } elseif (is_wp_error($result) && $result->get_error_code() === '409') { // Nom déjà utilisé - on pourrait mettre à jour au lieu d'ignorer $errors++; } else { $errors++; } } fclose($handle); $msg = "Créés: $created, Erreurs: $errors"; wp_redirect(admin_url('admin.php?page=crvi_agenda&import=success&msg=' . urlencode($msg))); exit; } public static function validate_local_data($data) { if (empty($data['nom'])) { return new \WP_Error('missing_nom', 'Le nom du local est obligatoire'); } if (!empty($data['capacite']) && !is_numeric($data['capacite'])) { return new \WP_Error('invalid_capacite', 'La capacité doit être un nombre'); } return true; } }