Fichier CSV vers SPIP

Dans une récente contribution Jacques J. proposait d’intégrer un tableau issu d’un fichier csv dans un article. Pour cela, il utilisait une boucle gérant les documents joints du type csv.
Le principe a été repris dans la contribution proposée ici.

1 – Préparation de la base

Comme indiqué dans la précédente contribution, SPIP n’accepte pas par défaut les fichiers csv en téléchargement.
Pour cela, il faut suivre la procédure décrite dans l’article correspondant sur www.spip.net

Il faudra retenir les valeurs suivantes :

-  titre : CSV
-  extension : csv
-  inclus : embed
-  upload : oui

Par ftp, créer un répertoire csv dans /IMG/, et ne pas oublier de régler les droits d’écriture…

2 – La fonction

Elle s’inspire à la fois de la contribution de Jacques J., et de routines présentées notamment sur nexen.net.

Vous pouvez l’installer dans mes_fonctions.php3 comme à l’accoutumée. Voici les explications pas à pas :

Dans un premier temps, le filtre va ouvrir le fichier que vous avez téléchargé.

Il va lire les lignes une à une et les placer dans un tableau

function tableurcv($fichier){

/* ouverture du fichier CSV */ 
$fic = @fopen ($fichier,"rb") or die ("Impossible d'ouvrir le fichier *.csv");
 
$lignes=0; // compteur de lignes 

// chargement des données des lignes/cellules
while ($data = @fgetcsv ($fic, 1024, ";")) { // on lit chaque ligne 
  $num = count ($data); // nbr enregistrements par ligne 
  for ($c=0; $c<$num; $c++) { // on lit chaque cellule 
  $tabfic[$lignes][$c]=$data[$c]; // on met dans le tableau 		} 
 $lignes++; // incrementation du nombre de lignes 
}

// on ferme le fichier 
  fclose ($fic);

L’apport principal de cette contribution est de permettre au créateur du fichier csv de « préprogrammer la mise en page ». En effet, il peut renseigner son fichier dont la structure ressemble à ceci :

Copie ecran tableur

Les variables pouvant être modifiées sont :
-  largeur totale du tableau dans la mise en page du squelette (en relatif ou en absolu)
-  présence d’une entete
-  l’alignement de chacune des colonnes
-  la largeur relative des colonnes, afin d’équilibrer le tableau

A noter que le filtre traite les retours de ligne générés sous Excel par la combinaison de touches ALT + Retour

Il va récupérer dans les lignes 1 et 2 les paramétrages du tableau :
-  rang 1 col 2 : largeur du tableau
-  rang 2 col 2 : existence d’une entete de tableau

S’il y a une entete, il va la traiter spécifiquement en lui affectant le style ad hoc.

// 
//construction du tableau
$result ="";
$colstyle="row_odd";

/*
    calcul des largeurs 
*/
$largeurmax = $tabfic[0][1]; // largeur du tableau souhaité, situé en rang 1 colonne 2
$largeur = array_sum($tabfic[2]); // largeur du tableau csv, situé en rang 3 = somme des cellules
//alignements des colonnes
$align = ($tabfic[3]);

Lecture et traitement de l’entete
//debut du tableau
$result .= "<table width='".$largeurmax."'align='center'>\n"; //peut être paramétré par css

$maligne=5;
// test d'entete
if ($tabfic[1][1]==1){
  $maligne++;
  $entstyle="row_first";
  $result .= "<tr class='".$entstyle."'>\n"; 
  //lecture des cellules
  for ($colonne=1 ; $colonne<=$num ; $colonne++) { 
    //calcul des largeurs
    $largcol = ($tabfic[2][$colonne-1]/$largeur)*100;
    //alignements
    switch (TRUE) {
      case ($tabfic[3][$colonne-1] == "C"):
      $talign="center";
      break;
      case ($tabfic[3][$colonne-1] == "D"):
      $talign="right";
      break;
    default:
      $talign="left";
      break;
     }

    //saut de ligne manuel?
    $cellule=$tabfic[$maligne-2][$colonne-1];
    $cellule = str_replace("\n","<br/>", $cellule);
			
    $result .= "<td width='".$largcol."%' align='".$talign."'nowrap>&nbsp;".$cellule."&nbsp;</td>\n"; 
    } 
  $result .= "</tr>\n"; 
}

Les autres lignes du tableau seront traitées en reprenant, cellule par cellule, les éléments situés dans la même colonne en rang 3 (largeur de la cellule) et 4 ( alignement).

A noter que le fichier d’exemple fourni au format xls comporte une fonction, en rang 3, qui affiche automatiquement la largeur de la colonne.

// lecture des lignes
for ($ligne=$maligne ; $ligne<=$lignes ; $ligne++) {

//petit truc pour les styles alternés
if ($colstyle=="row_odd") {
  $colstyle="row_even";
} else {
  $colstyle="row_odd";
} 
  $result .= "<tr class='".$colstyle."'>\n"; 

//lecture des cellules
for ($colonne=1 ; $colonne<=$num ; $colonne++) {
  //calcul des largeurs
  $largcol = ($tabfic[2][$colonne-1]/$largeur)*100; 
  //alignements
  switch (TRUE) {
    case ($tabfic[3][$colonne-1] == "C"):
    $talign="center";
    break;

    case ($tabfic[3][$colonne-1] == "D"):
    $talign="right";
    break;

    default:
    $talign="left";
    break;
  }

  //saut de ligne manuel?
  $cellule=$tabfic[$ligne-1][$colonne-1];
  $cellule = str_replace("\n","<br/>", $cellule);
  $result .= "<td width='".$largcol."%' align='".$talign."'nowrap>&nbsp;".$cellule."&nbsp;</td>\n"; 
} 

$result .= "</tr>\n"; 
} 

$result .= "</table>\n"; 

return $result;
}

On notera au passage un « petit bricolage » permettant de traiter correctement les styles spip des tableaux :

$colstyle="row_odd";
...
// lecture des lignes
for ($ligne=$maligne ; $ligne<=$lignes ; $ligne++) {

//petit truc pour les styles alternés
if ($colstyle=="row_odd") {
$colstyle="row_even";
} else {
$colstyle="row_odd";
} 
...

La boucle document à utiliser est du type :

<BOUCLE_documentsCSV(DOCUMENTS){id_article}{par titre}{mode=document}{doublons}{extension=csv}>
      
  <h4>[(#TITRE|supprimer_numero)]</h4>
  <p>[(#DESCRIPTIF)]</p>
  <dt>[(#URL_DOCUMENT|tableurcv)]</dt>
  <div class="clear">&nbsp;</div>
</BOUCLE_documentsCSV>

Vous trouverez dans l’archive :
-  le fichier mes_fonctions.php3.txt (extension en .txt par sécurité pour les manœuvres hatives)
-  un modèle de tableau au format .xls
-  un modèle de tableau au format .csv

Un exemple de réalisation est visible ici.

Extension : adapter cette contribution pour traiter des balises de documents directement inclus dans les articles, mais vraisemblablement, cela nécessiterait une intervention sur le coeur du code.

Discussion

6 discussions

  • 2

    salut !

    ya moyen d’avir une mise à jour pour spip 2 et 2.1 ??

    codialement

    Répondre à ce message

  • 1
    fredlefred

    J’ai modifié le code de la façon suivante :

    <?
    /*
     *   +----------------------------------+
     *    Nom du Filtre : tableau csv vers spip                                               
     *   +----------------------------------+
     *    Date : lundi 13 février
     *    Auteur :  "gpl"  : gpl@macplus.org  
     *              Christophe Vanackère : vanackere@provita.fr
     *    Date : vendredi 26 septembre 2008
     *    Auteur : Fred 
     *    		Frédéric Bonnaud : frederic.bonnaud chez laposte fr
     *    Modification : le paramètrage du tableau est passé à la fonction
     *    plutot que dans le tableau (ça permet d'importer des tableaux qui
     *    ne sont pas prévus pour cela.
     *   +-------------------------------------+
     *    Fonctions de ce filtre :
     *		Il sert à intégrer un fichier csv en document joint sous forme de tableau mis en page
     	  Particularités :
    	  	Respecte les styles spip (titre-alternance des lignes) 
    			et permet aussi les alternances de colonnes.
     		Gère les sauts de ligne manuels (je ne sais pas)
    		Gère la largeur relative des tableaux
    		Permet de forcer une disposition visuellement verticale ou
    			horizontale.
    		Permet de donner un titre au tableau (conforme aux tableaux 
    			spip)
    		Permet d'avoir plusieurs lignes/colonnes de titres
    		Choix automatique de la disposition horizontale/verticale en
    			fonction des titres
    		Par défaut, la disposition est horizontale
     *   +-------------------------------------+ 
     * Inspiré par une contribtion de Jean-Jacques (http://www.spip-contrib.net/ecrire/?exec=articles&id_article=1236) 
     * Pour toute suggestion, remarque, proposition d'ajout
     * reportez-vous au forum de l'article :
     * http://www.uzine.net/spip_contrib/article.php3?id_article=62
     */
    function csvclass($classes) {
    	foreach($classes as $id => $class) {
    		if ($class==='') {
    			unset($classes[$id]) ;
    		}
    	}
    	return (count($classes)?" class='".join(" ",$classes)."'":'') ;
    }
    function tableurcsv($fichier, $avec_titre='HV', $largeurs='', $alignements='', $largeurmax='100%', $force_disposition='', $caption=''){
    
    /* ouverture du fichier CSV */ 
    $fic = @fopen ($fichier,"rb") or die ("Impossible d'ouvrir le fichier $fichier");
     
    $lignes=0; // compteur de lignes 
    
    // chargement des données des lignes/cellules
    while ($data = @fgetcsv ($fic, 1024, ";")) { // on lit chaque ligne 
    	$num = count ($data); // nbr enregistrements par ligne 
    		for ($c=0; $c<$num; $c++) { // on lit chaque cellule 
    			$tabfic[$lignes][$c]=$data[$c]; // on met dans le tableau 
    		} 
    	$lignes++; // incrementation du nombre de lignes 
    }
    
    // on ferme le fichier 
    fclose ($fic);
    
    //construction du tableau
    $result ="";
    
    //largeurs 
    $largeurs=split("\s*;\s*", trim($largeurs)) ;
    $largeur = array_sum($largeurs) ;
    $largeur=($largeur?$largeur:1);
    //alignements des colonnes
    $align=split("\s*;\s*", trim($alignements)) ;
    
    //debut du tableau
    $result .= "<table class='spip' width='".$largeurmax."' align='center'>\n"; //peut être paramétré par css
    if ($caption) {
    	$result .= "<caption>$caption</caption>\n" ;
    }
    $maligne=1;
    
    $titres = split("\s*;\s*", trim($avec_titre)) ;
    $titres_lc = array() ;
    $table_verticale=$table_horizontale=false ;
    foreach($titres as $titre) {
    
    	if (preg_match("/^(l|c)(\d+)$/i", strtolower($titre), $match)) {
    		$titres_lc[$match[0]] = true ;
    		($match[1] == 'l') && ($table_verticale = true) ;
    		($match[1] == 'c') && ($table_horizontale = true) ;
    	}
    }
    
    $rowstyle="row_odd";
    
    if (!$table_horizontale && !$table_verticale) {
    	$table_horizontale=true ;
    }
    
    if (strpos(strtolower($force_disposition),"horiz")===0) {
    	$table_horizontale=true ;
    	$table_verticale=false ;
    } elseif (strpos(strtolower($force_disposition),"vertic")===0) {
    	$table_horizontale=false ;
    	$table_verticale=true ;
    }
    
    
    // lecture des lignes
    for ($ligne=$maligne ; $ligne<=$lignes ; $ligne++) {
    
    	//petit truc pour les styles alternés
    	if ($table_horizontale) {
    		if ($rowstyle=="row_odd") {
    			$rowstyle="row_even";
    		} else {
    			$rowstyle="row_odd";
    		} 
    	} else {
    		$rowstyle="" ;
    	}
    	$result .= "<tr".csvclass(array($rowstyle)).">\n"; 
    		//lecture des cellules
    		$colstyle="col_even" ;
    		for ($colonne=1 ; $colonne<=$num ; $colonne++) {
    			if ($table_verticale) {	
    				if ($colstyle=="col_odd") {
    					$colstyle="col_even";
    				} else {
    					$colstyle="col_odd";
    				}
    			} else {
    				$colstyle="" ;
    			}
    			//calcul des largeurs
    			$largcol = ($largeurs[$colonne-1]/$largeur)*100; 
    			//alignements
    			switch (TRUE) {
    			case (strtolower($align[$colonne-1]) == "c"):
    				$talign="align-center";
    				break;
    			case (strtolower($align[$colonne-1]) == "d"):
    				$talign="align-right";
    				break;
    			default:
    				$talign="align-left";
    				break;
            }
    		//saut de ligne manuel?
    		$cellule=$tabfic[$ligne-1][$colonne-1];
    		$cellule = str_replace("\n","<br/>", $cellule);
    		if ($titres_lc["c".$colonne] || $titres_lc["l".$ligne])  {
    			$balise="th" ;
    		} else {
    			$balise="td" ;
    		}
    		$result .= "<$balise width='$largcol%'".csvclass(array($colstyle,$talign)).">$cellule</$balise>\n"; 
    		} 
    	$result .= "</tr>\n"; 
    } 
    $result .= "</table>\n"; 
    
    return $result;
    }
     ?>

    Et j’utilise cela avec spip 1.9.2 et un modèle du type :

    <BOUCLE_documentCSV(DOCUMENTS){id_document=#ENV{id}}{doublons}{extension=csv}>
            #SET{titre,(#TITRE|supprimer_numero)}
            [(#URL_DOCUMENT|tableurcsv{#ENV{titres},#ENV{largeurs},#ENV{alignements},#ENV{largeur},#ENV{disposition},#GET{titre}})]
    </BOUCLE_documentCSV>

    Ce qui permet des appels du type :

    <csv 115
     |largeur=40%
     |largeurs=15;10;10;10
     |alignements=g;c;c;c
     |titres=l1;c1;c4>

    pour mettre des titres sur la ligne 1 et sur les colonnes 1 et 4
    ou :

    <csv 115
     |largeur=40%
     |largeurs=15;10;10;10
     |alignements=g;c;c;c
     |disposition=verticale>

    pour forcer une disposition verticale

    • fredlefred

      Oups, j’ai oublié de préciser que les effets visuels (verticaux/horizontaux) sont obtenus par les classes CSS : row_odd/row_even (conforme spip) et col_odd/col_even.

      J’ai mis les titres dans des cellules ’th’ plutôt que ’td’ (ça évite de définir la classe row_first qui n’avait plus de sens vu que les titres ne sont plus forcément en première ligne et/ou colonne.

    Répondre à ce message

  • très bonne contrib. J’errai sur macro word et je suis arrivé ici... avec les mêmes problèmes :

    l’idée est de gérer des tableaux un peu plus évolués. Visiblement CSV ne gère pas les fusions de cellules (colspan et rowspan). Or spip le fait via |<| & |^|
    (respectivement colspan et rowspan).

    Je me demandais si l’on pouvait procéder exactement de la même façon : on intègre un petit «  <  » dans le csv (entre les points virgules) et on l’interprète dans mes fonctions.

    Est-ce la bonne voie ?

    Répondre à ce message

  • 5

    Bonjour,

    je débute sous spip, et ce plugin m’interesse. Je l’ai installé mais je n’arrive pas à afficher le csv dans l’article. J’ai du raté un truc lors de l’instal, mise en ligne et tout mais je sais pas quoi.
    Si quelqun pouvait expilquer étape par étape comment installer et faire fonctionner ce plugin, ca serait génial.

    Merci

    • JacquesJ

      Bonjour,

      Il ne s’agit pas encore d’un plugin. Le mode opératoire est détaillé dans l’article. Dis nous plutôt ce que tu as fait pour que ça ne fonctionne pas !

    • Bonjour,

      J’ai récemment approfondi, grace à l’aide de Marabbeh (voir plus bas). D’où une amorce de plugin qui n’est pas encore suscptible d’être diffusé en état, mais qui peut rendre service. Envoyez-moi un message en MP. J’envoie le fichier dès que je j’aurai un peu nettoyé.

    • Juste oublié la signature du post

    • Bonjour,

      J’ai un souci, je n’arrive pas a faire fonctionner cette fonction. Voici ce que j’ai fait avec la version 1.9 de SPIP :
      -  insertion dans la base de donnees de
      titre : CSV
      extension : csv
      inclus : embed
      upload : oui
      -  Ajout de la fonction dans mes_fonctions.php
      -  Dans dist/modeles creation de doc_csv.html avec

      (#TITRE

      [(#DESCRIPTIF)]

      (#URL_DOCUMENT
       


      -  Ensuite lorsque j’ecris un article je joins mon document csv et dans l’article je tape

      Lorsque je vais dans la partie publique je ne vois pas mon tableau, je ne vois que l’icone du document que l’on peut telecharger.

      Ou est mon erreur ?
      Que faut il faire en plus ?

    • Jacques

      Bonjour,

      Appelle-tu ton modèle dans ton article par <doc_csvXX> ? (XX = n° du document) par exemple.

    Répondre à ce message

  • 1
    Maquisard

    Merci pour cette contrib !

    Quelqu’un connait une manière facile de lui faire accepter les accents ?

    Merci !

    • Maquisard

      Je me répond à moi même. Si ca peut aider quelqu’un.

      Remplacer cette ligne :

      $tabfic[$lignes][$c]=$data[$c] ; // on met dans le tableau

      Par cette ligne :

      $tabfic[$lignes][$c]=utf8_encode($data[$c]) ; // on met dans le tableau

    Répondre à ce message

  • 2

    Très intéressante cette contrib. J’ai fait un petit essai, ça marche bien.

    Une petite erreur :

    $result .= "<td width='".$largcol."%' align='".$talign."' nowrap>&nbsp;".$cellule."&nbsp;</td>\n";

    cad mettre un espace devant nowrap.

    J’ai aussi essayé d’en faire un modèle. Cad au lieu de mettre la boucle présentée ci-dessus dans le squelette, le rédacteur peut indiquer lui-même, dans l’article, l’endroit où il va mettre son tableau, avec la balise <docXXX|csv>.

    Voici le modèle doc_csv.html (quasiment la boucle de la contrib) :

    <BOUCLE_documentCSV(DOCUMENTS){id_document}{doublons}{extension=csv}>
        <h4>[(#TITRE|supprimer_numero)]</h4>
        <p>[(#DESCRIPTIF)]</p>
        <dt>[(#URL_DOCUMENT|tableurcsv)]</dt>
        <div class="clear">&nbsp;</div>
    </BOUCLE_documentCSV>

    à mettre dans le dossier squelettes/modeles/

    • Merci beaucoup, je regardais comment porter cette contrib vers des versions 1.9x. Je n’avais pas pensé au modèle.

    • Dans mon squelette, le critère doublons ne sert à rien. Puisque c’est dans un modèle et pas dans le squelette. D’ailleurs ça peut poser un problème. Mais dans mon cas, ça ne me gène pas d’afficher le document .csv d’origine.

      Une autre suggestion :
      Si votre fichier .xls contient des urls (http://www.mon-domaine.tld ou maito:mon-adresse@email.tld), ces adresses seront affichées telles quelles dans le fichier .csv, puis dans le tableau Spip. Pour en faire des liens cliquables, il faut ajouter une fonction :

      function tableurcsv_formate_cellule($cellule, $url_max=25) {
          // formate le contenu de la cellule
          // change url en <a href="url">url</a> (et coupe à $url_max cars si plus long)
          $re_url = "(?:(?:(?:ht|f)tps?)\:\/\/)?(?:(?:[a-zA-Z0-9]+(?:[@\-\.]?[a-zA-Z0-9]+)*)(?:\:[a-zA-Z0-9\-\.]+)?@)?(?:www.|ftp.|[a-zA-Z]+.)?[a-zA-Z0-9\-\.]+\.(?:[a-zA-Z]{2,})(?:\:[0-9]+)?\/?";
      ;
          $re_email = "mailto:[\w|-]+(?:\.[\w|-]+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+";
          if (preg_match("/(?:" . $re_url . "|" . $re_email. ")/", $cellule, $regs)) {
              $url = (strlen($regs[0]) > $url_max) ? substr($regs[0], 0, $url_max) . '...' : $regs[0];
              $cellule = str_replace($regs[0], '<a href="' . $regs[0] . '">'. $url . '</a>', $cellule);
          }
          return $cellule;
      }

      Puis modifier le filtre tableurcsv() tout à la fin. Remplacer :

      $result .= "<td width='".$largcol."%' align='".$talign."' nowrap>&nbsp;".$cellule."&nbsp;</td>\n";

      Par :

      $result .= "<td width='".$largcol."%' align='".$talign."' nowrap>&nbsp;".tableurcsv_formate_cellule($cellule)."&nbsp;</td>\n";

      La fonction tableurcsv_formate_cellule() est à mettre aussi dans mes_fonctions.php.

      Dans cette fonction, le paramètre $url_max permet de limiter la taille du lien, s’il risque de prendre toute la largeur du tableau. Mais l’url de redirection reste intacte.

    Répondre à ce message

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom