Subversion Repositories eFlore/Applications.cel

Rev

Rev 1716 | Blame | Last modification | View Log | RSS feed

<?php
/**
* Service fournissant des informations concernant le CEL au format RSS1, RSS2 ou ATOM.
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* Format du service :
* /CelWidgetExport/format
* /CelWidgetExport/csv
*
* Les paramêtres :
*  - "start" indique le numéro du premier item à afficher
*  - "limit" nombre d'items à afficher
*
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version $Id$
* @copyright 2012
*/

set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
// la sortie est binaire (xls), mais OLE n'est pas compatible E_ALL en PHP-5.4
error_reporting(error_reporting() & ~E_STRICT);
require_once("lib/OLE.php");
require_once("lib/Spreadsheet/Excel/Writer.php");

class CelWidgetExport extends Cel {
        
        private $nom_fichier_export = 'cel_export';
        // certains paramètres apparaissent plusieurs fois car ils ont des alias
        // dans certains widgets
        private $parametres_autorises = array(
                'id_utilisateur' => 'ce_utilisateur',
                'utilisateur' => 'courriel_utilisateur',
                'commune' => 'zone_geo',
                'zone_geo' => 'zone_geo',
                'dept' => 'departement',
                'departement' => 'departement',
                'lieudit' => 'lieudit',
                'station' => 'station',
                'projet' => 'mots_cles',
                'num_taxon' => 'nt',
                'date_debut' => 'date_debut',
                'date_fin' => 'date_fin',
                'taxon' => 'taxon',
                'annee' => 'annee',
                'mois' => 'mois',
                'jour' => 'jour',
                'recherche' => 'recherche',
                'id_mots_cles' => 'id_mots_cles',
                'mots_cles' => 'mots_cles',
                'debut' => 'debut',
                'limite' => 'limite',
                'format' => 'format',
                'colonnes' => 'colonnes',
                'transmission' => 'transmission',
                'obsids' => 'obsids',
        );
        
        private $limite_decoupage_defaut = 9000; 
        
        private $format = 'csv';
        
        public $id_utilisateur = null;

        // un cache, initialisé par certaines fonctions de préchargement, à la manière
        // de ce qui est fait par FormateurGroupeColonne
        static $cache = Array();
        
        public function getRessource() {
                return $this->getElement(array());
        }
                
        /**
         * Méthode appelée avec une requête de type GET.
         */
        public function getElement($params = array()) {
                switch(@strtolower($params[0])) {
                case 'calcul':
                        $this->getCalcul();
                        break;
                                
                case 'export':
                        $this->getExport();
                        break;
                default:
                        $this->getExport();
                }
        }
        
        private function getCalcul() {
                $criteres = $this->traiterParametresAutorises($_GET);

                $criteres['transmission'] = 1;
                if($this->doitEtPeutExporterObsPrivees($criteres)) {
                        unset($criteres['transmission']);
                        $this->id_utilisateur = $criteres['id_utilisateur'];
                }
                $chercheur_observations = new RechercheObservation($this->config);
                
                $numero_page = isset($criteres['debut']) ? $criteres['debut'] : 0;
                $limite = isset($criteres['limite']) ? $criteres['limite'] : 0;
                $colonnes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
                
                unset($criteres['limite']);
                unset($criteres['debut']);
                unset($criteres['format']);
                unset($criteres['colonnes']);
                
                $nb_observations = $chercheur_observations->compterObservations(null, $criteres);
                $limite_decoupage = $this->calculerNbLignesMaxParFichier();
                
                $url_telechargements = array();
                $intervalle = 0;
                
                $params_url = $criteres;
                unset($params_url['transmission']);
                do {
                        $base_url = $this->config['settings']['baseURLAbsolu'].'CelWidgetExport/export';
                        $params_url['debut'] = $intervalle;
                        $params_url['limite'] = $limite_decoupage;
                        $url_telechargement_fichier = $base_url;
                        $url_telechargements[] = $base_url.'?'.http_build_query($params_url).'&format='.$this->format.'&colonnes='.$colonnes;
                        $intervalle += $limite_decoupage;
                        $nb_observations -= $limite_decoupage;
                } while($nb_observations >= $limite_decoupage);
                
                $this->envoyerJson($url_telechargements);
        }
        
        private function calculerNbLignesMaxParFichier() {
                $limite = $this->limite_decoupage_defaut;
                switch($this->format) {
                        case 'csv':
                                $limite = 20000;
                                break;
                        case 'xls':
                                $limite = 8000;
                                break;
                        case 'pdf':
                                $limite = 300;
                                break;
                }
                return $limite;
        }
        
        private function getExport() {
                $criteres = $this->traiterParametresAutorises($_GET);
                // ne pas faire de super-requête en cas d'absence de paramètres
                // par exemple "format", au minimum, devrait être défini
                if(!$criteres) die('erreur: pas de paramètre reçu');
                if(!in_array($this->format, array('pdf','csv','xls'))) die('erreur: format invalide');

                $criteres['transmission'] = 1;
                if($this->doitEtPeutExporterObsPrivees($criteres)) {
                        unset($criteres['transmission']);
                }
                $chercheur_observations = new RechercheObservation($this->config);
                
                $debut = isset($criteres['debut']) ? intval($criteres['debut']) : 0;
                $limite = isset($criteres['limite']) ? intval($criteres['limite']) : 0;
                $groupes = @FormateurGroupeColonne::colGroupsValidation($criteres['colonnes']);
                if(!$groupes) die('erreur: Ne peut identifier les groupes de champs demandés.');


                if($criteres['obsids']) $criteres['sql_brut'] = sprintf('id_observation IN (%s)',
                                                                                                                                implode(',', $criteres['obsids']));
                
                unset($criteres['limite']);
                unset($criteres['debut']);
                unset($criteres['format']);
                unset($criteres['colonnes']);
                unset($criteres['obsids']);

                $observations = $chercheur_observations->rechercherObservations(null, $criteres, $debut, $limite, TRUE)->get();
                $ids = array();
                foreach($observations as &$obs) {
                        $ids[] = $obs['id_observation'];
                }

                if($this->format == 'pdf') {
                        $pdf = $this->convertirEnPdf($observations);
                        $pdf->pdf->Output('etiquettes.pdf', 'I');
                        exit;
                }

                // cas XLS et CSV: on peut avoir besoin des champs étendus, des noms communs et des champs baseflor:

                // Obtention des colonnes correspondantes aux groupes de champs
                $colonnes = FormateurGroupeColonne::nomEnsembleVersListeColonnes($groupes);

                /*
                  Champs étendus et noms communs, si demandés.
                  * Pour "nom commun", "preload" retourne NULL, car c'est le cache statique de FormateurGroupeColonne
                  qu'il initialise et utilise en interne sans qu'un passage par paramètre dans le contexte de CelWidgetExport
                  ne soit nécessaire.
                  * Pour les champs étendus, c'est CelWidgetExport::$cache qui est utilisé, aussi bien pour les en-têtes que
                  pour les données préchargées, cf self::traiterLigneEtendue()
                */
                self::$cache = FormateurGroupeColonne::preload($colonnes, $this, $ids);

        // TODO: tous les champs étendus et les paramètres supplémentaires devraient être passés en un seul
        // tableau (et chaque formateur csv, xls etc... pourrait également être dans une classe à part)
                switch($this->format) {
                case 'csv':
                        $csv = $this->convertirEnCsv($observations, $colonnes);
                        $this->envoyerCsv($csv);
                        break;
                case 'xls':
                        $xls = $this->convertirEnXls($observations, $colonnes);
                        $this->envoyerXls($xls);
                        break;
                default:
                }
        }
        
        protected function traiterParametresAutorises(Array $parametres) {
                $parametres_traites = array();
                $this->format = (isset($parametres['format']) && $parametres['format'] != '') ? $parametres['format'] : $this->format;
                foreach($parametres as $cle => $valeur) {
                        if(is_string($valeur) && !trim($valeur)) continue;
                        if(isset($this->parametres_autorises[$cle])) {
                                $parametres_traites[$this->parametres_autorises[$cle]] = $valeur;
                        }
                }
                $parametres_traites['obsids'] = @self::traiterObsIds($parametres['obsids']);
                return $parametres_traites;
        }
        
        private function envoyerCsv($csv) {
                header('Content-Type: text/csv; charset=UTF-8');
                header('Content-Disposition: attachment;filename='.$this->nom_fichier_export.'.csv');
                echo $csv;
                exit;
        }
        
        private function envoyerXls($workbook) {
                $workbook->close();
                exit;
        }
        
        private function convertirEnCsv(&$data, $colonnes) {
                $chemin_temp = "php://temp";
                $outstream = fopen($chemin_temp, 'r+');

                $intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
                // en premier car utilisé génériquement dans getLigneObservation()
                if(isset($colonnes['baseflor'])) {
                        $intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
                }
                // en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
                if(isset($colonnes['etendu'])) {
                        $intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
                }

                // header
                fputcsv($outstream, $intitule_champs, ',', '"');                        
                // lignes
                foreach($data as &$ligne) {
                        $ligne = self::filtrerDonneesSensibles($ligne);
                        $ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
                        fputcsv($outstream, $ligne, ',', '"');
                }
                rewind($outstream);
                $csv = stream_get_contents($outstream);
                fclose($outstream);
                return $csv;
        }
        
        private function convertirEnXls(&$data, $colonnes) {
                $this->extendSpreadsheetProductor = new SpreadsheetProductor();
                $this->extendSpreadsheetProductor->initSpreadsheet();
                
                $workbook = new Spreadsheet_Excel_Writer();
                $worksheet = $workbook->addWorksheet('Liste');
                $workbook->setTempDir($this->config['cel']['chemin_stockage_temp']);
                $workbook->setVersion(8);

                $worksheet->setInputEncoding('utf-8');
                $workbook->send($this->nom_fichier_export.'.xls');
                
                $nb_lignes = 1;

                $intitule_champs = array_merge(FormateurGroupeColonne::getIntitulesColonnes($colonnes));
                // en premier car utilisé génériquement dans getLigneObservation()
                if(isset($colonnes['baseflor'])) {
                        $intitule_champs = array_merge($intitule_champs, FormateurGroupeColonne::$baseflor_col);
                }
                // en second car manuellement appellé plus bas, TODO: utiliser l'API du FormateurGroupeColonne
                if(isset($colonnes['etendu'])) {
                        $intitule_champs = array_merge($intitule_champs, array_values(self::$cache['etendu']['header']));
                }

                // header
                $indice = 0;
                foreach ($intitule_champs as &$intitule) {      
                        $worksheet->write(0,$indice++,$intitule);
                }

                foreach($data as &$ligne) {
                        $ligne = self::filtrerDonneesSensibles($ligne);
                        $ligne = FormateurGroupeColonne::getLigneObservation($ligne, $colonnes, $this);
                        $indice = 0;
                        foreach($ligne as &$champ) {
                                $worksheet->write($nb_lignes,$indice++,$champ);
                        }
                        $nb_lignes++;
                }
                return $workbook;
        }
        
        private function convertirEnPdf(&$observations) {
                if(count($observations) > 300) die('erreur: trop de données');
                //require_once('GenerateurPDF.php');
                $pdf = new GenerateurPDF();
                $pdf->export($observations);
                return $pdf;
        }
        
        static function filtrerDonneesSensibles($ligne) {
                if(stripos($ligne['mots_cles_texte'], 'sensible') !== false) {
                        $ligne['latitude'] = '';
                        $ligne['longitude'] = '';
                }
                return $ligne;
        }
        
        private function doitEtPeutExporterObsPrivees($criteres) {
                return isset($criteres['ce_utilisateur']) && 
                                        $this->peutExporterObsPrivees($criteres['ce_utilisateur']);
        }
        
        private function peutExporterObsPrivees($id_utilisateur) {
                $gestion_utilisateur = new User($this->config);
                $utilisateur = $gestion_utilisateur->obtenirIdentiteConnectee();
                return ! empty($utilisateur['id_utilisateur']) && $id_utilisateur == $utilisateur['id_utilisateur'];
        }

        static function traiterObsIds($range_param) {
                if (!isset($range_param)) return NULL;
                // trim() car: `POST http://url<<<"range=*"`
                if (trim($range_param) == '*') return NULL;
                return self::rangeToList(trim($range_param));
        }

        /*
         * @param $fieldSets: un range, eg: 1-5,8,32,58-101
         * @return un tableau trié, eg: 1,2,3,4,5,8,32,58,...,101
         * http://stackoverflow.com/questions/7698664/converting-a-range-or-partial-array-in-the-form-3-6-or-3-6-12-into-an-arra
         */
        static function rangeToList($in = '') {
                $inSets = explode(',', trim($in, ','));
                $outSets = array();

                foreach($inSets as $inSet) {
                        list($start,$end) = explode('-', $inSet . '-' . $inSet);
                        // ignore les ranges trop importants
                        if($start > 10000000 || $end > 10000000 || abs($start-$end) > 10000) continue;
                        $outSets = array_merge($outSets,range($start,$end));
                }
                $outSets = array_unique($outSets);
                $outSets = array_filter($outSets, 'is_numeric');
                sort($outSets);
                return $outSets;
        }
}
?>