Subversion Repositories eFlore/Applications.cel

Rev

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

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