Subversion Repositories eFlore/Applications.cel

Rev

Rev 2221 | 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',
2279 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);
112
		$limite_decoupage = $this->calculerNbLignesMaxParFichier();
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
 
1625 aurelien 132
	private function calculerNbLignesMaxParFichier() {
133
		$limite = $this->limite_decoupage_defaut;
1402 aurelien 134
		switch($this->format) {
135
			case 'csv':
1625 aurelien 136
				$limite = 20000;
137
				break;
138
			case 'xls':
139
				$limite = 8000;
140
				break;
1660 raphael 141
			case 'pdf':
142
				$limite = 300;
143
				break;
1625 aurelien 144
		}
145
		return $limite;
146
	}
147
 
148
	private function getExport() {
149
		$criteres = $this->traiterParametresAutorises($_GET);
1711 raphael 150
		// ne pas faire de super-requête en cas d'absence de paramètres
151
		// par exemple "format", au minimum, devrait être défini
1715 raphael 152
		if(!$criteres) die('erreur: pas de paramètre reçu');
153
		if(!in_array($this->format, array('pdf','csv','xls'))) die('erreur: format invalide');
1711 raphael 154
 
1625 aurelien 155
		$criteres['transmission'] = 1;
1654 aurelien 156
		if($this->doitEtPeutExporterObsPrivees($criteres)) {
157
			unset($criteres['transmission']);
1755 raphael 158
			$this->id_utilisateur = $criteres['ce_utilisateur'];
1654 aurelien 159
		}
1625 aurelien 160
		$chercheur_observations = new RechercheObservation($this->config);
161
 
1679 raphael 162
		$debut = isset($criteres['debut']) ? intval($criteres['debut']) : 0;
163
		$limite = isset($criteres['limite']) ? intval($criteres['limite']) : 0;
1714 raphael 164
		$groupes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
1835 raphael 165
		$groupes .= ',auteur';
166
 
1714 raphael 167
		if(!$groupes) die('erreur: Ne peut identifier les groupes de champs demandés.');
1679 raphael 168
 
1714 raphael 169
 
1679 raphael 170
		if($criteres['obsids']) $criteres['sql_brut'] = sprintf('id_observation IN (%s)',
171
																implode(',', $criteres['obsids']));
1625 aurelien 172
 
173
		unset($criteres['limite']);
174
		unset($criteres['debut']);
175
		unset($criteres['format']);
1654 aurelien 176
		unset($criteres['colonnes']);
1679 raphael 177
		unset($criteres['obsids']);
178
 
179
		$observations = $chercheur_observations->rechercherObservations(null, $criteres, $debut, $limite, TRUE)->get();
1659 aurelien 180
		$ids = array();
181
		foreach($observations as &$obs) {
182
			$ids[] = $obs['id_observation'];
183
		}
184
 
1703 raphael 185
		if($this->format == 'pdf') {
186
			$pdf = $this->convertirEnPdf($observations);
187
			$pdf->pdf->Output('etiquettes.pdf', 'I');
188
			exit;
1662 aurelien 189
		}
190
 
1703 raphael 191
		// cas XLS et CSV: on peut avoir besoin des champs étendus, des noms communs et des champs baseflor:
1715 raphael 192
 
193
		// Obtention des colonnes correspondantes aux groupes de champs
1714 raphael 194
		$colonnes = FormateurGroupeColonne::nomEnsembleVersListeColonnes($groupes);
1703 raphael 195
 
1715 raphael 196
		/*
197
		  Champs étendus et noms communs, si demandés.
198
		  * Pour "nom commun", "preload" retourne NULL, car c'est le cache statique de FormateurGroupeColonne
199
		  qu'il initialise et utilise en interne sans qu'un passage par paramètre dans le contexte de CelWidgetExport
200
		  ne soit nécessaire.
201
		  * Pour les champs étendus, c'est CelWidgetExport::$cache qui est utilisé, aussi bien pour les en-têtes que
202
		  pour les données préchargées, cf self::traiterLigneEtendue()
203
		*/
204
		self::$cache = FormateurGroupeColonne::preload($colonnes, $this, $ids);
1703 raphael 205
 
1659 aurelien 206
    	// TODO: tous les champs étendus et les paramètres supplémentaires devraient être passés en un seul
207
    	// tableau (et chaque formateur csv, xls etc... pourrait également être dans une classe à part)
1625 aurelien 208
		switch($this->format) {
1660 raphael 209
		case 'csv':
1715 raphael 210
			$csv = $this->convertirEnCsv($observations, $colonnes);
1660 raphael 211
			$this->envoyerCsv($csv);
212
			break;
213
		case 'xls':
1715 raphael 214
			$xls = $this->convertirEnXls($observations, $colonnes);
1660 raphael 215
			$this->envoyerXls($xls);
216
			break;
217
		default:
1402 aurelien 218
		}
1374 aurelien 219
	}
220
 
1611 raphael 221
	protected function traiterParametresAutorises(Array $parametres) {
1376 aurelien 222
		$parametres_traites = array();
1402 aurelien 223
		$this->format = (isset($parametres['format']) && $parametres['format'] != '') ? $parametres['format'] : $this->format;
1376 aurelien 224
		foreach($parametres as $cle => $valeur) {
1711 raphael 225
			if(is_string($valeur) && !trim($valeur)) continue;
226
			if(isset($this->parametres_autorises[$cle])) {
1376 aurelien 227
				$parametres_traites[$this->parametres_autorises[$cle]] = $valeur;
228
			}
229
		}
1679 raphael 230
		$parametres_traites['obsids'] = @self::traiterObsIds($parametres['obsids']);
1376 aurelien 231
		return $parametres_traites;
232
	}
233
 
1374 aurelien 234
	private function envoyerCsv($csv) {
235
		header('Content-Type: text/csv; charset=UTF-8');
1408 aurelien 236
		header('Content-Disposition: attachment;filename='.$this->nom_fichier_export.'.csv');
1374 aurelien 237
		echo $csv;
1376 aurelien 238
		exit;
1374 aurelien 239
	}
240
 
1402 aurelien 241
	private function envoyerXls($workbook) {
242
		$workbook->close();
243
		exit;
244
	}
245
 
1715 raphael 246
	private function convertirEnCsv(&$data, $colonnes) {
1374 aurelien 247
		$chemin_temp = "php://temp";
248
		$outstream = fopen($chemin_temp, 'r+');
1690 raphael 249
 
1711 raphael 250
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 251
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 252
		if(isset($colonnes['baseflor'])) {
1703 raphael 253
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
254
		}
255
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 256
		if(isset($colonnes['etendu'])) {
257
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
258
		}
1690 raphael 259
 
260
		// header
261
		fputcsv($outstream, $intitule_champs, ',', '"');
262
		// lignes
1617 aurelien 263
		foreach($data as &$ligne) {
1692 raphael 264
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 265
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1374 aurelien 266
			fputcsv($outstream, $ligne, ',', '"');
267
		}
268
		rewind($outstream);
269
		$csv = stream_get_contents($outstream);
270
		fclose($outstream);
271
		return $csv;
272
	}
273
 
1715 raphael 274
	private function convertirEnXls(&$data, $colonnes) {
1402 aurelien 275
		$this->extendSpreadsheetProductor = new SpreadsheetProductor();
276
		$this->extendSpreadsheetProductor->initSpreadsheet();
277
 
278
		$workbook = new Spreadsheet_Excel_Writer();
1804 raphael 279
		// avant la définition du titre de la worksheet !
280
		$workbook->setVersion(8);
281
 
1625 aurelien 282
		$worksheet = $workbook->addWorksheet('Liste');
283
		$workbook->setTempDir($this->config['cel']['chemin_stockage_temp']);
1612 raphael 284
		$worksheet->setInputEncoding('utf-8');
1408 aurelien 285
		$workbook->send($this->nom_fichier_export.'.xls');
1402 aurelien 286
 
287
		$nb_lignes = 1;
1690 raphael 288
 
1711 raphael 289
		$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
1703 raphael 290
		// en premier car utilisé génériquement dans getLigneObservation()
1711 raphael 291
		if(isset($colonnes['baseflor'])) {
1703 raphael 292
			$intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
293
		}
294
		// en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
1715 raphael 295
		if(isset($colonnes['etendu'])) {
296
			$intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
297
		}
1703 raphael 298
 
1690 raphael 299
		// header
300
		$indice = 0;
301
		foreach ($intitule_champs as &$intitule) {
1692 raphael 302
			$worksheet->write(0,$indice++,$intitule);
1690 raphael 303
		}
304
 
1617 aurelien 305
		foreach($data as &$ligne) {
1692 raphael 306
			$ligne = self::filtrerDonneesSensibles($ligne);
1711 raphael 307
			$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
1402 aurelien 308
			$indice = 0;
1617 aurelien 309
			foreach($ligne as &$champ) {
1692 raphael 310
				$worksheet->write($nb_lignes,$indice++,$champ);
1402 aurelien 311
			}
312
			$nb_lignes++;
313
		}
314
		return $workbook;
315
	}
316
 
1662 aurelien 317
	private function convertirEnPdf(&$observations) {
1715 raphael 318
		if(count($observations) > 300) die('erreur: trop de données');
1662 aurelien 319
		//require_once('GenerateurPDF.php');
320
		$pdf = new GenerateurPDF();
321
		$pdf->export($observations);
322
		return $pdf;
323
	}
324
 
1692 raphael 325
	static function filtrerDonneesSensibles($ligne) {
1429 aurelien 326
		if(stripos($ligne['mots_cles_texte'], 'sensible') !== false) {
327
			$ligne['latitude'] = '';
328
			$ligne['longitude'] = '';
329
		}
330
		return $ligne;
331
	}
332
 
1654 aurelien 333
	private function doitEtPeutExporterObsPrivees($criteres) {
334
		return isset($criteres['ce_utilisateur']) &&
335
					$this->peutExporterObsPrivees($criteres['ce_utilisateur']);
336
	}
337
 
338
	private function peutExporterObsPrivees($id_utilisateur) {
339
		$gestion_utilisateur = new User($this->config);
340
		$utilisateur = $gestion_utilisateur->obtenirIdentiteConnectee();
1703 raphael 341
		return ! empty($utilisateur['id_utilisateur']) && $id_utilisateur == $utilisateur['id_utilisateur'];
1654 aurelien 342
	}
1679 raphael 343
 
344
	static function traiterObsIds($range_param) {
345
		if (!isset($range_param)) return NULL;
346
		// trim() car: `POST http://url<<<"range=*"`
347
		if (trim($range_param) == '*') return NULL;
348
		return self::rangeToList(trim($range_param));
349
	}
350
 
351
	/*
352
	 * @param $fieldSets: un range, eg: 1-5,8,32,58-101
353
	 * @return un tableau trié, eg: 1,2,3,4,5,8,32,58,...,101
354
	 * http://stackoverflow.com/questions/7698664/converting-a-range-or-partial-array-in-the-form-3-6-or-3-6-12-into-an-arra
355
	 */
356
	static function rangeToList($in = '') {
357
		$inSets = explode(',', trim($in, ','));
358
		$outSets = array();
359
 
360
		foreach($inSets as $inSet) {
361
			list($start,$end) = explode('-', $inSet . '-' . $inSet);
362
			// ignore les ranges trop importants
363
			if($start > 10000000 || $end > 10000000 || abs($start-$end) > 10000) continue;
364
			$outSets = array_merge($outSets,range($start,$end));
365
		}
366
		$outSets = array_unique($outSets);
367
		$outSets = array_filter($outSets, 'is_numeric');
368
		sort($outSets);
369
		return $outSets;
370
	}
1374 aurelien 371
}
372
?>