Subversion Repositories eFlore/Applications.coel

Rev

Rev 1711 | Rev 1728 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1497 jpm 1
<?php
2
/**
3
 * Service fournissant la liste des structures et leurs informations.
4
 *
1706 raphael 5
 * @author Raphaël Droz <raphael@tela-botanica.org>
1497 jpm 6
 * @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
1706 raphael 7
 * @Copyright (c) 2009, 2013 Tela Botanica (accueil@tela-botanica.org)
1497 jpm 8
 * @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
9
 * @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
10
 */
11
class CoelStructure extends Coel {
1693 raphael 12
 
1706 raphael 13
	static $optional_bool_fields = array(
14
		'cs_nbre_personne',
15
		'csc_mark_formation', 'csc_mark_formation_interet',
1713 raphael 16
		'csc_mark_collection_commune', 'csc_mark_acces_controle', 'csc_mark_restauration', 'csc_mark_traitement',
1706 raphael 17
		'csc_mark_acquisition_collection', 'csc_mark_acquisition_echantillon',
18
		'cs_latitude', 'cs_longitude',
19
	);
1693 raphael 20
 
1497 jpm 21
	// ATTENTION : tjrs garder la table principale en premier, puis mettre les tables spécialisées.
1711 raphael 22
	protected $tables = array(	120 => array(
23
		'nom' => 'coel_structure',
24
		'prefixe' => 'cs',
25
		'id' => array('cs_id_structure')),
26
	122 => array(
27
		'nom' => 'coel_structure_conservation',
28
		'prefixe' => 'csc',
29
		'id' => array('csc_id_structure')),
30
	123 => array(
31
		'nom' => 'coel_structure_valorisation',
32
		'prefixe' => 'csv',
33
		'id' => array('csv_id_structure')));
1497 jpm 34
 
35
	/**
36
	 * Méthode principale appelée avec une requête de type GET.
37
	 */
38
	public function getElement($param = array()) {
39
		// Initialisation des variables
40
		$info = array();
41
 
42
		// Nour recherchons le type de requête demandé
43
		$type = $param[0];
44
 
45
		if ($type == '*' || is_numeric($type)) {
1673 raphael 46
			// Pré traitement des paramêtres
47
			$p = $this->traiterParametresUrl(array('id_projet', 'id_structure', 'recherche'), $param);
48
			$info = $this->getElementParDefaut($p);
1497 jpm 49
		} else {
50
			$methode = 'getElement'.$type;
51
			if (method_exists($this, $methode)) {
52
				array_shift($param);
53
				$info = $this->$methode($param);
54
			} else {
55
				$this->messages[] = "Le type d'information demandé '$type' n'est pas disponible.";
56
			}
57
		}
58
 
59
		// Envoie sur la sortie standard
60
		$this->envoyer($info);
61
	}
62
 
63
	/**
64
	 * Méthode par défaut pour garder la compatibilité avec Coel.
65
	 * Appelée avec les paramêtres d'url suivant :
66
	 * /CoelStructure/_/_/_
67
	 * ou les _ représentent dans l'ordre : id_projet, id_structure et nom
68
	 * Si un des paramêtres est abscent, il prendre la valeur *
69
	 */
1673 raphael 70
	public function getElementParDefaut($p) {
1497 jpm 71
		// Initialisation des variables
72
		$info = array();
1673 raphael 73
 
1706 raphael 74
		$whereClause = array();
75
		if(isset($p['id_projet'])) $whereClause[] = "cs_ce_projet = {$p['id_projet']}";
76
		if(isset($p['id_structure'])) $whereClause[] = "cs_id_structure = {$p['id_structure']}";
1688 raphael 77
 
1706 raphael 78
		if(isset($p['recherche'])) {
1711 raphael 79
			if(@$this->searchCity && trim($this->searchCity) == 'true') {
1706 raphael 80
				$whereClause[] = "(" . implode(" OR ", array("cs_nom LIKE {$p['recherche']}", "cs_ville LIKE {$p['recherche']}")) . ")";
81
			} else {
82
				$whereClause[] = "cs_nom LIKE {$p['recherche']}";
83
			}
84
		}
1688 raphael 85
 
1497 jpm 86
		// Construction de la requête
1688 raphael 87
		$requete = sprintf(
1706 raphael 88
			'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 '
89
			. ' FROM coel_structure AS cs '
90
			. ' LEFT JOIN coel_meta_historique_ligne ON (cs_ce_meta = cmhl_id_historique_ligne) '
91
			. ' LEFT JOIN coel_structure_conservation AS csc ON (cs_id_structure = csc_id_structure) '
92
			. ' LEFT JOIN coel_structure_valorisation AS csv ON (cs_id_structure = csv_id_structure) '
93
			. ' WHERE %s ORDER BY %s LIMIT %d, %d -- %s:%d',
1497 jpm 94
 
1706 raphael 95
			$this->distinct ? 'DISTINCT' : '',
96
			$whereClause ? implode(" AND ", $whereClause) : TRUE,
97
			is_null($this->orderby) ? 'cs.cs_nom ASC' : $this->orderby,
98
			$this->start, $this->limit,
99
			__FILE__, __LINE__);
1688 raphael 100
 
1706 raphael 101
		// Récupération des résultats
1497 jpm 102
		try {
103
			// SPÉCIAL :
104
			// Lorsqu'on cherche une seule structure avec un id passé en paramêtre, nous devons renvoyer un objet
1595 aurelien 105
			$donnees = ($this->formatRetour == 'objet' && isset($p['id_structure'])) ? $this->bdd->query($requete)->fetch(PDO::FETCH_OBJ) : $this->bdd->query($requete)->fetchAll(PDO::FETCH_ASSOC);
1497 jpm 106
			if ($donnees === false) {
107
				$this->messages[] = "La requête a retourné aucun résultat.";
108
			}
1689 raphael 109
 
1706 raphael 110
			// l'UI java n'aime pas les NULL
111
			if(!is_array($donnees)) {
112
				// $donnees est un objet PHP
113
				array_walk($donnees, create_function('&$val', '$val = is_null($val) ? "" : $val;'));
114
			}
115
			else {
116
				// $donnees est un tableau d'objets PHP
117
				foreach($donnees as &$structure) {
118
					array_walk($structure, create_function('&$val', '$val = is_null($val) ? "" : $val;'));
119
				}
120
			}
1689 raphael 121
 
1688 raphael 122
			$elements_nbre = $this->bdd->query("SELECT FOUND_ROWS() AS c")->fetch(PDO::FETCH_ASSOC);
1689 raphael 123
			$info['nbElements'] = intval($elements_nbre['c']);
1497 jpm 124
			$info['structures'] = $donnees;
125
		} catch (PDOException $e) {
126
			$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage());
127
		}
128
 
129
		return $info;
130
	}
131
 
132
	/* Méthode pour récupérer le nombre de structure par zone géographique.
133
	 * Appelée avec les paramêtres d'url suivant :
134
	 * /CoelStructure/ParZoneGeo/_
135
	 * ou les _ représentent dans l'ordre : type.
136
	 * ou type peut valoir: FRD (= département français)
137
	 * Si un des paramêtres est abscent, il prendre la valeur *
138
	 */
139
	public function getElementParZoneGeo($param) {
140
		// Pré traitement des paramêtres
1581 jpm 141
		$p = $this->traiterParametresUrl(array('type', 'projets'), $param);
1497 jpm 142
		if (!isset($p['type'])) {
143
			$this->messages[] = "Il est obligatoire d'indiquer type de recherche pour utiliser ce service.";
1711 raphael 144
			return array();
1497 jpm 145
		}
1693 raphael 146
 
1711 raphael 147
		// Construction de la requête
148
		$requete =	(($this->distinct) ? 'SELECT DISTINCT' : 'SELECT').' '.
149
			'	IF ( SUBSTRING( cs_code_postal FROM 1 FOR 2 ) >= 96, '.
150
			'		SUBSTRING( cs_code_postal FROM 1 FOR 3 ), '.
151
			'		SUBSTRING( cs_code_postal FROM 1 FOR 2 ) ) AS id, '.
152
			'	COUNT( cs_id_structure ) AS nbre '.
153
			'FROM coel_structure '.
154
			'WHERE cs_ce_truk_pays = 2654 '.
155
			(isset($p['projets']) ? "	AND cs_ce_projet IN ({$p['projets']}) " : '').
156
			'GROUP BY IF ( SUBSTRING( cs_code_postal FROM 1 FOR 2 ) >= 96, '.
157
			'	SUBSTRING( cs_code_postal FROM 1 FOR 3 ), '.
158
			'	SUBSTRING( cs_code_postal FROM 1 FOR 2 ) ) '.
159
			'ORDER BY '.((!is_null($this->orderby)) ? $this->orderby  : 'id ASC').' ';
1693 raphael 160
 
1711 raphael 161
		// Récupération des résultats
162
		try {
163
			$donnees = $this->bdd->query($requete)->fetchAll(PDO::FETCH_ASSOC);
164
		} catch (PDOException $e) {
165
			$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage());
166
		}
1706 raphael 167
 
1711 raphael 168
		if ($donnees === false) {
169
			$this->messages[] = "La requête a retourné aucun résultat.";
170
			return array();
171
		}
1706 raphael 172
 
1711 raphael 173
		$info = array();
174
		foreach ($donnees as $donnee) {
175
			$info[$donnee['id']] = $donnee['nbre'];
176
		}
177
		return $info;
178
	}
1706 raphael 179
 
180
 
181
	static function NULLifNotNum(&$params, $keys_to_null) {
182
		foreach($keys_to_null as $v) {
183
			if(array_key_exists($v, $params) && !is_numeric($params[$v])) {
184
				$params[$v] = NULL;
185
			}
186
		}
187
	}
1497 jpm 188
 
189
	/**
190
	 * Méthode appelée pour ajouter un élément.
191
	 */
192
	public function createElement($params) {
193
		// Identification de l'utilisateur
194
		list($id_utilisateur, $id_session) = $this->getIdentification($params);
1648 raphael 195
 
1497 jpm 196
		// Contrôle du non détournement de l'utilisateur
1648 raphael 197
		if (!$this->etreAutorise($id_utilisateur)) {
198
			$this->envoyer();
199
			return;
200
		}
201
		try {
1709 raphael 202
			$form_needs_refresh = self::callNominatim($params, $this->bdd);
1649 raphael 203
 
1706 raphael 204
			self::NULLifNotNum($params, self::$optional_bool_fields);
1693 raphael 205
 
1706 raphael 206
			// Vérification des tables à vraiment mettre à jour en fonction des données passées.
1648 raphael 207
			$tables_a_modifier = $this->recupererTablesAModifier($params);
208
			reset($tables_a_modifier);
1649 raphael 209
 
1648 raphael 210
			$id_structure = null;
211
			while (list($table_id, $table) = each($tables_a_modifier)) {
212
				if (is_null($table['champs'])) continue;
213
				if ($this->avoirCleComplete($table)) {
214
					$this->mettreAJourAvecCle($id_utilisateur, $id_session, $table_id, $table);
215
					continue;
216
				}
217
 
218
				// Ajout des données à la table des données
219
				$id_structure = $this->ajouter($table);
220
				if ($id_structure === false) continue;
221
 
222
				$table['champs_valeurs_id']['cs_id_structure'] = $id_structure;
223
				$table['champs_valeurs_brut']['cs_id_structure'] = $id_structure;
224
				$tables_a_modifier[122]['champs_valeurs_id']['csc_id_structure'] = $id_structure;
225
				$tables_a_modifier[122]['champs_valeurs_brut']['csc_id_structure'] = $id_structure;
226
				$tables_a_modifier[122]['champs_valeurs_protege']['csc_id_structure'] = $this->bdd->quote($id_structure);
227
				$tables_a_modifier[123]['champs_valeurs_id']['csv_id_structure'] = $id_structure;
228
				$tables_a_modifier[123]['champs_valeurs_brut']['csv_id_structure'] = $id_structure;
229
				$tables_a_modifier[123]['champs_valeurs_protege']['csv_id_structure'] = $this->bdd->quote($id_structure);
1649 raphael 230
 
1648 raphael 231
				// Historisation (Ajout des méta-données)
232
				$etat = 1; // Ajout
233
				$cle = $this->recupererCle($table);
234
				$info = $this->creerXmlHisto($table['champs_valeurs_brut']);
235
				$id_meta = $this->historiser($table_id, $cle, $info, $id_utilisateur, $etat, $id_session);
1497 jpm 236
 
1648 raphael 237
				// Liaison de la table des données à ses méta-données
238
				$champ_meta = "{$table['prefixe']}_ce_meta";
239
				$table['champs_valeurs_protege'] = array($champ_meta => $id_meta);
240
				$this->modifier($table);
241
			}
242
 
243
			if(isset($params['cpr_abreviation']) && !empty($params['cpr_abreviation'])) {
1497 jpm 244
				$this->ajouterGuid($params['cpr_abreviation'], $id_structure);
245
			}
1648 raphael 246
		} catch (PDOException $e) {
247
			$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
248
		}
1708 raphael 249
 
250
		if($form_needs_refresh) { // coordonnées mises à jour en DB: en informer le formulaire
251
			$this->envoyer($this->getElementParDefaut(array('id_structure' => $id_structure)));
252
			exit;
253
		}
254
 
1669 raphael 255
		$this->envoyer($id_structure);
1497 jpm 256
	}
1706 raphael 257
 
1497 jpm 258
	/**
259
	 * Méthode appelée pour mettre à jour un élément
260
	 */
261
	public function updateElement($uid, $params) {
262
		// Vérification de la présence des id passés par l'url
263
		if (!isset($uid[0])) {
264
			$this->messages[] = "Identifiant de structure manquant. Vous ne devriez pas avoir accès à ce service.";
1648 raphael 265
			$this->envoyer();
266
			return;
267
		}
268
 
269
		// Identification de l'utilisateur
270
		list($id_utilisateur, $id_session) = $this->getIdentification($params);
271
		// Contrôle du non détournement de l'utilisateur
272
		if (!$this->etreAutorise($id_utilisateur)) {
273
			$this->envoyer();
274
			return;
275
		}
276
		try {
1709 raphael 277
			$form_needs_refresh = self::callNominatim($params, $this->bdd);
1651 raphael 278
 
1706 raphael 279
			self::NULLifNotNum($params, self::$optional_bool_fields);
1693 raphael 280
 
1648 raphael 281
			// Vérification des tables à vraiment mettre à jour en fonction des données passées.
282
			$tables_a_modifier = $this->recupererTablesAModifier($params);
283
			// Pour chaque table du module nous lançons si nécessaire l'historisation puis la mise à jour
284
			foreach ($tables_a_modifier as $table_id => $table) {
1670 raphael 285
				if(@$table['nom'] == 'coel_structure' && !$this->avoirCleComplete($table)) {
286
					error_log("tentative d'UPDATE sans contrainte de WHERE, \$table = " . print_r($table, TRUE));
1669 raphael 287
					continue; // ne pas mettre à jour sans contrainte de WHERE
288
				}
1648 raphael 289
				$this->mettreAJourAvecCle($id_utilisateur, $id_session, $table_id, $table);
290
			}
291
		} catch (PDOException $e) {
292
			$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
1497 jpm 293
		}
1651 raphael 294
 
1497 jpm 295
		// Envoie sur la sortie standard
1651 raphael 296
 
297
		if($form_needs_refresh) { // coordonnées mises à jour en DB: en informer le formulaire (si resté ouvert)
1673 raphael 298
			$this->envoyer($this->getElementParDefaut(array('id_structure' => $uid[0])));
299
			exit;
1651 raphael 300
		}
301
		$this->envoyer(); // OK par défaut
1497 jpm 302
	}
303
 
304
	/**
305
	 * Méthode appelée pour supprimer un élément
306
	 */
307
	public function deleteElement($uid) {
1669 raphael 308
		// NOTES : une structure ne peut pas être supprimée si elle possède des collections liées.
1497 jpm 309
		// Vérification de la présence des id passés par l'url
310
		if (!isset($uid[0]) || !isset($uid[1])) {
1711 raphael 311
			$this->messages[] = "Identifiant de structure ou d'utilisateur manquant. Vous ne devriez pas avoir accès à ce service.";
312
			$this->envoyer();
313
			return;
1706 raphael 314
		}
315
 
1711 raphael 316
		// Identification de l'utilisateur
317
		list($id_utilisateur, $id_session) = $this->getIdentification($uid[0]);
318
		// Contrôle du non détournement de l'utilisateur
319
		if (! $this->etreAutorise($id_utilisateur)) {
320
			$this->envoyer();
321
			return;
322
		}
1706 raphael 323
 
1711 raphael 324
		// Récupération des id passés par l'url
325
		$identifiants = explode(',', rtrim($uid[1], ','));
1706 raphael 326
 
1711 raphael 327
		if (count($identifiants) == 0) {
328
			$this->messages[] = "Aucun enregistrement n'a été supprimé.";
329
			$this->envoyer();
330
			return;
331
		}
1497 jpm 332
 
1711 raphael 333
		try {
334
			foreach ($identifiants as $id_structure) {
335
				// Vérification que la structure ne possède pas de collections liées
336
				if (self::verifierPresenceCollection($this->bdd, $id_structure)) {
337
					$this->messages[] = "La structure '$id_structure' ne peut pas être supprimée car elle possède des collections liées.";
338
					continue;
339
				}
1706 raphael 340
 
1711 raphael 341
				$params = array('cs_id_structure' => $id_structure, 'csc_id_structure' => $id_structure, 'csv_id_structure' => $id_structure);
342
				$tables_a_modifier = $this->recupererTablesAModifier($params);
1497 jpm 343
 
1711 raphael 344
				foreach ($tables_a_modifier as $table_id => $table) {
345
					if (! $this->avoirEnregistrement($table)) continue;
1497 jpm 346
 
1711 raphael 347
					if ($this->supprimer($table) === true) {
348
						// Historisation (Ajout des méta-données)
349
						$cle = $this->recupererCle($table);
350
						$this->historiser($table_id, $cle, 'NULL', $id_utilisateur, 3, $id_session);
351
					}
352
				}
353
			}
354
		} catch (PDOException $e) {
355
			$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
356
		}
1706 raphael 357
 
1497 jpm 358
		// Envoie sur la sortie standard
359
		$this->envoyer();
360
	}
361
 
1706 raphael 362
	static function verifierPresenceCollection($db, $id_structure) {
1497 jpm 363
		// Vérification que la structure ne possède pas de collections liées
1711 raphael 364
		return ($db->query(sprintf(
365
			'SELECT COUNT(cc_id_collection) AS nbre_collection FROM coel_collection ' .
366
			' WHERE cc_ce_structure = %d GROUP BY cc_ce_structure ',
367
			$id_structure))->fetchColumn() != 0);
1706 raphael 368
	}
369
 
1709 raphael 370
	static function callNominatim(&$params, $db = NULL) {
1711 raphael 371
		// lon/lat déjà saisies ?
1709 raphael 372
		if (@$params['cs_latitude'] && @$params['cs_longitude']) return FALSE;
373
 
1711 raphael 374
		// ni adresse, ni CP, ni ville ? rien n'est possible
375
		if (!@$params['cs_adresse_01'] && !@$params['cs_code_postal'] && !@$params['cs_ville']) return FALSE;
1709 raphael 376
 
1711 raphael 377
		$lonlat = array();
378
		if(Coel::coordGuess(Coel::addrReStruct($params, $db), $lonlat)) {
379
			$params['cs_latitude'] = $lonlat['lat'];
380
			$params['cs_longitude'] = $lonlat['lon'];
381
			return TRUE;
382
		}
1709 raphael 383
 
1711 raphael 384
		// second guess, sans code postal
385
		if(@$params['cs_code_postal']) {
386
			$params2 = $params;
387
			unset($params2['cs_code_postal']);
388
			if(Coel::coordGuess(Coel::addrReStruct($params2, $db), $lonlat)) {
389
				$params['cs_latitude'] = $lonlat['lat'];
390
				$params['cs_longitude'] = $lonlat['lon'];
391
				return TRUE;
392
			}
393
		}
1706 raphael 394
		return FALSE;
1497 jpm 395
	}
396
 
397
	private function ajouterGuid($abr_projet, $id_structure) {
398
		if ($id_structure !== false) {
1708 raphael 399
			$table_guid = $this->tables[120];
1497 jpm 400
			$table_guid['champs_valeurs_id']['cs_id_structure'] = $id_structure;
401
			$table_guid['champs_valeurs_protege']['cs_guid'] = $this->bdd->quote(sprintf($this->config['coel']['guid'], $abr_projet, 'str'.$id_structure));
402
			$this->modifier($table_guid);
403
		}
404
	}
405
}