Subversion Repositories eFlore/Applications.cel

Rev

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