Subversion Repositories eFlore/Applications.cel

Rev

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

Rev Author Line No. Line
1654 aurelien 1
<?php
2461 jpm 2
// declare(encoding='UTF-8');
1656 raphael 3
/**
2461 jpm 4
 * Classe métier de mise en forme des groupes de colonnes pour les exports.
5
 *
6
 * @internal   Mininum PHP version : 5.2
7
 * @category   CEL
8
 * @package    Services
9
 * @subpackage Bibliothèques
10
 * @version    0.1
11
 * @author     Mathias CHOUET <mathias@tela-botanica.org>
12
 * @author     Raphaël Droz <raphael@tela-botania.org>
13
 * @author     Jean-Pascal MILCENT <jpm@tela-botanica.org>
14
 * @author     Aurelien PERONNET <aurelien@tela-botanica.org>
15
 * @license    GPL v3 <http://www.gnu.org/licenses/gpl.txt>
16
 * @license    CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
17
 * @copyright  1999-2014 Tela Botanica <accueil@tela-botanica.org>
18
 */
19
define('SEPARATEUR_IMAGES', ' / ');
20
define('PREFIX_CHAMPS_ETENDUS', 'ext:');
1835 raphael 21
// utilisé par formaterUrlUser() [ nécessaire pour le widget d'export)
22
define('USER_BASEURL', 'http://www.tela-botanica.org/profil:%d');
1656 raphael 23
 
2461 jpm 24
class FormateurGroupeColonne {
1656 raphael 25
 
1702 raphael 26
	// cache pour les données des fonctions
1656 raphael 27
	static $cache = Array();
28
 
1702 raphael 29
	// test sur la table cel_references, mis à TRUE si la table existe
30
	static $is_table = false;
31
 
1714 raphael 32
	// les groupes de champs utilisables
33
	static $fieldGroups = array(
34
		'standard',
35
		'avance',
36
		'etendu',
1835 raphael 37
		'baseflor',
38
		'auteur'
1714 raphael 39
	);
40
 
1702 raphael 41
	// les données baseflor à récupérer: colonnes présentes dans cel_references
42
	// et intitulés associés
43
	static $baseflor_col = array(
2461 jpm 44
		've_lumiere' => 'Lumière',
45
		've_temperature' => 'Température',
46
		've_continentalite' => 'Continentalité',
47
		've_humidite_atmos' => 'Humidité Atmosphérique',
48
		've_humidite_edaph' => 'Humidité',
49
		've_reaction_sol' => 'Réaction (pH)',
50
		've_nutriments_sol' => 'Nutriments',
51
		've_salinite' => 'Salinité',
52
		've_texture_sol' => 'Texture' ,
53
		've_mat_org_sol' => 'Matière Organique',
54
		'catminat_code' => 'Code Catminat',
55
		'syntaxon' => 'Syntaxon',
1702 raphael 56
	);
57
 
1741 raphael 58
	// TODO: dirty, ordre des champs étendus... souhaité pour florilèges:
59
	static $ordre_champ_etendus_Florileges = array(
2461 jpm 60
		'personneStructure',
61
		'personneService',
62
		'personneFonction',
63
		'adresse',
64
		'latitudeDebutRue',
65
		'longitudeDebutRue',
66
		'latitudeFinRue',
67
		'longitudeFinRue',
68
		'typoUrbaine',
69
		'revetementSol',
70
		'presenceZoneVegetalise',
71
		'hauteurBatimentAvoisinant',
72
		'intensiteGestion',
73
		'periodiciteTraitementPhyto',
74
		'dateArretTraitementPhyto',
75
		'itineraireGestion',
76
		'dateDerniereIntervention',
77
		'hauteurPlante',
78
		'resistanceTraitementPhyto',
79
		'vitesseCroissance',
80
		'perceptionTechnicien',
81
		'perceptionRiverainMauvaise',
1741 raphael 82
	);
83
 
1714 raphael 84
	static function colGroupsValidation($groupe_de_champs = 'standard,avance') {
2461 jpm 85
		if (! $groupe_de_champs) return FALSE;
86
		if (is_string($groupe_de_champs)) {
1714 raphael 87
			$groupe_de_champs = array_flip(explode(',', $groupe_de_champs));
2461 jpm 88
		} elseif(is_array($groupe_de_champs)) {
1714 raphael 89
			$groupe_de_champs = array_flip($groupe_de_champs);
2461 jpm 90
		} else {
91
			return null;
1714 raphael 92
		}
2461 jpm 93
		$groupe_de_champs = array_intersect_key(array_flip(self::$fieldGroups), $groupe_de_champs);
94
		if (!$groupe_de_champs) {
95
			return false;
1714 raphael 96
		}
97
		// toujours ajouter standard
2461 jpm 98
		$groupe_de_champs['standard'] = true;
1714 raphael 99
		return implode(',', array_keys($groupe_de_champs));
100
	}
101
 
1654 aurelien 102
	/*
103
	* @param $fieldSets: un liste de noms de colonnes ou de sets de colonnes
104
	*		séparés par des virgules
105
	* 		eg: "espece" ou "champs-etendus", ...
106
	*
107
	* @return: un tableau associatif déjà ordonné
108
	* 		clé: abbrev [machine-name] de la colonne (eg: "espece" ou "mot-clef")
109
	* 		valeur: des données relative à cette colonne, cf GenColInfo
110
	*
1757 raphael 111
	* Si la colonne n'utilise pas de fonction de récupération particulière
112
	* (ie: si le champ exportés [ou importé] correspond exactement au champ dans la base de donnée)
113
	* Alors 'abbrev' doit avoir la même valeur que le nom de la colonne dans la table mysql `cel_obs`.
1654 aurelien 114
	*/
115
	static function nomEnsembleVersListeColonnes($groupe_de_champs = 'standard') {
2461 jpm 116
		if (! $groupe_de_champs) {
117
			$groupe_de_champs = 'standard';
118
		}
119
		if (is_string($groupe_de_champs)) {
1711 raphael 120
			$groupe_de_champs = array_flip(explode(',', $groupe_de_champs));
2461 jpm 121
		} elseif(is_array($groupe_de_champs)) {
1711 raphael 122
			$groupe_de_champs = array_flip($groupe_de_champs);
2461 jpm 123
		} else {
124
			return null;
1711 raphael 125
		}
2461 jpm 126
		$groupe_de_champs = array_intersect_key(array_flip(self::$fieldGroups), $groupe_de_champs);
127
		if (!$groupe_de_champs) {
128
			return null;
1711 raphael 129
		}
130
 
2461 jpm 131
		$colonnes = array();
132
		if (isset($groupe_de_champs['standard'])) {
1654 aurelien 133
			$colonnes += Array(
1757 raphael 134
				'nom_sel'			=> self::GenColInfo(Array('abbrev' => 'nom_sel',
135
															  'nom' => 'Espèce')),
136
				'nom_sel_nn'		=> self::GenColInfo(Array('abbrev' => 'nom_sel_nn',
137
															  'nom' => 'Numéro nomenclatural',
138
															  'importable' => FALSE)),
139
				'nom_ret'			=> self::GenColInfo(Array('abbrev' => 'nom_ret',
140
															  'nom' => 'Nom retenu',
141
															  'importable' => FALSE)),
142
				'nom_ret_nn'		=> self::GenColInfo(Array('abbrev' => 'nom_ret_nn',
143
															  'nom' => 'Numéro nomenclatural nom retenu',
144
															  'importable' => FALSE)),
145
				'nt'				=> self::GenColInfo(Array('abbrev' => 'nt',
146
															  'nom' => 'Numéro taxonomique',
147
															  'importable' => FALSE)),
148
				'famille'			=> self::GenColInfo(Array('abbrev' => 'famille',
149
															  'nom' => 'Famille',
150
															  'importable' => FALSE)),
151
				'nom_referentiel'	=> self::GenColInfo(Array('abbrev' => 'nom_referentiel',
152
															  'nom' => 'Referentiel taxonomique')),
153
				'zone_geo'			=> self::GenColInfo(Array('abbrev' => 'zone_geo',
154
															  'nom' => 'Commune')),
155
				'ce_zone_geo'		=> self::GenColInfo(Array('abbrev' => 'ce_zone_geo',
156
															  'nom' => 'Identifiant Commune',
157
															  'fonction' => 'convertirCodeZoneGeoVersDepartement')),
158
				'date_observation'	=> self::GenColInfo(Array('abbrev' => 'date_observation',
159
															  'nom' => 'Date',
160
															  'fonction' => 'formaterDate')),
161
				'lieudit'			=> self::GenColInfo(Array('abbrev' => 'lieudit',
162
															  'nom' => 'Lieu-dit')),
163
				'station'			=> self::GenColInfo(Array('abbrev' => 'station',
164
															  'nom' => 'Station')),
165
				'milieu'			=> self::GenColInfo(Array('abbrev' => 'milieu',
166
															  'nom' => 'Milieu')),
167
				'commentaire'		=> self::GenColInfo(Array('abbrev' => 'commentaire',
168
															  'nom' => 'Notes')),
169
				'latitude'			=> self::GenColInfo(Array('abbrev' => 'latitude',
170
															  'nom' => 'Latitude',
171
															  'extra' => 1,
172
															  'fonction' => 'trim0')),
173
				'longitude'			=> self::GenColInfo(Array('abbrev' => 'longitude',
174
															  'nom' => 'Longitude',
175
															  'extra' => 1,
176
															  'fonction' => 'trim0')),
177
				'altitude'			=> self::GenColInfo(Array('abbrev' => 'altitude',
178
															  'nom' => 'Altitude',
1816 raphael 179
															  'extra' => 1)), // pas de trim0 car INT(5) en DB
1757 raphael 180
				'geodatum'			=> self::GenColInfo(Array('abbrev' => 'geodatum',
181
															  'nom' => 'Référentiel Géographique',
182
															  'extra' => 1,
183
															  'importable' => FALSE)),
1654 aurelien 184
			);
185
		}
2143 jpm 186
 
1654 aurelien 187
		if(isset($groupe_de_champs['avance'])) {
1656 raphael 188
			$colonnes += array(
1757 raphael 189
				// TODO: importable = FALSE car pas de merge de données importées
190
				'ordre'				=> self::GenColInfo(Array('abbrev' => 'ordre',
191
															  'nom' => 'Ordre',
192
															  'extra' => 1,
193
															  'importable' => FALSE)),
194
				'id_observation'	=> self::GenColInfo(Array('abbrev' => 'id_observation',
195
															  'nom' => 'Identifiant',
196
															  'extra' => 1,
197
															  'importable' => FALSE)),
1656 raphael 198
 
1757 raphael 199
				'mots_cles_texte'	=> self::GenColInfo(Array('abbrev' => 'mots_cles_texte',
200
															  'nom' => 'Mots Clés',
201
															  'extra' => 1)),
202
				'date_creation'		=> self::GenColInfo(Array('abbrev' => 'date_creation',
203
															  'nom' => 'Date Création',
204
															  'extra' => 1,
205
															  'importable' => FALSE)),
206
				'date_modification'	=> self::GenColInfo(Array('abbrev' => 'date_modification',
207
															  'nom' => 'Date Modification',
208
															  'extra' => 1,
209
															  'importable' => FALSE)),
1656 raphael 210
 
1757 raphael 211
				// rappel transmission = 1, signifie simplement "public"
212
				// des données importées peuvent être d'emblée "publiques"
213
				// "importable" = TRUE
214
				'transmission'		=> self::GenColInfo(Array('abbrev' => 'transmission',
215
															  'nom' => 'Transmis',
216
															  'extra' => 1,
217
															  'fonction' => 'boolOuiNon')),
218
				'date_transmission'	=> self::GenColInfo(Array('abbrev' => 'date_transmission',
219
															  'nom' => 'Date Transmission',
220
															  'extra' => 1,
221
															  'importable' => FALSE)),
222
				'abondance'			=> self::GenColInfo(Array('abbrev' => 'abondance',
223
															  'nom' => 'Abondance',
224
															  'extra' => 1)),
225
				'certitude'			=> self::GenColInfo(Array('abbrev' => 'certitude',
226
															  'nom' => 'Certitude',
227
															  'extra' => 1)),
228
				'phenologie'		=> self::GenColInfo(Array('abbrev' => 'phenologie',
229
															  'nom' => 'Phénologie',
230
															  'extra' => 1)),
2143 jpm 231
 
1757 raphael 232
				// XXX: getImages() dépend du contexte de Cel, et doit être appelée comme cas particulier
233
				// cf ExportXLS::traiterLigneObservation()
234
				'images'			=> self::GenColInfo(Array('abbrev' => 'images',
235
															  'nom' => 'Image(s)',
236
															  'extra' => 1,
237
															  'fonction_data' => NULL /* cas particulier 'getImages' */,
238
															  'importable' => TRUE,
239
															  //'preload' => array(__CLASS__, 'getImages_preload')//TODO
240
				)),
1656 raphael 241
 
1757 raphael 242
				/* 'nom_commun'			=> self::GenColInfo(Array('abbrev' => 'nom_commun',
243
				   'nom' => 'Nom Commun',
244
				   'extra' => 1,
245
				   'fonction_data' => 'getNomCommun',
246
				   'importable' => FALSE),
1685 raphael 247
 
1757 raphael 248
				   'nom-commun'			=> self::GenColInfo(Array('abbrev' => 'nom-commun',
249
				   'nom' => 'Nom Commun',
250
				   'extra' => 1,
251
				   'fonction_data' => 'getNomCommun_v2'),
252
 
253
				   'nom-commun'			=> self::GenColInfo(Array('abbrev' => 'nom-commun',
254
				   'nom' => 'Nom Commun',
255
				   'extra' => 1,
256
				   'fonction_data' => 'getNomCommun_v3'),
257
				   'importable' => FALSE), */
2461 jpm 258
				'nom-commun' => self::GenColInfo(array(
259
					'abbrev' => 'nom-commun',
260
					'nom' => 'Nom Commun',
261
					'extra' => 1,
262
					'fonction_data' => null /* cas particu 'getNomCommun_v4' */,
263
					'preload' => array(__CLASS__, 'getNomCommun_preload')))
1702 raphael 264
			);
265
		}
1685 raphael 266
 
1702 raphael 267
		if(isset($groupe_de_champs['baseflor'])) {
268
			$colonnes += array(
269
				// champ dynamique
2461 jpm 270
				'baseflor' => self::GenColInfo(array(
271
					'abbrev' => 'baseflor',
272
					'nom' => '',
273
					'extra' => 1,
274
					'importable' => false,
275
					'preload' => array(__CLASS__, 'baseflor_preload'),
276
					'dyna' => array(__CLASS__, 'baseflor_ligne'))),
1654 aurelien 277
			);
1702 raphael 278
		}
1656 raphael 279
 
2461 jpm 280
		if (isset($groupe_de_champs['etendu'])) {
1714 raphael 281
			$colonnes += array(
282
				// champ dynamique
2461 jpm 283
				'etendu' => self::GenColInfo(array(
284
					'abbrev' => 'etendu',
285
					'nom' => '',
286
					'extra' => 1,
287
					'importable' => false,
288
					'preload' => array(__CLASS__, 'champsEtendus_preload'),
289
					'dyna' => array(__CLASS__, 'champsEtendus_ligne'))),
1714 raphael 290
			);
291
		}
1835 raphael 292
 
2461 jpm 293
		if (isset($groupe_de_champs['auteur'])) {
1835 raphael 294
			$colonnes += array(
2461 jpm 295
				'observateur' => self::GenColInfo(array(
296
					'abbrev' => 'observateur',
297
					'nom' => 'Observateur',
298
					'extra' => 1,
299
					'fonction_data' => 'formaterUrlUser',
300
					'importable' => false)),
1835 raphael 301
			);
302
		}
1654 aurelien 303
		return $colonnes;
304
	}
1694 raphael 305
 
306
	static function preload($colonnes, $cel, $ids) {
307
		$result = array();
2461 jpm 308
		foreach ($colonnes as $abbrev => $colonne) {
309
			if (!$colonne['preload']) {
310
				continue;
311
			}
1702 raphael 312
			$result[$abbrev] = call_user_func($colonne['preload'], $cel, $ids);
1694 raphael 313
		}
314
		return $result;
315
	}
2143 jpm 316
 
1656 raphael 317
	public static function getIntitulesColonnes($colonnes) {
1702 raphael 318
		// array_filter pour supprimer les colonnes "dynamique" n'ayant pas défini $nom (cf GenColInfo())
319
		return array_filter(array_map(array('FormateurGroupeColonne', 'retournerNomItem'), $colonnes));
1654 aurelien 320
	}
2143 jpm 321
 
1671 aurelien 322
	public static function retournerNomItem(&$item) {
323
		return $item['nom'];
324
	}
1694 raphael 325
 
1656 raphael 326
	public static function getLigneObservation(&$obs, &$colonnes, $cel = false) {
1654 aurelien 327
		$ligne_formatee = array();
328
		foreach($colonnes as $abbrev => $colonne) {
329
			$valeur = null;
2461 jpm 330
			if ($colonne['extra'] == 2 || ! is_null($colonne['dyna'])) {
331
				continue;
332
			}
2143 jpm 333
 
1835 raphael 334
			// valeur directe depuis cel_obs ?
2461 jpm 335
			if (isset($obs[$abbrev])) {
336
				$valeur = $obs[$abbrev];
337
			}
2143 jpm 338
 
1835 raphael 339
			// pré-processeur des champs
2461 jpm 340
			if (function_exists($colonne['fonction'])) {
1654 aurelien 341
				$valeur = $colonne['fonction']($valeur);
2461 jpm 342
			} else if(method_exists(__CLASS__, $colonne['fonction'])) {
1654 aurelien 343
				$valeur = call_user_func(array(__CLASS__, $colonne['fonction']), $valeur);
2461 jpm 344
			} else if($colonne['fonction']) {
1654 aurelien 345
				die("méthode {$colonne['fonction']} introuvable");
2461 jpm 346
			} else if(function_exists($colonne['fonction_data'])) {// fonction pour obtenir des champs (étendus)
1654 aurelien 347
				$valeur = $colonne['fonction_data']($obs);
2461 jpm 348
			} else if(method_exists(__CLASS__, $colonne['fonction_data'])) {
1654 aurelien 349
				$valeur = call_user_func(array(__CLASS__, $colonne['fonction_data']), $obs);
350
			}
2143 jpm 351
 
1654 aurelien 352
			// // cette section devrait être vide:
353
			// // cas particuliers ingérable avec l'architecture actuelle:
2461 jpm 354
			if (false && $abbrev == 'date_observation' && $valeur == '0000-00-00') {
1654 aurelien 355
				/* blah */
356
			}
1765 raphael 357
			// ici à cause du passage de $cel ($this->utilisateur)
2461 jpm 358
			if ($abbrev == 'images') {
1765 raphael 359
				$valeur = FormateurGroupeColonne::getImages($obs, $cel->id_utilisateur);
1656 raphael 360
			}
2461 jpm 361
			if ($abbrev == 'nom-commun') {
1765 raphael 362
				$valeur = FormateurGroupeColonne::getNomCommun_v4($obs);
1685 raphael 363
			}
2143 jpm 364
 
2461 jpm 365
			if ($valeur == null) {
366
				$valeur = '';
1654 aurelien 367
			}
2143 jpm 368
 
2461 jpm 369
			// fin de section "cas particuliers"
1654 aurelien 370
			$ligne_formatee[] = $valeur;
371
		}
1694 raphael 372
 
1835 raphael 373
		// uniquement les champs dynamiques
1702 raphael 374
		foreach($colonnes as $abbrev => $colonne) {
375
			$valeur = null;
2461 jpm 376
			if (is_null($colonne['dyna'])) {
377
				continue;
378
			}
379
			call_user_func_array($colonne['dyna'], array($obs, &$ligne_formatee));
1702 raphael 380
		}
1654 aurelien 381
		return $ligne_formatee;
382
	}
2143 jpm 383
 
1654 aurelien 384
	/*
385
	* Wrapper générant un tableau associatif:
1694 raphael 386
	* Ne pas changer les valeurs par défaut du prototype sans réflexion sur l'implication pour nomEnsembleVersListeColonnes()
2143 jpm 387
 
1654 aurelien 388
	* @param $abbrev (obligatoire): nom court de colonne, largement utilisé lors de l'import.
389
	*		  En effet chaque ligne importée est accessible à l'aide du `define` de $abbrev en majuscule, préfixé de "C_"
390
	*		  Exemple: $ligne[C_LONGITUDE] pour "longitude".
391
	*		  cf: ImportXLS::detectionEntete()
2143 jpm 392
 
1654 aurelien 393
	* @param $nom (obligatoire): nom complet de colonne (utilisé pour la ligne d'en-tête)
1702 raphael 394
	*		  Les définition de champs dynamique (correspondant à de multiples colonnes) doivent laisser cette valeur
395
	*		  vide afin de ne pas créer une colonne supplémentaire erronée.
2143 jpm 396
 
1654 aurelien 397
	* @param $is_extra:
398
	* Si 0, la colonne est une colonne standard
399
	* Si 1, la colonne est extra [le plus souvent générée automatiquement]
400
	*		 (auquel cas une bordure bleue entoure son nom dans la ligne d'entête)
401
	* Si 2, la colonne n'est pas traité à l'export, mais une définition peut lui être donnée
402
	*		 qui pourra être utilisée à l'import, exemple: "image"
2143 jpm 403
 
1654 aurelien 404
	* @param $fonction (optionnel): un nom d'un fonction de préprocessing
405
	* 		  $fonction doit prendre comme seul argument la valeur d'origine et retourner la valeur transformée
2143 jpm 406
 
1654 aurelien 407
	* @param $fonction_data (optionnel): une *méthode* d'obtention de donnée
408
	* 		  $fonction_data doit prendre comme premier argument le tableau des champs de l'enregistrement existant
409
	*		  $fonction_data doit retourner une valeur
2143 jpm 410
 
1654 aurelien 411
	* @param $importable (optionnel): défini si la colonne est traitée (ou absolument ignorée par PHPExcel) lors de
412
	*		  l'import.
1694 raphael 413
 
414
	* @param $preload (optionnel): défini une fonction de préchargement massif de donnée potentiellement utilisable par $fonction_data.
415
	*		  Utile, notamment, dans le cadre de l'export
1702 raphael 416
 
417
	* @param $fonction_dynamique (optionnel): défini une fonction ajoutant un nombre arbitraire de colonnes à une ligne donnée
418
	*		  Utile, notamment, dans le cadre de l'export des champs étendus ou des données baseflor
419
	*		  La fonction doit TOUJOURS alterer la ligne en lui ajoutant une nombre CONSTANT d'éléments (NULL ou non)
1765 raphael 420
	*		  La fonction doit prendre comme arguments ($obs, &$ligne_formatee)
1654 aurelien 421
	*/
1757 raphael 422
	static function GenColInfo($args) {
2461 jpm 423
		$default = array(
424
			'abbrev' => null,
425
			'nom' => null,
426
			'extra' => 0,
427
			'fonction' => null,
428
			'fonction_data' => null,
429
			'importable' => true,
430
			'preload' => null,
431
			'dyna' => null);
1757 raphael 432
		$ret = array_intersect_key($args, $default);
433
		return array_merge($default, $ret);
1654 aurelien 434
	}
2143 jpm 435
 
1656 raphael 436
	static function formaterDate($date_heure_mysql) {
1671 aurelien 437
		//return "";
2461 jpm 438
		if (!$date_heure_mysql || $date_heure_mysql == "0000-00-00 00:00:00") {
439
			return null;
440
		}
1671 aurelien 441
		// malheureusement pas disponible en php < 5.3
442
		//$date_format = DateTime::createFromFormat("Y-m-d H:i:s", $date_heure_mysql);
443
		$val = explode(' ', $date_heure_mysql);
444
		$date = explode('-', $val[0]);
445
		$heure = explode(':', $val[1]);
446
		$timestamp = mktime((int) $heure[0], (int) $heure[1], (int) $heure[2], (int) $date[1], (int) $date[2], (int) $date[0]);
2461 jpm 447
		if (!$timestamp) {
448
			return null;
449
		}
1656 raphael 450
		// TODO: les widgets ne font malheureusement pas usage de l'heure dans le CEL
451
		// TODO: si modification, ne pas oublier de modifier le format d'import correspondant
1698 raphael 452
		//	dans ImportXLS, traiterDateObs() (actuellement: "Y/m/d" car utilisation de strtotime() qui ne lit pas tout)
453
		// $date_formatee = strftime('%d/%m/%Y', $timestamp);
454
		$date_formatee = strftime('%Y/%m/%d', $timestamp);
2461 jpm 455
		if (!$date_formatee) {
456
			return '00/00/0000';
457
		}
1654 aurelien 458
		return $date_formatee;
459
	}
1757 raphael 460
 
1835 raphael 461
	static function formaterUrlUser($obs) {
462
		$is_id = is_numeric($obs['ce_utilisateur']);
463
		return sprintf("%s %s <%s>%s",
2461 jpm 464
			$obs['prenom_utilisateur'],
465
			$obs['nom_utilisateur'],
466
			preg_replace(';@.*;', '@...', $obs['courriel_utilisateur']),
467
			$is_id ? sprintf(' (' . USER_BASEURL . ')', $obs['ce_utilisateur']) : '');
1835 raphael 468
	}
469
 
1759 raphael 470
	static function getImages_preload($cel, $obsids) {
2461 jpm 471
		if (!$obsids) return;
1766 raphael 472
		$rec = Cel::db()->requeter(
1759 raphael 473
			sprintf("SELECT o.id_observation, GROUP_CONCAT(nom_original ORDER BY nom_original ASC SEPARATOR '%s') AS i " .
2446 jpm 474
					"FROM cel_images i LEFT JOIN cel_obs o ON (i.ce_observation = o.id_observation) " .
1759 raphael 475
					"WHERE o.ce_utilisateur = %d AND o.id_observation IN (%s) " .
476
					"GROUP BY id_observation",
477
					SEPARATEUR_IMAGES,
478
					$cel->id_utilisateur,
479
					implode(',', $obsids)));
2461 jpm 480
		foreach ($rec as $v) {
1759 raphael 481
			self::$cache['getImages'][$v['id_observation']] = $v['i'];
482
		}
2461 jpm 483
		return null;
1759 raphael 484
	}
485
 
1765 raphael 486
	static function getImages($obs, $id_utilisateur) {
1656 raphael 487
		if(! $id_utilisateur) return NULL;
1759 raphael 488
		if(isset(self::$cache['getImages'][$obs['id_observation']]))
489
			return self::$cache['getImages'][$obs['id_observation']];
490
 
1765 raphael 491
		$rec = Cel::db()->requeter(
2446 jpm 492
			sprintf("SELECT GROUP_CONCAT(nom_original ORDER BY nom_original ASC SEPARATOR '%s') AS i ".
493
					"FROM cel_images i ".
494
					"	LEFT JOIN cel_obs o ON (i.ce_observation = o.id_observation) ".
495
					"WHERE o.ce_utilisateur = %d ".
496
					"	AND o.id_observation = %d ".
497
					'LIMIT 1',
1656 raphael 498
					SEPARATEUR_IMAGES,
1757 raphael 499
					$id_utilisateur,
500
					$obs['id_observation']));
501
		return $rec ? $rec[0]['i'] : NULL;
1654 aurelien 502
	}
2143 jpm 503
 
504
	public static function convertirCodeZoneGeoVersDepartement($code_zone_geo) {
1654 aurelien 505
		$code_departement = '';
506
		if(self::estUnCodeInseeDepartement($code_zone_geo)) {
507
			$code_departement = substr(ltrim($code_zone_geo,'INSEE-C:'),0,2);
508
		}
2143 jpm 509
 
1654 aurelien 510
		return $code_departement;
511
	}
1757 raphael 512
 
513
	public static function trim0($lonlat) {
514
		return trim(trim($lonlat, "0"), ".");
515
	}
516
 
517
	public static function boolOuiNon($transmission) {
518
		return $transmission ? 'oui' : '';
519
	}
2143 jpm 520
 
1654 aurelien 521
	public static function estUnCodeInseeDepartement($code_a_tester) {
522
		return preg_match('/^INSEE-C:[0-9]{5}/',$code_a_tester);
523
	}
2143 jpm 524
 
1654 aurelien 525
	// TODO: référentiel ne devrait pas être généré au moment d'un Config::get,
526
	// comme dans Config::get('nomsVernaRechercheLimiteeTpl')
527
	// Par exemple, la variable pour "nva" ?
528
	function getNomCommun($obs) {
529
		$langue = 'fra';
530
		list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
531
		if($referentiel == 'bdtfx') $referentiel = 'nvjfl';
532
		else return '';
2143 jpm 533
 
1654 aurelien 534
		$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
1657 raphael 535
		if(isset(self::$cache['getNomCommun'][$cache_id])) {
1656 raphael 536
			//debug: error_log("require url_service_nom_attribution: OK ! (pour \"{$obs['nom_ret']}\")");
1657 raphael 537
			return self::$cache['getNomCommun'][$cache_id];
1654 aurelien 538
		}
539
		// pas de cache:
540
		//debug: error_log("require url_service_nom_attribution pour \"{$obs['nom_ret']}\"");
2143 jpm 541
 
1654 aurelien 542
		// pour bdtfx:
543
		// /service:eflore:0.1/nvjfl/noms-vernaculaires/attributions?masque.nt=X&masque.lg=fra&retour.champs=num_statut
544
		// /projet/services/modules/0.1/nvjfl/NomsVernaculaires.php
545
		$url = str_replace(Array('{referentiel}', '{valeur}', '{langue}'),
1656 raphael 546
						   Array($referentiel, $obs['nt'], $langue),
1657 raphael 547
						   self::$config['eflore']['url_service_nom_attribution']) . // TODO !
1656 raphael 548
			"&retour.champs=num_statut";
1654 aurelien 549
		$noms = @json_decode(file_get_contents($url));
550
		if(! $noms) return '';
1673 raphael 551
		$noms = array_filter((array)($noms->resultat), array($this, retournerNumStatutUn)); // XXX: php 5.3
1654 aurelien 552
		$nom = array_pop($noms)->nom_vernaculaire;
2143 jpm 553
 
1654 aurelien 554
		// cache
1657 raphael 555
		self::$cache['getNomCommun'][$cache_id] = $nom;
1654 aurelien 556
		return $nom;
557
	}
2143 jpm 558
 
1671 aurelien 559
	private function retournerNumStatutUn(&$item) {
560
		return ($item->num_statut == 1);
561
	}
1673 raphael 562
 
563
	private function retournerNumStatutUnArr(&$item) {
564
		return ($item['num_statut'] == 1);
565
	}
2143 jpm 566
 
1656 raphael 567
	// si getNomCommun_v2 ou getNomCommun_v3 sont utilisés
568
	/* require_once('/home/raphael/eflore/framework/framework/Framework.php');
569
	Framework::setCheminAppli("/home/raphael/eflore/projets/services/index.php");
570
	Framework::setInfoAppli(Config::get('info'));
571
	require_once('/home/raphael/eflore/projets/services/modules/0.1/Projets.php');*/
2143 jpm 572
 
1654 aurelien 573
	/* Tente de bootstraper le framework au plus court et d'initialiser une instance de
1656 raphael 574
	   NomsVernaculaires pour obtenir le nom commun */
1654 aurelien 575
	function getNomCommun_v2($obs) {
576
		static $service;
1656 raphael 577
		$service = new Projets();
2143 jpm 578
 
1654 aurelien 579
		$langue = 'fra';
580
		list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
581
		if($referentiel == 'bdtfx') $referentiel = 'nvjfl';
582
		else return '';
2143 jpm 583
 
1654 aurelien 584
		$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
1657 raphael 585
		if(isset(self::$cache['getNomCommun'][$cache_id])) {
1656 raphael 586
			error_log("require NomsVernaculaires.php: OK ! (pour \"{$obs['nom_ret']}\")");
1657 raphael 587
			return self::$cache['getNomCommun'][$cache_id];
1654 aurelien 588
		}
589
		// pas de cache:
590
		error_log("require NomsVernaculaires.php pour \"{$obs['nom_ret']}\"");
2143 jpm 591
 
1656 raphael 592
		$donnees = Array('masque.nt' => $obs['nt'],
593
						 'masque.lg' => $langue,
594
						 'retour.champs' => 'num_statut');
1654 aurelien 595
		$noms = $service->consulter(Array('nvjfl', 'noms-vernaculaires'), $donnees);
2143 jpm 596
 
1654 aurelien 597
		if(! $noms) return '';
1673 raphael 598
		$noms = array_filter((array)($noms->resultat), array($this, retournerNumStatutUn)); // XXX: php 5.3
1654 aurelien 599
		$nom = array_pop($noms)->nom_vernaculaire;
2143 jpm 600
 
1656 raphael 601
		// cache
1657 raphael 602
		self::$cache['getNomCommun'][$cache_id] = $nom;
1654 aurelien 603
		return $nom;
604
	}
2143 jpm 605
 
606
 
1654 aurelien 607
	/* Effectue un bootstraping plus sage que ci-dessus, mais le gain d'efficacité
1656 raphael 608
	   n'est pas aussi retentissant qu'espéré */
1654 aurelien 609
	static $service;
610
	function getNomCommun_v3($obs) {
611
		if(! $this->service) $this->service = new Projets();
2143 jpm 612
 
1654 aurelien 613
		$langue = 'fra';
614
		list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
615
		if($referentiel == 'bdtfx') $referentiel = 'nvjfl';
616
		else return '';
2143 jpm 617
 
1654 aurelien 618
		$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
1657 raphael 619
		if(isset(self::$cache['getNomCommun'][$cache_id])) {
1656 raphael 620
			error_log("require NomsVernaculaires.php: OK ! (pour \"{$obs['nom_ret']}\")");
1657 raphael 621
			return self::$cache['getNomCommun'][$cache_id];
1654 aurelien 622
		}
623
		// pas de cache:
1656 raphael 624
		error_log("require NomsVernaculaires.php pour \"{$obs['nom_ret']}\"");
2143 jpm 625
 
1654 aurelien 626
		$donnees = Array('masque.nt' => $obs['nt'],
1656 raphael 627
						 'masque.lg' => $langue,
628
						 'retour.champs' => 'conseil_emploi');
629
		$this->service->initialiserRessourcesEtParametres(Array('nvjfl', 'noms-vernaculaires', 'attributions'), $donnees);
1654 aurelien 630
		try {
1656 raphael 631
			$noms = $this->service->traiterRessources();
2143 jpm 632
		} catch(Exception $e) {
1656 raphael 633
			return '';
1654 aurelien 634
		}
1656 raphael 635
		if(! $noms) return '';
1673 raphael 636
		$noms = array_filter($noms['resultat'], array($this, retournerNumStatutUnArr)); // XXX: php 5.3
1656 raphael 637
		$premier_nom = array_pop($noms);
1654 aurelien 638
		$nom = $premier_nom['nom_vernaculaire'];
2143 jpm 639
 
1654 aurelien 640
		// cache
1657 raphael 641
		self::$cache['getNomCommun'][$cache_id] = $nom;
1654 aurelien 642
		return $nom;
643
	}
1685 raphael 644
 
1694 raphael 645
	/* Cette fonction initialise le cache des noms communs en 1 fois, sur la liste des observations à exporter.
1702 raphael 646
	   Ainsi, les appels successifs à getNomCommun_v4() ne sont pas couteux (pas de requête SQL) */
1694 raphael 647
	static function getNomCommun_preload($cel, $obsids) {
648
		if(!$obsids) return;
1765 raphael 649
		if(!self::referenceTableExiste()) return NULL;
1694 raphael 650
 
651
		// CREATE INDEX i_nom_referentiel ON cel_obs (nom_referentiel(5));
652
		$req = sprintf("SELECT r.referentiel, r.num_taxon, r.nom_commun FROM cel_references r" .
653
					   " INNER JOIN cel_obs c ON (r.referentiel = substring_index(c.nom_referentiel, ':', 1) and r.num_taxon = c.nt)" .
654
					   " WHERE c.id_observation IN (%s)",
655
					   implode(',', $obsids));
1765 raphael 656
		$res = Cel::db()->requeter($req);
1694 raphael 657
		foreach($res as $v) {
658
			self::$cache['getNomCommun'][$v['referentiel'] . '-' . $v['num_taxon'] . '-' . 'fra'] = $v['nom_commun'];
659
		}
660
		return NULL;
661
	}
1702 raphael 662
 
1765 raphael 663
	static function referenceTableExiste() {
2143 jpm 664
		if (!self::$is_table) {
1702 raphael 665
			// une seule fois
2143 jpm 666
			if (! Cel::db()->requeterLigne("SHOW TABLES LIKE 'cel_references'")) return FALSE;
1702 raphael 667
			self::$is_table = TRUE;
668
		}
669
		return TRUE;
670
	}
2143 jpm 671
 
1765 raphael 672
	static function getNomCommun_v4($obs) {
2446 jpm 673
		// Attention la fonction suppose que l'on ait fait appel à getNomCommun_preload avant
2281 aurelien 674
		// d'être appelée
1685 raphael 675
		if(! $obs['nt']) return NULL;
1765 raphael 676
		if(! self::referenceTableExiste()) return NULL;
1689 raphael 677
 
1685 raphael 678
		$langue = 'fra';
679
		list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
680
		$cache_id = $referentiel . '-' . $obs['nt'] . '-' . $langue;
681
 
2281 aurelien 682
		$nom = null;
1757 raphael 683
		// cache:
2281 aurelien 684
		if(isset(self::$cache['getNomCommun'])) {
1757 raphael 685
			if(isset(self::$cache['getNomCommun'][$cache_id])) return self::$cache['getNomCommun'][$cache_id];
686
			// XXX: problème de valeurs NULL ?
2281 aurelien 687
			if(array_key_exists($cache_id, self::$cache['getNomCommun'])) {
688
				$nom = self::$cache['getNomCommun'][$cache_id];
689
			}
1757 raphael 690
		}
1685 raphael 691
 
692
		return $nom;
693
	}
1702 raphael 694
 
695
 
696
	/* Cette fonction initialise le cache des données baseflor en 1 fois, sur la liste des observations à exporter.
697
	   Ainsi, les appels successifs à baseflor_ligne() ne sont pas couteux (pas de requête SQL) */
698
	static function baseflor_preload($cel, $obsids) {
699
		if(!$obsids) return;
1765 raphael 700
		if(!self::referenceTableExiste()) return NULL;
1702 raphael 701
 
2254 aurelien 702
		// Attention (en attendant de faire une meilleure table et une meilleure requete) le distinct est très important
703
		$req = sprintf("SELECT DISTINCT referentiel, num_nom_retenu, %s FROM cel_references r" .
2297 mathias 704
						" INNER JOIN cel_obs c ON (r.num_nom_retenu = c.nom_ret_nn)" .
705
						" AND r.referentiel = IF(LOCATE(':', c.nom_referentiel) = 0, c.nom_referentiel, SUBSTR(c.nom_referentiel FROM 1 FOR LOCATE(':', c.nom_referentiel) - 1))" .
706
						" WHERE c.id_observation IN (%s)",
707
						//" AND catminat_code IS NOT NULL", // TODO: suppression des NULL ici signifie que le cache sera partiel...
708
						implode(',', array_keys(self::$baseflor_col)),
709
						implode(',', $obsids));
1765 raphael 710
		$res = Cel::db()->requeter($req);
1706 raphael 711
		if(!$res) return NULL;
1702 raphael 712
 
713
		foreach($res as $v) {
714
			$data = $v;
715
			unset($data['referentiel']); // non nécessaire
716
			unset($data['num_nom_retenu']); // non nécessaire
2446 jpm 717
 
2244 aurelien 718
			// Des fois les synonymes ont des valeurs pour baseflor et pas le nom retenu et vice versa
2446 jpm 719
			// on les fusionne pour avoir le maximum d'infos, en attendant de repenser la table référence
2244 aurelien 720
			if(isset(self::$cache['getBaseflor'][$v['referentiel'] . '-' . $v['num_nom_retenu']])) {
2446 jpm 721
				$orig  = array_filter(self::$cache['getBaseflor'][$v['referentiel'] . '-' . $v['num_nom_retenu']], 'strlen');
722
				$data  = array_filter($data , 'strlen');
2244 aurelien 723
				$data = array_merge($orig, $data);
724
			}
2446 jpm 725
 
1702 raphael 726
			self::$cache['getBaseflor'][$v['referentiel'] . '-' . $v['num_nom_retenu']] = $data;
727
		}
728
 
729
		return NULL;
730
	}
1685 raphael 731
 
2446 jpm 732
	/**
2312 mathias 733
	 * Attention la fonction suppose que l'on ait fait appel à baseflor_preload avant
734
	 * d'être appelée
735
	 * @CASSECOUILLES elle pourrait le détecter et le faire elle-même
736
	 */
1765 raphael 737
	static function baseflor_ligne($obs, &$ligne) {
2312 mathias 738
		$clefsBF = array_keys(self::$baseflor_col);
739
		// par défaut des colonnes vides pour ne pas décaler le bousin
740
		$donneesBF = array_fill_keys($clefsBF, "");
1702 raphael 741
 
2312 mathias 742
		// s'il y a des données baseflor
743
		if ($obs['nom_ret_nn'] && self::referenceTableExiste() && count(self::$cache['getBaseflor']) > 0) {
744
			// l'astuce à un franc vingt
745
			list($referentiel) = explode(':', strtolower($obs['nom_referentiel']));
746
			$cache_id = $referentiel . '-' . $obs['nom_ret_nn'];
1702 raphael 747
 
2312 mathias 748
			// si les données baseflor existent dans le cache pour ce nom_ret_nn
749
			if (array_key_exists($cache_id, self::$cache['getBaseflor'])) {
750
				$donneesBFATrous = self::$cache['getBaseflor'][$cache_id];
751
				foreach($clefsBF as $colbf) { // remplit les trous tout en préservant l'ordre
752
					if(array_key_exists($colbf, $donneesBFATrous)) {
753
						$donneesBF[$colbf] = $donneesBFATrous[$colbf];
754
					} else {
755
						$donneesBF[$colbf] = "";
756
					}
757
				}
758
			}
1702 raphael 759
		}
2312 mathias 760
 
761
		// Quand les données sont prêtes, on les fusionne
762
		$ligne = array_merge($ligne, $donneesBF);
1702 raphael 763
	}
2446 jpm 764
 
1715 raphael 765
	static function champsEtendus_preload($cel, $obsids) {
2446 jpm 766
		$gestion_champs_etendus = new GestionChampsEtendus($cel->config, 'obs');
1718 raphael 767
		$colonnes_champs_supp_par_obs = $gestion_champs_etendus->consulterClesParLots($obsids);
2446 jpm 768
 
769
		// Supprime les champs étendus considérés comme privés dans le cas de l'export public en chargeant
2403 aurelien 770
		// le catalogue et en excluant ceux qui sont explicitement privés
771
		if(!$cel->export_prive) {
772
			$indices_a_supprimer = array();
773
			$catalogue_champs_etendus = $gestion_champs_etendus->consulterCatalogueChampsEtendusPredefinis();
774
			foreach($catalogue_champs_etendus as $champ_catalogue) {
775
				if($champ_catalogue['options']['prive'] == 1) {
2446 jpm 776
					// Les champs étendus peuvent avoir des variantes lorsqu'ils apparaissent de multiples fois.
2407 aurelien 777
					// Vont donc matcher monChamp mais aussi monChamp:1, monChamp:2 ou bien monChamp1, monChamp: etc...
778
					// pour plus de sécurité (ce filtra n'est affectué qu'une fois au début de l'export donc on ne s'en prive pas)
779
					$entrees = preg_grep("/".$champ_catalogue['cle']."(?::?\d*)?$/", $colonnes_champs_supp_par_obs);
2403 aurelien 780
					$indices_a_supprimer = array_merge($indices_a_supprimer, array_keys($entrees));
781
				}
782
			}
783
			// les champs étendus sont renvoyés dans l'export suivant les colonnes présentes dans ce tableau
784
			// les éliminer de la liste des colonnes suffit à les faire ignorer par l'export
2446 jpm 785
			foreach($indices_a_supprimer as $indice_supp) {
786
				unset($colonnes_champs_supp_par_obs[$indice_supp]);
2403 aurelien 787
			}
788
		}
2446 jpm 789
 
1741 raphael 790
		// ces deux lignes réordonnent l'ordre des colonnes des champs étendus en fonction de l'ordre (très spécifique)
791
		// de self::$ordre_champ_etendus_Florileges, les champs non-mentionnés sont ajoutés à la fin.
792
		$colonnes_champs_supp_par_obs = self::sortArrayByArray(array_flip($colonnes_champs_supp_par_obs),
793
															   self::$ordre_champ_etendus_Florileges);
794
		$colonnes_champs_supp_par_obs = array_keys($colonnes_champs_supp_par_obs);
795
 
1718 raphael 796
		// si le SELECT des clefs ne retourne rien, une autre requêtes est inutile
797
		// TODO: optimize, 1 seule requête
798
		if(!$colonnes_champs_supp_par_obs) return Array('header' => array(), 'data' => array());
799
 
1715 raphael 800
		$champs_supp_par_obs = $gestion_champs_etendus->consulterParLots($obsids);
1741 raphael 801
 
1791 raphael 802
		self::$cache['champsEtendus']['header'] = self::champsEtendus_prefixHeader($colonnes_champs_supp_par_obs);
803
 
1715 raphael 804
		foreach($champs_supp_par_obs as &$v) {
805
			$v = self::champsEtendus_aplatir($v);
806
		}
807
		self::$cache['champsEtendus']['data'] = $champs_supp_par_obs;
808
		// ce return est temporaire,
809
		// le temps que toutes les fonctions bougent ici et utilise plutôt le cache statique
1716 raphael 810
		// encore utilisé pour les entêtes (self::$cache['champsEtendus']['header'])
1715 raphael 811
		return self::$cache['champsEtendus'];
812
	}
813
 
814
	// XXX: PHP-5.3, fonction anonyme + array_map
1791 raphael 815
	static function champsEtendus_prefixHeader($array) {
816
		return array_map(create_function('$v', 'return "' . PREFIX_CHAMPS_ETENDUS . '".$v;'), $array);
817
	}
818
 
819
	// XXX: PHP-5.3, fonction anonyme + array_map
1715 raphael 820
	static function champsEtendus_aplatir($ligne_champs_etendus) {
821
		$champs_etendus_fmt = array();
822
		if(!$ligne_champs_etendus) return $champs_etendus_fmt;
823
		foreach($ligne_champs_etendus as $champ) {
1791 raphael 824
			$champs_etendus_fmt[PREFIX_CHAMPS_ETENDUS . $champ->cle] = $champ->valeur;
1715 raphael 825
		}
826
		return $champs_etendus_fmt;
827
	}
1716 raphael 828
 
1765 raphael 829
	static function champsEtendus_ligne($obs, &$ligne) {
1716 raphael 830
		// si header n'est pas défini, aucune observation ne possède de champ étendu
831
		// et nous n'ajoutons ni colonnes, ni valeurs.
832
		if(! isset(self::$cache['champsEtendus']['header'])) return;
1736 raphael 833
		$ligne_etendue_aplatie = @self::$cache['champsEtendus']['data'][$obs['id_observation']];
1716 raphael 834
 
835
		$ligne_supp = array_fill(0, count(self::$cache['champsEtendus']['header']), '');
836
		$ligne_etendue_fmt = array();
837
 
838
		// si, cependant cette seule observation n'a pas de champs étendus,
839
		// nous devons rajouter des blancs (notamment dans le cas ou d'autres
840
		// champs viennent à être ajoutés en aval à l'avenir
841
		// cf: $fonction_dynamique dans FormateurGroupeColonne::GenColInfo()
842
		if(! $ligne_etendue_aplatie) {
843
			$ligne = array_merge($ligne, $ligne_supp);
844
			return;
845
		}
846
 
847
		foreach(self::$cache['champsEtendus']['header'] as $colonne) {
848
			if(!isset($ligne_etendue_aplatie[$colonne])) {
2143 jpm 849
				$ligne_etendue_fmt[$colonne] = '';
1716 raphael 850
			} else {
851
				$ligne_etendue_fmt[$colonne] = $ligne_etendue_aplatie[$colonne];
852
			}
853
		}
854
 
855
		// XXX/ array_merge() ?
856
		$ligne += $ligne_etendue_fmt;
857
	}
1741 raphael 858
 
859
	/* HELPERS */
860
 
861
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
862
	// XXX; redéfinition, utilisé aussi par ExportXLS
863
	static function sortArrayByArray($array, $orderArray) {
864
		$ordered = array();
865
		foreach($orderArray as $key) {
866
			if(array_key_exists($key, $array)) {
867
				$ordered[$key] = $array[$key];
868
				unset($array[$key]);
869
			}
870
		}
871
		return $ordered + $array;
872
	}
2461 jpm 873
}