579 lines
21 KiB
PHP
579 lines
21 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ESI_CRVI_AGENDA\models;
|
|
|
|
use ESI_CRVI_AGENDA\helpers\Api_Helper;
|
|
|
|
class CRVI_Traducteur_Model extends Main_Model {
|
|
public $id;
|
|
public $nom;
|
|
public $prenom;
|
|
public $email;
|
|
public $langues_parlees;
|
|
public $jours_de_disponibilite;
|
|
public $indisponibilitee_ponctuelle;
|
|
public $organisme;
|
|
public $commentaires;
|
|
public $type_de_fiche;
|
|
|
|
/**
|
|
* Carte des codes de langue vers le nom de la langue (Europe continentale + Afrique du Nord)
|
|
*/
|
|
public static $lg_map = [
|
|
'fr' => 'Français',
|
|
'de' => 'Allemand',
|
|
'it' => 'Italien',
|
|
'es' => 'Espagnol',
|
|
'pt' => 'Portugais',
|
|
'nl' => 'Néerlandais',
|
|
'en' => 'Anglais',
|
|
'ru' => 'Russe',
|
|
'pl' => 'Polonais',
|
|
'ro' => 'Roumain',
|
|
'el' => 'Grec',
|
|
'tr' => 'Turc',
|
|
'ar' => 'Arabe',
|
|
'kab' => 'Kabyle',
|
|
'ber' => 'Berbère',
|
|
'tzm' => 'Tamazight',
|
|
'da' => 'Danois',
|
|
'sv' => 'Suédois',
|
|
'no' => 'Norvégien',
|
|
'fi' => 'Finnois',
|
|
'cs' => 'Tchèque',
|
|
'sk' => 'Slovaque',
|
|
'hu' => 'Hongrois',
|
|
'bg' => 'Bulgare',
|
|
'hr' => 'Croate',
|
|
'sr' => 'Serbe',
|
|
'bs' => 'Bosnien',
|
|
'sq' => 'Albanais',
|
|
'mk' => 'Macédonien',
|
|
'sl' => 'Slovène',
|
|
'he' => 'Hébreu',
|
|
'lt' => 'Lituanien',
|
|
'lv' => 'Letton',
|
|
'et' => 'Estonien',
|
|
];
|
|
|
|
/**
|
|
* Schéma des champs ACF : nom => type (ex : group, repeater, taxonomy, text...)
|
|
*/
|
|
public static $acf_schema = [
|
|
'nom' => 'text',
|
|
'prenom' => 'text',
|
|
'email' => 'email',
|
|
'organisme' => 'taxonomy',
|
|
'langues_parlees' => 'taxonomy',
|
|
'jours_de_disponibilite' => 'checkbox',
|
|
'indisponibilitee_ponctuelle' => 'repeater',
|
|
'coordonnees' => 'group',
|
|
'commentaires' => 'textarea',
|
|
'type_de_fiche' => 'select',
|
|
];
|
|
|
|
public function __construct($data = []) {
|
|
foreach ($data as $key => $value) {
|
|
if (property_exists($this, $key)) {
|
|
$this->$key = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function get_traducteurs($filters = [],$simple_list = false) {
|
|
$posts = get_posts([
|
|
'post_type' => 'traducteur',
|
|
'numberposts' => -1,
|
|
'meta_query' => $filters,
|
|
]);
|
|
|
|
if ($simple_list) {
|
|
$posts = array_map(function($post) {
|
|
return [
|
|
'id' => $post->ID,
|
|
'nom' => $post->post_title,
|
|
];
|
|
}, $posts);
|
|
}
|
|
return $posts;
|
|
}
|
|
|
|
public static function load($id, $fields = []) {
|
|
// Charger depuis CPT/meta
|
|
$traducteur = get_post($id);
|
|
if (!$traducteur) {
|
|
return null;
|
|
}
|
|
|
|
// Si des champs spécifiques sont demandés, ne charger que ceux-ci
|
|
if (!empty($fields)) {
|
|
$data = [];
|
|
foreach ($fields as $field) {
|
|
if ($field === 'id') {
|
|
$data['id'] = $traducteur->ID;
|
|
} elseif (property_exists(self::class, $field)) {
|
|
$data[$field] = get_field($field, $traducteur->ID);
|
|
}
|
|
}
|
|
return new self($data);
|
|
}
|
|
|
|
// Sinon, charger tous les champs par défaut
|
|
return new self([
|
|
'id' => $traducteur->ID,
|
|
'nom' => get_field('nom', $traducteur->ID),
|
|
'prenom' => get_field('prenom', $traducteur->ID),
|
|
'email' => get_field('email', $traducteur->ID),
|
|
'langues_parlees' => get_field('langues_parlees', $traducteur->ID),
|
|
'jours_de_disponibilite' => get_field('jours_de_disponibilite', $traducteur->ID),
|
|
'indisponibilitee_ponctuelle' => get_field('indisponibilitee_ponctuelle', $traducteur->ID),
|
|
'organisme' => get_field('organisme', $traducteur->ID),
|
|
'commentaires' => get_field('commentaires', $traducteur->ID),
|
|
'type_de_fiche' => get_field('type_de_fiche', $traducteur->ID),
|
|
]);
|
|
}
|
|
|
|
public function save() {
|
|
// À implémenter : sauvegarder dans CPT/meta
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Retourne les traducteurs disponibles à une date donnée et pour une langue donnée.
|
|
* @param string $date au format Y-m-d
|
|
* @param string $langue_slug (slug de la langue)
|
|
* @param int|null $event_id ID de l'événement en cours d'édition (pour inclure le traducteur actuel)
|
|
* @return array Liste des traducteurs disponibles (CRVI_Traducteur_Model)
|
|
*/
|
|
public static function filtrer_disponibles($date, $langue_slug, $event_id = null) {
|
|
if (empty($date)) {
|
|
return [];
|
|
}
|
|
|
|
// Construire la requête de base
|
|
$args = [
|
|
'post_type' => 'traducteur',
|
|
'numberposts' => -1,
|
|
];
|
|
|
|
// Ajouter le filtre de langue seulement si spécifié
|
|
if (!empty($langue_slug)) {
|
|
$args['tax_query'] = [
|
|
[
|
|
'taxonomy' => 'langue',
|
|
'field' => 'slug',
|
|
'terms' => $langue_slug,
|
|
],
|
|
];
|
|
}
|
|
|
|
$traducteurs = get_posts($args);
|
|
$disponibles = [];
|
|
|
|
foreach ($traducteurs as $traducteur_post) {
|
|
$traducteur = new self([
|
|
'id' => $traducteur_post->ID,
|
|
'nom' => get_field('nom', $traducteur_post->ID),
|
|
'prenom' => get_field('prenom', $traducteur_post->ID),
|
|
'email' => get_field('email', $traducteur_post->ID),
|
|
'langues_parlees' => get_field('langues_parlees', $traducteur_post->ID),
|
|
'jours_de_disponibilite' => get_field('jours_de_disponibilite', $traducteur_post->ID),
|
|
'indisponibilitee_ponctuelle' => get_field('indisponibilitee_ponctuelle', $traducteur_post->ID),
|
|
'organisme' => get_field('organisme', $traducteur_post->ID),
|
|
'commentaires' => get_field('commentaires', $traducteur_post->ID),
|
|
'type_de_fiche' => get_field('type_de_fiche', $traducteur_post->ID),
|
|
]);
|
|
|
|
// Vérifier si le traducteur est disponible
|
|
$is_disponible = $traducteur->is_disponible($traducteur->id, $date, $langue_slug);
|
|
|
|
// Vérifier les conflits d'événements existants
|
|
if ($is_disponible) {
|
|
$conflits = self::verifier_conflits_traducteur($traducteur->id, $date, $event_id);
|
|
if (!empty($conflits)) {
|
|
$is_disponible = false;
|
|
}
|
|
}
|
|
|
|
// Si c'est l'événement en cours d'édition, inclure le traducteur même s'il est "pris"
|
|
if ($event_id && self::is_traducteur_of_event($traducteur->id, $event_id)) {
|
|
$is_disponible = true;
|
|
}
|
|
|
|
if ($is_disponible) {
|
|
$disponibles[] = $traducteur;
|
|
}
|
|
}
|
|
return $disponibles;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un traducteur est associé à un événement donné
|
|
* @param int $traducteur_id
|
|
* @param int $event_id
|
|
* @return bool
|
|
*/
|
|
private static function is_traducteur_of_event($traducteur_id, $event_id) {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'crvi_agenda';
|
|
$result = $wpdb->get_var($wpdb->prepare(
|
|
"SELECT id FROM $table_name WHERE id = %d AND id_traducteur = %d",
|
|
$event_id,
|
|
$traducteur_id
|
|
));
|
|
return !empty($result);
|
|
}
|
|
|
|
/**
|
|
* Vérifie si ce traducteur est disponible à une date donnée et pour une langue donnée.
|
|
* @param string $date au format Y-m-d
|
|
* @param string|null $langue_slug
|
|
* @return bool
|
|
*/
|
|
public function is_disponible($traducteur_id, $date, $langue_slug = null) {
|
|
// Vérifier que la date n'est pas null ou vide
|
|
if (empty($date)) {
|
|
return false;
|
|
}
|
|
|
|
$timestamp = strtotime($date);
|
|
if ($timestamp === false) {
|
|
return false;
|
|
}
|
|
|
|
$jour = strtolower(date('l', $timestamp));
|
|
|
|
$jours_disponibles = get_field('jours_de_disponibilite', 'user_' . $traducteur_id);
|
|
if (!is_array($jours_disponibles) || !in_array($jour, $jours_disponibles, true)) {
|
|
return false;
|
|
}
|
|
|
|
$indisponibilites = get_field('indisponibilitee_ponctuelle', 'user_' . $traducteur_id);
|
|
if (is_array($indisponibilites)) {
|
|
foreach ($indisponibilites 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) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Optionnel : vérifier la langue si besoin
|
|
if ($langue_slug && is_array($this->langues_parlees) && !in_array($langue_slug, $this->langues_parlees)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function get_relations() {
|
|
// À implémenter : récupérer les rendez-vous associés, etc.
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Créer un traducteur.
|
|
* @param array $data
|
|
* @return int|WP_Error
|
|
*/
|
|
public static function create(array $data, bool $as_rest = false) {
|
|
if (empty($data['nom']) || empty($data['prenom']) || empty($data['email'])) {
|
|
if ($as_rest) {
|
|
return [
|
|
'success' => false,
|
|
'code' => 400,
|
|
'message' => 'Champs obligatoires manquants',
|
|
'data' => null
|
|
];
|
|
}
|
|
return false;
|
|
}
|
|
// Chercher un traducteur existant par email
|
|
$existing = get_posts([
|
|
'post_type' => 'traducteur',
|
|
'meta_key' => 'email',
|
|
'meta_value' => $data['email'],
|
|
'post_status'=> 'any',
|
|
'numberposts'=> 1,
|
|
]);
|
|
if ($existing) {
|
|
// Mise à jour
|
|
$post_id = $existing[0]->ID;
|
|
// Mettre à jour le titre si nom/prenom fournis
|
|
$post_title = ($data['nom'] ?? get_post_meta($post_id, 'nom', true)) . ' ' . ($data['prenom'] ?? get_post_meta($post_id, 'prenom', true));
|
|
wp_update_post([
|
|
'ID' => $post_id,
|
|
'post_title' => $post_title,
|
|
]);
|
|
foreach ($data as $key => $value) {
|
|
if ($key === 'organisme') {
|
|
if (!taxonomy_exists('organisme')) {
|
|
register_taxonomy(
|
|
'organisme',
|
|
'traducteur',
|
|
[
|
|
'label' => 'Organismes',
|
|
'public' => false,
|
|
'hierarchical' => false,
|
|
'show_ui' => true,
|
|
'show_in_rest' => false,
|
|
]
|
|
);
|
|
}
|
|
$org_ids = [];
|
|
$organismes = is_array($value) ? $value : explode('|', $value);
|
|
foreach ($organismes as $org_nom) {
|
|
$org_nom = trim($org_nom);
|
|
if (empty($org_nom)) continue;
|
|
$slug = sanitize_title($org_nom);
|
|
$term = get_term_by('slug', $slug, 'organisme');
|
|
if (!$term) {
|
|
$result = wp_insert_term($org_nom, 'organisme', ['slug' => $slug]);
|
|
if (!is_wp_error($result) && isset($result['term_id'])) {
|
|
$org_ids[] = $result['term_id'];
|
|
}
|
|
} else {
|
|
$org_ids[] = $term->term_id;
|
|
}
|
|
}
|
|
if (!empty($org_ids)) {
|
|
update_field('organisme', $org_ids, $post_id);
|
|
}
|
|
} else if ($key === 'langues_parlees') {
|
|
if (!taxonomy_exists('langue')) {
|
|
register_taxonomy(
|
|
'langue',
|
|
'traducteur',
|
|
[
|
|
'label' => 'Langues',
|
|
'public' => false,
|
|
'hierarchical' => false,
|
|
'show_ui' => true,
|
|
'show_in_rest' => false,
|
|
]
|
|
);
|
|
}
|
|
$langue_ids = [];
|
|
$langues = is_array($value) ? $value : explode('|', $value);
|
|
foreach ($langues as $slug) {
|
|
$slug = trim($slug);
|
|
if (empty($slug)) continue;
|
|
$term = get_term_by('slug', $slug, 'langue');
|
|
if (!$term) {
|
|
$nom = self::$lg_map[$slug] ?? $slug;
|
|
$result = wp_insert_term($nom, 'langue', ['slug' => $slug]);
|
|
if (!is_wp_error($result) && isset($result['term_id'])) {
|
|
$langue_ids[] = $result['term_id'];
|
|
}
|
|
} else {
|
|
$langue_ids[] = $term->term_id;
|
|
}
|
|
}
|
|
if (!empty($langue_ids)) {
|
|
update_field('langue', $langue_ids, $post_id);
|
|
}
|
|
} else if (isset(self::$acf_schema[$key])) {
|
|
self::set_acf_field($post_id, $key, $value, self::$acf_schema);
|
|
} else {
|
|
update_post_meta($post_id, $key, $value);
|
|
}
|
|
}
|
|
return $post_id;
|
|
}
|
|
// Création classique
|
|
$post_id = wp_insert_post([
|
|
'post_type' => 'traducteur',
|
|
'post_title' => $data['nom'] . ' ' . $data['prenom'],
|
|
'post_status' => 'publish',
|
|
]);
|
|
if (is_wp_error($post_id)) {
|
|
if ($as_rest) {
|
|
return [
|
|
'success' => false,
|
|
'code' => 500,
|
|
'message' => 'Erreur lors de la création',
|
|
'data' => json_encode($post_id)
|
|
];
|
|
}
|
|
return $post_id;
|
|
}
|
|
foreach ($data as $key => $value) {
|
|
if ($key === 'organisme') {
|
|
if (!taxonomy_exists('organisme')) {
|
|
register_taxonomy(
|
|
'organisme',
|
|
'traducteur',
|
|
[
|
|
'label' => 'Organismes',
|
|
'public' => false,
|
|
'hierarchical' => false,
|
|
'show_ui' => true,
|
|
'show_in_rest' => false,
|
|
]
|
|
);
|
|
}
|
|
$org_ids = [];
|
|
$organismes = is_array($value) ? $value : explode('|', $value);
|
|
foreach ($organismes as $org_nom) {
|
|
$org_nom = trim($org_nom);
|
|
if (empty($org_nom)) continue;
|
|
$slug = sanitize_title($org_nom);
|
|
$term = get_term_by('slug', $slug, 'organisme');
|
|
if (!$term) {
|
|
$result = wp_insert_term($org_nom, 'organisme', ['slug' => $slug]);
|
|
if (!is_wp_error($result) && isset($result['term_id'])) {
|
|
$org_ids[] = $result['term_id'];
|
|
}
|
|
} else {
|
|
$org_ids[] = $term->term_id;
|
|
}
|
|
}
|
|
if (!empty($org_ids)) {
|
|
update_field('organisme', $org_ids, $post_id);
|
|
}
|
|
} else if ($key === 'langues_parlees') {
|
|
if (!taxonomy_exists('langue')) {
|
|
register_taxonomy(
|
|
'langue',
|
|
'traducteur',
|
|
[
|
|
'label' => 'Langues',
|
|
'public' => false,
|
|
'hierarchical' => false,
|
|
'show_ui' => true,
|
|
'show_in_rest' => false,
|
|
]
|
|
);
|
|
}
|
|
$langue_ids = [];
|
|
$langues = is_array($value) ? $value : explode('|', $value);
|
|
foreach ($langues as $slug) {
|
|
$slug = trim($slug);
|
|
if (empty($slug)) continue;
|
|
$term = get_term_by('slug', $slug, 'langue');
|
|
if (!$term) {
|
|
$nom = self::$lg_map[$slug] ?? $slug;
|
|
$result = wp_insert_term($nom, 'langue', ['slug' => $slug]);
|
|
if (!is_wp_error($result) && isset($result['term_id'])) {
|
|
$langue_ids[] = $result['term_id'];
|
|
}
|
|
} else {
|
|
$langue_ids[] = $term->term_id;
|
|
}
|
|
}
|
|
if (!empty($langue_ids)) {
|
|
update_field('langue', $langue_ids, $post_id);
|
|
}
|
|
} else if (isset(self::$acf_schema[$key])) {
|
|
self::set_acf_field($post_id, $key, $value, self::$acf_schema);
|
|
} else {
|
|
update_post_meta($post_id, $key, $value);
|
|
}
|
|
}
|
|
return $post_id;
|
|
}
|
|
|
|
/**
|
|
* Mettre à jour un traducteur.
|
|
* @param int $id
|
|
* @param array $data
|
|
* @return bool|WP_Error
|
|
*/
|
|
public function update(int $id, array $data) {
|
|
if (!is_user_logged_in() || !get_current_user_id()) {
|
|
return Api_Helper::json_error('Authentification requise', 401);
|
|
}
|
|
if (!current_user_can('edit_posts')) {
|
|
return Api_Helper::json_error('Non autorisé', 403);
|
|
}
|
|
$post = get_post($id);
|
|
if (!$post || $post->post_type !== 'traducteur') {
|
|
return Api_Helper::json_error('Traducteur introuvable', 404);
|
|
}
|
|
// Vérifier unicité de l'email si modifié
|
|
if (!empty($data['email'])) {
|
|
$existing = get_posts([
|
|
'post_type' => 'traducteur',
|
|
'meta_key' => 'email',
|
|
'meta_value' => $data['email'],
|
|
'post_status'=> 'any',
|
|
'exclude' => [$id],
|
|
'numberposts'=> 1,
|
|
]);
|
|
if ($existing) {
|
|
return Api_Helper::json_error('Email déjà utilisé', 409);
|
|
}
|
|
}
|
|
$result = wp_update_post([
|
|
'ID' => $id,
|
|
'post_title' => ($data['nom'] ?? get_post_meta($id, 'nom', true)) . ' ' . ($data['prenom'] ?? get_post_meta($id, 'prenom', true)),
|
|
], true);
|
|
if (is_wp_error($result)) {
|
|
return $result;
|
|
}
|
|
foreach ($data as $key => $value) {
|
|
update_post_meta($id, $key, $value);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Supprimer un traducteur (corbeille WordPress)
|
|
* @param int $id
|
|
* @return bool|WP_Error
|
|
*/
|
|
public function delete(int $id) {
|
|
if (!is_user_logged_in() || !get_current_user_id()) {
|
|
return Api_Helper::json_error('Authentification requise', 401);
|
|
}
|
|
if (!current_user_can('delete_posts')) {
|
|
return Api_Helper::json_error('Non autorisé', 403);
|
|
}
|
|
$post = get_post($id);
|
|
if (!$post || $post->post_type !== 'traducteur') {
|
|
return Api_Helper::json_error('Traducteur introuvable', 404);
|
|
}
|
|
$result = wp_trash_post($id);
|
|
if (!$result) {
|
|
return Api_Helper::json_error('Erreur lors de la suppression', 500);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Vérifie s'il y a des conflits d'événements pour un traducteur donné
|
|
* @param int $traducteur_id
|
|
* @param string $date
|
|
* @param int|null $event_id - ID de l'événement à exclure (pour l'édition)
|
|
* @return array
|
|
*/
|
|
private static function verifier_conflits_traducteur($traducteur_id, $date, $event_id = null) {
|
|
global $wpdb;
|
|
|
|
$table_events = $wpdb->prefix . 'crvi_agenda';
|
|
|
|
$where_conditions = ['id_traducteur = %d'];
|
|
$where_values = [$traducteur_id];
|
|
|
|
// Exclure l'événement en cours d'édition si spécifié
|
|
if ($event_id) {
|
|
$where_conditions[] = 'id != %d';
|
|
$where_values[] = $event_id;
|
|
}
|
|
|
|
// Ajouter la condition de date
|
|
$where_conditions[] = 'date_rdv = %s';
|
|
$where_values[] = $date;
|
|
|
|
$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);
|
|
}
|
|
}
|