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
|
1690 |
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',
|
|
|
35 |
'commune' => 'zone_geo',
|
1671 |
aurelien |
36 |
'zone_geo' => 'zone_geo',
|
1376 |
aurelien |
37 |
'dept' => 'departement',
|
1654 |
aurelien |
38 |
'departement' => 'departement',
|
|
|
39 |
'lieudit' => 'lieudit',
|
|
|
40 |
'station' => 'station',
|
1376 |
aurelien |
41 |
'projet' => 'mots_cles',
|
1379 |
aurelien |
42 |
'num_taxon' => 'nt',
|
1378 |
aurelien |
43 |
'date_debut' => 'date_debut',
|
1387 |
aurelien |
44 |
'date_fin' => 'date_fin',
|
1625 |
aurelien |
45 |
'taxon' => 'taxon',
|
1654 |
aurelien |
46 |
'annee' => 'annee',
|
|
|
47 |
'mois' => 'mois',
|
|
|
48 |
'jour' => 'jour',
|
|
|
49 |
'recherche' => 'recherche',
|
|
|
50 |
'id_mots_cles' => 'id_mots_cles',
|
1662 |
aurelien |
51 |
'mots_cles' => 'mots_cles',
|
1625 |
aurelien |
52 |
'debut' => 'debut',
|
|
|
53 |
'limite' => 'limite',
|
1654 |
aurelien |
54 |
'format' => 'format',
|
|
|
55 |
'colonnes' => 'colonnes',
|
1679 |
raphael |
56 |
'transmission' => 'transmission',
|
|
|
57 |
'obsids' => 'obsids',
|
1376 |
aurelien |
58 |
);
|
1402 |
aurelien |
59 |
|
1625 |
aurelien |
60 |
private $limite_decoupage_defaut = 9000;
|
|
|
61 |
|
1402 |
aurelien |
62 |
private $format = 'csv';
|
1579 |
aurelien |
63 |
|
1659 |
aurelien |
64 |
public $id_utilisateur = null;
|
|
|
65 |
|
1579 |
aurelien |
66 |
public function getRessource() {
|
|
|
67 |
return $this->getElement(array());
|
|
|
68 |
}
|
1374 |
aurelien |
69 |
|
|
|
70 |
/**
|
|
|
71 |
* Méthode appelée avec une requête de type GET.
|
|
|
72 |
*/
|
1625 |
aurelien |
73 |
public function getElement($params = array()) {
|
|
|
74 |
if(count($params) > 0) {
|
|
|
75 |
switch(strtolower($params[0])) {
|
|
|
76 |
case 'calcul':
|
|
|
77 |
$this->getCalcul();
|
|
|
78 |
break;
|
|
|
79 |
|
|
|
80 |
case 'export':
|
|
|
81 |
$this->getExport();
|
|
|
82 |
break;
|
|
|
83 |
|
|
|
84 |
default:
|
|
|
85 |
$this->getExport();
|
|
|
86 |
}
|
|
|
87 |
} else {
|
|
|
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;
|
1654 |
aurelien |
104 |
$colonnes = isset($criteres['colonnes']) ? $criteres['colonnes'] : 'standard,avance';
|
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;
|
|
|
127 |
} while($nb_observations >= $limite_decoupage);
|
|
|
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);
|
|
|
150 |
$criteres['transmission'] = 1;
|
1654 |
aurelien |
151 |
if($this->doitEtPeutExporterObsPrivees($criteres)) {
|
|
|
152 |
unset($criteres['transmission']);
|
|
|
153 |
}
|
1625 |
aurelien |
154 |
$chercheur_observations = new RechercheObservation($this->config);
|
|
|
155 |
|
1679 |
raphael |
156 |
$debut = isset($criteres['debut']) ? intval($criteres['debut']) : 0;
|
|
|
157 |
$limite = isset($criteres['limite']) ? intval($criteres['limite']) : 0;
|
1654 |
aurelien |
158 |
$colonnes = isset($criteres['colonnes']) ? $criteres['colonnes'] : 'standard,avance';
|
1679 |
raphael |
159 |
|
|
|
160 |
if($criteres['obsids']) $criteres['sql_brut'] = sprintf('id_observation IN (%s)',
|
|
|
161 |
implode(',', $criteres['obsids']));
|
1625 |
aurelien |
162 |
|
|
|
163 |
unset($criteres['limite']);
|
|
|
164 |
unset($criteres['debut']);
|
|
|
165 |
unset($criteres['format']);
|
1654 |
aurelien |
166 |
unset($criteres['colonnes']);
|
1679 |
raphael |
167 |
unset($criteres['obsids']);
|
|
|
168 |
|
|
|
169 |
$observations = $chercheur_observations->rechercherObservations(null, $criteres, $debut, $limite, TRUE)->get();
|
1659 |
aurelien |
170 |
$ids = array();
|
|
|
171 |
foreach($observations as &$obs) {
|
|
|
172 |
$ids[] = $obs['id_observation'];
|
|
|
173 |
}
|
|
|
174 |
|
1662 |
aurelien |
175 |
if($this->format != 'pdf') {
|
|
|
176 |
$gestion_champs_etendus = new GestionChampsEtendus($this->config, 'obs');
|
|
|
177 |
$champs_supp_par_obs = $gestion_champs_etendus->consulterParLots($ids);
|
|
|
178 |
$colonnes_champs_supp_par_obs = $gestion_champs_etendus->consulterClesParLots($ids);
|
1694 |
raphael |
179 |
// $cache pourrait être utilisé par les fonctions de colonnes
|
|
|
180 |
// * Pour "nom commun", "preload" retourne NULL, car c'est le cache statique de FormateurGroupeColonne
|
|
|
181 |
// qu'il initialise et utilise en interne sans qu'un passage par paramètre ne soit nécessaire
|
|
|
182 |
$cache = FormateurGroupeColonne::preload(FormateurGroupeColonne::nomEnsembleVersListeColonnes($colonnes),
|
|
|
183 |
$this,
|
|
|
184 |
$ids);
|
1662 |
aurelien |
185 |
}
|
|
|
186 |
|
1659 |
aurelien |
187 |
// TODO: tous les champs étendus et les paramètres supplémentaires devraient être passés en un seul
|
|
|
188 |
// tableau (et chaque formateur csv, xls etc... pourrait également être dans une classe à part)
|
1625 |
aurelien |
189 |
switch($this->format) {
|
1660 |
raphael |
190 |
case 'csv':
|
|
|
191 |
$csv = $this->convertirEnCsv($observations, $colonnes, $colonnes_champs_supp_par_obs, $champs_supp_par_obs);
|
|
|
192 |
$this->envoyerCsv($csv);
|
|
|
193 |
break;
|
|
|
194 |
case 'xls':
|
|
|
195 |
$xls = $this->convertirEnXls($observations, $colonnes, $colonnes_champs_supp_par_obs, $champs_supp_par_obs);
|
|
|
196 |
$this->envoyerXls($xls);
|
|
|
197 |
break;
|
|
|
198 |
case 'pdf':
|
1662 |
aurelien |
199 |
$pdf = $this->convertirEnPdf($observations);
|
|
|
200 |
$this->envoyerPdf($pdf);
|
1660 |
raphael |
201 |
break;
|
|
|
202 |
default:
|
1402 |
aurelien |
203 |
}
|
1374 |
aurelien |
204 |
}
|
|
|
205 |
|
1611 |
raphael |
206 |
protected function traiterParametresAutorises(Array $parametres) {
|
1376 |
aurelien |
207 |
$parametres_traites = array();
|
1402 |
aurelien |
208 |
$this->format = (isset($parametres['format']) && $parametres['format'] != '') ? $parametres['format'] : $this->format;
|
1376 |
aurelien |
209 |
foreach($parametres as $cle => $valeur) {
|
|
|
210 |
if(trim($valeur) != '' && isset($this->parametres_autorises[$cle])) {
|
|
|
211 |
$parametres_traites[$this->parametres_autorises[$cle]] = $valeur;
|
|
|
212 |
}
|
|
|
213 |
}
|
1679 |
raphael |
214 |
$parametres_traites['obsids'] = @self::traiterObsIds($parametres['obsids']);
|
1376 |
aurelien |
215 |
return $parametres_traites;
|
|
|
216 |
}
|
|
|
217 |
|
1374 |
aurelien |
218 |
private function envoyerCsv($csv) {
|
|
|
219 |
header('Content-Type: text/csv; charset=UTF-8');
|
1408 |
aurelien |
220 |
header('Content-Disposition: attachment;filename='.$this->nom_fichier_export.'.csv');
|
1374 |
aurelien |
221 |
echo $csv;
|
1376 |
aurelien |
222 |
exit;
|
1374 |
aurelien |
223 |
}
|
|
|
224 |
|
1402 |
aurelien |
225 |
private function envoyerXls($workbook) {
|
|
|
226 |
$workbook->close();
|
|
|
227 |
exit;
|
|
|
228 |
}
|
|
|
229 |
|
1692 |
raphael |
230 |
private function convertirEnCsv(&$data, &$colonnes, &$colonnes_supplementaires, &$champs_supplementaires = array()) {
|
1374 |
aurelien |
231 |
$chemin_temp = "php://temp";
|
|
|
232 |
$outstream = fopen($chemin_temp, 'r+');
|
1690 |
raphael |
233 |
|
1654 |
aurelien |
234 |
$groupe_colonnes = FormateurGroupeColonne::nomEnsembleVersListeColonnes($colonnes);
|
1690 |
raphael |
235 |
|
1692 |
raphael |
236 |
$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($groupe_colonnes),
|
|
|
237 |
array_values($colonnes_supplementaires));
|
1690 |
raphael |
238 |
|
|
|
239 |
// header
|
|
|
240 |
fputcsv($outstream, $intitule_champs, ',', '"');
|
|
|
241 |
// lignes
|
1617 |
aurelien |
242 |
foreach($data as &$ligne) {
|
1659 |
aurelien |
243 |
$id_obs = $ligne['id_observation'];
|
1692 |
raphael |
244 |
$ligne = self::filtrerDonneesSensibles($ligne);
|
1659 |
aurelien |
245 |
$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $groupe_colonnes, $this);
|
1690 |
raphael |
246 |
$ligne_etendue_aplatie = self::aplatirChampsEtendus($champs_supplementaires[$id_obs]);
|
|
|
247 |
self::traiterLigneEtendue($ligne, $colonnes_supplementaires, $ligne_etendue_aplatie);
|
1374 |
aurelien |
248 |
fputcsv($outstream, $ligne, ',', '"');
|
|
|
249 |
}
|
|
|
250 |
rewind($outstream);
|
|
|
251 |
$csv = stream_get_contents($outstream);
|
|
|
252 |
fclose($outstream);
|
|
|
253 |
return $csv;
|
|
|
254 |
}
|
|
|
255 |
|
1662 |
aurelien |
256 |
private function convertirEnXls(&$data, &$colonnes, &$colonnes_supplementaires, &$champs_supplementaires = array()) {
|
1402 |
aurelien |
257 |
$this->extendSpreadsheetProductor = new SpreadsheetProductor();
|
|
|
258 |
$this->extendSpreadsheetProductor->initSpreadsheet();
|
|
|
259 |
|
|
|
260 |
$workbook = new Spreadsheet_Excel_Writer();
|
1625 |
aurelien |
261 |
$worksheet = $workbook->addWorksheet('Liste');
|
|
|
262 |
$workbook->setTempDir($this->config['cel']['chemin_stockage_temp']);
|
1612 |
raphael |
263 |
$workbook->setVersion(8);
|
1402 |
aurelien |
264 |
|
1612 |
raphael |
265 |
$worksheet->setInputEncoding('utf-8');
|
1408 |
aurelien |
266 |
$workbook->send($this->nom_fichier_export.'.xls');
|
1402 |
aurelien |
267 |
|
|
|
268 |
$nb_lignes = 1;
|
1654 |
aurelien |
269 |
$groupe_colonnes = FormateurGroupeColonne::nomEnsembleVersListeColonnes($colonnes);
|
1692 |
raphael |
270 |
$intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($groupe_colonnes),
|
|
|
271 |
array_values($colonnes_supplementaires));
|
1690 |
raphael |
272 |
|
|
|
273 |
// header
|
|
|
274 |
$indice = 0;
|
|
|
275 |
foreach ($intitule_champs as &$intitule) {
|
1692 |
raphael |
276 |
$worksheet->write(0,$indice++,$intitule);
|
1690 |
raphael |
277 |
}
|
|
|
278 |
|
1617 |
aurelien |
279 |
foreach($data as &$ligne) {
|
1659 |
aurelien |
280 |
$id_obs = $ligne['id_observation'];
|
1692 |
raphael |
281 |
$ligne = self::filtrerDonneesSensibles($ligne);
|
1659 |
aurelien |
282 |
$ligne = FormateurGroupeColonne::getLigneObservation($ligne, $groupe_colonnes, $this);
|
1690 |
raphael |
283 |
|
|
|
284 |
$ligne_etendue_aplatie = self::aplatirChampsEtendus($champs_supplementaires[$id_obs]);
|
|
|
285 |
$ligne_supp = self::traiterLigneEtendue($ligne, $colonnes_supplementaires, $ligne_etendue_aplatie);
|
1402 |
aurelien |
286 |
$indice = 0;
|
1617 |
aurelien |
287 |
foreach($ligne as &$champ) {
|
1692 |
raphael |
288 |
$worksheet->write($nb_lignes,$indice++,$champ);
|
1402 |
aurelien |
289 |
}
|
|
|
290 |
$nb_lignes++;
|
|
|
291 |
}
|
|
|
292 |
return $workbook;
|
|
|
293 |
}
|
|
|
294 |
|
1662 |
aurelien |
295 |
private function convertirEnPdf(&$observations) {
|
|
|
296 |
if(count($observations) > 300) die('trop de données');
|
|
|
297 |
//require_once('GenerateurPDF.php');
|
|
|
298 |
$pdf = new GenerateurPDF();
|
|
|
299 |
$pdf->export($observations);
|
|
|
300 |
return $pdf;
|
|
|
301 |
}
|
|
|
302 |
|
|
|
303 |
private function envoyerPdf(&$pdf) {
|
|
|
304 |
$pdf->pdf->Output('etiquettes.pdf', 'I');
|
|
|
305 |
exit;;
|
|
|
306 |
}
|
|
|
307 |
|
1690 |
raphael |
308 |
static function traiterLigneEtendue(&$ligne, &$colonnes_etendues, $ligne_etendue_aplatie) {
|
|
|
309 |
if(! $colonnes_etendues) return;
|
|
|
310 |
if(! $ligne_etendue_aplatie) return;
|
|
|
311 |
$nb_colonnes_supp = count($colonnes_etendues);
|
|
|
312 |
|
|
|
313 |
$ligne_supp = array_fill(0, $nb_colonnes_supp, '');
|
1659 |
aurelien |
314 |
$ligne_etendue_fmt = array();
|
1690 |
raphael |
315 |
|
1659 |
aurelien |
316 |
foreach($colonnes_etendues as $colonne) {
|
|
|
317 |
if(!isset($ligne_etendue_aplatie[$colonne])) {
|
|
|
318 |
$ligne_etendue_fmt[$colonne] = '';
|
|
|
319 |
} else {
|
|
|
320 |
$ligne_etendue_fmt[$colonne] = $ligne_etendue_aplatie[$colonne];
|
|
|
321 |
}
|
|
|
322 |
}
|
1690 |
raphael |
323 |
|
|
|
324 |
$ligne += $ligne_etendue_fmt;
|
1659 |
aurelien |
325 |
}
|
|
|
326 |
|
1690 |
raphael |
327 |
static function aplatirChampsEtendus(&$ligne_champs_etendus) {
|
1659 |
aurelien |
328 |
$champs_etendus_fmt = array();
|
|
|
329 |
foreach($ligne_champs_etendus as $champ) {
|
|
|
330 |
$champs_etendus_fmt[$champ->cle] = $champ->valeur;
|
|
|
331 |
}
|
|
|
332 |
return $champs_etendus_fmt;
|
|
|
333 |
}
|
|
|
334 |
|
1692 |
raphael |
335 |
static function filtrerDonneesSensibles($ligne) {
|
1429 |
aurelien |
336 |
if(stripos($ligne['mots_cles_texte'], 'sensible') !== false) {
|
|
|
337 |
$ligne['latitude'] = '';
|
|
|
338 |
$ligne['longitude'] = '';
|
|
|
339 |
}
|
|
|
340 |
return $ligne;
|
|
|
341 |
}
|
|
|
342 |
|
1654 |
aurelien |
343 |
private function doitEtPeutExporterObsPrivees($criteres) {
|
|
|
344 |
return isset($criteres['ce_utilisateur']) &&
|
|
|
345 |
$this->peutExporterObsPrivees($criteres['ce_utilisateur']);
|
|
|
346 |
}
|
|
|
347 |
|
|
|
348 |
private function peutExporterObsPrivees($id_utilisateur) {
|
|
|
349 |
$gestion_utilisateur = new User($this->config);
|
|
|
350 |
$utilisateur = $gestion_utilisateur->obtenirIdentiteConnectee();
|
|
|
351 |
return $utilisateur['connecte'] &&
|
|
|
352 |
$utilisateur['id_utilisateur'] != '' &&
|
|
|
353 |
$id_utilisateur == $utilisateur['id_utilisateur'];
|
|
|
354 |
}
|
1679 |
raphael |
355 |
|
|
|
356 |
static function traiterObsIds($range_param) {
|
|
|
357 |
if (!isset($range_param)) return NULL;
|
|
|
358 |
// trim() car: `POST http://url<<<"range=*"`
|
|
|
359 |
if (trim($range_param) == '*') return NULL;
|
|
|
360 |
return self::rangeToList(trim($range_param));
|
|
|
361 |
}
|
|
|
362 |
|
|
|
363 |
/*
|
|
|
364 |
* @param $fieldSets: un range, eg: 1-5,8,32,58-101
|
|
|
365 |
* @return un tableau trié, eg: 1,2,3,4,5,8,32,58,...,101
|
|
|
366 |
* http://stackoverflow.com/questions/7698664/converting-a-range-or-partial-array-in-the-form-3-6-or-3-6-12-into-an-arra
|
|
|
367 |
*/
|
|
|
368 |
static function rangeToList($in = '') {
|
|
|
369 |
$inSets = explode(',', trim($in, ','));
|
|
|
370 |
$outSets = array();
|
|
|
371 |
|
|
|
372 |
foreach($inSets as $inSet) {
|
|
|
373 |
list($start,$end) = explode('-', $inSet . '-' . $inSet);
|
|
|
374 |
// ignore les ranges trop importants
|
|
|
375 |
if($start > 10000000 || $end > 10000000 || abs($start-$end) > 10000) continue;
|
|
|
376 |
$outSets = array_merge($outSets,range($start,$end));
|
|
|
377 |
}
|
|
|
378 |
$outSets = array_unique($outSets);
|
|
|
379 |
$outSets = array_filter($outSets, 'is_numeric');
|
|
|
380 |
sort($outSets);
|
|
|
381 |
return $outSets;
|
|
|
382 |
}
|
1374 |
aurelien |
383 |
}
|
|
|
384 |
?>
|