On ne fait pas de la magie

Aller au contenu | Aller au menu | Aller à la recherche

vendredi 18 mai 2012

Comment télécharger un fichier xlsx ou docx avec Internet Explorer ?

Les fichiers Excel et Word sont, depuis la version 2007 d'Office, des fichiers compressés au format zip.

Lorsqu'ils sont mis à disposition sur un site Web et qu'on souhaite les télécharger avec Internet Explorer, celui ci ne les considèrent malheureusement pas comme des fichiers bureautiques et veut les ouvrir comme des archives .zip plutôt que des documents Excel ou Word.

En effet, Internet Explorer analyse le contenu des fichiers pour déterminer les actions qu'il doit effectuer et il ne connait pas, dans les versions antérieures à la version 9, ces formats de fichier. Il considère donc qu'il doit les traiter comme des fichiers compressés .zip.

Heureusement, il existe un moyen simple de contourner ce problème en enregistrant ces fichiers au lieu de les ouvrir.

Pour cela, cliquez sur le bouton "Enregistrer" de la boite de dialogue de téléchargement :

Telechargement1.png

Modifiez ensuite, dans la boite de choix de l'emplacement, l'extension .zip en .xlsx ou .docx :

Telechargement2.pngTelechargement3.png

Une fois le téléchargement effectué, vous pouvez cliquer sur Ouvrir pour lancer Excel ou Word avec votre fichier téléchargé !

Telechargement4.png

Comment utiliser des données Internet avec Excel ?

Lorsque vous souhaitez utiliser des données provenant d'autres logiciels à une feuille Excel et que ces données sont susceptibles d'être modifiées, vous pouvez utiliser la liaison avec des sources de données afin qu'une mise à jour des données soit prise en compte dans votre classeur.

Parfois, ces données proviennent de pages Web. Si ces pages sont mises en forme à l'aide de tableaux, vous pouvez utiliser la même approche et avoir votre classeur Excel mis automatiquement à jour.

Par exemple, si vous souhaitez placer dans une feuille les derniers bulletins d'alerte du CERTA (http://www.certa.ssi.gouv.fr/), il suffit, avec Excel 2010, de choisir le ruban Données et de cliquer sur le bouton "A partir du Web".

DonneesInternetExcel0.PNG

Dans la fenêtre qui apparaît, indiquez l'adresse de la page Web contenant les informations que vous souhaitez lier et cliquez sur le bouton Ok pour afficher la page.

Des flèches dans un cadre jaune indiquent les zones dans lesquelles Excel peut sélectionner des informations. SI vous cliquez sur l'une d'elle, elle se transforme en sélection cochée, dans un cadre vert :

DonneesInternetExcel1.PNG

Cliquez sur le bouton Options à droite de la barre d'adresse pour indiquer que vous voulez conserver la mise en forme HTML, validez ces options en cliquant sur le bouton Ok.

DonneesInternetExcel2.PNG

Vous pouvez ensuite cliquer sur le bouton Importer en bas à droite de la fenêtre et choisir la zone d'Excel où vous collez ces informations.

DonneesInternetExcel3.PNG

Vous obtenez ainsi votre nouveau document qui vous proposera de se mettre à jour à chaque ouverture du classeur à l'aide des informations de la page Web.

DonneesInternetExcel4.PNG

lundi 16 avril 2012

Fancybox 2 en français

Fancybox est une bibliothèque jQuery très utile lorsqu'on souhaite présenter des images en galerie ou des fenêtres d'informations particulières, par exemple un écran de connexion.

Une nouvelle version, la 2.0, vient de sortir. Elle ajoute de nouvelles fonctionnalités, dont notamment un diaporama intégré.

Seul soucis pour un site français, ses messages sont en anglais, et même s'ils très peu nombreux et largement compréhensibles, ils peuvent détonner.

Pour cette raison, j'ai écrit une traduction de Fancybox en français, que je vous propose en fichiers joints.
(Il n'y a malheureusement pas de structure prévue pour internationaliser proprement Fancybox, sans modifier les fichiers eux mêmes).

Pour l'installer, il suffit de télécharger les deux fichiers et de remplacer les fichiers standard. Attention à jquery.fancybox-button.js qui doit être placé dans le répertoire helpers.

mercredi 11 avril 2012

How to use Tumblr titles to create a menu on your website ?

French version

When you add on your Web site a blog hosted on tumblr,  you may wish add a menu with the latest posts of the blog.

You will need some javascript, with the jQuery  library and the tumblr  API. 

First, we have to registrer a tumblr application, in order to get a public key.

Then, we have to do a jQuery Ajax request with the API /posts function. We do this with the classical JQuery method $('document').ready().

<script type="text/javascript">
$('document').ready(function () {
$.ajax({
url: 'http://api.tumblr.com/v2/blog/myblog.tumblr.com/posts',
method: 'get',
data : ({
api_key :'your_public_key',
jsonp : 'ProcessTitle'
}),
dataType: "jsonp"
});
});


This request use jsonp to declare the javascript callback function : ProcessTitle.
This function get an object encapsulating several other objects which allow to get the list of the posts.
Then, we can create the title list, with the links to the articles, and add this list to a div tag.

function ProcessTitle(reponse) {
var posts = reponse.response.posts;
var text ='';
for (var i in posts)
{
p = posts[i];
text += '<a href='+p.post_url+'>'+p.title+'</a><br>';
}
$('#titleBlog').append(text);
}

Then, we have to write two function to show and hide the div tag, and to bind these functions to the events mouseover and mouseout of the menu.

function onTitleBlog() {
var pos = $("#blog").offset();
pos.top += 20;
pos.left -= 200;
$("#titleBlog").css({top: pos.top,left: pos.left}).show();
}
function offTitleBlog() {
$("#titleBlog").hide();
}
</script>
<style>
#titleBlog {
display:none;
position:fixed;
background-color:white;
border:#999999 thin solid;
border-radius:5px;
padding:5px;
}
#titreBlog h3 {
text-align:center;
}
</style>

<div id="menu">
<ul>
<li><a href="..." >Menu1</a></li>
<li><a href="..." >Menu2</a></li>
<li><a href="http://blog.company.com" target="_blank" id="blog"
onmouseover="onTitleBlog()" onmouseout="offTitleBlog()">Blog
<div id="titleBlog"><h3>Latests posts</h3></div></a></li>

</ul>
</div>

mardi 10 avril 2012

Ecrire un plugin jQuery pour centrer des éléments

Lorsqu'on utilise jQuery, on peut écrire des fonctions javascript classiques qui font simplement appel à jQuery pour effectuer le traitement souhaité.

L'ennui avec ces fonctions est que leur utilisation est plus délicate que l'utilisation de jQuery.

Par exemple, prenons le cas d'une fonction qui permet de centrer un élément. Cette fonction peut s'écrire ainsi :

function centreElement(element) {
        $(element).css({
            'top': (($(window).height() - $(element).height()) / 2) + $(window).scrollTop(),
            'left': (($(window).width() - $(element).width()) / 2) + $(window).scrollLeft()
          });
}

Cette fonction peut être appelée sur n'importe quel élément, de cette façon centreElement('#image1'). Mais que se passe t-il si on veut centrer horizontalement plusieurs éléments images ?

Il suffit de modifier un peu cette fonction pour ne centrer qu'horizontalement. Par contre, il faut exécuter une boucle, par exemple avec la fonction each de jQuery, pour appeler autant de fois que nécessaire cette fonction. Une solution plus élégante peut être d'écrire son propre plugin jQuery, c'est à dire, tout simplement, une fonction personnelle qui se comporte comme une fonction intégrée de jQuery. 

Voilà la façon dont on pourrait écrire un tel plugin :

(function($) {
        $.fn.centreElement = function() {
            this.each(function() {
                   var $e = $(this);
                   $e.css({
                     'left': (($(window).width() - $e.width()) / 2) + $(window).scrollLeft()
                   });
            });   
            return this;   
        };
})(jQuery);

Un plugin jQuery nom est tout simplement une fonction définie avec $.fn.nom. Pour éviter tout télescopage avec d'autres bibliothèques installées, on utilise une fonction anonyme qui reçoit un paramètre $, et on appelle cette fonction avec jQuery :

(function($) {

// ici, $ représente jQuery sans aucun risque de télescopage avec une autre bibliothèque

})(jQuery);

Il suffit maintenant de définir une fonction anonyme pour $.fn.centreElement.
Cette fonction doit pouvoir être chainée avec d'autres appels de fonction jQuery et pour cela, elle doit renvoyer l'objet this fourni par jQuery.
Elle doit également exécuter son traitement sur chaque noeud référencé par jQuery, ce que l'on peut réaliser avec la fonction each().
Voilà donc la structure que doit avoir tout plugin jQuery :

$.fn.centreElement = function() {
    this.each(function() { // boucle générique sur chacun de éléments avec la fonction each()
         // code spécifique au plugin écrit
    });   
    return this; // valeur de retour pour pouvoir effectuer le chainage  
};

Et il ne reste plus, pour effectuer le centrage, qu'à reprendre ce que nous avions écrit dans la première fonction, en enlevant le top :

var $e = $(this);
$e.css({
     'left': (($(window).width() - $e.width()) / 2) + $(window).scrollLeft()
});

Notre plugin permet maintenant d'écrire des instructions comme :

$('img').centreElement().show();

Cet exemple est parfois employé comme exercice de cours jQuery

samedi 31 mars 2012

Développer un système de notation complet avec jQuery, Php et MySQL

Lorsqu'on souhaite développer une fonctionnalité interactive pour un site Web, on peut chercher un plugin jQuery qui répond au problème à résoudre.

Malheureusement, il faut souvent adapter le site et le plugin au besoin réel et c'est souvent là que les choses se gâtent : il faut modifier l'architecture, créer une table, écrire du SQL, écrire des nouvelles pages php, de nouvelles fonctions, ...

J'ai souvent constaté ces difficultés lors de formations et la lecture des forums confirme la difficulté.
Tant qu'on n'a pas un exemple effectif complet qui fonctionne, on ne sait pas pourquoi notre développement échoue : est ce une incompréhension majeure ou est ce une faute de frappe ?

Ce billet présente les différentes parties à développer pour réaliser un site de vote pour des photos : conception de la base de données, écriture du php nécessaire pour accéder à la base, écriture du php pour répondre à une requête Ajax, écriture de la page d'affichage, écriture du javascript avec jQuery et les plugin rating et form.

C'est lui aussi un exemple que j'utilise lors de formation que j'anime (PHP, Javascript ou jQuery) et il devrait être suffisamment détaillé pour que vous pussiez le comprendre, le reproduire et l'adapter à votre propre besoin.

Le cahier des charges est simple : la page Web doit afficher une photo, avec une note mémorisée et permettre au visiteur de voter une seule fois.
Il n'y a pas de contrôle de votant : s'il effectue une nouvelle visite (ou recharge la page), il peut à nouveau voter.

Vous pouvez voir un exemple de fonctionnement ici

La mémorisation des votes s'effectue dans une table SQL contenant trois champs : le nom de la photo (varchar(30), clé primaire), la note (float) et le nombre de votant (int). L'instruction SQL de création de la table est :

CREATE TABLE `basededonnee`.`VotePhoto` (`nom` VARCHAR(30) NOT NULL, `note` FLOAT NOT NULL, `nbvote` INT NOT NULL, PRIMARY KEY (`nom`)) ENGINE = MyISAM;

L'accès à la base se fait avec une classe PHP dont voici la définition :

<?php
require_once("bddClasse.php");
class Vote {
    public $nom;
    public $nbvote;
    public $note;
    public function __construct($nom,$nbvote,$note) {
        $this->nom = $nom;
        $this->nbvote = $nbvote;
        $this->note = $note;
    }
    static public function getVote($nom) {
        $base = new bdd();
        $nom = mysql_real_escape_string($nom);
        $sql = "SELECT nom,nbvote,note FROM VotePhoto"
        . " WHERE nom = '$nom'";
        $res = $base->requete($sql);
        if (($val = mysql_fetch_object($res)) !== false)    {
            return new Vote($val->nom, $val->nbvote,$val->note);
        }
        else {
            $sql = "INSERT INTO VotePhoto (nom,nbvote,note) values "
                . " ('$nom',0,0)";
            $res = $base->requete($sql);
            return new Vote($nom,0,0);
        }
    }
    static public function setVote($vote,$note) {
        $vote->note = ($vote->note * $vote->nbvote++ + $note) / $vote->nbvote;
        $base = new bdd();
        $nom = mysql_real_escape_string($nom);
        $note = mysql_real_escape_string($note);
        $sql = "UPDATE VotePhoto set note='" . $vote->note . "',nbvote=nbvote+1"
        . " WHERE nom = '$vote->nom'";
        $res = $base->requete($sql);   
        return $vote;
    }
}
?>

La classe Vote dispose d'un constructeur __construct qui ne sert qu'à initialiser les champs.

Elle fournit deux méthodes statiques :

  • l'une pour construire une instance de Vote à partir du nom de la photo et du contenu de la base de données : getVote
    Cette méthode crée automatiquement un enregistrement vide si la photo n'a pas encore eu de vote.
  • l'autre pour mettre à jour la note dans la base de données : setVote
    Elle renvoie l'instance de Vote passée en paramètre après l'avoir mise à jour.

Notez l'utilisation de mysql_real_escape_string pour éviter des attaques de la base de données.

Cette classe Vote emploie une classe utilitaire pour accéder à MySQL :

<?php
class bdd {
    private $connexion;
    const NOM='domi';
    const PASSE='imod38vps17';
    const SERVEUR ='localhost';
    const BASE = 'domi';
    function __construct() {
        $this->connexion = mysql_pconnect(self::SERVEUR, self::NOM, self::PASSE);
        if (!$this->connexion)
        {
                throw new Exception("Connexion au serveur impossible");
        }
        if (!mysql_select_db(self::BASE, $this->connexion)) {
                throw new Exception("Accès à la base impossible");
        }
    }
function requete($req,$debug=false) {
    $res = mysql_query($req,$this->connexion);
    if ($res === false ) {
        print $req . "<br>";
        print mysql_error();
    }
    if ($debug) {
        error_log($req);
        error_log(mysql_error());
    }
    return $res;
}

function __destruct() {
    mysql_close($this->connexion);
    }
}
?>

Cette classe ne vise pas à fournir une abstraction complète de l'accès à la base de données, mais uniquement à nous faciliter la vie pour travailler avec MySQL.

La page web complète est

<?php
$home = $_SERVER['DOCUMENT_ROOT'];
$photo = "GeminidAurora.jpg";
require($home . "/notation/voteClasse.php");
$vote = Vote::getVote($photo);
function check($val,$vote) {
    if (round($vote->note,0) == $val) {
        print 'checked="checked"';
    }
}
?>

<!DOCTYPE HTML>
<html LANG="en">
<head>
<meta CHARSET="iso-8859-1">
<title>Exemple de vote sur une photo</title>
<script TYPE="text/javascript" SRC="/js/jquery-1.6.min.js"></script>
<script TYPE="text/javascript" SRC="/js/rating/jquery.MetaData.js"></script>
<script TYPE="text/javascript" SRC="/js/rating/jquery.rating.js"></script>
<script TYPE="text/javascript" SRC="/js/jquery.form.js"></script>
<link HREF="/js/rating/jquery.rating.css" REL="stylesheet" TYPE="text/css">

<script TYPE="text/javascript" >
$('document').ready(function () {
    envoi = true;
    $('input.vote').rating({
    callback: function(value,link) {
            if (envoi) {
                envoi = false;
                $(this.form).ajaxSubmit({
                    dataType:'json',
                    success: function(rep) {
                        var note = rep.note;
                        var nombre = rep.nombre;
                        $('input.vote').rating('select', note);
                        $("#nombre").html(nombre);
                        $('input.vote').rating('disable');
                    }
                });
            }
        }
    });
});
</script>
</head>
<body>
<form ID="note" ACTION="/notation/vote.php" METHOD="post">
<img SRC="/notation/<?php print $photo; ?>"><br>
<input NAME="photo" TYPE="hidden" VALUE="<?php print $photo; ?>">
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="1" <?php check(1,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="2" <?php check(2,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="3" <?php check(3,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="4" <?php check(4,$vote); ?>>
<input NAME="star" TYPE="radio" CLASS="vote" VALUE="5" <?php check(5,$vote); ?>>
<br>(<span ID="nombre"><?php print $vote->nbvote ?> vote<?php print ($vote->nbvote<2)?"":"s"; ?></span>)
</form>
</body>
</html>

Cette page comporte plusieurs parties :

  • une initialisation php qui permet d'obtenir la note mémorisée dans la base de données. On y trouve l'utilisation de la méthode statique getVote.
  • le script javascript qui permet d'activer le plugin rating et de définir une fonction appelée lorsque le visiteur vote pour une note. Nous allons y revenir plus en détail.
  • le code html qui affiche la photo et définit le formulaire qui est transformé par le plugin rating en affichage d'étoiles correspondant à la note.

Il y a quelques éléments php pour peaufiner l'affichage. La fonction check permet de définir l'étoile correspondant à la note. Il y a également un peu de code PHP pour gérer correctement l'accord du mot vote. 

Revenons sur la fonction anonyme appelée lorsque le visiteur vote :

envoi = true;
function(value,link) {
    if (envoi) {
            envoi = false;  // utilisation d'une variable de portée étendue pour ne permettre qu'un seul vote
            $(this.form).ajaxSubmit({ // envoi du formulaire à l'aide d'une requête ajax
            dataType:'json',
            success: function(rep) { // définition de la fonction anonyme appelée lorsque la réponse Ajax est reçue

                 var note = rep.note;
                 var nombre = rep.nombre;
                 $('input.vote').rating('select', note);
                 $("#nombre").html(nombre);
                 $('input.vote').rating('disable');
            }
         }); // fin de définition de la fonction anonyme appelée lorsque la réponse Ajax est reçue
     }
}

Elle utilise le plugin form pour effectuer l'envoi du formulaire à l'aide d'une requête Ajax. Cette requête AJax attend une réponse au format json, de la forme : {'note':'3', 'nombre' : '5 votes'}, qui est utilisée pour mettre à jour les informations affichées sur la page.
Comme un visiteur ne doit pas voter plusieurs fois, on utilise une variable externe pour n'envoyer qu'une seule fois la requête. Attention, si vous voulez gérer plusieurs votes sur la même page, il faut penser à utiliser une variable différente pour chaque photo !

Et le dernier élément de notre architecture est la page PHP utilisée par la requête Ajax :

<?php
$home = $_SERVER['DOCUMENT_ROOT'];
require($home . "/notation/voteClasse.php");
$photo = $_POST['photo'];
$note = $_POST['star'];
$vote = Vote::getVote($photo);
$vote = Vote::setVote($vote,$note);
$texte = "$vote->nbvote vote" . (($vote->nbvote<2)?"":"s");
print '{"note" : "' . round($vote->note,0) . '", "nombre" : "' . $texte . '" }';
?>

On utilise à nouveau la classe Vote, en appelant successivement les deux méthodes statiques pour obtenir l'instance de Vote correspondant à la photo, puis pour mettre à jour la note. La dernière instruction crée la réponse json attendue pour faire la mise à jour de la page HTML.

Chaque partie de cet exemple est relativement simple et la complexité vient plutôt de l'imbrication des différentes parties.

Si certains points demandent plus d'explications, n'hésitez pas à l'indiquer dans les commentaires.

dimanche 25 mars 2012

Comment créer un menu sur son site avec les titres de son blog tumblr ?

English version

Lorsque l'on dispose d'un site Web et qu'on l'enrichit avec un blog hébergé sur tumblr,  on peut souhaiter ajouter sur son site un menu contenant les dernières entrées du blog.

Pour cela, il suffit d'utiliser un peu de javascript, en s'aidant de la bibliothèque jQuery et de l'API proposée par tumblr

Il faut tout d'abord enregistrer une application tumblr, afin d'obtenir une clé publique.

Ensuite, il faut effectuer avec jQuery une requête ajax avec la fonction /posts de l'API. Nous le faisons de façon classique en appelant la méthode $('document').ready() de jQuery.

<script type="text/javascript">
$('document').ready(function () {
var texte;
$.ajax({
url: 'http://api.tumblr.com/v2/blog/monblog.tumblr.com/posts',
method: 'get',
data : ({
api_key :'votre_cle',
jsonp : 'TraiteTitre'
}),
dataType: "jsonp"
});
});


Cette requête utilise jsonp pour définir la fonction javascript à appeler lorsque tumblr répond, dans notre cas TraiteTitre.
La fonction reçoit en paramètre un objet contenant plusieurs autres objets, qui permettent d'accéder à la liste des articles publiés.
Il suffit alors de préparer une liste des titres avec des liens et de l'ajouter au contenu d'une balise div réservée à cet effet.

function TraiteTitre(reponse) {
var posts = reponse.response.posts;
var text ='';
for (var i in posts)
{
p = posts[i];
text += '<a href='+p.post_url+'>'+p.title+'</a><br>';
}
$('#titreBlog').append(text);
}

Pour finir la mise en place du menu, on peut écrire deux fonctions pour afficher et masquer le div contenant les titres, et activer l'appel de ces fonctions avec les événements mouseover et mouseout du menu.

function onTitreBlog() {
var pos = $("#blog").offset();
pos.top += 20;
pos.left -= 200;
$("#titreBlog").css({top: pos.top,left: pos.left}).show();
}
function offTitreBlog() {
$("#titreBlog").hide();
}
</script>
<style>
#titreBlog {
display:none;
position:fixed;
background-color:white;
border:#999999 thin solid;
border-radius:5px;
padding:5px;
}
#titreBlog h3 {
text-align:center;
}
</style>

<div id="menu">
<ul>
<li><a href="..." >Menu1</a></li>
<li><a href="..." >Menu2</a></li>
<li><a href="http://blog.societe.com" target="_blank" id="blog"
onmouseover="onTitreBlog()" onmouseout="offTitreBlog()">Blog
<div id="titreBlog"><h3>Dernières parutions du blog</h3></div></a></li>

</ul>
</div>
Cet exemple est souvent utilisé dans les formations Javascript et jQuery que j'anime, pour montrer une utilisation sur une application réelle.

lundi 5 mars 2012

Elearning, Opale et Dokeos

Lorsque je travaille sur la réalisation de supports pédagogiques, j'apprécie d'employer le logiciel Opale qui permet de construire des supports utilisables lors de présentation mais également imprimables.

Mais comment utiliser ensuite ces mêmes supports pour les publier dans un cursus e-learning, par exemple avec Dokeos ou Chamilo ?

La solution vient de SCORM (Sharable Content Object Reference Model) qui est une norme d'échange de support pédagogique.

Sous Opale (qui doit disposer de la version Opale Advanced), il suffit de créer un support Web et de faire une publication au format SCORM multi SCO :

Publication Opale Multi SCO

Une fois cette publication faite, il faut créer un package, au format SCORM 1.2 et le télécharger sur le poste :

Packaging SCORM Téléchargement Package

Sous Dokeos, il faut importer le cours dans une formation, en indiquant qu'il s'agit d'un format SCORM :

Importation format SCORM dans Dokeos

vendredi 19 août 2011

Quelques ressources Filemaker

En cette période d'été, un petit billet pour présenter de nouvelles ressources sur Filemaker.

Comment modifier un champ au moment de sa saisie ? décrit un moyen pour appliquer automatiquement une transformation sur les données saisies. On peut ainsi, par exemple, transformer une saisie en majuscule, grâce à l'utilisation de la fonction Upper sur le champ saisi.

Comment définir une contrainte d'unicité sur plusieurs champs décrit une méthode pour appliquer une contrainte d'unicité sur un groupe de champs au lieu d'un seul. Par exemple, cela permet de garantir l'unicité des paires "nom" et "prénom" dans une base.

samedi 30 juillet 2011

Comment utiliser Installshield pour forcer l'installation de fichiers par Windows Installer ?

Parfois, il est nécessaire, lors d'une installation sous Windows, de forcer l'installation de tous les fichiers, indépendamment des versions et des dates des fichiers éventuellement présents sur le système cible.

J'ai décrit dans cet article les différentes possibilités à notre disposition avec Installshield. Une solution passe par l'utilisation de la propriété REINSTALLMODE, mais il faut veiller aux éventuels effets de bord !

- page 1 de 10