Subversion Repositories eFlore/Applications.cel

Rev

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