Subversion Repositories eFlore/Applications.cel

Rev

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

Rev Author Line No. Line
1374 aurelien 1
<?php
2462 jpm 2
// declare(encoding='UTF-8');
1374 aurelien 3
/**
2462 jpm 4
 * Service fournissant des exports des données publiques du CEL pour le widget.
5
 *
6
 * Format du service :
7
 * /CelWidgetExport/format
8
 * /CelWidgetExport/csv
9
 *
10
 * Les paramêtres :
11
 *  - "start" indique le numéro du premier item à afficher
12
 *  - "limit" nombre d'items à afficher
13
 *
14
 * @internal   Mininum PHP version : 5.2
15
 * @category   CEL
16
 * @package    Services
17
 * @subpackage Widget
18
 * @version    0.1
19
 * @author     Mathias CHOUET <mathias@tela-botanica.org>
20
 * @author     Jean-Pascal MILCENT <jpm@tela-botanica.org>
21
 * @author     Aurelien PERONNET <aurelien@tela-botanica.org>
22
 * @license    GPL v3 <http://www.gnu.org/licenses/gpl.txt>
23
 * @license    CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
24
 * @copyright  1999-2014 Tela Botanica <accueil@tela-botanica.org>
25
 */
1610 raphael 26
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
27
// la sortie est binaire (xls), mais OLE n'est pas compatible E_ALL en PHP-5.4
1700 raphael 28
error_reporting(error_reporting() & ~E_STRICT);
2459 jpm 29
require_once 'lib/OLE.php';
30
require_once 'lib/Spreadsheet/Excel/Writer.php';
1610 raphael 31
 
1374 aurelien 32
class CelWidgetExport extends Cel {
3131 delphine 33
 
1408 aurelien 34
	private $nom_fichier_export = 'cel_export';
1671 aurelien 35
	// certains paramètres apparaissent plusieurs fois car ils ont des alias
36
	// dans certains widgets
1376 aurelien 37
	private $parametres_autorises = array(
3131 delphine 38
			'id_utilisateur' => 'ce_utilisateur',
39
			'utilisateur' => 'courriel_utilisateur',
40
			'courriel_utilisateur' => 'courriel_utilisateur',
41
			'pays' => 'pays',
42
			'commune' => 'zone_geo',
43
			'zone_geo' => 'zone_geo',
44
			'dept' => 'departement',
45
			'departement' => 'departement',
46
			'lieudit' => 'lieudit',
47
			'station' => 'station',
48
			'projet' => 'mots_cles',
49
			'programme' => 'programme',
50
			'num_taxon' => 'nt',
51
			'date_debut' => 'date_debut',
52
			'date_fin' => 'date_fin',
53
			'taxon' => 'taxon',
54
			'identiplante' => 'validation_identiplante',
55
			'validation_identiplante' => 'validation_identiplante',
56
			'annee' => 'annee',
57
			'mois' => 'mois',
58
			'jour' => 'jour',
59
			'recherche' => 'recherche',
60
			'id_mots_cles' => 'id_mots_cles',
61
			'mots_cles' => 'mots_cles',
62
			'debut' => 'debut',
63
			'limite' => 'limite',
64
			'format' => 'format',
65
			'colonnes' => 'colonnes',
66
			'transmission' => 'transmission',
67
			'obsids' => 'obsids',
1376 aurelien 68
	);
3131 delphine 69
 
2459 jpm 70
	private $limite_decoupage_defaut = 9000;
3131 delphine 71
 
1402 aurelien 72
	private $format = 'csv';
3131 delphine 73
 
1659 aurelien 74
	public $id_utilisateur = null;
3131 delphine 75
 
2403 aurelien 76
	public $export_prive = false;
3131 delphine 77
 
1715 raphael 78
	// un cache, initialisé par certaines fonctions de préchargement, à la manière
79
	// de ce qui est fait par FormateurGroupeColonne
80
	static $cache = Array();
3131 delphine 81
 
1579 aurelien 82
	public function getRessource() {
83
		return $this->getElement(array());
84
	}
3131 delphine 85
 
1374 aurelien 86
	/**
87
	 * Méthode appelée avec une requête de type GET.
88
	 */
1709 raphael 89
	public function getElement($params = array()) {
1711 raphael 90
		switch(@strtolower($params[0])) {
3131 delphine 91
			case 'calcul':
92
				$this->getCalcul();
93
				break;
94
 
95
			case 'export':
96
				$this->getExport();
97
				break;
98
			default:
99
				$this->getExport();
1625 aurelien 100
		}
101
	}
3131 delphine 102
 
1625 aurelien 103
	private function getCalcul() {
1611 raphael 104
		$criteres = $this->traiterParametresAutorises($_GET);
1374 aurelien 105
		$criteres['transmission'] = 1;
3131 delphine 106
 
2403 aurelien 107
		// Définit si l'on exporte les obs privées ainsi que les champs étendus privés
108
		$this->export_prive = $this->doitEtPeutExporterObsPrivees($criteres);
109
		if($this->export_prive) {
1654 aurelien 110
			unset($criteres['transmission']);
1659 aurelien 111
			$this->id_utilisateur = $criteres['id_utilisateur'];
1654 aurelien 112
		}
3131 delphine 113
		$chercheur_observations = new RechercheObservationExport($this->config);
1379 aurelien 114
		$numero_page = isset($criteres['debut']) ? $criteres['debut'] : 0;
115
		$limite = isset($criteres['limite']) ? $criteres['limite'] : 0;
1714 raphael 116
		$colonnes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
3131 delphine 117
 
1374 aurelien 118
		unset($criteres['limite']);
119
		unset($criteres['debut']);
1625 aurelien 120
		unset($criteres['format']);
1654 aurelien 121
		unset($criteres['colonnes']);
3131 delphine 122
 
1625 aurelien 123
		$nb_observations = $chercheur_observations->compterObservations(null, $criteres);
2250 aurelien 124
		$limite_decoupage = $this->calculerNbLignesMaxParFichier(explode(',', $colonnes));
3131 delphine 125
 
1625 aurelien 126
		$url_telechargements = array();
127
		$intervalle = 0;
3131 delphine 128
 
1625 aurelien 129
		$params_url = $criteres;
130
		unset($params_url['transmission']);
131
		do {
132
			$base_url = $this->config['settings']['baseURLAbsolu'].'CelWidgetExport/export';
133
			$params_url['debut'] = $intervalle;
134
			$params_url['limite'] = $limite_decoupage;
135
			$url_telechargement_fichier = $base_url;
1654 aurelien 136
			$url_telechargements[] = $base_url.'?'.http_build_query($params_url).'&format='.$this->format.'&colonnes='.$colonnes;
1625 aurelien 137
			$intervalle += $limite_decoupage;
138
			$nb_observations -= $limite_decoupage;
2190 mathias 139
		} while($nb_observations > 0);
3131 delphine 140
 
1625 aurelien 141
		$this->envoyerJson($url_telechargements);
142
	}
3131 delphine 143
 
2250 aurelien 144
	private function calculerNbLignesMaxParFichier($colonnes) {
2459 jpm 145
		$limite = $this->limite_decoupage_defaut;
3131 delphine 146
 
1402 aurelien 147
		switch($this->format) {
148
			case 'csv':
1625 aurelien 149
				$limite = 20000;
150
				break;
151
			case 'xls':
152
				$limite = 8000;
153
				break;
1660 raphael 154
			case 'pdf':
155
				$limite = 300;
156
				break;
1625 aurelien 157
		}
3131 delphine 158
 
2253 aurelien 159
		return $limite;
1625 aurelien 160
	}
3131 delphine 161
 
1625 aurelien 162
	private function getExport() {
163
		$criteres = $this->traiterParametresAutorises($_GET);
1711 raphael 164
		// ne pas faire de super-requête en cas d'absence de paramètres
165
		// par exemple "format", au minimum, devrait être défini
1715 raphael 166
		if(!$criteres) die('erreur: pas de paramètre reçu');
167
		if(!in_array($this->format, array('pdf','csv','xls'))) die('erreur: format invalide');
3131 delphine 168
 
169
		//$criteres['transmission'] = 1;
2403 aurelien 170
		// Définit si l'on exporte les obs privées ainsi que les champs étendus privés
171
		$this->export_prive = $this->doitEtPeutExporterObsPrivees($criteres);
172
		if($this->export_prive) {
3131 delphine 173
			//unset($criteres['transmission']);
1755 raphael 174
			$this->id_utilisateur = $criteres['ce_utilisateur'];
1654 aurelien 175
		}
3131 delphine 176
		$chercheur_observations = new RechercheObservationExport($this->config);
177
 
1679 raphael 178
		$debut = isset($criteres['debut']) ? intval($criteres['debut']) : 0;
179
		$limite = isset($criteres['limite']) ? intval($criteres['limite']) : 0;
1714 raphael 180
		$groupes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
1835 raphael 181
		$groupes .= ',auteur';
3131 delphine 182
 
1714 raphael 183
		if(!$groupes) die('erreur: Ne peut identifier les groupes de champs demandés.');
3131 delphine 184
 
185
 
1679 raphael 186
		if($criteres['obsids']) $criteres['sql_brut'] = sprintf('id_observation IN (%s)',
3131 delphine 187
				implode(',', $criteres['obsids']));
188
 
1625 aurelien 189
		unset($criteres['limite']);
190
		unset($criteres['debut']);
191
		unset($criteres['format']);
1654 aurelien 192
		unset($criteres['colonnes']);
1679 raphael 193
		unset($criteres['obsids']);
3131 delphine 194
 
1679 raphael 195
		$observations = $chercheur_observations->rechercherObservations(null, $criteres, $debut, $limite, TRUE)->get();
1659 aurelien 196
		$ids = array();
197
		foreach($observations as &$obs) {
198
			$ids[] = $obs['id_observation'];
199
		}
3131 delphine 200
 
1703 raphael 201
		if($this->format == 'pdf') {
202
			$pdf = $this->convertirEnPdf($observations);
203
			$pdf->pdf->Output('etiquettes.pdf', 'I');
204
			exit;
1662 aurelien 205
		}
3131 delphine 206
 
1703 raphael 207
		// cas XLS et CSV: on peut avoir besoin des champs étendus, des noms communs et des champs baseflor:
3131 delphine 208
 
1715 raphael 209
		// Obtention des colonnes correspondantes aux groupes de champs
1714 raphael 210
		$colonnes = FormateurGroupeColonne::nomEnsembleVersListeColonnes($groupes);
3131 delphine 211
 
1715 raphael 212
		/*
3131 delphine 213
		 Champs étendus et noms communs, si demandés.
214
		 * Pour "nom commun", "preload" retourne NULL, car c'est le cache statique de FormateurGroupeColonne
215
		 qu'il initialise et utilise en interne sans qu'un passage par paramètre dans le contexte de CelWidgetExport
216
		 ne soit nécessaire.
217
		 * Pour les champs étendus, c'est CelWidgetExport::$cache qui est utilisé, aussi bien pour les en-têtes que
218
		 pour les données préchargées, cf self::traiterLigneEtendue()
219
		 */
1715 raphael 220
		self::$cache = FormateurGroupeColonne::preload($colonnes, $this, $ids);
3131 delphine 221
 
222
		// TODO: tous les champs étendus et les paramètres supplémentaires devraient être passés en un seul
223
		// tableau (et chaque formateur csv, xls etc... pourrait également être dans une classe à part)
1625 aurelien 224
		switch($this->format) {
3131 delphine 225
			case 'csv':
226
				$csv = $this->convertirEnCsv($observations, $colonnes);
227
				$this->envoyerCsv($csv);
228
				break;
229
			case 'xls':
230
				$xls = $this->convertirEnXls($observations, $colonnes);
231
				$this->envoyerXls($xls);
232
				break;
233
			default:
1402 aurelien 234
		}
1374 aurelien 235
	}
3131 delphine 236
 
1611 raphael 237
	protected function traiterParametresAutorises(Array $parametres) {
1376 aurelien 238
		$parametres_traites = array();
1402 aurelien 239
		$this->format = (isset($parametres['format']) && $parametres['format'] != '') ? $parametres['format'] : $this->format;
1376 aurelien 240
		foreach($parametres as $cle => $valeur) {
1711 raphael 241
			if(is_string($valeur) && !trim($valeur)) continue;
242
			if(isset($this->parametres_autorises[$cle])) {
1376 aurelien 243
				$parametres_traites[$this->parametres_autorises[$cle]] = $valeur;
244
			}
245
		}
1679 raphael 246
		$parametres_traites['obsids'] = @self::traiterObsIds($parametres['obsids']);
1376 aurelien 247
		return $parametres_traites;
248
	}
3131 delphine 249
 
1374 aurelien 250
	private function envoyerCsv($csv) {
251
		header('Content-Type: text/csv; charset=UTF-8');
1408 aurelien 252
		header('Content-Disposition: attachment;filename='.$this->nom_fichier_export.'.csv');
1374 aurelien 253
		echo $csv;
1376 aurelien 254
		exit;
1374 aurelien 255
	}
3131 delphine 256
 
1402 aurelien 257
	private function envoyerXls($workbook) {
258
		$workbook->close();
259
		exit;
260
	}
3131 delphine 261
 
1715 raphael 262
	private function convertirEnCsv(&$data, $colonnes) {
1374 aurelien 263
		$chemin_temp = "php://temp";
264
		$outstream = fopen($chemin_temp, 'r+');
3131 delphine 265
 
1711 raphael 266
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 267
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 268
		if(isset($colonnes['baseflor'])) {
1703 raphael 269
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
270
		}
271
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 272
		if(isset($colonnes['etendu'])) {
273
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
274
		}
3131 delphine 275
 
1690 raphael 276
		// header
2459 jpm 277
		fputcsv($outstream, $intitule_champs, ',', '"');
1690 raphael 278
		// lignes
1617 aurelien 279
		foreach($data as &$ligne) {
1692 raphael 280
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 281
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1374 aurelien 282
			fputcsv($outstream, $ligne, ',', '"');
283
		}
284
		rewind($outstream);
285
		$csv = stream_get_contents($outstream);
286
		fclose($outstream);
287
		return $csv;
288
	}
3131 delphine 289
 
1715 raphael 290
	private function convertirEnXls(&$data, $colonnes) {
1402 aurelien 291
		$this->extendSpreadsheetProductor = new SpreadsheetProductor();
292
		$this->extendSpreadsheetProductor->initSpreadsheet();
3131 delphine 293
 
1402 aurelien 294
		$workbook = new Spreadsheet_Excel_Writer();
1804 raphael 295
		// avant la définition du titre de la worksheet !
296
		$workbook->setVersion(8);
3131 delphine 297
 
1625 aurelien 298
		$worksheet = $workbook->addWorksheet('Liste');
299
		$workbook->setTempDir($this->config['cel']['chemin_stockage_temp']);
1612 raphael 300
		$worksheet->setInputEncoding('utf-8');
1408 aurelien 301
		$workbook->send($this->nom_fichier_export.'.xls');
3131 delphine 302
 
1402 aurelien 303
		$nb_lignes = 1;
3131 delphine 304
 
1711 raphael 305
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 306
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 307
		if(isset($colonnes['baseflor'])) {
1703 raphael 308
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
309
		}
310
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 311
		if(isset($colonnes['etendu'])) {
312
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
313
		}
3131 delphine 314
 
1690 raphael 315
		// header
316
		$indice = 0;
2459 jpm 317
		foreach ($intitule_champs as &$intitule) {
1692 raphael 318
			$worksheet->write(0,$indice++,$intitule);
1690 raphael 319
		}
3131 delphine 320
 
1617 aurelien 321
		foreach($data as &$ligne) {
1692 raphael 322
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 323
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1402 aurelien 324
			$indice = 0;
1617 aurelien 325
			foreach($ligne as &$champ) {
1692 raphael 326
				$worksheet->write($nb_lignes,$indice++,$champ);
1402 aurelien 327
			}
328
			$nb_lignes++;
329
		}
330
		return $workbook;
331
	}
3131 delphine 332
 
1662 aurelien 333
	private function convertirEnPdf(&$observations) {
1715 raphael 334
		if(count($observations) > 300) die('erreur: trop de données');
1662 aurelien 335
		//require_once('GenerateurPDF.php');
336
		$pdf = new GenerateurPDF();
337
		$pdf->export($observations);
338
		return $pdf;
339
	}
3131 delphine 340
 
1692 raphael 341
	static function filtrerDonneesSensibles($ligne) {
1429 aurelien 342
		if(stripos($ligne['mots_cles_texte'], 'sensible') !== false) {
343
			$ligne['latitude'] = '';
344
			$ligne['longitude'] = '';
345
		}
346
		return $ligne;
347
	}
3131 delphine 348
 
1654 aurelien 349
	private function doitEtPeutExporterObsPrivees($criteres) {
2459 jpm 350
		return isset($criteres['ce_utilisateur']) &&
3131 delphine 351
		$this->peutExporterObsPrivees($criteres['ce_utilisateur']);
1654 aurelien 352
	}
3131 delphine 353
 
1654 aurelien 354
	private function peutExporterObsPrivees($id_utilisateur) {
2806 aurelien 355
		$gestion_utilisateur = new GestionUtilisateur($this->config);
1654 aurelien 356
		$utilisateur = $gestion_utilisateur->obtenirIdentiteConnectee();
1703 raphael 357
		return ! empty($utilisateur['id_utilisateur']) && $id_utilisateur == $utilisateur['id_utilisateur'];
1654 aurelien 358
	}
3131 delphine 359
 
1679 raphael 360
	static function traiterObsIds($range_param) {
361
		if (!isset($range_param)) return NULL;
362
		// trim() car: `POST http://url<<<"range=*"`
363
		if (trim($range_param) == '*') return NULL;
364
		return self::rangeToList(trim($range_param));
365
	}
3131 delphine 366
 
1679 raphael 367
	/*
368
	 * @param $fieldSets: un range, eg: 1-5,8,32,58-101
369
	 * @return un tableau trié, eg: 1,2,3,4,5,8,32,58,...,101
370
	 * http://stackoverflow.com/questions/7698664/converting-a-range-or-partial-array-in-the-form-3-6-or-3-6-12-into-an-arra
371
	 */
372
	static function rangeToList($in = '') {
373
		$inSets = explode(',', trim($in, ','));
374
		$outSets = array();
3131 delphine 375
 
1679 raphael 376
		foreach($inSets as $inSet) {
377
			list($start,$end) = explode('-', $inSet . '-' . $inSet);
378
			// ignore les ranges trop importants
379
			if($start > 10000000 || $end > 10000000 || abs($start-$end) > 10000) continue;
380
			$outSets = array_merge($outSets,range($start,$end));
381
		}
382
		$outSets = array_unique($outSets);
383
		$outSets = array_filter($outSets, 'is_numeric');
384
		sort($outSets);
385
		return $outSets;
386
	}
1374 aurelien 387
}
388
?>