Subversion Repositories eFlore/Applications.cel

Rev

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

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