* @author Jean-Pascal MILCENT * @Copyright (c) 2009, 2013 Tela Botanica (accueil@tela-botanica.org) * @license GPL v3 * @license CECILL v2 */ class CoelStructure extends Coel { static $optional_bool_fields = array( 'cs_nbre_personne', 'csc_mark_formation', 'csc_mark_formation_interet', 'csc_mark_collection_commune', 'csc_mark_acces_controle', 'csc_mark_restauration', 'csc_mark_traitement', 'csc_mark_acquisition_collection', 'csc_mark_acquisition_echantillon', 'csv_mark_action', 'csv_mark_action_future', 'csv_mark_recherche', 'csv_mark_acces_ss_motif', 'csv_mark_visite_avec_motif', 'cs_latitude', 'cs_longitude', ); // ATTENTION : tjrs garder la table principale en premier, puis mettre les tables spécialisées. protected $tables = array( 120 => array( 'nom' => 'coel_structure', 'prefixe' => 'cs', 'id' => array('cs_id_structure')), 122 => array( 'nom' => 'coel_structure_conservation', 'prefixe' => 'csc', 'id' => array('csc_id_structure')), 123 => array( 'nom' => 'coel_structure_valorisation', 'prefixe' => 'csv', 'id' => array('csv_id_structure'))); /** * Méthode principale appelée avec une requête de type GET. */ public function getElement($param = array()) { // Initialisation des variables $info = array(); // Nour recherchons le type de requête demandé $type = $param[0]; if ($type == '*' || is_numeric($type)) { // Pré traitement des paramêtres $p = $this->traiterParametresUrl(array('id_projet', 'id_structure', 'recherche'), $param); $info = $this->getElementParDefaut($p); } else { $methode = 'getElement'.$type; if (method_exists($this, $methode)) { array_shift($param); $info = $this->$methode($param); } else { $this->messages[] = "Le type d'information demandé '$type' n'est pas disponible."; } } // Envoie sur la sortie standard $this->envoyer($info); } /** * Méthode par défaut pour garder la compatibilité avec Coel. * Appelée avec les paramêtres d'url suivant : * /CoelStructure/_/_/_ * ou les _ représentent dans l'ordre : id_projet, id_structure et nom * Si un des paramêtres est abscent, il prendre la valeur * */ public function getElementParDefaut($p) { // Initialisation des variables $info = array(); $whereClause = array(); if(isset($p['id_projet'])) $whereClause[] = "cs_ce_projet = {$p['id_projet']}"; if(isset($p['id_structure'])) $whereClause[] = "cs_id_structure = {$p['id_structure']}"; if(isset($p['recherche'])) { if(@$this->searchCity && trim($this->searchCity) == 'true') { $whereClause[] = "(" . implode(" OR ", array("cs_nom LIKE {$p['recherche']}", "cs_ville LIKE {$p['recherche']}")) . ")"; } else { $whereClause[] = "cs_nom LIKE {$p['recherche']}"; } } // Construction de la requête $requete = sprintf( 'SELECT SQL_CALC_FOUND_ROWS %s cs.*, csc.*, csv.*, cmhl_date_modification, cmhl_notes, cmhl_source, cmhl_ce_modifier_par, cmhl_ce_etat, cmhl_ip ' . ' FROM coel_structure AS cs ' . ' LEFT JOIN coel_meta_historique_ligne ON (cs_ce_meta = cmhl_id_historique_ligne) ' . ' LEFT JOIN coel_structure_conservation AS csc ON (cs_id_structure = csc_id_structure) ' . ' LEFT JOIN coel_structure_valorisation AS csv ON (cs_id_structure = csv_id_structure) ' . ' WHERE %s ORDER BY %s LIMIT %d, %d -- %s:%d', $this->distinct ? 'DISTINCT' : '', $whereClause ? implode(" AND ", $whereClause) : TRUE, is_null($this->orderby) ? 'cs.cs_nom ASC' : $this->orderby, $this->start, $this->limit, __FILE__, __LINE__); // Récupération des résultats try { // SPÉCIAL : // Lorsqu'on cherche une seule structure avec un id passé en paramêtre, nous devons renvoyer un objet $donnees = ($this->formatRetour == 'objet' && isset($p['id_structure'])) ? $this->bdd->query($requete)->fetch(PDO::FETCH_OBJ) : $this->bdd->query($requete)->fetchAll(PDO::FETCH_ASSOC); if ($donnees === false) { $this->messages[] = "La requête a retourné aucun résultat."; } // l'UI java n'aime pas les NULL if(!is_array($donnees)) { // $donnees est un objet PHP array_walk($donnees, create_function('&$val', '$val = is_null($val) ? "" : $val;')); } else { // $donnees est un tableau d'objets PHP foreach($donnees as &$structure) { array_walk($structure, create_function('&$val', '$val = is_null($val) ? "" : $val;')); } } $elements_nbre = $this->bdd->query("SELECT FOUND_ROWS() AS c")->fetch(PDO::FETCH_ASSOC); $info['nbElements'] = intval($elements_nbre['c']); $info['structures'] = $donnees; } catch (PDOException $e) { $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage()); } return $info; } /* Méthode pour récupérer le nombre de structure par zone géographique. * Appelée avec les paramêtres d'url suivant : * /CoelStructure/ParZoneGeo/_ * ou les _ représentent dans l'ordre : type. * ou type peut valoir: FRD (= département français) * Si un des paramêtres est abscent, il prendre la valeur * */ public function getElementParZoneGeo($param) { // Pré traitement des paramêtres $p = $this->traiterParametresUrl(array('type', 'projets'), $param); if (!isset($p['type'])) { $this->messages[] = "Il est obligatoire d'indiquer type de recherche pour utiliser ce service."; return array(); } // Construction de la requête $requete = (($this->distinct) ? 'SELECT DISTINCT' : 'SELECT').' '. ' IF ( SUBSTRING( cs_code_postal FROM 1 FOR 2 ) >= 96, '. ' SUBSTRING( cs_code_postal FROM 1 FOR 3 ), '. ' SUBSTRING( cs_code_postal FROM 1 FOR 2 ) ) AS id, '. ' COUNT( cs_id_structure ) AS nbre '. 'FROM coel_structure '. 'WHERE cs_ce_truk_pays = 2654 '. (isset($p['projets']) ? " AND cs_ce_projet IN ({$p['projets']}) " : ''). 'GROUP BY IF ( SUBSTRING( cs_code_postal FROM 1 FOR 2 ) >= 96, '. ' SUBSTRING( cs_code_postal FROM 1 FOR 3 ), '. ' SUBSTRING( cs_code_postal FROM 1 FOR 2 ) ) '. 'ORDER BY '.((!is_null($this->orderby)) ? $this->orderby : 'id ASC').' '; // Récupération des résultats try { $donnees = $this->bdd->query($requete)->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage()); } if ($donnees === false) { $this->messages[] = "La requête a retourné aucun résultat."; return array(); } $info = array(); foreach ($donnees as $donnee) { $info[$donnee['id']] = $donnee['nbre']; } return $info; } static function NULLifNotNum(&$params, $keys_to_null) { foreach($keys_to_null as $v) { if(! array_key_exists($v, $params)) continue; if(!is_numeric($params[$v]) && ! preg_match('/^-?\d+,\d*$/', $params[$v])) { $params[$v] = NULL; } else { $params[$v] = str_replace(',', '.', $params[$v]); } } } /** * Méthode appelée pour ajouter un élément. */ public function createElement($params) { // Identification de l'utilisateur list($id_utilisateur, $id_session) = $this->getIdentification($params); // Contrôle du non détournement de l'utilisateur if (!$this->etreAutorise($id_utilisateur)) { $this->envoyer(); return; } try { $form_needs_refresh = self::callNominatim($params, $this->bdd); self::NULLifNotNum($params, self::$optional_bool_fields); // Vérification des tables à vraiment mettre à jour en fonction des données passées. $tables_a_modifier = $this->recupererTablesAModifier($params); reset($tables_a_modifier); $id_structure = null; while (list($table_id, $table) = each($tables_a_modifier)) { if (is_null($table['champs'])) continue; if ($this->avoirCleComplete($table)) { $this->mettreAJourAvecCle($id_utilisateur, $id_session, $table_id, $table); continue; } // Ajout des données à la table des données $id_structure = $this->ajouter($table); if ($id_structure === false) continue; $table['champs_valeurs_id']['cs_id_structure'] = $id_structure; $table['champs_valeurs_brut']['cs_id_structure'] = $id_structure; $tables_a_modifier[122]['champs_valeurs_id']['csc_id_structure'] = $id_structure; $tables_a_modifier[122]['champs_valeurs_brut']['csc_id_structure'] = $id_structure; $tables_a_modifier[122]['champs_valeurs_protege']['csc_id_structure'] = $this->bdd->quote($id_structure); $tables_a_modifier[123]['champs_valeurs_id']['csv_id_structure'] = $id_structure; $tables_a_modifier[123]['champs_valeurs_brut']['csv_id_structure'] = $id_structure; $tables_a_modifier[123]['champs_valeurs_protege']['csv_id_structure'] = $this->bdd->quote($id_structure); // Historisation (Ajout des méta-données) $etat = 1; // Ajout $cle = $this->recupererCle($table); $info = $this->creerXmlHisto($table['champs_valeurs_brut']); $id_meta = $this->historiser($table_id, $cle, $info, $id_utilisateur, $etat, $id_session); // Liaison de la table des données à ses méta-données $champ_meta = "{$table['prefixe']}_ce_meta"; $table['champs_valeurs_protege'] = array($champ_meta => $id_meta); $this->modifier($table); } if(isset($params['cpr_abreviation']) && !empty($params['cpr_abreviation'])) { $this->ajouterGuid($params['cpr_abreviation'], $id_structure); } } catch (PDOException $e) { $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); } if($form_needs_refresh) { // coordonnées mises à jour en DB: en informer le formulaire $this->envoyer($this->getElementParDefaut(array('id_structure' => $id_structure))); exit; } $this->envoyer($id_structure); } /** * Méthode appelée pour mettre à jour un élément */ public function updateElement($uid, $params) { // Vérification de la présence des id passés par l'url if (!isset($uid[0])) { $this->messages[] = "Identifiant de structure manquant. Vous ne devriez pas avoir accès à ce service."; $this->envoyer(); return; } // Identification de l'utilisateur list($id_utilisateur, $id_session) = $this->getIdentification($params); // Contrôle du non détournement de l'utilisateur if (!$this->etreAutorise($id_utilisateur)) { $this->envoyer(); return; } try { $form_needs_refresh = self::callNominatim($params, $this->bdd); self::NULLifNotNum($params, self::$optional_bool_fields); // Vérification des tables à vraiment mettre à jour en fonction des données passées. $tables_a_modifier = $this->recupererTablesAModifier($params); // Pour chaque table du module nous lançons si nécessaire l'historisation puis la mise à jour foreach ($tables_a_modifier as $table_id => $table) { if(@$table['nom'] == 'coel_structure' && !$this->avoirCleComplete($table)) { error_log("tentative d'UPDATE sans contrainte de WHERE, \$table = " . print_r($table, TRUE)); continue; // ne pas mettre à jour sans contrainte de WHERE } $this->mettreAJourAvecCle($id_utilisateur, $id_session, $table_id, $table); } } catch (PDOException $e) { $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); } // Envoie sur la sortie standard if($form_needs_refresh) { // coordonnées mises à jour en DB: en informer le formulaire (si resté ouvert) $this->envoyer($this->getElementParDefaut(array('id_structure' => $uid[0]))); exit; } $this->envoyer(); // OK par défaut } /** * Méthode appelée pour supprimer un élément */ public function deleteElement($uid) { // NOTES : une structure ne peut pas être supprimée si elle possède des collections liées. // Vérification de la présence des id passés par l'url if (!isset($uid[0]) || !isset($uid[1])) { $this->messages[] = "Identifiant de structure ou d'utilisateur manquant. Vous ne devriez pas avoir accès à ce service."; $this->envoyer(); return; } // Identification de l'utilisateur list($id_utilisateur, $id_session) = $this->getIdentification($uid[0]); // Contrôle du non détournement de l'utilisateur if (! $this->etreAutorise($id_utilisateur)) { $this->envoyer(); return; } // Récupération des id passés par l'url $identifiants = explode(',', rtrim($uid[1], ',')); if (count($identifiants) == 0) { $this->messages[] = "Aucun enregistrement n'a été supprimé."; $this->envoyer(); return; } try { foreach ($identifiants as $id_structure) { // Vérification que la structure ne possède pas de collections liées if (self::verifierPresenceCollection($this->bdd, $id_structure)) { $this->messages[] = "La structure '$id_structure' ne peut pas être supprimée car elle possède des collections liées."; continue; } $params = array('cs_id_structure' => $id_structure, 'csc_id_structure' => $id_structure, 'csv_id_structure' => $id_structure); $tables_a_modifier = $this->recupererTablesAModifier($params); foreach ($tables_a_modifier as $table_id => $table) { if (! $this->avoirEnregistrement($table)) continue; if ($this->supprimer($table) === true) { // Historisation (Ajout des méta-données) $cle = $this->recupererCle($table); $this->historiser($table_id, $cle, 'NULL', $id_utilisateur, 3, $id_session); } } } } catch (PDOException $e) { $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); } // Envoie sur la sortie standard $this->envoyer(); } static function verifierPresenceCollection($db, $id_structure) { // Vérification que la structure ne possède pas de collections liées return ($db->query(sprintf( 'SELECT COUNT(cc_id_collection) AS nbre_collection FROM coel_collection ' . ' WHERE cc_ce_structure = %d GROUP BY cc_ce_structure ', $id_structure))->fetchColumn() != 0); } static function callNominatim(&$params, $db = NULL) { // lon/lat déjà saisies ? if (@$params['cs_latitude'] && @$params['cs_longitude']) return FALSE; // ni adresse, ni CP, ni ville ? rien n'est possible if (!@$params['cs_adresse_01'] && !@$params['cs_code_postal'] && !@$params['cs_ville']) return FALSE; $lonlat = array(); if(Coel::coordGuess(Coel::addrReStruct($params, $db), $lonlat)) { $params['cs_latitude'] = $lonlat['lat']; $params['cs_longitude'] = $lonlat['lon']; return TRUE; } // second guess, sans code postal if(@$params['cs_code_postal']) { $params2 = $params; unset($params2['cs_code_postal']); if(Coel::coordGuess(Coel::addrReStruct($params2, $db), $lonlat)) { $params['cs_latitude'] = $lonlat['lat']; $params['cs_longitude'] = $lonlat['lon']; return TRUE; } } return FALSE; } private function ajouterGuid($abr_projet, $id_structure) { if ($id_structure !== false) { $table_guid = $this->tables[120]; $table_guid['champs_valeurs_id']['cs_id_structure'] = $id_structure; $table_guid['champs_valeurs_protege']['cs_guid'] = $this->bdd->quote(sprintf($this->config['coel']['guid'], $abr_projet, 'str'.$id_structure)); $this->modifier($table_guid); } } }