^^
Bon voila, j'ai fini la mise en page, en espérant avoir été assez clair.

Le code fourni a été testé, mais je ne garanti pas l'absence de coquilles dans le code...

2 - Les éléments XHTML pour l'interface

2.1 - Boutons pour générer les documents

On propose la liste ci-dessous pour pointer vers la génération des différents fichiers. On aurait tout aussi bien pu écrire directement des liens vides. Les éléments de la liste possède tous la classe .generer qui va nous servir de sélecteur avec Jquery ainsi qu'un identifiant unique.

<ul id='blocLienGenerer'>
    <li><p><span class='generer' id='PDF'>Générer un PDF avec la librairie de phpExcel</span></p></li>
    <li><p><span class='generer' id='MPDF'>Générer un PDF avec la librairie MPDF</span></p></li>
    <li><p><span class='generer' id='Excel5'>Générer un document Excel5</span></p></li>
</ul>

2.2 - Bloc d'informations

On ajoute ensuite une div #loading qui va contenir l'image de chargement, du texte pour informer l'utilisateur sur l'état du chargement et une div optionnel pour afficher les informations de debug.

<div id="loading">
    <div id='loading-image'></div>
    <div id='loading-content'></div>
</div>
<div id='loading-debug'></div>

Les blocs div #loading et #loading-debug sont cachés en CSS par défaut

#loading{display:none;}
#loading-debug{display:none;}

Au lancement de la génération, on obtient le résultat suivant :

http://img4.hostingpics.net/pics/437235waitingMsgOnGeneratedFlux01.png

A la fin du traitement, on obtient le résultat suivant :

http://img4.hostingpics.net/pics/346424waitingMsgOnGeneratedFlux02.png



2.3 - Conteneur du script générant le flux de données (Excel, PDF, ...)

Enfin on place une iframe vide qui servira de conteneur pour charger le fichier à générer.

<iframe id="iframePhpExcel" src=""></iframe>

L'iframe est caché en CSS par défaut

#iframePhpExcel{visibility:hidden;position:absolute;}


3 - Génération du flux de données avec phpExcel côté serveur

3.1 - Script phpExcel_generation.php

Concernant la génération du flux de données, j'ai choisi d'utiliser la librairie phpExcel, je ne vais pas rentrer dans les détails sur la manière d'utiliser cette librairie.

Le script phpExcel_generation.php attend une variable passé en GET. Il s'agit de la variable idGenerer qui précise quel type de document doit être généré avec la librairie phpExcel. Il peut s'agir d'un document généré avec la librairie PDF par défaut de phpExcel à savoir TCPDF, il peut s'agir d'une librairie ajouter MPDF ou bien d'un fichier Excel5. La librairie MPDF est généralement utilisé pour des raisons de performances et prendre en compte le no-border. Le document généré utilise des données aléatoires créés pour remplir le document. On va voir séquentiellement, étape par étape les instructions de ce script.

On commence le script par créer une variable de session qui va prendre la valeur 'progress' pour indiquer que le traitement (la génération du flux de données est en cours). Puis tout de suite après on termine la session courante avec l'instruction session_write_close(); et ainsi on force le stockage de la variable de session pour qu'elle puisse être lu dans un autre script php (generationStatus.php) lance en Ajax retardé récursif.

3.2 - Initialisation variable de session

Le script commence par la déclaration de la variable de session generationStatut qui va renseigner si la génération du document est en cours ou fini.

@session_start();  
$_SESSION['generationStatut'] = "progress";

3.3 - Fermeture de la session courante

On ferme alors la session courrante immédiatement après, afin de permettre à d'autres script php de pouvoir lire cette variable de session. La fonction session_write_close termine la session courante, après avoir stocké les données, autrement dit on force le stockage de la variable de session pour qu'ellee puiss être utilisé dans le script generationStatus.php lancé en AJAX retardé récursif.

3.4 - Instanciation de phpExcel

On instancie l'objet phpExcel à partir duquel la librairie phpExcel va générer le document. La suite concerne la génération du flux de données via la librairie phpExcel. L'instance $objPHPExcel contient toutes les informations nécessaires à la génération du flux. (je ne rentre pas dans les détails d'utilisation de phpExcel ici)

include_once(dirname(__FILE__)."/lib/php/phpExcel/Classes/PHPExcel.php");
$objPHPExcel = new PHPExcel();
   //(instructions pour générer le document)

3.5 - Instanciation du writer de phpExcel

On choisi ensuite le constructeur du flux de données ($objWriter) selon le paramètre qui définit le mode choisi du flux de données, passé en GET. Un des nombreux avantages de la librairie phpExcel est de fournir une fabrique permettant de générer aisément différent type de flux.


$mode = (isset($_GET['mode'])) ? $_GET['mode'] : null;
$date = date("Y_m_d_H.i.s");
$nomFichier = "test_".$date;
switch ($mode) {
 
    // Librairie PDF de phpExcel par défaut utilisé (basé sur TCPDF)
    case 'PDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_PDF($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        break;
 
    // On utilise plutot MPDF pour regler les problèmes de no-border et amélioré les performances
    case 'MPDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_HTML($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        $objWriter->setUseInlineCSS(true);
        ob_clean();
        $html = $objWriter->generateHTMLHeader();
        $html .= $objWriter->generateStyles();
        $html .= $objWriter->generateSheetData();
        $html .= $objWriter->generateHTMLFooter();
        include_once(dirname(__FILE__)."/lib/php/MPDF46/mpdf.php");
        $mpdf = new mPDF();
        $mpdf->WriteHTML($html);           
        break;
 
    // Génération du document en version Excel5
    Case 'Excel5':
        $objWriter = new PHPExcel_Writer_Excel5($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);   
        break;
 
    default:
        echo "<pre>Le mode de génération choisi <b>(.".$mode.".)</b> n'existe pas.</pre>";
}

3.6 - Mise à jour de la variable de session

On relance alors la session, afin de changer la valeur de session generationStatut, pour indiquer que la génération du flux de données côté serveur est terminé. Afin que le script php generationStatus.php lancé en Ajax retardé récursif puisse indiqué à l'utilisateur la fin du traitement (onFinishTraitement)

session_start();
$_SESSION['generationStatut'] = "fini";

3.7 - Envoie du fichier généré au navigateur

On envoie le fichier au navigateur en précisant les headers en fonction du fichier généré. A la fin, il suffit donc maintenant d'envoyer le flux de données généré au navigateur en lui précisant les headers du documents. Un appel Ajax traditionnel d'un script php permettrait de récuperer une chaîne de caractères auquel il nous serait impossible de lui préciser qu'il s'agit bien d'un document PDF par exemple.

ob_clean();
switch ($mode) {
    case 'PDF':
        header('Content-type: application/pdf');
        header("Content-Disposition: attachment; filename=".$nomFichier.".pdf");
        header('Cache-Control: max-age=0');   
        $objWriter->save('php://output');   
        break;
    case 'MPDF':
        $mpdf->Output($nomFichier.".pdf",D);       
        break;
    Case 'Excel5':
        header('Content-type:application/vnd.ms-excel');
        header('Content-Disposition:inline;filename='.$nomFichier.'.xls');
        header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date dans le passé
        $objWriter->save('php://output');
        break;
}

3.8 - Script complet de phpExcel_generation.php

Au final le script complet est :

<?php
 
//------------------------------------------------------------------.
// Variable de session pour informer sur le statut de la génération |
//------------------------------------------------------------------'
@session_start(); 
$_SESSION['generationStatut'] = "progress"; 
 
 
//----------------------------------------------------------------------------.
// Termine la session courante, après avoir stocké les données, autrement dit |
// on force le stockage de la variable de session pour qu’elle puisse être    |
// utilisé dans le script generationStatus.php lancé en AJAX retardé récursif |
//----------------------------------------------------------------------------'
session_write_close();
 
 
//------------------------------.
// Création de l'objet PHPExcel |
//------------------------------'
include_once(dirname(__FILE__)."/lib/php/phpExcel/Classes/PHPExcel.php");
$dateGeneration = date('d/m/Y \à H:i:s');
$objPHPExcel = new PHPExcel();
    $objPHPExcel->setActiveSheetIndex(0);
    $objPHPExcel->getDefaultStyle()->getFont()->setName('Calibri');
    $objPHPExcel->getDefaultStyle()->getFont()->setSize(10);
    $activeSheet = $objPHPExcel->getActiveSheet();
    $activeSheet->setTitle('Exemple de generation phpExcel');
 
    // Header document PHPExcel
    $activeSheet->setCellValue('A1',utf8_encode("Suivi des ventes et contrôle"));
    $activeSheet->setCellValue('B3',"Pays");
    $activeSheet->setCellValue('B4',utf8_encode("Contexte"));
    $activeSheet->setCellValue('B5',"Dates des ventes");
    $activeSheet->setCellValue('B7',utf8_encode("Nombre de vendeur dans le pays"));
    $activeSheet->setCellValue('B8',utf8_encode("Nombre d'usine dans le pays"));
    $activeSheet->setCellValue('B9',utf8_encode("Nombre de publicité dans le pays"));
    $activeSheet->setCellValue('B10',utf8_encode("Taux de vente"));
    $activeSheet->setCellValue('B11',utf8_encode("Taux de rentabilité"));
    $activeSheet->setCellValue('C3',utf8_encode("Espagne"));
    $activeSheet->setCellValue('C4',utf8_encode("Vente d'équipement"));
    $activeSheet->setCellValue('C5',utf8_encode("Du 01/01/2010 au 05/02/2010"));
    $activeSheet->setCellValue('E7',71);
    $activeSheet->setCellValue('E8',37);
    $activeSheet->setCellValue('E9',16);
    $activeSheet->setCellValue('E10',utf8_encode("52 %"));
    $activeSheet->setCellValue('E11',utf8_encode("23 %"));
    $activeSheet->setCellValue('B12',utf8_encode("Document généré le ".$dateGeneration));
    $activeSheet->mergeCells('A1:G1');
    $activeSheet->mergeCells('C3:D3');
    $activeSheet->mergeCells('C4:D4');
    $activeSheet->mergeCells('C5:D5');
    $activeSheet->mergeCells('B7:D7');
    $activeSheet->mergeCells('B8:D8');
    $activeSheet->mergeCells('B9:D9');
    $activeSheet->mergeCells('B10:D10');
    $activeSheet->mergeCells('B11:D11');
    $activeSheet->mergeCells('B12:D12');
 
    // Mise en page du Header document PHPExcel
    $activeSheet->getStyle('A1:G1')->applyFromArray(
        array(
            'font' => array('bold' => true, 'size'=>16),
            'alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER),
            'borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)),
            'fill'=>array('type'=>PHPExcel_Style_Fill::FILL_SOLID,'color'=>array('argb'=>'FF99CCFF'))
        )
    );
    $activeSheet->getStyle('B12')->applyFromArray(array('font' => array('italic' => true)));
    $activeSheet->getStyle('B2:B11')->applyFromArray(array('font' => array('bold' => true)));
    $activeSheet->getStyle('B3:D5')->applyFromArray(
        array('borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)))
    );
    $activeSheet->getStyle('B7:E11')->applyFromArray(
        array('borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)))
    );
    $activeSheet->getStyle('E7:E11')->applyFromArray(
        array('alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER))
    );
 
    // Body document PHPExcel
    $activeSheet->setCellValueByColumnAndRow(0, 14, "Ident");
    $activeSheet->setCellValueByColumnAndRow(1, 14, "Nom");
    $activeSheet->setCellValueByColumnAndRow(2, 14, "Secteur");
    $activeSheet->setCellValueByColumnAndRow(3, 14, "Nature");
    $activeSheet->setCellValueByColumnAndRow(4, 14, "Ref");
    $activeSheet->setCellValueByColumnAndRow(5, 14, "Date\n".utf8_encode("Réponse"));
    $activeSheet->setCellValueByColumnAndRow(6, 14, "Controle\n".utf8_encode("des produits"));
    $activeSheet->getStyle('A14:G14')->applyFromArray(
        array(
            'font' => array('bold' => true),
            'alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER),
            'borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)),
            'fill'=>array('type'=>PHPExcel_Style_Fill::FILL_SOLID,'color'=>array('argb'=>'FF99CCFF'))
        )
    );
    $activeSheet->getStyle('F14')->getAlignment()->setWrapText(true);
    $activeSheet->getStyle('G14')->getAlignment()->setWrapText(true);
    $activeSheet->getStyle('A14:G14')->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP);
    $arrayNatureLibelle = array('Agroalimentaire', 'Usine', 'Université');
    $arrayNatureCode = array(151,152,153,168,198,286,292,336,541,542,543,556);
    $ligne = 15;
    for($i=0;$i<71;$i++) {
        $rneEcole = "XID".($i+10);
        $nom = "VENTE ".$i;
        $secteur = (rand(0,1)==0) ? 'GLOBAL' : 'ATOME';
        $natureLibelle = $arrayNatureLibelle[array_rand($arrayNatureLibelle,1)];
        $natureCode = $arrayNatureCode[array_rand($arrayNatureCode,1)];
        $dateReponse = date("d/m/Y",strtotime("2010-01-01 + "
            .rand(0,round((strtotime('2010-02-05')-strtotime('2010-01-01'))/(60*60*24)))." days"));
        $campagneValiderEcole = (rand(0,1)==0) ? 'OUI' : 'NON';
        $activeSheet->setCellValueByColumnAndRow(0, $ligne, utf8_encode($rneEcole));
        $activeSheet->setCellValueByColumnAndRow(1, $ligne, utf8_encode($nom));
        $activeSheet->setCellValueByColumnAndRow(2, $ligne, utf8_encode($secteur));
        $activeSheet->setCellValueByColumnAndRow(3, $ligne, utf8_encode($natureLibelle));
        $activeSheet->setCellValueByColumnAndRow(4, $ligne, utf8_encode($natureCode));
        $activeSheet->setCellValueByColumnAndRow(5, $ligne, utf8_encode($dateReponse));
        $activeSheet->setCellValueByColumnAndRow(6, $ligne, utf8_encode($campagneValiderEcole));
        if(strcmp($campagneValiderEcole,'OUI')==0) {
            $activeSheet->getStyle('A'.$ligne.':G'.$ligne)->applyFromArray(
                array('fill'=>array('type'=>PHPExcel_Style_Fill::FILL_SOLID,'color'=>array('argb'=>'FFCCFFCC')))
            );
        }
        $ligne++;
    }
    $activeSheet->getStyle('A15:G'.($ligne-1))->applyFromArray(
        array(
            'borders'=>array(
                'left'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'right'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'bottom'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'vertical'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'horizontal'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  )
            )       
        )
    );
    $styleColonneCentrer = array('alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER));
    $activeSheet->getStyle('C15:C'.($ligne-1))->applyFromArray($styleColonneCentrer);
    $activeSheet->getStyle('E15:G'.($ligne-1))->applyFromArray($styleColonneCentrer);
    $activeSheet->getColumnDimension('A')->setWidth(12);
    $activeSheet->getColumnDimension('B')->setWidth(40);
    $activeSheet->getColumnDimension('C')->setWidth(12);
    $activeSheet->getColumnDimension('D')->setWidth(40);
    $activeSheet->getColumnDimension('E')->setWidth(6);
    $activeSheet->getColumnDimension('F')->setWidth(12);
    $activeSheet->getColumnDimension('G')->setWidth(22);
 
    // Set Zone d'impression
    $activeSheet->getPageSetup()->setPrintArea('A1:G'.($ligne));
    $activeSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE);
    $activeSheet->getPageSetup()->setPaperSize(PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4);
    $activeSheet->getPageMargins()->SetLeft(0.1);
    $activeSheet->getPageMargins()->SetRight(0.1);
    $activeSheet->getPageMargins()->setTop(0.4);
    $activeSheet->getPageMargins()->setBottom(0.6);
    $activeSheet->getPageMargins()->setHeader(0.1);
    $activeSheet->getPageMargins()->setFooter(0.4);
    $activeSheet->getHeaderFooter()->setOddHeader("&C&HAPPLICATION v.0.1");
    $activeSheet->getHeaderFooter()->setOddFooter('&L&BGeneration flux de donnees&RPage &P / &N');
    $activeSheet->getPageSetup()->setHorizontalCentered(true);
    $activeSheet->getPageSetup()->setVerticalCentered(false);
    $activeSheet->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(14, 14);
 
 
//-----------------------------------------------------.
// Création du writer en fonction du moge passé en GET |
//-----------------------------------------------------'
$date = date("Y_m_d_H.i.s");
$mode = (isset($_GET['mode'])) ? $_GET['mode'] : null;
$nomFichier = "test_".$date;
switch ($mode) {
    // Librairie PDF de phpExcel par défaut utilisé (basé sur TCPDF)
    case 'PDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_PDF($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        break;
    // On utilise plutot MPDF pour regler les problèmes de no-border et amélioré les performances
    case 'MPDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_HTML($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        $objWriter->setUseInlineCSS(true);
        ob_clean();
        $html = $objWriter->generateHTMLHeader();
        $html .= $objWriter->generateStyles();
        $html .= $objWriter->generateSheetData();
        $html .= $objWriter->generateHTMLFooter();
        include_once(dirname(__FILE__)."/lib/php/MPDF46/mpdf.php");
        $mpdf = new mPDF();
        $mpdf->SetHeader(
            array (
                'L' => array(
                    'content' => '',
                    'font-size' => 10,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'C' => array(
                    'content' => ' Exemple',
                    'font-size' => 9,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'R' => array(
                    'content' => '',
                    'font-size' => 10,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'line' => 1
            ),
            'O'
        );
        $mpdf->SetFooter(
            array (
                'L' => array(
                    'content' =>
                    'Suivi de validation des directeurs',
                    'font-size' => 9,'font-style' => '','font-family' => 'FreeSerif','color'=>'#000000'),
                'C' => array(
                    'content' => '',
                    'font-size' => 9,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'R' => array(
                    'content' => 'Page {PAGENO} / {nbpg}',
                    'font-size' => 9,'font-style' => '','font-family' => 'FreeSerif','color'=>'#000000'),
                'line' => 1
            ),
            'O'
        );
        $mpdf->WriteHTML($html);           
        break;
    // Génération du document en version Excel5
    Case 'Excel5':
        $objWriter = new PHPExcel_Writer_Excel5($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);   
        break;
    default:
        echo "<pre>Le mode de génération choisi <b>(.".$mode.".)</b> n'existe pas.</pre>";
}
 
 
//-----------------------------------------------.
// Informe que la génération du fichier est fini |
//-----------------------------------------------'
session_start();
$_SESSION['generationStatut'] = "fini";
 
 
//--------------------------------------------------.
// Envoi le fichier au browser selon le mode choisi |
//--------------------------------------------------'   
ob_clean();
switch ($mode) {
    case 'PDF':
        header('Content-type: application/pdf');
        header("Content-Disposition: attachment; filename=".$nomFichier.".pdf");
        header('Cache-Control: max-age=0');   
        $objWriter->save('php://output');   
        break;
    case 'MPDF':
        $mpdf->Output($nomFichier.".pdf",D);       
        break;
    Case 'Excel5':
        header('Content-type:application/vnd.ms-excel');
        header('Content-Disposition:inline;filename='.$nomFichier.'.xls');
        header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date dans le passé
        $objWriter->save('php://output');
        break;
}   
 
?>


4 - Lecture de la variable de session côte serveur avec generationStatus.php

4.1 - Script generationStatus.php

Le script generationStatus.php est tout simple, il s’agit simplement de faire un echo de la variable de session qui renseigne sur l’avancée du traitement. Ce script sera par la suite appellé en JavaScript afin d'atteindre l'évenement onFinishTraitement.

<?php 
    session_start();
    echo $_SESSION['generationStatut'];
?>


5 - Lecture de la variable de session par appel AJAX retardé recursif

5.1 - Chargeur JQuery

On utilise la librairie JQuery pour nous simplifier la vie et avec JQuery on commence toujours par attendre que l'arbre DOM soit entièrement chargé avec l'appel de l'instruction $(document).ready(function(){...});.

On utlise ensuite le sélecteur sur la classe .generer avec $('.generer'), et pour chaque élément selectionné on ajoute un listeneur sur l'évenement clik.

$(document).ready(function(){
    $('.generer').each(function() {
        $(this).click(
            function(){
                (...)
            }
        );
    });
});

5.2 - Mise en oeuvre du listener onClick sur les boutons de l'interface

En JavaScript sur l’événement de clic pour lancer la génération du fichier :

  • La première instruction est de récupère l'identifiant de l'élément cliqué (PDF, MPDF ou Excel5)

  • On informe l'utilisateur que la génération du fichier est en cours en affichant une image et du texte. Le chargement de l'image dans #loading-image se fait simplement par l'ajout de la classe .chargement dans l'élément

  • On charge dans l’iframe le script php phpExcel_generation.php?mode=idGenerer qui génère le document PDF, MPDF ou Excel5

  • On fait un appel retardé en utilisant setTimeout sur la fonction getGenerationStatus. On remarque que pour pouvoir appellé une fonction avec des paramètres dans setTimeout, il est nécessaire d'utiliser une fonction anonyme.

$(document).ready(function(){
 
    $('.generer').each(function() {
        $(this).click(
            function(){               
                // Récupération de l'identifiant du fichier à générer surlequel l'utilisateur à cliquer
                var idGenerer = $(this).attr('id');
 
                // Informe l'utilisateur que la génération du fichier est en cours
                var stringTemp = "<p>Génération du fichier "+idGenerer+" en cours...</p><p>";
                $("#loading-content").html(stringTemp);
                $("#loading-image").removeClass("information");
                $("#loading-image").addClass("chargement");
                $("#loading").css("display","block");
                $("#loading-debug").html("");
                $("#loading-debug").css("display","none");
 
                // Chargement de l'iframe associé à l'idGenerer clické
                var oIFrm = document.getElementById("iframePhpExcel");
                oIFrm.contentWindow.document.location = "phpExcel_generation.php?mode="+idGenerer;
 
                // Appel de la fonction récursive pour tester le statut après une attente d'une seconde.
                var timeWait = 1000;
                var timeFactor = 2;
                var timeWaitMax = 8000;
                var timeOut = 30000;
                var timeExec = 1000;
                var nbAppel = 0;                   
                setTimeout(
                    function() {
                        getGenerationStatus(timeWait, timeFactor, timeWaitMax, timeOut, timeExec, nbAppel, idGenerer, true);
                    }
                    , timeWait
                );
            }
        );
    });
 
});

5.3 - Appel AJAX récursif retardé

La fonction getGenerationStatus effectue un appel AJAX récursif retardé. L'appel AJAX se fait de manière asynchrone sur la page phpExcel_generationStatus.php et cette fonction prend en compte plusieurs paramètres :

  • param (int) timeWait : Temps en ms d'attente avant de relancer une requête AJAX

  • param (int) timeFactor : Facteur pour incrémenter le temps avant le lancement récursif

  • param (int) timeWaitMax : Temps en ms d'attente maximum avant de relancer une requête AJAX

  • param (int) timeOut : Temps en ms d'attente global maximum

  • param (int) timeExec : Temps d'execution

  • param (int) nbAppel : Nombre d'appel récursif effectué

  • param (String) idGenerer : Type de fichier generer (PDF, MPDF, Excel5)

  • param (Bool) debug : True affiche le debug

Avec cette fonction on repond à la question suivante : est-ce que la génération du document est terminé ?

Tant que la réponse est négative, le fichier est en cours de génération et la relance de cette question ne se fait qu'a intervalle de temps timeWait qui s'incremente à chaque appel à l'aide d'un facteur timeFactor. Ainsi on interroge fréquement le serveur au début de la génération, et le temps d'attente s'agrandit d'appel en appel avant d'atteindre un temps maximum d'attente timeWaitMax. Si le nombre d'appel est trop important, un arret est prevu à l'aide d'un temps d'execution maximum timeOut. Il est aussi possible d'afficher des informations sur les appels effectués si le paramètre debug vaut true.

/**
* param (int) timeWait : Temps en ms d'attente avant de relancer une requête AJAX
* param (int) timeFactor : Facteur pour incrémenter le temps avant le lancement récursif
* param (int) timeWaitMax : Temps en ms d'attente maximum avant de relancer une requête AJAX
* param (int) timeOut : Temps en ms d'attente global maximum
* param (int) timeExec : Temps d'execution
* param (int) nbAppel : Nombre d'appel récursif effectué
* param (String) idGenerer : Type de fichier generer (PDF, MPDF, Excel5)
* param (Bool) debug : True affiche le debug
*/

function getGenerationStatus(timeWait, timeFactor, timeWaitMax, timeOut, timeExec, nbAppel, idGenerer, debug) {
    nbAppel = nbAppel+1;
    timeExec = timeExec+timeWait;
    var timeWaitCurrent = timeWait*timeFactor;
    if(timeWaitCurrent>timeWaitMax) {
        timeWaitCurrent = timeWaitMax;
    }
 
    if(timeExec<timeOut) {
        $.ajax({
            url: "phpExcel_generationStatus.php",
            type: "POST",
            success: function(data) {
                if(data=="progress") {
                    var stringTemp = "<p>Génération du fichier "+idGenerer+" en cours...<p/>";
                    $("#loading-content").html(stringTemp);
                    if(debug) {
                        var stringTempDebug =
                        "<p class='monospace'>"+
                            "--> Numéro d'appel = "+nbAppel+
                            "<br/> --> Temps d'attente avant le prochain appel = "+timeWaitCurrent/1000+" sec"+
                            "<br/> --> Temps d'execution total = "+timeExec/1000+" sec"+
                        "</p><p class='monospace'>Return statut : progress</p>";
                        $("#loading-debug").append(stringTempDebug);
                        $("#loading-debug").css("display","block");
                    }
                    setTimeout(
                        function() {
                            getGenerationStatus(
                                timeWaitCurrent, timeFactor, timeWaitMax, timeOut, timeExec, nbAppel, idGenerer, true);
                            }
                        , timeWaitCurrent
                    );
                } else {
                    if(debug) {
                        var stringTempDebug =
                        "<p class='monospace'>"+
                            "--> Numéro d'appel = "+nbAppel+
                            "<br/> --> Temps d'execution total = "+timeExec/1000+" sec"+
                        "</p><p class='monospace'>Return statut : fini</p>";
                        $("#loading-debug").append(stringTempDebug);
                        $("#loading-debug").css("display","block");
                    }
                    var stringTemp = "<p>Génération du fichier "+idGenerer+" terminé<p/>";
                    $("#loading-content").html(stringTemp);
                    $('#loading-image').addClass('information');
                }
            }
        });
    } else {
        if(debug) {
            var stringTempDebug =
            "<p class='monospace'>"+
                "--> Numéro d'appel = "+nbAppel+
                "<br/> --> Temps d'execution total = "+timeExec/1000+" sec"+
            "</p><p class='monospace'>TimeOut atteint</p>";
            $("#loading-debug").append(stringTempDebug);
            $("#loading-debug").css("display","block");
        }
        var stringTemp = "<p>TimeOut atteint.<p/>";
        $("#loading-content").html(stringTemp);
        $('#loading-image').addClass('information');
    }
}
Message d'attente pendant la génération d'un flux de données (Excel, PDF, ...)


1 - Introduction

1.1 - Motivations

Il est souvent utile d'afficher un message d'attente pendant la génération d'un document côté serveur afin d'informer l'utilisateur de l'état : en cours de traitement, échec ou succès. On prendra l'exemple d'une génération d'un document Excel avec la librairie phpExcel, mais ceci pourrait être adapté à n'importe quel long traitement php générant un flux de données à envoyer au navigateur.


1.2 - Présentation de la solution

Par l'envoi d'une requête Ajax, il est possible d'identifier un succès ou un échec d'une requête HTTP simple, mais certainement pas dans le cas d'un flux de données à générer. On sera dans l'incapacité d'utiliser le flux de données généré dans l'attribut réponse de notre instance Ajax, on mieux au aura dans une string le contenu du flux de données sans possibilité de lui préciser le type du header dans le document.

Ainsi la principale difficulté réside dans le cas d'un flux de données complexe à détecter, coté client donc en JavaScript, ce que l'on va appeler l'événement onFinishTraitement, et l'astuce (simple une fois établie) consiste à interroger une variable de session renseignant le statut de la progression du traitement par appel Ajax récursif. On pourra même avec ce mécanisme, mettre en place une progressbar PHP, JavaScript. Toutes les personnes qui se sont déjà penché sur cette question d'implémenter une véritable progressbar peuvent mesurer cette difficulté, mais l'article concerne uniquement à la présentation de ce mécanisme onFinishTraitement.


1.3 - Code source à télécharger de la démonstration

Le zip ci-dessous contient les codes sources de la démonstration avec la gestion d'un retardateur qui s'incrémente pour lancer les requête Ajax ainsi que d'un mode debug pour contrôler dans le navigateur le lancement des requêtes Ajax.

Télécharger les sources de cette démonstration v0.2.1
- gestion d'un mode de debug
- lancement des requêtes AJAX avec un retardateur incrémenté dans le temps

Cette démonstration nécessite les librairies PHP phpExcel et MPDF, ainsi que la librairie JavaScript JQuery, qui ne sont pas forcement présente dans le zip. Voici les récommandations de version et qui ont été testés à utiliser pour faire fonctionner la démonstration :

Une autre piste serait de créer un bot qui parcours régulièrement les pages hébergés (par exemple en COM Automation sous windows) et qui contrôle le contenu des pages selon un dictionnaire prédéfini et qui t'alertes par mail en cas de suspicion. Certes ça n'enlève pas le contrôle humain à la fin mais au moins ça automatise la recherche de pages douteuses.

Autrement dit, tu créer un dictionnaire contenant les String qui te paraissent douteux (genre BNP, ect...), ton script compare par expression régulière le contenu de la page avec ton dictionnaire... un peu comme un bruteforce ...

iMacros
Ce qu'AutoIt est à Windows, Imacros l'est pour Firefox.

iMacros permet d'automatiser les taches répétives effectués dans votre navigateur.Avec iMacros, vous pouvez remplir, rapidement et facilement, les formulaires sur Internet, vous rappeler des mots de passe, télécharger les informations d’autres sites, « scraper » le Web (récupérer les données de plusieurs sites), et beaucoup plus. Vous pouvez sauver les macros sur votre ordinateur pour votre usage personnel, ou les partager avec d’autres en les diffusant.

Une des principales utilités dans le contexte du développement web est d'enregistrer des macros pour simuler une visite sur votre site afin d'en tester la fonctionnalité et détecter les bugs éventuelles.

Installer iMacros

http://www.linternaute.com/0_newsletter/high-tech/217/images/imacros.png

Tamper Data

Certainement incorporée dans une prochaine mise à jour de Firebug, mais pour l'instant il faut passer par lui. Il s'agit d'un outil d'analyse et de modification très complet des requêtes HTTP. Son principal intérêt réside dans la possibilité de modifier à la volée les en-têtes, comme par exemple changer des paramètres POST/GET d'une requête sans modification du code.

Installer Tamper Data

http://www.derekallard.com/img/post_resources/tamper_data_getting_data.jpg

YSlow

Extension pour analyser le temps de chargement de vos pages. Il donne les éléments peu performants qui ralentisse la navigation ainsi que quelques conseils pour améliorer le temps de chargement.

Yslow nécessite l’extension Firebug pour fonctionner.

Installer YSlow

http://www.cobolian.com/wp-content/uploads/yslow6.png

Html Validator

Voila comment contrôler la validité de votre code HTML/XHTML selon les normes préconisés par le W3C. Des outils vous permettent de "nettoyer" votre code, ou de vous conseiller sur les erreurs rencontrées.

Installer Html Validator

http://html.nhndesign.com/guidelines/html/validation/img/validatorInterface1.png

ColorZilla

Comment recuperé le code couleur (RVB, CMYK, ect...) sur une page web, tout simplement en un click avec cette extension.

http://www.bram.us/wordpress/wp-content/uploads/2006/12/fx_colorzilla.jpg

Comme vous voyez dans l'image ci-dessus, vous pointez avec votre souris un pixel de couleur et ColorZilla vous donne instantanément le code couleur correspondant. De plus, il est fourni une palette browser, vous permettant de moduler à loisir sur le choix d'une couleur pour le fond de votre site.

Installer Colorzilla

Firebug

Extension ultime pour le développement web, elle permet d'inspecter le code d'une page web simplement en parcourant le DOM, on peut y modifier les CSS à la volée, vérifier ce qui transite dans les en-têtes HTTP, voir les parametères passés en POST ou en GET, comparés les temps de chargement des divers éléments de la page.

... une fois testé, une fois pour toute adopté ...

Installer Firebug

http://www.gnucitizen.org/images/391953301_2101c534f2.jpg

Juste pour faire partager à la communauté des Cigales les extensions de Mozilla Firefox pour le développement Web.

Pour ceux qui ne connaisse pas, une petite piqure de rappel s'impose. Mozilla Firefox en plus d'être un des navigateurs des plus "stables" propose un concept dans l'air du temps, à savoir la personnalisation de son navigateur par l'ajout d'extensions. L'ajout d'extension est très simple : vous cliquez sur le lien de l’extension, Firefox vous demande la permission de l’installer, la télécharge, l’installe et redémarre. Vous pouvez désinstaller l'extension en 2 clics par le menu Outils > Modules complémentaires. Et enfin, les extensions vous préviennent si une nouvelle version est disponible. Bref, c'est du easyToInstall, easyToUse ...

Un petit bémol quand même avec ces extensions, si vous en installer trop d'extensions ou bien des extensions très lourdes, comme par exemple Firebug, attendez vous à des ralentissements notables dans la navigation ou encore à des erreurs internes. Utilisez deux browsers, un pour le développement charger avec vos extensions, et un autre pour la navigation.

Voici ci-dessous, une sélection (à étoffer) des extensions que je vous recommande pour le développement web.

- Firebug
- ColorZilla
- HtmlValidator
- Yslow
- iMacros

12

(3 réponses, dans Problèmes)

Pour ceux qui se pose la question comment fonctionne les Favicons, voici un exemple tout simple d'utilisation.

<html>
<head>
    <title>Titre de mon Site</title>
    <link rel="shortcut icon" href="./image/favicon.gif" />
</head>    

<body>
    <h1>Test Favicon</h1>
</body>    
</html>

13

(3 réponses, dans Présentations)

En effet, pour la mise en page j'utilise les feuilles de style CSS... c'est le plus simple pour la relecture du code html, sans compter la facilité de changement de mise en page, beaucoup plus simple que de mettre des balises <font> dans tout les sens. Il faut juste ne pas tomber dans un autre excès propre au css mettre des balises <div> dans tout les sens.

J'utilise une extension de FireFox, nommé FireBug pour travailler les CSS, et en règle générale pour contrôler ce qui transite sur le site que je réalise.

http://www.codinghorror.com/blog/images/firebug-screenshot.png

Je te le conseille vivement si tu aimes travaillé sous FireFox (j'aime pas le moteur DOM d'IE pour pas dire que c'est de la merde...)

Voici mon premier site hébergé sur les cigales portant sur le jeu Final Fantasy premier du nom, je me suis amusé sur mon émulateur à prendre un screenshot, puis deux, puis faire des vidéos, et finalement un petit site en piochant diverses infos à droite et à gauche.

http://20100.lescigales.org/FF1/image/logoFF1.png

Il s'agit plus ici de suivre l'histoire de FF1 pour ceux qui ne connaissent pas ou encore ceux qui ont oublié, ainsi vous n'avez plus qu'à lire pas à pas la solution et vous spolier toute l'aventure du premier épisode de cette mythique série.

http://20100.lescigales.org/FF1/