darkBlog

mercredi 25 octobre 2006

Quick and dirty : Live HTTP Headers 0.12 avec Firefox 2.0

Téléchargez la dernière version de Live HTTP Headers (à ce jour la 0.12, incompatible avec Firefox 2.0), dézippez l'archive, éditez le fichier install.rdf, remplacez la ligne :

<em:maxVersion>1.5+</em:maxVersion>

par :

<em:maxVersion>2.0.0.*</em:maxVersion>

Rezippez l'archive (en conservant l'extension .xpi bien entendu), glissez le nouveau fichier dans Firefox , et le tour est joué.

jeudi 19 octobre 2006

Attention aux recherches avec les éditeurs de contenu WYSIWYG

S'il est attrayant d'intégrer un éditeur de contenu WYSIWYG dans ses applications web, un point sur lequel il convient de faire attention est la recherche. En effet, ce type d'éditeur produit du code HTML que l'on enregistre typiquement dans une base de données. Le problème est qu'une recherche sur le contenu saisi par ce biais peut être largement déficiente, et ce pour deux raisons principales :

  • Parce qu'on peut ne pas retrouver les informations recherchées à cause de caractères spéciaux transformés en entités HTML, et
  • Plus grave, parce qu'on peut retrouver des informations liées à la présentation : couleurs (red, blue, etc), polices ("times new roman", etc), ou même plus simplement des balises HTML (strong, etc).

Ces problèmes doivent être traités séparément. Le premier peut se résoudre simplement, soit en empêchant la transformation des caractères spéciaux en entités HTML coté éditeur (pour TinyMCE par exemple, spécifier le paramètre entity_encoding : "raw"), soit en refaisant la transformation inverse coté serveur.

Pour le second point, c'est plus délicat. On peut commencer par retirer certains contrôles de l'éditeur comme celui du choix de la police ou de la couleur, mais ça n'empêchera pas l'utilisateur de faire un copier/coller brutal d'un fichier Word, pas plus que ça ne règlera le problème des balises HTML (mais au moins votre application ne ressemblera pas à un skyblog, c'est toujours ça de gagné).

Une solution acceptable est de stocker dans un champ à part le code généré par l'éditeur mais dénué de toute code HTML. Une telle opération de nettoyage se fait très facilement avec PHP (voir la fonction strip_tags), plus difficilement avec Domino (voir un peu plus bas pour une implémentation possible). Le texte résultant de cette opération n'aura plus forcément de sens, mais constituera une liste de phrases et de mots clés pertinents sur laquelle on pourra baser une recherche avec confiance. Je dis acceptable et non pleinement satisfaisante car une partie des informations peut être perdue lors du nettoyage : les URL des liens par exemple (il n'en restera plus que les libellés).

Voilà quelques conseils issus de mon expérience mes déboires en la matière. Pour l'implémentation avec Domino, ça peut se faire en quelques lignes à l'aide de l'expression rationnelle qui va bien (ici windows uniquement, mais une implémentation multi-plateforme [et plus lente] est possible avec Java) :

doc.ContentWithoutHTML = Join(Fulltrim(Split(stripHTML(doc.Content(0)), " ")))

Function stripHTML(sTexteHTML As String) As String
    Dim re As New RegExp
    re.Global = True
    stripHTML = re.Replaces(sTexteHTML, "(<[^>]+>)", " ")
End Function

vendredi 22 septembre 2006

Paris Web 2006 - le (très rapide) bilan

Ca s'est terminé il y a deux petites heures, et c'était bien. Une bonne organisation, des orateurs pointus et passionnés, des conférences (pour la plupart) intéressantes, des débats vifs et riches, un apéro communautaire improbable comme sympathique, bref, une première en France, et deux jours vraiment exceptionnels (et un peu fatiguants, aussi).

billet d'entrée de paris web 2006

J'aurais bien plus de choses à en dire, malheureusement mon agenda assez serré (voir deux billets plus en dessous ; je pars dans 4 jours à Londres et j'ai un serveur à installer/migrer entre temps) ne me permet pas d'y consacrer plus de temps. Un grand merci aux organisateurs, et bien entendu à FTEL pour m'avoir permis d'y participer. Vous pourrez probablement en savoir un peu plus prochainement du coté des 2 acolytes qui m'ont accompagné : JMF et Renaud (hein, Renaud, tu vas le faire ce compte rendu ;).

vendredi 11 août 2006

Trois astuces pour personnaliser YUI Treeview

Treeview est un contrôle graphique de la librairie Yahoo! UI Library représentant une arborescence. Il en existe des tas bien sûr (il n'y a qu'à googler un peu pour s'en convaincre), mais ce composant est doté de qualités remarquables qui le rendent particulièrement attractif :

  • Pour commencer, il s'intègre parfaitement avec l'ensemble de la librarie YUI. C'est une évidence, mais ce n'est pas pour autant inutile de le préciser. Ce n'est pas toujours facile de faire cohabiter plusieurs librairies ensemble, et c'est donc une difficilté en moins (essayez donc de faire une page avec Prototype et Jsval par exemple1, vous allez vous amuser). De même, le fait qu'il soit développé par des gens comme Yahoo! laisse planer peu de doutes sur son futur ainsi que son support.
  • Il est fourni avec une API très riche, ce qui permet d'étendre ses fonctionnalités comme d'intéragir avec chacun des éléments composant l'arborescence avec (une relative) simplicité.
  • Il est particulièrement bien documenté : utilisation rapide, description de l'API, exemples détaillés.
  • Enfin, il propose des fonctionnalités avancées qui font défaut à la plupart de ses congénères, comme le (re)chargement dynamique des noeuds (avec de l'AJAX en arrière-plan).

Nous avons donc là un composant de grande qualité, et les trois astuces suivantes vont vous permettre d'en améliorer le rendu visuel.

Astuce n°1 : Associer des icônes aux branches

Ce n'est pas réellement une astuce nouvelle puisqu'elle est présentée dans les exemples, mais elle fera une bonne introduction à ce qui va suivre. Par défaut, Treeview affiche une branche (un noeud possédant des enfants, par opposition à une feuille) avec des symbôles + et - assez classiques (). Pour plaire au boss ainsi qu'aux utilisateurs, on peut avoir envie d'y ajouter des icônes de dossiers.

Les développeurs de Treeview ont prévu le coup, et c'est donc quelque chose de très simple à mettre en oeuvre. Lesdites icônes sont en réalité des cellules de tableau vides, de largeurs fixes, et composées d'images de fond. Il y a 11 classes définies, auxquelles correspondent 11 images, en fonction de l'état de la branche (pliée, dépliée, surlignée, etc), en voilà par exemple une :

/* first or middle sibling, collapsable */
.ygtvtm {
  width:16px; height:22px;
  cursor:pointer ;
  background: url(treeview/tm.gif) 0 0 no-repeat;
}
...

Il suffit donc de redéfinir ces classes pour modifier l'aspect visuel des branches. Dans notre cas, on augmente la largeur des cellules et on change l'image de fond :

/* first or middle sibling, collapsable */
.ygtvtm {
  width:34px; height:22px;
  cursor:pointer ;
  background: url(tree/tm-folder.gif) 0 0 no-repeat;
}
...

Et voilà le résultat :

Branches par défaut, et avec icônes

Astuce n°2 : Associer des icônes aux feuilles

On a associé des icônes aux branches, on peut maintenant vouloir en associer aux feuilles. Treeview propose deux types de noeuds : des noeuds texte (TextNode) et des noeuds HTML (HTMLNode). Le premier prend un label et éventuellement une URL pour paramètres, et Treeview se charge de créer le noeud avec, le second accepte du code HTML "brut". J'entends par "brut" qu'il est injecté tel quel, ce qui pour notre cas n'est pas forcément très intéressant, puisque requerrait alors de créer les liens à la main (et tous les événements et attributs qui vont avec). Nous allons donc rester avec les noeuds texte.

Treeview rend un noeud texte de la façon suivante (je n'ai conservé que les infos utiles) :

<a onmouseout="..." onmouseover="..." onclick="..." href="..." class="ygtvlabel" id="ygtvlabelel1">Répertoire 1</a>

Sur le même principe que l'astuce précédente, on peut imaginer modifier la classe ygtvlabel du lien pour ajouter une image de fond ainsi qu'un padding à gauche.

/* the style of the text label in ygTextNode */
.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover {
  margin-left:2px;
  text-decoration: none;
}

Problème : la classe ygtvlabel est appliquée aussi bien sur les branches que sur feuilles. Une solution alternative est donc d'affecter à tous nos noeuds de type feuille une seconde classe qui se chargera du padding et de l'image de fond :

#treeview a.file {
  background: transparent url(tree/file.gif) no-repeat top left ;
  padding-left: 19px ;
}

Et à la création du noeud, on ajoute cette classe spécifique à l'aide de la propriété TextNode.labelStyle, qui définit le style du lien :

var node = new YAHOO.widget.TextNode({ label: "Darkmag.net", href: "http://darkmag.net"}, parentNode, false);
node.labelStyle += " file" ;

Allons un peu plus loin. Imaginons que l'on souhaite représenter des fichiers dans cette arborescence. Il deviendrait du coup assez intéressant d'avoir une icône propre au type du fichier. Modifions la règle CSS précédente de façon suivante :

#treeview a.file {
  background: transparent url(tree/ext/unknown.gif) no-repeat top left ;
  padding-left: 19px ;
}

#treeview a.doc {
  background-image: url(tree/ext/doc.gif) !important;
}

#treeview a.xls {
  background-image: url(tree/ext/xls.gif) !important;
}
...

Et changeons l'affectation du style comme suit :

node.labelStyle += " file " + getExtensionFichier() ;

Avec getExtensionFichier() qui retourne l'extension du fichier à représenter dans l'arbre (à définir, bien sûr). Les feuilles ont alors 3 classes, mais la dernière peut ne peut avoir de définition dans la feuille de style. Dans l'affirmative, c'est l'icône du type de fichier qui sera affichée (car la priorité est supérieure grâce au !important), dans le cas contraire, c'est l'icône "fichier inconnu". Voilà le résultat :

Associer des icônes aux feuilles

Astuce n°3 : Spécifier des styles spécifiques pour certaines branches

Là, ça devient un peu plus compliqué, car Treeview n'a pas vraiment été conçu dans cette optique : il y a 11 classes définies, et elles sont attribuées systématiquement à chaque branche, point. On peut les modifier (comme on l'a fait dans l'astuce n°1), mais c'est répercuté sur l'ensemble des branches de l'arbre. Et la propriété TextNode.labelStyle ne concerne que le lien des feuilles. L'objectif est de pouvoir attribuer une classe spécifique pour une branche donnée. Et ça, ce n'est pas prévu à la base.

Comme je le disais précédemment, Treeview est fourni avec une API très riche, et c'est bien ça qui va nous sauver. Nous allons créer une nouvelle classe, héritant de toutes les propriétés d'une branche classique, mais qui possède un paramètre supplémentaire : une classe CSS spécifique (enfin, un suffixe, pour être plus précis). Dans un premier temps, il convient de faire un nouveau jeu de 11 images et de 11 classes exploitant cette nouvelle image :

/* first or middle sibling, collapsable */
.files-ygtvtm,
.ygtvtm {
  width:34px; height:22px;
  cursor:pointer ;
  background: url(tree/tm-folder.gif) 0 0 no-repeat;
}

.files-ygtvtm {
  background-image: url(tree/tm-files.gif) !important;
}
...

Vient la partie amusante : la création de la nouvelle classe, sobrement intitulée CustomCssTextNode :

// constructeur de CustomCssTextNode
YAHOO.widget.CustomCssTextNode = function(sCssPrefix, oData, oParent, expanded) {
  this.sCssPrefix = sCssPrefix;
  YAHOO.widget.CustomCssTextNode.superclass.constructor.call(this, oData, oParent, expanded);
  return true ;
}

// héritage
YAHOO.extend(YAHOO.widget.CustomCssTextNode, YAHOO.widget.TextNode);

// et on surcharge de la méthode getStyle()
YAHOO.widget.CustomCssTextNode.prototype.getStyle = function() {
  return this.sCssPrefix + YAHOO.widget.CustomCssTextNode.superclass.getStyle.call(this) ;
}

La première partie est le constructeur de cet classe. Il prend les mêmes paramètres que TextNode, plus un : le préfixe de la classe CSS. On copie donc la valeur de sCssPrefix en paramètre dans un nouvel attribut (que TextNode ne possède pas), puis on appelle le constructeur de TextNode. Une fois la classe déclarée, on la fait hériter de toutes les méthodes et propriétés de TextNode grâce à la méthode YAHOO.extend(). Enfin, on surcharge la méthode getStyle() qui s'avère être, après un peu d'introspection dans le code de Treeview, la méthode appelée par Treeview pour indiquer la classe CSS de la branche, et on la fait retourner la chaîne préfixe + style-normalement-défini. Et c'est tout.

A l'usage, la création d'une telle branche se fait tout à fait naturellement, comme on peut le voir ci-dessous :

var branch1 = new YAHOO.widget.TextNode({ label: "Répertoire1" }, parentNode, false);
var branch11 = new YAHOO.widget.CustomCssTextNode("files-", { label: "Répertoire 1.1" }, branch1, false);

Le premier paramètre de CustomCssTextNode est le préfixe de la classe CSS, dont on a défini les classes précédemment. Et voilà le résultat :

Spécifier des styles spécifiques pour certaines branches

Conclusion

Au lieu des quatre astuces prévues initialement vous n'en aurez eu que trois, faute de temps (et un peu de motivation, je dois bien l'admettre), mais j'espère que cela vous suffira à vous convaincre du potentiel de la bête, et vous donnera envie d'essayer - si ce n'est déjà fait - la librairie YUI, qui est de loin ma préférée parmi les quatre qui font beaucoup parler d'elles en ce moment. Et à venir prochainement un billet sur l'intégration de Treeview avec rechargement dynamique sur plateforme Domino (sans agent, sinon ça serait trop facile) quand je l'aurais terminé (je le prédis pour avant ceux sur FreeBSD ;).

1 : Ces librairies définissent toutes les deux un objet Field dans le namespace global.

jeudi 2 mars 2006

Conférence sur les clients riches avec XUL

Ce soir l'AFUP organise une conférence sur les clients riches avec XUL, à 20h00 dans le 14ème (à 5 stations de métro de chez moi !). Y interviendront Laurent Jouanneau de Xulfr.org et Edouard Andrieu ainsi qu'Olivier Grange-Labat de LeMonde.fr. C'est donc avec grand plaisir que je vais m'y rendre (avec plus ou moins de retard, selon la séance de squash), et j'espère voir de mes yeux à quoi ressemble le back-office de LeMonde.fr fait à base de XUL (enfin une vrai application à montrer ?).

Edit : Bon ben c'était très intéressant, même si je m'attendais à un peu plus de pragmatisme et moins d'évangélisme de la part de l'équipe de LeMonde.fr. En tout cas, merci l'AFUP, merci JMF pour l'invit, et merci aux intervenants.

Fichiers créés par des applications web et droits d'accès

Ca ne vous est jamais arrivé qu'une application web vous génère des fichiers auxquels vous n'avez pas accès en écriture avec votre utilisateur courant ? Moi si, souvent. Et pas plus tard qu'il y a quelques jours, quand j'ai voulu migrer ce Dotclear en 1.2.3. Jusqu'à présent, je m'étais toujours trouvé dans l'incapacité à régler ce genre de problème. Par chance, mon hébergeur étant fort sympathique, il me rétablissait gentiment les droits derrière.

Pourtant, il existe une solution toute simple, tellement simple que cela explique probablement pourquoi je n'y ai jamais pensé (...) : modifier les droits des fichiers concernés au travers d'un script exécuté depuis le web1 (et donc - c'est là l'astuce - avec l'utilisateur sous lequel serveur web tourne).

Démonstration :

> ll
-rwxr-xr-x 1 darkmag darkmag 43 Nov 30 15:44 UPDATE*
-rw------- 1 nobody darkmag 1893 Feb 24 12:44 config.php
-rwxr-xr-x 1 darkmag darkmag 1908 Nov 30 15:44 config.php.in*

> chmod 777 config.php
chmod: config.php: Operation not permitted

> vi chmod.php
<?php
system("chmod 777 /usr/home/darkmag/public_html/blog/conf/config.php");
?>

> lynx http://darkmag.net/blog/chmod.php

> ll
-rwxr-xr-x 1 darkmag darkmag 43 Nov 30 15:44 UPDATE*
-rwxrwxrwx 1 nobody darkmag 1893 Feb 24 12:44 config.php*
-rwxr-xr-x 1 darkmag darkmag 1908 Nov 30 15:44 config.php.in*

Et voilà !

1 : en mode SAPI pour PHP, tout du moins. Je me demande sous quel utilisateur tourne un PHP en mode CGI. J'imagine que c'est celui du serveur web également, vu qu'à ma connaissance c'est lui qui lance l'exécutable PHP. Quelqu'un peut confirmer ?

jeudi 2 février 2006

IE et type de contenu des formulaires

Le type de contenu d'un formulaire (mais si, l'attribut enctype), comme tous les autres attributs d'un formulaire, est théoriquement accessible par le DOM et consultable / modifiable par les méthodes de scripting. Théoriquement, car vous l'aurez deviné, IE 6 ne prend pas en compte les modifications que l'on peut apporter à cet attribut. Le plus étrange dans l'histoire, pourtant, c'est que la valeur de l'attribut est bien mise à jour (ce que l'on peut vérifier en inspectant le DOM) ; IE ne fait que l'ignorer, tout simplement. Alors que tout changement apporté aux attributs action ou encore method est bien pris en compte, comme on peut le voir ci-dessous :

<form id="myform"></form>

<script type="text/javascript">
var frm = document.getElementById("myform");
frm.method = "post"; // par défaut "get"
frm.action = "http://www.vatican.va";
frm.enctype = "multipart/form-data"; // par défaut "application/x-www-form-urlencoded"
frm.submit();
</script>

Va donner :

POST http://www.vatican.va/ HTTP/1.0
...
Content-Type: application/x-www-form-urlencoded

Allez comprendre.

jeudi 10 novembre 2005

Labels, focus et Internet Explorer

Dans un formulaire HTML, le tag label permet d'associer un libellé à un contrôle. Les utiliser est fortement conseillé, tant pour l'accessibilité que pour l'usabilité du formulaire ; en effet, un click sur le libellé donne le focus au contrôle. Jusque là, je ne vous apprends rien.

Dans la grande majorité des exemples et usages courants (et à juste titre, vous allez comprendre) que l'on peut trouver, le label est lié à son contrôle à l'aide de l'attribut for comme suit : <label for="plop">libellé :</label> <input type="text" id="plop" name="plop">. Une lecture récente des spécifications m'appris qu'un autre usage, sans l'attribut for, était possible : <label>libellé : <input type="text" name="plop"></label>. Dans ce cas, le label doit être associé avec les éléments qu'il contient (je cite la spec : "When (the 'for' attribut is) absent, the label being defined is associated with the element's contents").

Je vois plusieurs avantages à cette dernière syntaxe. Par exemple, il n'est pas nécessaire d'indiquer l'identifiant du contrôle à chaque label, ce qui doit certainement se traduire par un gain de temps non négligeable autant qu'une source potentielle d'erreur en moins quand on travaille avec de gros formulaires. Mieux : pour ce qui concerne la mise en page des formulaires avec CSS, on peut également économiser un conteneur par contrôle (un paragraphe, par exemple) ; il serait en effet possible d'indiquer que le label se comporte comme un bloc (car c'est un élément en ligne par défaut) et de positionner le contrôle dans ce conteneur (en flottant, ou position absolue). Dans le fond rien de bien révolutionnaire, mais des petits avantages dont il serait dommage de se priver.

Seulement voilà, avec cette syntaxe, Internet Explorer 6 ne donne pas le focus au contrôle quand on clique sur le libellé. Du coup, on perd l'un des gros avantages des labels coté usabilité des formulaires. Dommage, je crains qu'il faille abandonner (pour le moment ?) cette syntaxe pourtant fort plaisante.

mardi 27 septembre 2005

XUL Zen Garden ?

J'ai l'impression que XUL est un peu au développement web ce que Duke Nukem Forever est aux FPS : on en parle beaucoup, mais on ne voit pas grand chose de concret. Je m'explique. On peut voir fleurir un peu partout des articles sur le sujet (et surtout depuis que le monde en a fait son back-office), il existe plétore de sites dédiés à cette technologie (XUL-fr ou encore XULPlanet), mais par contre, impossible de mettre la main sur un exemple qui soit plus évolué qu'une simple calculatrice ou un menu démarrer et quelques fenêtres.

Pourtant, quand je vois des interfaces comme celles de Firefox ou Thunderbird, je suis convaincu qu'il est possible de faire des interfaces web riches formidables avec XUL, et je suis par conséquent frustré de ne pouvoir mettre la main sur un jeu d'exemples convainquant. Frustré parce que quand je parle de XUL autour de moi, je n'ai rien à montrer (rappelez vous : Don't sell, show). Frustré parce que j'aime apprendre par l'exemple. J'ai le sentiment qu'en matière d'évangélisme, il manque clairement un site qui (dé)montrerait les capacités de cette technologie, un peu à la manière de CSS Zen Garden pour les mises en page tableless.

Alors à défaut d'une telle ressource, si vous connaissez des exemples sympas et/ou probants, vous pouvez m'en faire part en commentaires.

jeudi 22 septembre 2005

Forum PHP édition 2005, c'est pour bientôt

Ce n'est pas encore tout à fait officiel, mais sachez que l'AFUP organise son 5ème forum PHP les 9 et 10 novembre 2005, avec des conférenciers prestigieux comme Rasmus LERDORF (ni plus ni moins le créateur de PHP) dans un cadre non moins prestigieux, puisque cela se déroule à la Société Nationale d'Horticulture de France (Paris VIIème), et le tout pour la modique somme de 100 euroboules HT par jour, ou 150 HT pour les 2 jours (oui ma petite Marise, vous avez parfaitement entendu, c'est une offre e-x-c-e-p-t-i-o-n-n-e-l-l-e).

Voici le communiqué dans son intégralité :

L'AFUP, l'Association des Utilisateurs Française de PHP, organise son traditionnel forum PHP. L'édition 2005 se déroulera les 9 et 10 novembre prochain à Paris. Le programme de cette édition est particulièrement alléchant avec, notamment, la présence du créateur de PHP, Rasmus Lerdorf, et de Wez Furlong, membre du PHPGroup et responsable du module PDO. Parmi les sujets abordés cette année, on trouve :

  • PDO : Abstraction de base de données avec PHP 5.1
  • MySQL 5 : Les nouveautés
  • Création d'une extension PHP
  • Programmation Orientée Aspects en PHP
  • AJAX
  • PHP et performances
  • Bonnes Pratiques PHP

Ainsi que les retours d'expérience d'Air Turquoise, LeMonde.fr et du Crédit Agricole.

vendredi 2 septembre 2005

HiTechPros plus convivial

Une dernière petite contribution avant de filer en vacances : un script utilisateur (Greasemonkey 0.5.1) qui permet l'ouverture de plusieurs demandes de collaborateurs dans des tabs plutôt que de les ouvrir séparément et unes à unes, et ce en remplaçant les liens javascript par des liens HTML : hitechpros.user.js.

lundi 29 août 2005

Javascript : fonctions anonymes et portée des variables

Je viens de réaliser que les fonctions anonymes partagent le même contexte que la portion de code (ex: une fonction) depuis laquelle elles sont appelées. Jusque là j'avais toujours cru qu'elles avaient un contexte propre à elles, au même titre que les fonctions "classiques" dans lesquelles elles sont incluses. Plutôt qu'un long discours, un simple exemple :

<script type="text/javascript">
var msg = "variable globale";

function testPortee() {
  var msg = "variable locale";
  alert(msg);
  setTimeout(function() { alert(msg); msg += " modifiée" ;}, 1000);
  setTimeout(function() { alert(msg) }, 2000);
}

testPortee();
setTimeout(function() { alert(msg) }, 3000);
</script>

Ce script fera apparaître successivement les messages : "variable locale", "variable locale", "variable locale modifiée", "variable globale", alors que j'avais toujours cru qu'un tel traitement afficherait "variable locale", "variable globale", "variable globale modifiée", "variable globale modifiée". Et ça, pour réaliser des traitements simultanés (à la manière des threads), c'est du bonheur.

Bref, il n'est jamais trop tard pour apprendre les bases.