skip to main | skip to sidebar
Code 18
Manuel du savoir-faire à l'usage des geeks et des curieux
RSS
  • Accueil
  • Le web au Québec
  • Liens
  • Twitter
  • Facebook
  • À propos

jeudi 30 avril 2009

Coeur de pirate

Publié par Infinite Loop, à 23 h 31 0 commentaire

Demain, j'attends de la grande visite. Le genre d'invité qu'on ne voit pas souvent et qu'il faut en profiter pendant que ça passe! Alors j'ai passé une partie de la soirée à faire du ménage (incluant la vaisselle de la semaine...) et ne voyant pas l'heure avancer, j'ai presque oublié de venir écrire quelques mots ici.

Comme l'heure est tardive et que je n'ai pas le courage d'écrire et de commenter un snippet de code, je me dis que mettre un peu de musique pourrait m'aider à m'inspirer. Quelques nouveaux CD traînent sur le coin du bureau... Je suis peut-être "old school" pour un gars techno, mais quand le CD coûte seulement 2$ de plus que l'album en format mp3 sur iTunes... J'ai l'impression de me répéter, non ?

Mes yeux croisent le disque de Coeur de pirate et ça me fait penser à The Pirate Bay. Arrgh!!! J'ai acheté ce CD pour ma blonde et je ne l'ai pas encore écouté. C'est peut-être l'occasion ? Au moins, j'ai le mérite de ne pas l'avoir téléchargé illégalement; je respecte trop la musique pour ça. Je vais l'encoder pour l'écouter demain au travail.

J'ouvre l'encodeur de musique CDEx (gratuit!), je règle le bitrate à 320 kbps, l'application se connecte sur freedb.org pour récupérer automatiquement les informations de l'oeuvre et un simple F9 active le travail pour obtenir deux minutes plus tard l'album en format numérique. Juste deux minutes ? J'ai le temps de faire plus de tests :-)

Ça faisait longtemps que je voulais tester le format OGG ainsi que d'autres formats de compression. Juste pour le fun, je vais essayer tous les types possibles supportés par CDEx. Voici les résultats pour la même pièce (Ensemble de Coeur de Pirate) à 320 kBit/s (quand c'est possible de le spécifier) :

  • Lame Mp3 (5,49 Mo)
  • Internal Mp2 (5,49 Mo)
  • OGG Vorbis (5,96 Mo)
  • Monkey's Audio - Ape (14,4 Mo)
  • Wav (24,2 Mo)
Le format Wma de Microsoft étant limité à un encodage maximum à 160 kBit/s, le format reste quand même proportionnel au mp3. Sinon, j'ai aussi iTunes d'installé sur mon poste. Voici le résultat pour ses formats :
  • Mp3 (5,49 Mo)
  • AAC / m4a (5,51 Mo)
  • Apple Lossless / m4a (16,1 Mo)
  • AIFF (24,2 Mo)
Avec la version 8 d'iTunes, pour changer le format d'encodage par défaut, rendez-vous au menu Edit / Preferences / Onglet General / Import Settings. Ensuite, dans la liste des morceaux, cliquez sur la pièce à convertir avec le bouton droit de la souris et choisissez Create [format] version dans le menu contextuel.

En conclusion, à part que ça sent le propre chez moi, je dirais qu'à taille semblable, mieux vaut conserver sa collection au format mp3 pour des questions de compatibilité avec les appareils portatifs comme le iPod. Un coup d'oeil à l'heure me fait penser qu'il ne vous reste que 29 minutes avant la limite pour envoyer votre rapport d'impôt (Canada et Québec).


Tags: Coffre à outils, Musique

mercredi 29 avril 2009

Photos des développeurs d'OpenOffice

Publié par Infinite Loop, à 19 h 27 0 commentaire

Dans le tableur Calc d'OpenOffice 3, si on entre dans n'importe quelle cellule la formule =STARCALCTEAM(), le nom des développeurs s'affichera, de même qu'une photo de l'équipe.



Dans le cas de Writer, on peut entrer le mot StarWriterTeam puis appuyer sur F3 pour la faire apparaître.



Beau montage Photoshop! Oups... GIMP!

Les photos se trouvent respectivement dans des archives compressées (zip) : /basis/share/config/images.zip, à l'intérieur de /sc/res/calcteam.png et /sw/res/writerteam.png. Remplacez-la par la votre pour épater vos amis... ou tout simplement pour perdre votre temps.


Tags: Easter Eggs

mardi 28 avril 2009

Audiotool, studio musical en Flash

Publié par Infinite Loop, à 19 h 01 0 commentaire

Amateurs de musique électronique et adeptes du classique synthétiseur Roland TB-303 et du TR-808, voici une application Flash qui devrait vous intéresser : Audiotool par Hobnox. À partir de votre fureteur préféré, accédez au studio virtuel, choisissez votre style, votre configuration, le tempo, le rythme, ajoutez une panoplie d'appareils pour produire un son personnalisé et faites même vos raccordements entre les instruments pour avoir le contrôle total sur votre musique. Le nombre configurations possible est impressionnant. Enregistrez aussi vos créations et partagez-les avec vos amis grâce au service de réseautage social.

Merci à PL pour pour cette découverte (inspiré de son lien original sur ToneMatrix par Andre Michelle)! Vraiment très cool comme service.


Tags: Musique

lundi 27 avril 2009

Compresser TinyMCE

Publié par Infinite Loop, à 19 h 52 0 commentaire

Hier, en abordant le sujet de GZip, j'ai démontré comment on pouvait facilement réduire le volume des données transférées du serveur au client, à l'aide de la compression HTTP.

Pour accélérer la vitesse de chargement du contenu d'une page web, on peut principalement faire deux choses :

  • minimiser les librairies, le CSS et le HTML en les compressant
  • réduire au maximum le nombre de requêtes HTTP
Pour ce dernier point, on peut s'inspirer de ce que Yahoo! et Dojo font. Le portail Yahoo! utilise une technique qui permet de charger en un seul fichier des icônes groupés en un unique fichier graphique. Donc une seule requête HTTP, un seul fichier en cache qui peut être réutilisé constamment en ne faisant afficher que la zone correspondante à l'aide du CSS. À titre d'exemple, le fichier suivant pèse seulement 31 kb et contient au delà de 130 icônes. Un calcul rapide permet de constater qu'en moyenne, 1 kb peut contenir 4 icône! Essayez de faire mieux ;-)

Sinon, à l'image de Dojo Toolkit qui possède un système de compression (ShrinkSafe) des librairies qui permet de construire des builds personnalisés qui contiennent uniquement ce qu'on a besoin, le Rich Text Editor TinyMCE possède aussi un compresseur propriétaire pour son composant. À la base, comme il est lourd à télécharger (surtout si plusieurs extensions sont appelés), on peut accélérer le chargement à la vitesse de l'éclair en combinant les deux procédés : compression GZip et réduction des appels HTTP (pour GZip, référez-vous à mon article d'hier).

Une fois GZip en marche, on voit immédiatement descendre la taille de tout ce qui est relié à TinyMCE descendre de 285 kb à 95 kb, ce qui est déjà plus respectable. En utilisant le noyau compressé offert par son éditeur Moxiecode, on peut atteindre une réduction de 75% plus petit. Pour le mettre en application, voici les quelques étapes simples à suivre :
  • Installez TinyMCE normalement (j'ai écrit un post sur le sujet dans le passé)
  • GZip doit être activé sur le serveur et l'extension zlib ne doit pas être actif pour que ça fonctionne
  • Téléchargez le compresseur pour le langage de votre choix (dans mon cas PHP)
  • À partir de l'archive Zip, récupérez le fichier tiny_mce_gzip.js et tiny_mce_php et les déposer le répertoire d'installation de tiny_mce (au même niveau que le script tiny_mce.js)
  • Dans votre page, modifiez le chemin du script qui pointe sur tiny_mce.js pour qu'il pointe vers la version tiny_mce_gzip.js
  • Le fichier PHP sera utilisé pour rassembler les différents scripts en un seul fichier et créera un fichier .gz en cache dans le même répertoire pour chaque configuration demandée
  • Juste avant l'appel d'initialisation du composant RTE ( tinyMCE.init() ), placez le bloc de script suivant (selon votre configuration nécessaire) :
<script type="text/javascript">
tinyMCE_GZ.init({
plugins : 'style,layer,table,save,advhr,advimage,advlink,emotions,iespell,insertdatetime,preview,media,'+
'searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras',
themes : 'advanced',
language : 'fr',
disk_cache : true,
debug : false
});
</script>
  • Remarquez le nom de la fonction : tinyMCE_GZ.init()
  • Ce bloc de code doit paraître avant le script d'initialisation et doit être positionné dans un bloc script séparé
  • Testez le chargement à nouveau. Firebug indique que le RTE s'est effectué avec seulement 19 kb !


Tags: JavaScript, PHP

dimanche 26 avril 2009

Compression HTTP avec GZip

Publié par Infinite Loop, à 12 h 21 0 commentaire

Le développement web utilise de plus en plus de librairies JavaScript pour agrémenter l'expérience d'un site web mais qui vient inmanquablement avec l'inconvénient d'augmenter considérablement le volume de l'information à télécharger. Bien qu'on puisse utilise YUI Compressor pour réduire la taille des fichiers, on peut aussi tirer avantage de sa combinaison avec GZip (GNU Zip) au niveau du serveur web pour automatiser la compression HTTP des types de contenus textes (HTML, CSS, JavaScript, etc).

L'objectif avec GZip est de faire en sorte que le serveur compresse l'information à envoyer au client (fureteur) et celui-ci est normalement en mesure d'accepter et d'interpréter le contenu en le dézippant, tout cela de façon transparente pour l'utilisateur. Ça permet entre autre d'augmenter la vitesse de téléchargement puisque les fichiers sont plus petits et donc de réduire la bande passante utilisée.

Avec Firefox, on peut voir s'il est configuré pour accepter du contenu compressé en entrant about:config dans la barre d'adresse et en recherchant la clé network.http.accept-encoding. S'il est activé (par défaut avec les fureteurs récents), il devrait avoir la valeur gzip,deflate. Certains fureteurs plus vieux n'ont pas cette capacité, par exemple ceux basés sur Mozilla 4.

Pour ajouter la compression sur un serveur Apache, il faudra utiliser le module mod_deflate. Son activation repose principalement sur ceci dans le fichier httpd.conf :

  • Ajouter le chargement du module :

    LoadModule deflate_module modules/mod_deflate.so

  • Ajouter les lignes suivantes (de façon globale ou dans le vhost qui doit l'utiliser) :

    Activer le filtre :
    SetOutputFilter DEFLATE

    Spécifier les types à compresser (ne pas oublier d'ajouter JavaScript!) :
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/x-javascript

  • Apache recommande la configuration suivante pour gérer les exceptions de certains types de fureteurs. Pour s'assurer que tout fonctionne normalement, n'oubliez pas de tester dans un environnement de développement avant de le mettre en production!

    # Insert filter
    SetOutputFilter DEFLATE

    # Netscape 4.x has some problems...
    BrowserMatch ^Mozilla/4 gzip-only-text/html

    # Netscape 4.06-4.08 have some more problems
    BrowserMatch ^Mozilla/4\.0[678] no-gzip

    # MSIE masquerades as Netscape, but it is fine
    # BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

    # NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
    # the above regex won't work. You can use the following
    # workaround to get the desired effect:
    BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html

    # Don't compress images
    SetEnvIfNoCase Request_URI \
    \.(?:gif|jpe?g|png)$ no-gzip dont-vary

    # Make sure proxies don't deliver the wrong content
    Header append Vary User-Agent env=!dont-vary

Une fois la configuration terminée, on peut noter quelques chiffres concernant les améliorations apportées :
  • Sur la page d'accueil de jQuery, ils annoncent que leur librairie ne pèse que 19 kb. Remarquez à côté que les termes "Minified and Gzipped" sont ajoutés.
  • Le fichier original de la version 1.3.2 a un poids non compressé de 117 kb.
  • En compressant le fichier (minified) avec la dernière version du YUI Compressor, on obtient un résultat de 56 kb, soit le même que celui offert en téléchargement sur le site de jQuery. C'est son poids réel occupé sur le disque et c'est lui qui sera mis en ligne dans notre projet.
  • Durant le transfert, si GZip est activé, on note grâce à Firebug et son onglet Net, que le fichier zippé n'a nécessité que 20 kb de téléchargement par le client, soit 65% de moins que la version réduite et 5 fois plus petite que l'originale.
En conclusion, la compression HTTP est une excellente pratique pour réaliser une économie d'échelle sur le bandwidth et accélérer la vitesse de chargement des composants. N'hésitez surtout pas à vous en servir.


Tags: Apache, JavaScript

Citation no. 27 sur les geeks

Publié par Infinite Loop, à 07 h 39 0 commentaire

Don't make fun of geeks because one day you will end up working for one.

- Inconnu


Tags: Citations

samedi 25 avril 2009

Compter sur ses doigts

Publié par Infinite Loop, à 10 h 28 0 commentaire

Il y a un cliché qui dit qu'on ne devrait pas compter sur ses doigts une fois rendu à l'âge adulte. Pourtant, beaucoup de gens continuent d'utiliser cette technique, à défaut d'être fort en calcul mental ou d'être trop habitué à l'utilisation de la calculatrice. D'ailleurs, j'ai remarqué que ça semble être la méthode de prédilection des analystes sportifs, où dans certains cas, il y a surutilisation abusive du petit doigt...

Voici un petit truc visuel que j'ai réalisé récemment qui permet aux enfants d'apprendre à compter les multiples de 9 sur leurs doigts.

  • Placer les mains devant soi, paumes vers le sol
  • Pour n'importe quelle multiplication par 9, il faut abaisser le doigt correspondant au nombre qui est multiplié, en commençant à compter par le premier doigt vers la gauche
  • Par exemple, 3 x 9, il faut plier vers l'intérieur le troisième doigt de la main gauche
  • L'espace créé par le doigt caché permettra de séparer les dizaines des unités
  • Il restera donc 2 doigts à la gauche de l'espace et 7 à droite, donc le résultat est 27
  • Un autre exemple avec 7 x 9 : le 7ème doigt est l'index de la main droite. Pliez-le et remarquez le résultat : 63
Une fois adulte, vous n'avez qu'à vous représenter une image mentale de vos mains pour effectuer le calcul, sans avoir peur d'être gêné en public.


Tags: Curiosités, Mathématique

vendredi 24 avril 2009

Disque dur externe

Publié par Infinite Loop, à 20 h 37 1 commentaire

Tout à l'heure, sur la route en revenant du bureau, je réfléchissais à une question d'espace de stockage quand je me suis mis à calculer la capacité totale de tous les disques durs connectés et fonctionnels que j'utilisais chez moi, soit pour des backups ou pour mes systèmes informatiques.

En somme, j'en ai six, dont trois disques externes. Parmi ceux-ci, deux sont placés dans des boîtiers Vantec (à disque dur interchangeable), un à connexion IDE et l'autre SATA, tandis que le dernier est un Western Digital Passport de 320 Go que je trimballe un peu partout vu son format de poche pratique.

Une question m'est venue à l'esprit : pourquoi mes boîtiers externes devaient, en plus de la connexion USB, être branchés au mur par un câble d'alimetation alors que le Passport n'avait besoin que du câble USB ?

En arrivant chez moi, j'ai vérifié les spécifications techniques pour me rendre compte que les disques de 3.5 pouces nécessitaient une charge de 12 volts alors que le disque de 2.5 pouces du Passport pouvait s'approvisionner à même la source USB 2.0, soit à peine 5 volts, ce qui favorise l'utilisation de ce genre de périphérique à faible consommation énergétique lorsqu'on est en déplacement, par exemple avec son ordinateur portable. Mais est-ce suffisant pour parler de matériel "écologique" ?

Ce qui m'amène à un autre problème : mon poste de travail à la maison utilise pas moins de 9 prises d'électricité (ordinateur, 2 écrans, haut-parleurs, routeur, modem, scanner, imprimante, disque dur externe), et ça, c'est loin d'être écologiquement responsable. Au moins ils ne sont jamais tous allumés en même temps. Si par hasard vous avez des solutions à proposer...


Tags: Le coin du geek

jeudi 23 avril 2009

Cocher toutes les cases d'un formulaire

Publié par Infinite Loop, à 19 h 29 0 commentaire

Dans les formulaires web, par exemple la liste des courriels de Gmail ou Hotmail, on voit généralement une série d'éléments à cocher et une option supplémentaire qui permet de cocher l'ensemble de la liste d'un seul coup pour nous faciliter la tâche.

Ce genre de comportement est très facile à reproduire en JavaScript, en utilisant une librairie comme Prototype ou jQuery et les sélecteurs CSS. Voici un exemple de code équivalent pour le formulaire suivant :

<form id="frm" method="post">
<input type="checkbox" id="all" name="all" /> Tous
<ul>
<li><input type="checkbox" id="option1" name="options" /> Option 1</li>
<li><input type="checkbox" id="option2" name="options" /> Option 2</li>
<li><input type="checkbox" id="option3" name="options" /> Option 3</li>
<li><input type="checkbox" id="option4" name="options" /> Option 4</li>
<li><input type="checkbox" id="option5" name="options" /> Option 5</li>
</ul>
</form>
Avec Prototype 1.6.0.3
function checkAll(){
$$('input[type=checkbox]').without($('all')).each(
function(e) {
$(e.id).checked = true;
// ou bien inverser la sélection
// $(e.id).checked = !$(e.id).checked;
}
)
}

function initialize() {
Event.observe('all', 'click', checkAll);
}

document.observe('dom:loaded', initialize);
Avec jQuery 1.3.2
function checkAll(){
$('input:checkbox').not( $('#all') ).each(
function() {
this.checked = true;
// ou inverser la sélection
// this.checked = !this.checked;
}
)
}

function initialize() {
$('#all').click(checkAll);
}

$(document).ready( initialize );
Remarquez les différences entre les deux sources :
  • jQuery utilise toujours $() car il accède aux éléments par des sélecteurs CSS. L'ID est spécifié grâce au symbole #
  • Quant à Prototype, il utilise $ pour accéder à un ID. Pour utiliser un sélecteur avec Prototype , il faut utiliser la notation $$()
  • l'utilisation du pseudo sélecteur dans jQuery
  • La fonction without() de Prototype est remplacé par not() pour ne pas inclure dans la liste le checkbox "all"
  • la fonction anonyme de Prototype reçoit un élément "e" en paramètre alors que jQuery y fait référence directement par le mot clé this


Tags: JavaScript

mercredi 22 avril 2009

Plugin mp3 Fluendo sur Fedora

Publié par Infinite Loop, à 18 h 36 0 commentaire

Ce matin, j'ai eu un problème sur mon portable Linux Fedora : les codecs mp3 que j'avais installé la veille (voir l'entrée d'hier pour plus de détails) n'étaient plus fonctionnels une fois que l'ordinateur eut redémarré (malchance ?). En fait, j'ai même perdu le son, comme si la carte audio ne répondait plus. Pire, VMWare ne démarrait plus et pourtant, le support mp3 était le seul ajout que j'avais fait... Une mauvaise surprise que j'aurais bien pu me passer. Était-ce dû aux packages installés à partir de sources non-officielles ? Sans doute. Donc à installer à vos risques.

Après des recherches supplémentaires dans le site de Fedora, les concepteurs recommandent d'utiliser un plugin nommé Fluendo, facile à installer et disponible pour d'autres distributions Linux aux prises avec ce même problème de licence.

  • Se rendre sur le site de Fluendo, choisir Fluendo's Mp3 Decoder
  • Ajouter au shopping cart (gratuit / licence d'un an)
  • Cliquez checkout
  • Se créer un compte et s'identifier
  • Cliquer sur la page Proceed to my products page
  • Sous My Products, cliquez sur le bouton download vis à vis Mp3 Decoder
  • Choisir la distribution Linux correspondante (dans mon cas Fedora)
  • Télécharger le RPM (se retrouvera par défaut dans /home/username/Download/
  • Double clic sur le RPM et cliquer Install
  • Un message s'affichera : Missing GPG signature. Sélectionner Force install
  • Entrez le mot de passe root
  • Le processus d'installation démarrera
Les fichiers mp3 devraient pouvoir être lus dans toutes les applications, la source étant peut être plus fiable (au moins, tout fonctionne normalement et sans dégâts depuis son installation).


Tags: Linux, Musique

mardi 21 avril 2009

Ajouter du support mp3 à Fedora 10

Publié par Infinite Loop, à 22 h 11 0 commentaire

Pendant que j'étais en train de travailler sur une distribution Linux Fedora 10, je me suis dit que ça serait agréable d'écouter un peu de musique. J'ai branché ma clé USB où se trouvait quelques fichiers mp3 et j'ai été surpris de voir que le format n'était pas pris en charge par défaut.

Selon ce que j'ai lu, il n'y a aucun problème légal à faire la lecture de fichiers mp3 sur Fedora, mais il y a une question de paiement de royautés si on veut encoder en utilisant ce format ou l'utiliser à des fins commerciales.

Cependant, même en essayant les commandes reconnues comme fonctionnelles yum install gstreamer-plugins-good, etc, les packages n'étaient plus disponibles dans le repository. Fedora semble avoir retiré tout le support mp3 des applications Amarok, Rhythmbox, xmms, etc.

Cependant, après avoir ajouté comme source d'installation RPM Fusion, on peut faire ceci :

  • Ouvrir un terminal
  • Se connecter en tant que root :

    su -

  • Exécuter les commandes suivantes pour ajouter RPM Fusion :

    rpm -ivh http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-stable.noarch.rpm

    rpm -ivh http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-stable.noarch.rpm

  • Ensuite, si j'essayais d'installer les plugins, le programme tentait d'accéder à tous les sites miroirs dont la plupart se soldaient par un échec.

    yum -y install gstreamer-plugins-bad gstreamer-plugins-ugly xine-lib-extras-nonfree

  • Heureusement, j'ai pu au moins obtenir le plugin mp3 pour xmms sans problème :

    yum install xmms xmms-mp3

  • Après quelques instants, le système a automatiquement détecté des packages supplémentaires à installer mais il n'a pu obtenir tout le nécessaire pour écouter les mp3 dans les différentes applications.
C'est aussi bête que ça. Pourtant, je n'avais jamais rencontré de problème du genre avec Kubuntu! Comme le mp3 est un format propriétaire, certaines compagnies se battent pour les brevets et ça semble causer problème pour les droits. Peut-être que Fedora n'a pu être en mesure de négocier une entente et qu'il a été contraint d'en retirer le support ? Sinon, comme tous les formats propriétaires, il y a des alternatives en format libre vers lesquelles les mélomanes peuvent se tourner, comme le format ogg par exemple. Pour moi, il faudra que je m'arme de patience lorsque je voudrai convertir l'ensemble de ma collection dans un autre format.


Tags: Linux, Musique

lundi 20 avril 2009

Injection sur PostgreSQL

Publié par Infinite Loop, à 20 h 17 0 commentaire

Prenons une page vulnérable au SQL Injection, par exemple le code PHP suivant :

$id = $_GET['id']; // mauvais!

$dbh = new PDO("pgsql:host=localhost;dbname=database", "username", "password");
$sql = 'SELECT * FROM command WHERE command_id = ' . $id;

foreach($dbh->query($sql) as $row) {
echo $row['client_name'] . '\n';
}
Comme la valeur $id est concaténée à la requête SQL sans même vérifier le type ni utiliser de placeholders, on peut exploiter cette faille pour extraire de l'information utile pour pratiquer une attaque en règle.

Pour valider que le site repose sur la base de données Postgre, il suffit d'ajouter à la suite de la query string une condition en utilisant l'opérateur :: (cast) qui convertit la valeur en nombre entier (par exemple) et qui évalue l'équivalence pour donner une condition vraie. Ici, on veut qu'elle soit positive pour que l'enregistrement soit quand même retourné et que l'affichage se fasse normalement.

page.php?id=100 AND 1::int = 1--

Si aucune erreur ne se produit, c'est que PostgreSQL est utilisé puisque cette syntaxe lui est propre. Ensuite, on peut tentez une autre requête pour connaître la version installée. L'idée est de créer un enregistrement supplémentaire qui s'affichera dans la page, suite à une boucle par exemple :

page.php?id=100 UNION ALL SELECT null, version();--

Le truc est de deviner le nombre de champs retournés par la requête, de substituer une valeur nulle pour chacun et de s'arranger pour que la valeur à extraire se retrouve au sein d'un champ varchar. Une fois le bon pattern découvert pour le recordset à retourner, on peut facilement extraire :
  • le nom de la base de données : current_database()
  • le nom d'utilisateur : utiliser la constante current_user ou la fonction getpgusername()
Ainsi, en obtenant le nom de la base de données, on aurait la voie libre pour effectuer diverses manipulations néfastes, la pire étant probablement un DROP DATABASE...

Rappelez-vous de toujours vérifier les paramètres reçus. Dans mon exemple PHP, une simple vérification de la valeur de $_GET['id'] avec is_numeric() aurait été suffisant pour contrer ce type d'injection. Une autre protection possible serait d'attribuer des droits restreints à l'utilisateur qui se connecte par le web. On peut alors limiter les dégâts...


Tags: PostgreSQL, Sécurité

dimanche 19 avril 2009

Préférer la balise button à un bouton de formulaire

Publié par Infinite Loop, à 09 h 47 1 commentaire

Est-ce que vous avez déjà tenté de styliser les boutons de formulaires (input type button) ? Si oui, vous savez certainement que ce n'est pas une tâche facile. C'est pourquoi il est préférable et recommandé d'utiliser la balise <button>.

Par exemple, les deux déclarations suivantes sont équivalentes :

<input type="button" id="btn" value="Cliquez moi" />
génère le même genre de bouton que :
<button id="btn">Cliquez moi</button>
Mais comme cette dernière a l'avantage de posséder une balise d'ouverture et de fermeture, il est possible d'y glisser du contenu HTML à l'intérieur (au lieu du value) :
<!-- portion de texte en gras -->
<button>Ceci est un bouton <strong>en gras</strong></button>

<!-- placer une icône dans le bouton, voir plus loin pour stylized -->
<button type="submit" class="stylized">
<img src="Images/icons/famfamfam/accept.png" alt=""/>
Soumettre
</button>
De plus, une balise button n'a pas besoin d'être imbriquée dans un formulaire pour fonctionner, donc on peut l'utiliser dans différents contextes. L'attribut type est optionnel si on compte utiliser celui par défaut (button), mais on peut aussi lui donner les valeurs submit ou reset, qui jouent le même rôle que dans l'input standard.

Finalement, on peut facilement combiner différents éléments pour styliser nos boutons et leurs contenus :
<button style="width:50;height:50;background-color:black; color:white;">
Test
</button>

<button style="background-color:black;">
<div style="background-color:white; float:left; width:25px;">1</div>
<div style="background-color:gray; float:left; width:25px;">2</div>
</button>
Appliquer une classe CSS :
button.stylized {
display:block;
width:auto;
font-size:12px;
font-family:Verdana,Arial,sans-serif;
font-weight:bold;
padding:5px 10px 5px 7px;
color:#686868;
cursor:pointer;
line-height:12px;
margin:0 7px 0 0;
text-decoration:none;
}

button.stylized img {
margin:0 3px -3px 0 !important;
}


Tags: CSS, Intégration

Citation no. 26 sur les logiciels gratuits

Publié par Infinite Loop, à 07 h 46 0 commentaire

Software is like sex; it's better when it's free.

- Linus Torvalds


Tags: Citations

samedi 18 avril 2009

Écrire du texte à l'envers

Publié par Infinite Loop, à 10 h 12 0 commentaire

Vous vous souvenez du poisson d'avril de YouTube, où tout le texte et le contenu étaient affichés à l'envers ? Si vous ne l'avez pas vu, il est toujours actif à ce jour, il suffit d'ajouter à la fin de l'URL "&flip=1" pour voir n'importe quelle page inversée (texte, vidéo, etc). Voyez un exemple avec RickRoll (Rick Astley).

Suivant les exemples disponibles de différentes implémentations, une table associative de conversion de caractères Unicode est utilisée pour obtenir chaque caractère correspondant ('\u0250' équivaut à la lettre "a"). La plupart du temps, un caractère semblable est utilisé en raison de la similitude de sa forme et non de sa valeur. Aussi, les nombres sont plutôt difficiles à faire correspondre à un symbole. Seul le 6 pourrait être convertit en 9 et vice versa dans cet effet miroir (0, 1 et 8 demeureraient inchangés). Dans cet exemple, on n'en tient pas compte.

Créez d'abord un formulaire avec deux champs textarea et un bouton:

<form id="myform">
<div>
<label for="txtOriginal">Texte original</label><br />
<textarea id="txtOriginal"></textarea>
</div>

<div>
<label for="txtResult">Résultat</label><br />
<textarea id="txtResult"></textarea>
</div>

<button type="button" id="flip">Inverser le texte</button>
</form>
Assurez-vous d'avoir une entête meta UTF-8 dans la balise HEAD :
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Attachez la librairie jQuery (elle est optionnelle mais je l'utilise dans le code) :
<script type="text/javascript" src="/js/jquery-1.3.2.min.js"></script>
Définissez la table des caractères en JavaScript. Pour une question de lisibilité, chaque association est représentée sur une ligne. Les symboles commençant par \u correspondent aux symboles équivalents inversés en Unicode.
var characters = {
a : '\u0250',
b : 'q',
c : '\u0254',
d : 'p',
e : '\u01DD',
f : '\u025F',
g : '\u0183',
h : '\u0265',
i : '\u0131',
j : '\u027E',
k : '\u029E',
m : '\u026F',
n : 'u',
r : '\u0279',
t : '\u0287',
v : '\u028C',
w : '\u028D',
y : '\u028E',
'.' : '\u02D9',
'[' : ']',
'(' : ')',
'{' : '}',
'?' : '\u00BF',
'!' : '\u00A1',
"\'" : ',',
'<' : '>',
'_' : '\u203E',
'\\' : '/',
';' : '\u061B',
'\u203F' : '\u2040',
'\u2045' : '\u2046',
'\u2234' : '\u2235'
}
Créez la fonction qui procédera à la conversion du texte à l'envers :
function flipString(aString) {
var last = aString.length - 1;
var result = new Array(aString.length);
var i, c, r;

for (i = last; i >= 0; --i) {
c = aString.charAt(i);
r = characters[c];
result[last - i] = r != undefined ? r : c;
}
return result.join('');
}
Et finalement, attachez la fonction à l'événement click() du bouton à l'aide de jQuery :
function initialize() {
$('#flip').click(
function() {
var text = flipString($('#txtOriginal').val().toLowerCase());
$('#txtResult').attr('value', text);
}
);
}

$(document).ready( initialize );
J'ai aussi trouvé un site qui permet d'inverser le contenu de n'importe quelle page (complète), flip.sytes.org, mais il n'a pas été en mesure de restaurer en version originale RickRoll ;-)

Source : je dois mentionner que je ne suis pas l'auteur original de la totalité du JavaScript. J'ai récupéré certaines portions à gauche et à droite dans différents exemples, corrigé une ou deux erreurs, simplifié le code et je l'ai adapté à jQuery. À vrai dire, j'aurais aimé pouvoir donner le crédit, mais l'auteur semble plutôt difficile à identifier, le même exemple se retrouvant un peu partout sur le Web.


Tags: Curiosités, JavaScript

vendredi 17 avril 2009

Amazon = rapide !

Publié par Infinite Loop, à 20 h 51 0 commentaire

Tout à l'heure, en parlant d'Amazon, j'ai presque oublié ce dont je voulais parler. La semaine dernière, j'ai passé une commande (encore une autre!) pour me procurer:

  • un livre de geek (en stock)
  • le jeu Guitar Hero Metallica pour Playstation 2
Comme le jeu était lancé officiellement le mardi 14 avril 2009 en Amérique du Nord, le colis allait m'être expédié dès sa sortie pour livrer la totalité de ma commande en un seul paquet.

J'hésitais de l'acheter en magasin en raison du prix car les Future Shop et Best Buy de ce monde l'étiquettaient à 49,99$ alors qu'Amazon le vendait à peine 35,99$ (un peu plus normal pour un jeu d'une console de plus vieille génération, non?). Pour bénéficier du meilleux prix, je devais prendre mon mal en patience et attendre la livraison par la poste. J'espérais recevoir le jeu à temps pour le weekend car je comptais recevoir la visite de mon frère, un grand fan du groupe, et lui faire la surprise.

Dans les options d'envoi, je pouvais sélectionner un des choix suivant:
  • Livraison super-économique : de 4 à 7 jours ouvrables pour la livraison, gratuit
  • Express : de 3 à 5 jours, pour des frais de 6,89$
  • Priorité : de 2 à 3 jours, 14,47$
Pour que ça vaille la peine, j'ai pris une chance et j'ai opté pour la solution économique, sachant que le colis aurait une probabilité d'arriver dans la journée du vendredi. Lorsque j'ai reçu le courriel de confirmation de la commande, il annonçait que la date planifiée de la livraison était prévue entre le 23 et le 27 avril. Ouch! De quoi faire regretter une décision...

Pour mon plus grand bonheur, j'ai eu la surprise jeudi matin de recevoir l'emballage cartonné à l'effigie d'Amazon. Wow, une livraison en seulement 2 jours, sans frais additionnels. Pourquoi payer pour le service de livraison accéléré lorsqu'on peut avoir un service encore plus rapide pour moins que rien ?


Tags: Curiosités

Amazon.ca en français

Publié par Infinite Loop, à 18 h 14 0 commentaire

Au nombre de fois que j'ai acheté sur Amazon.ca, c'est pourtant la première fois que je le remarque. Cette fois-ci, ça m'a sauté aux yeux :


Oups...


Tags: Curiosités

jeudi 16 avril 2009

Centrer un objet dans la page en CSS

Publié par Infinite Loop, à 20 h 05 1 commentaire

À la demande d'un client, j'ai eu à placer sur une page HTML une boîte de contenu centrée horizontalement et verticalement, en plein centre de la page. Pour y arriver, j'ai utilisé le code CSS ci-dessous.

À supposer que l'élément à centrer est de forme carrée et mesure 200 x 200 pixels :

#box {
width:200px;
height:200px;
background-color:black;
}
Il ne reste qu'à définir la classe centered :
.centered {
position: absolute;
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: -100px;
}
Et appliquer la classe sur le conteneur à centrer :
<div id="box" class="centered"></div>
Notez que les marges de gauche et du haut sont présentes et divisent par deux la taille réelle de l'objet. Autrement, ce sera le coin supérieur gauche qui sera centré à l'écran.

Testé initialement sur Firefox 3, IE 7 et Chrome 1.
Mise à jour 2009-04-17 : Testé fonctionnel
pour la plupart des fureteurs sur browsershots.org.


Tags: CSS, Intégration

mercredi 15 avril 2009

La fonction PHP la moins utilisée

Publié par Infinite Loop, à 18 h 19 1 commentaire

Une entrée de blogue rapide par un gars qui doit manger une soupe sur le pouce car il manque de temps pour écrire ce soir car il va à une soirée à la Société des arts technologiques...

Question quiz : Qui est-ce qui utilise la fonction PHP suivante : phpcredits()

Fort probablement personne. On ne s'éternisera pas sur la question!


Tags: PHP

mardi 14 avril 2009

PHP CLI

Publié par Infinite Loop, à 18 h 28 0 commentaire

Avez-vous déjà utilisé le PHP Command Line Interface (CLI) ?

À première vue, ça peut sembler sans intérêt puisque le but principal de PHP est de générer des pages web dynamiques. En revanche, ça peut devenir vraiment pratique dans certaines tâches de gestion, sans que ça soit directement lié au web, au même titre que ce que font les bash, batch files, Perl, VB Scripts et autres. En créeant nos routines avec PHP, on peut facilement les lier à des tâches planifiées, comme un CRON job. D'où l'avantage de se familiariser avec le CLI.

En ouvrant un invite de commande, on peut obtenir la version de PHP installée simplement en entrant ceci :

php -v

Mieux, on peut aussi analyser et vérifier la syntaxe d'un fichier PHP sans même avoir à l'exécuter :

php -l file.php

Parse error: parse error in file.php on line 4
Errors parsing file.php

phpinfo()
Si on appelle par le CLI un fichier contenant l'instruction phpinfo(), le détail s'affichera en texte plutôt qu'en HTML. Tant mieux si le fichier PHP contient l'instruction, mais la commande suivante aurait été équivalente :

php -i

Contexte d'exécution
Donc l'instruction phpinfo() est capable de détecter le contexte de son appel (command line ou serveur web) et de faire afficher l'information en HTML ou en texte brut selon le cas. Comment est-ce possible ?

Au besoin, on pourra reproduire ce comportement à l'aide du Server API (SAPI) qui retournera le contexte de l'appel. Cette valeur pourra s'obtenir de deux façons :

echo PHP_SAPI;
echo php_sapi_name();

En ligne de commande, le résultat sera cli, tandis que apache2handler devrait s'afficher si le même script est exécuté par le serveur web Apache.


Tags: PHP

lundi 13 avril 2009

Filtrer une liste de fichiers avec glob

Publié par Infinite Loop, à 19 h 45 0 commentaire

Dans PHP, la fonction glob() permet de récupérer les chemins de fichiers qui correspondent à un certain pattern défini. Bien que cette technique ressemble à opendir/readdir qui liste les fichiers d'un répertoire, celle-ci permet de faire l'équivalent, en appliquant un filtre sur les extensions des fichiers. Donc nul besoin de vérifier si l'élément en cours est '.' ou '.' comme dans un exemple que j'ai donné il y a quelques semaines : Lister les fichiers d'un répertoire en PHP.

Parmi les flags à utiliser, on en note un qui s'appelle GLOB_BRACE. Il permet de lister plusieurs patterns de recherche, par exemple une série d'extensions.

$directory = 'backup/files/';
$types = '*.{gif,jpg,jpeg,png}';

$list = glob($directory.$types, GLOB_BRACE);

foreach($list as $imagepath) {
echo basename($imagepath) . ' (' . filesize($imagepath) . ')';
}
Basename() permet d'extraire le nom du fichier et filesize() retourne sa taille en octets. Mais attention, les extensions sont sensibles à la case! Il faudra donc prévoir le coup si on veut que tous les fichiers sont pris en compte.


Tags: PHP

dimanche 12 avril 2009

Image style Polaroid avec ImageMagick

Publié par Infinite Loop, à 13 h 16 0 commentaire

Je viens de passer l'avant-midi à installer, configurer et tester ImageMagick pour m'amuser avec les fonctions graphiques qu'il propose. Et je dois dire que c'est très puissant! Ce que j'ai aimé, c'est qu'on peut non seulement l'appeler par la console, mais aussi à partir de nombreux langages de programmation, dont PHP. Il suffit de faire appel à l'API à l'aide d'une extension PHP comme IMagick (PECL) ou MagickWand. J'ai préféré IMagick à ce dernier puisqu'il possède une notation plus orientée objet.

J'ai découvert une curiosité intéressante : on peut convertir nos photos pour en créer des images du style Polaroid! Je ne sais pas exactement de quelle façon je pourrais m'en servir dans un projet d'entreprise mais bon... C'est cool les Polaroid.

Pour convertir par la console :

convert -caption "Vacances 2008" image-originale.png -gravity center -background black +polaroid image-polaroid.png

Où :

  • caption représente le texte à inscrire
  • gravity center indique qu'on positionnera le texte au centre
  • background : noir pour l'ombrage
  • +polaroid pour y ajouter l'effet graphique
Par PHP, avec IMagick :
// créer un objet IMagick à partir d'une image à transformer
$image = new IMagick('image-originale.png');

// nécessaire pour transformer l'image en Polaroid
$draw = new IMagickDraw();

// angle de rotation de 5 degrés
$image->polaroidImage($draw, 5);

// afficher immédiatement
header( "Content-Type: image/png" );
echo $image;

// ou enregistrer l'image sur le disque
$image->writeImage('image-polaroid.jpg');

// ménage de la mémoire (un peu comme avec GD)
$draw->clear();
$draw->destroy();

$image->clear();
$image->destroy();
Un petit conseil avec les librairies graphiques en général : elles sont toutes très gourmandes en mémoire alors allez-y avec modération pour ne pas surcharger le serveur. Plus tôt, j'ai fait un test de performance entre GD et ImageMagick et la mémoire d'Apache a grimpé momentanément à 1,8 Go...


Tags: PHP

Citation no. 25 sur le langage de programmation parfait

Publié par Infinite Loop, à 09 h 49 0 commentaire

Anybody who comes to you and says he has a perfect language is either naive or a salesman.

- Bjarne Stroustrup, créateur du langage C++


Tags: Citations

samedi 11 avril 2009

Plugin jQuery pour upload de fichiers multiples

Publié par Infinite Loop, à 11 h 49 1 commentaire

Plus tôt cette semaine, j'ai fait quelques tests avec PHP et l'extension APC dans le but de créer un contrôle d'upload de fichiers, avec une barre de progression en temps réel. Suite à mes expériences, je n'ai trouvé aucun moyen simple de pouvoir sélectionner plusieurs fichiers en même temps pour les envoyer au serveur. La conclusion de mes lectures a été que je devais développer un contrôle en Java, ou encore controurner le problème en utilisant une boîte de dialogue lancée par Flash.

Mon idée était de développer un petit composant capable de sélectionner plusieurs fichiers, de les envoyer au serveur et de montrer la progression de l'envoi à l'utilisateur pour qu'il patiente pendant le transfert. J'avais l'intention d'utiliser PHP, APC, jQuery et un objet jQuery UI progress bar mais avant de me lancer dans un développement qui aurait pu prendre plusieurs heures, je me suis dit qu'il vaudrait mieux jeter un oeil aux solutions déjà existantes. Et je crois avoir bien fait car je suis tombé par hasard sur une solution qui réglait la plupart de mes problèmes d'un seul coup, configurable et que je pouvais utiliser comme base pour mon développement.

Il s'agit d'un plugin jQuery nommé Uploadify, développé par Ronnie Garcia, qui peut être utilisé sous licence GPL et MIT. Comme le sélectionneur de fichiers est lancé par Flash, on peut en choisir plusieurs en même temps. De plus, la source fla et le swf sont inclus dans le zip de téléchargement. On peut aussi restreindre le type (par les extensions) et la taille des fichiers, choisir le répertoire de dépôt, contrôler les comportements à chacune des étapes et modifier l'apparence (CSS, textes et traductions, etc). Mieux encore, aucun besoin de patenter le tout avec APC pour obtenir la barre de progression!

Vous verrez, c'est étonnant à quel point c'est facile à installer. Décompressez l'archive dans votre projet (par exemple le répertoire "uploadify"). Dans votre page, faites référence aux scripts de base :

<script type="text/javascript" src="uploadify/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="uploadify/jquery.uploadify.js"></script>
Dans le code HTML, créez un DIV pour contenir le contrôle et assignez lui un ID unique. Ça peut être tout simplement quelque chose comme ceci :
<div id="fileUpload"></div>
Créez ensuite un bouton qui servira à lancer le transfert :
<input type="button" id="buttonSubmit" value="Envoyer" />
Pour terminer, initialisez le contrôle et attachez l'événement au bouton. Dans fileUpload, chaque propriété permet de personnaliser le comportement du contrôle :
  • uploader : le contrôle Flash qui permet la sélection des fichiers
  • script : le script qui prend en charge l'enregistrement des fichiers. Il y en a un de base fournit par défaut qu'on peut utiliser et modifier au besoin.
  • folder : le répertoire où déposer les fichiers
  • cancelImg : l'icône du X pour annuler la sélection d'un fichier
  • buttonText : le texte sur le bouton. J'en profite pour le mettre en français
  • multi : excellent, je peux choisir plusieurs fichiers à la fois!
Lorsqu'on cliquera sur le bouton buttonSubmit, on lancera la fonction fileUploadStart() qui débutera le transfert.
<script type="text/javascript">
function initialize() {
$('#"fileUpload"').fileUpload({
'uploader': 'uploadify/uploader.swf',
'script': 'uploadify/upload.php',
'folder': 'uploaded_files',
'cancelImg': 'uploadify/cancel.png',
'buttonText': 'Choisir un fichier',
'multi': true
});

// attacher l'événement au bouton
$('#buttonSubmit').click(
function() {
$("#filepicker").fileUploadStart();
}
);
}

$(document).ready( initialize );
</script>
Facile, n'est-ce pas ? Enfin, presque. J'ai quand même quelques conseils à vous donner pour finaliser l'installation :
  • Si vous voulez voir tout l'interface dans une langue autre que l'anglais, il faudra apporter au moins une modification au fichier source jquery.uploadify.js. Dans la version non compressée, il faudra changer certaines traductions car elles ne sont pas encore personnalisables. Je viens justement d'écrire à l'auteur pour lui faire la proposition.

    - Ligne 175, la phrase "Do you want to replace the file"
    - Ligne 216, le terme "Completed"
    - Les unités de mesures KB et MB, entre les lignes 130 et 140

  • Comme mon serveur est configuré avec des répertoires virtuels, j'ai dû changer l'utilisation des fichiers proposés upload.php et check.php pour ne pas tenir compte de la variable $_SERVER['DOCUMENT_ROOT']
  • Ne pas oublier de placer le CSS, sinon vous ne verrez pas apparaître la barre de progression :-)

    <link type="text/css" href="uploadify/uploadify.css" rel="Stylesheet" />


Tags: JavaScript

vendredi 10 avril 2009

Comparer des fichiers avec Dreamweaver

Publié par Infinite Loop, à 13 h 31 0 commentaire

Si vous développez vos projets web avec Dreamweaver, vous utilisez probablement le client FTP intégrer pour synchroniser vos fichiers en ligne. Avant de procéder, on a parfois besoin de comparer le contenu des fichiers pour avoir un aperçu des différences. La bonne vieille méthode de télécharger le fichier distant et de le renommer sous un autre nom pour ensuite le comparer manuellement n'est pas très efficace. Mieux vaut utiliser un outil de comparaison qui fait automatiquement le travail pour nous.

Dreamweaver n'a pas cette capacité de pouvoir comparer un fichier local avec un fichier distant. Par contre, c'est possible de le configurer pour utiliser une application externe. Heureusement, plusieurs sont disponibles, comme l'utilitaire open source WinMerge.

Dans l'explorateur de fichiers de DW, si on clique sur le document à comparer avec le bouton droit de la souris, on voit l'option "Compare with Remote". Pour que cette option soit activée, le FTP doit être configuré dans le Remote Info du projet. Une fois cette étape complétée, l'option ne sera plus grisée et on pourra la choisir. Le message suivant devrait apparaître :

You have not specified an application for file comparison.
Would you like to choose an application to compare these files?

Cliquez OK et entrez le chemin complet de l'exécutable qui effectuera la comparaison, dans mon cas : C:\Program Files\WinMerge\WinMergeU.exe. Lorsqu'un fichier local sera sélectionné pour être comparé, une copie du fichier distant sera téléchargé et enregistré sous un nom temporaire pour pouvoir procéder à la comparaison.

Si en cours de route vous souhaitez changer d'application, rendez-vous dans le menu Edit / Preferences / File Compare pour accéder à l'écran qui définit le chemin du exe.

D'autres alternatives à considérer

  • Le client FTP TotalCommander (shareware) peut aussi comparer des fichiers locaux et distants
  • TortoiseSVN possède un outil pour comparer des fichiers locaux nommé TortoiseIDiff.exe


Tags: Coffre à outils

jeudi 9 avril 2009

Compatibilité Internet Explorer 8

Publié par Infinite Loop, à 19 h 17 0 commentaire

Depuis la sortie d'IE 8, l'équipe de Microsoft a fait en sorte que son fureteur devienne plus strict dans l'interprétation du code en suivant un peu plus les recommandations du W3C, notamment en ce qui a trait à la conformité au CSS 2.1.

Dans certains cas, les pages qui étaient rendues correctement sous IE7 deviennent altérées sous la nouvelle version. On peut corriger ce comportement de deux façons.

Option 1
Dans la balise HEAD, ajouter le code HTML suivant pour forcer IE à émuler l'interprétation du code comme le faisait IE 7 :

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7";
Pour qu'il prenne effet, il est préférable de le placer à l'intérieur de HEAD, immédiatement après la balise TITLE mais avant les autres META.
<head>
<title>Code 18</title>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7";
...
Option 2
Ajouter un header HTTP au niveau serveur.

Dans le fichier Web.config de IIS :

<add name="X-UA-Compatible" value="IE=EmulateIE7" />

Pour Apache, assurez-vous que mod_headers.so soit chargé dans httpd.conf (en retirant le # devant la ligne), ensuite, faites le suivre la déclaration du header. Toutes les pages du serveur incluront cette entête.

LoadModule headers_module modules/mod_headers.so
Header set X-UA-Compatible "IE=EmulateIE7"

Pour restreindre son application un répertoire du site, insérez-le à l'intérieur de <location></location>.

4 modes possibles
  • IE=5 : mode "Quirks", compatibilité Internet Explorer 5
  • IE=7 pour suivre les standards de la version 7
  • IE=8 tient compte des dernières fonctionnalités ajoutées, y compris la mise en page conforme au CSS 2.1
  • IE=edge choisit le meilleur mode possible selon la version la plus récente disponible. Idéal pour fins de tests mais non conseillé en production.
Plusieurs modes de compatibilité peuvent être déclarés en les séparant par un point-virgule : content="IE=7; IE=8". De plus, les alternatives "IE=EmulateIE7" et "IE=EmulateIE8" peuvent être utilisées pour ignorer la déclaration du DOCTYPE, même si celle-ci est présente.


Tags: HTML, Intégration

mercredi 8 avril 2009

Temps de chargement d'une page web

Publié par Infinite Loop, à 19 h 05 0 commentaire

Il existe un moyen très simple d'évaluer le temps de chargement d'une page web. Dans Firefox, si on ouvre l'extension Firebug (Tools / Firebug / Open Firebug, ou encore en cliquant sur l'insecte dans le coin inférieur droit), on notera dans le haut du panneau plusieurs onglets : Console, HTML, CSS, Script, DOM et Net.

Pour le calculer, cliquez sur l'onglet Net (Network Monitoring). Dans certains cas, vous devrez activer la fonction si vous voyez apparaître le message suivant : Net panel is disabled. Cochez la case "Net" et cliquez sur le bouton "Apply settings for [nom du site web]". Cette manipulation sera nécessaire pour chaque site web à monitorer.

Assurez-vous que le filtre "All" (à côté de Inspect et Clear) soit sélectionné pour pouvoir suivre tous les types de ressources et accédez au site en question. Vous verrez défiler chaque requête HTTP GET et POST nécessaire au chargement des différentes ressources incluses dans la page. Pour chacune d'elles, le code HTTP associé sera affiché, par exemple :

  • 200 OK - chargé avec succès
  • 304 Not Modified - récupéré de la cache
  • 404 Not Found - la source est introuvable
La colonne suivante indiquera l'origine (sur Amazon ou Yahoo!, on voit que les images proviennent de différents serveurs ou sous-domaines), ensuite le poids du fichier et le temps de chargement. Au bas de chaque colonne, on peut voir le cumulatif pour l'ensemble de la page.

Ainsi, je peux savoir qu'en visitant la page d'accueil de Facebook :
  • 249 Kb de matériel sont téléchargés
  • Pour un temps de chargement total de 1,45 secondes
  • 27 requêtes HTTP ont été nécessaires
Au rafraîchissement de la page, je peux voir que le processus récupérera 67 Kb à partir de la cache du fureteur et que le chargement se fera plutôt en 1,2 secondes. Basculez entre les options All, HTML, CSS, JS, XHR, Images et Flash pour filtrer selon le type de ressource.


Tags: Extensions Firefox

mardi 7 avril 2009

Installer APC sur Windows

Publié par Infinite Loop, à 19 h 09 4 commentaires

Sur mon environnement Windows XP, j'utilise souvent EasyPHP pour faire des tests rapides et rouler des snippets de code. Cette fois-ci, j'avais besoin d'utiliser APC.

Alternative PHP Cache (APC) est une extension PECL très pratique qui permet de stocker dans la mémoire vive des pages PHP, ce qui permet de réduire l'accès au disque et d'améliorer la performance. Comme il ne vient pas par défaut dans l'installation de PHP ou de EasyPHP, il faut l'installer manuellement. Sur Windows, on peut normalement l'obtenir à partir de Pecl4Win mais comme il est présentement en maintenance en raison du développement d'un nouveau système, il me fallait trouver le DLL ailleurs. Je pensais le dénicher dans le musée PHP, là où on peut trouver toutes les versions de PECL et PHP, mais ce n'était pas le cas. J'ai finalement réussi à trouver une archive téléchargeable qui contenait plusieurs versions d'APC pour Windows, sur le site de Justin Silverton.

Fait à noter, il est important de prendre le DLL qui correspond à la version de PHP installée pour que ça fonctionne. J'ai utilisé celle qui se trouvait dans le répertoire 5.2.

Ensuite, déposez le DLL dans le répertoire des extensions PHP de EasyPHP (par défaut C:\Program Files\EasyPHP 3.0\php\ext).

Dans le fichier php.ini, ajoutez la ligne suivante dans la section des extensions ou, si elle est déjà présente, retirez le ";" placé devant la ligne.

extension=php_apc.dll

Ensuite, appelez phpinfo() dans une page PHP pour vous assurer que l'extension est chargée. Vous devriez voir une section nommée "apc" et une valeur indiquant APC Support = enabled. Je n'ai pas eu à redémarrer Apache pour qu'il en tienne compte.

Pour modifier la configuration de base, il suffira d'ajouter une section [APC] dans php.ini et d'y associer les nouvelles valeurs. Référez-vous à la documentation du APC runtime configuration pour le détail de chaque valeur possible.

Par exemple :

[APC]
apc.enabled = 1
apc.shm_segments = 1
apc.shm_size = 30
apc.ttl = 0
apc.user_ttl = 0
apc.num_files_hint = 1000
apc.enable_cli = 0
apc.max_file_size = 1M
apc.stat = 1
apc.rfc1867 = 0

Sur Windows, APC utilisera le répertoire temporaire TMP ou TEMP et doit posséder les droits en écriture. Avec Apache, ça devrait fonctionner sans qu'il n'y ait aucune manipulation à faire.

Finalement, un outil de monitoring est inclus avec le package d'APC. Il s'agit d'une page nommée apc.php à déposer sur le serveur et à accéder normalement avec un fureteur. Vous pourrez l'obtenir par l'archive APC que j'ai mentionné plus haut, sinon dans la dernière version stable du package tgz qui se trouve sur PECL.


Tags: Apache, PHP

lundi 6 avril 2009

Encore une fois Vidéotron...

Publié par Infinite Loop, à 21 h 02 0 commentaire

Il y a 2 ans, j'étais très heureux de quitter Bell pour faire le saut chez Vidéotron. Avec les années, je m'étais tanné des mauvaises surprises liées à la facturation et du service à la clientèle exécrable et j'étais fin prêt à donner la chance à un compétiteur.

Vidéotron offrait des prix avantageux et il attirait sans cesse de nouveaux clients grâce à ses forfaits trio et quattro. Dans mon cas, j'avais opté pour les services Téléphone - Câble - Internet et je pouvais obtenir un rabais si je prennais un contrat qui me liait à eux pendant une période de 12 mois.

Avec du recul, je crois que les clients en avaient juste assez du monopole de Bell et qu'ils seraient allés vers n'importe quel autre fournisseur. Parce que franchement, je dois dire que le service est du pareil au même, même qu'il n'est pas vraiment meilleur.

Par exemple, il m'arrive régulièrement qu'un de mes services soit coupé momentanément. Hier matin, j'ai carrément perdu l'accès à mes trois services pendant un peu plus de deux heures. La fois précédente, il y a peut-être 2 ou 3 mois, c'était toute une soirée. Le comble a été l'été dernier, où la coupure a duré toute la journée. Par la suite, lorsque j'ai téléphoné au soutien technique pour essayer de comprendre ce qui s'était passé, on m'a répondu qu'il aurait fallu que j'appelle durant l'interruption de service pour pouvoir trouver le diagnostic. J'aurais bien voulu... mais je n'avais pas accès à la ligne de téléphone! "Oh monsieur, dans ce cas, vous devriez adhérer au service quattro, le téléphone cellulaire aurait été fonctionnel pour nous rejoindre..." Ben kin, veux-tu rire de moi ?

Chez Vidéotron, comme la connexion passe par un modem unique, s'il y a un moindre problème, ça risque d'affecter la totalité des services. Même lorsqu'il y a une panne d'électricité, je suis incapable de téléphoner. Au moins chez Bell, le téléphone n'est pas directement relié au courant. Pour être franc, malgré tout ce qu'on a pu dire de Bell, en 10 ans avec eux, je peux compter sur une main le nombre de problèmes réels que j'ai eu (le dernier étant lorsque j'ai voulu quitter, on m'a retenu 45 minutes au téléphone parce que la commis ne voulait pas annuler mon service). Avec Vidéotron, non seulement le service à la clientèle est aussi mauvais, mais si je me mets à faire le décompte, je peux facilement m'attendre à un problème à chaque deux mois, surtout au niveau technique. Ce n'est pas tout à fait au point leur affaire...

Récemment, Bell m'a téléphoné pour me faire une offre incroyable afin de me récupérer comme client. L'offre était alléchante et agressive (un cadeau surprise m'attendait...), mais comme je suis pris avec un contrat de service qui me lie à Vidéotron pendant encore plusieurs mois, j'ai pris le temps de les écouter et je leur ai suggéré de me rappeler en temps et lieu.

Lorsque viendra le temps de renouveller, je me suis promis de magasiner auprès de compagnies indépendantes pour voir s'ils peuvent m'offrir quelque chose d'équivalent, même si le prix est un peu plus cher. Au moins avec les petites entreprises, ils ne prennent pas leur clientèle pour acquis.


Tags: Le coin du geek

dimanche 5 avril 2009

Shazam et SnapTell

Publié par Infinite Loop, à 16 h 47 0 commentaire

Hier soir, pendant que j'écoutais une partie de hockey en compagnie d'un ami, celui-ci m'a fait la démontration de deux applications gratuites pour le iPhone : Shazam et SnapTell (aussi disponibles sur Android).

SnapTell
À l'aide de la caméra numérique du iPhone, il suffit de prendre une photo de la couverture d'un livre, d'un CD, DVD ou encore d'un jeu vidéo et SnapTell utilise une technologie avancée de reconnaissance de l'image pour retrouver l'information reliée au produit (achat en ligne, eBay, IMDB, Wikipedia, comparaison des prix, etc.).

Je savais que des applications comme SnapTell existaient en Asie pour les téléphones mobiles puisque les utilisateurs pouvaient prendre en photo le code QR (code barre 2D) imprimé sur l'affiche promotionnelle d'un film et télécharger automatiquement la bande-annonce pour la visionner. La technologie a fait son chemin et ses résultats sont encore plus impressionnants.

Shazam
De son côté, Shazam fait sensiblement la même chose mais pour la musique. Si vous êtes dans un café ou dans votre voiture et que vous entendez un morceau que vous appréciez, le microphone du iPhone captera les fréquences pendant quelques secondes et elle sera en mesure de les interpréter pour vous annoncer ce qui est en train de jouer. L'application pourra aussi donner accès aux paroles, à la biographie de l'artiste, proposer d'acheter la pièce, lire les critiques, etc. Selon nos tests, il n'a pas reconnu l'artiste indépendant Greg Pattillo ni le très excentrique John Zorn mais il a identifié sans problème le groupe pop No Doubt. Les résultats seront plus intéressants au fur et à mesure qu'ils compléteront leur catalogue.

Le iPod touch de deuxième génération est aussi compatible avec Shazam, à condition de brancher un microphone externe (Apple Stereo Headset). Comme mon iPod est de la 1ère génération, je devrai m'en passer...


Tags: iPod, Musique

Citation no. 24 sur le parachutisme

Publié par Infinite Loop, à 07 h 24 0 commentaire

If at first you don't succeed, skydiving is not for you.

- Général Arthur McAuliff


Tags: Citations

samedi 4 avril 2009

Définir une séquence PostgreSQL

Publié par Infinite Loop, à 08 h 46 1 commentaire

Une demande qui arrive souvent de la part des clients qui ont un système de commandes, de soumissions ou de facturation est de pouvoir débuter la numérotation des demandes à un nombre précis. Habituellement, c'est soit que le commerçant avait un ancien système et qu'il souhaite poursuivre la numérotation au numéro actuel pour conserver la continuité séquentielle, sinon c'est qu'il veut faire croire que son système est plus populaire qu'il l'est en réalité. Son premier client pourra alors avoir l'entrée 1000 plutôt que la numéro 1.

Ça peut se faire facilement si on prend pour acquis que le numéro correspondra à la séquence de la clé primaire de la table.

Création d'une table de commandes (simplifiée)

CREATE TABLE command
(
command_id integer NOT NULL,
client_name varchar(100) NOT NULL,
...
);
Définition de la clé primaire

Si vous avez défini command_id en utilisant le type integer plutôt que serial, vous devrez définir la clé primaire explicitement en ajoutant une contrainte (plus de détails sur le type SERIAL de PostgreSQL).
ALTER TABLE command
ADD CONSTRAINT pk_command_id
PRIMARY KEY (command_id);
À ce stade-ci, si vous essayez de faire une insertion dans la table, la base de données lancera un message d'erreur car aucune séquence n'a été définie.
INSERT INTO command (client_name)
VALUES ('Sun Microsystems');
ERROR: null value in column "command_id" violates not-null constraint

Création de la séquence

Comme on veut obtenir un numéro incrémentiel pour chaque commande ajoutée, on doit définir un objet qu'on appelle une séquence. Celle-ci se caractérise par un nom (command_id_seq) auquel on pourra référer plus tard dans notre programmation. De plus, on indique la valeur la plus basse que peut porter un numéro de commande (souvent 1, mais dans notre cas 1000), la valeur de départ pour la numérotation et l'incrémentation (chaque insertion créera un numéro unique avec un saut de 1 par rapport à la valeur précédente). Normalement, la valeur maximale sera définie automatiquement à 9223372036854775807.
CREATE SEQUENCE command_id_seq
MINVALUE 1000
START 1000
INCREMENT 1;
Si la séquence existe déjà et qu'on veut seulement changer la valeur de départ :
ALTER SEQUENCE command_id_seq
MINVALUE 1000
START 1000
INCREMENT 1;
Ce n'est pas tout, il faut aussi attacher cette séquence à la clé primaire si on veut que la valeur de command_id soit attribuée automatiquement :
ALTER TABLE command
ALTER COLUMN command_id
SET DEFAULT nextval('command_id_seq');
C'est fait. Les prochaines insertions utiliseront les valeurs 1000 et suivantes.


Tags: PostgreSQL

vendredi 3 avril 2009

Pro PHP

Publié par Infinite Loop, à 19 h 29 0 commentaire

Plus tôt cette semaine, j'ai reçu par la poste un livre de programmation intitulé Pro PHP, Patterns, Frameworks, Testing and More, publié il y a moins d'un an par APress. En achetant ce livre, je cherchais à obtenir de l'information avancée sur la mécanique interne de PHP pour pouvoir apprendre à tirer profit au maximum du langage.

Souvent quand je consulte un ouvrage, j'ai tendance à sauter par dessus certains chapitres car je connaîs le sujet ou qu'ils ne correspondent pas tout à fait à ce que je cherche. Ici, au contraire, je suis devant un monde de nouveautés : PHP 6, SPL, MVC, Zend Framework... J'ai eu la chance de toucher un peu à tout ça mais sans jamais les approfondir pour les maîtriser à fond. Ceci constituera une bonne introduction à plusieurs sujets, même si je suis familier avec plusieurs dont Ajax, Json, Subversion, et pratiquement tout ce qui se trouve dans la première partie. Au total, il n'y aura qu'une trentaine de pages qui me serviront de révision. Et qui sait, j'y dénicherai certainement un truc ou deux.

D'autres livres m'intéressaient mais j'ai opté pour celui-ci car l'auteur, Kevin McArthur (un canadien), a contribué au développement du Zend Framework. Pour moi, j'ai toujours trouvé enrichissant de pouvoir apprendre d'un programmeur directement impliqué dans une technologie. Son survol du ZF risque d'être fort intéressant. Il a aussi écrit quelques articles sur PHPRiot.com qui datent un peu mais qui méritent quand même d'y jeter un oeil.

Ce bouquin d'environ 325 pages porte bien son nom car il aborde des sujets avancés de PHP. En voici un résumé :

  • Partie 1 : Classes abstraites, Variables et méthodes statiques, Patterns singleton et factory, les exceptions, nouveautés PHP 6
  • Partie 2 : documentation phpDoc et DocBook, l'API Reflection, les tests et le déploiement avec Subversion, PHPUnit, Phing, Xinc et Xdebug
  • La partie 3 est composée de 5 chapitres couvrant la SPL (The Standard PHP Library)
  • La partie 4 comprend 4 chapitres sur le MVC (Model - View - Controller) selon l'approche du Zend Framework
  • Ajax, Json
  • 2 chapitres sur les services web (SOAP, WSDL)
Finalement, j'ai pu trouver un bienfait à la crise économique qu'on vit actuellement. J'ai obtenu ce livre sur Amazon Marketplace, dans un état neuf, au prix ridicule de 12$ (au lieu de 40$)...


Tags: Livres, PHP

jeudi 2 avril 2009

J is null dans Prototype

Publié par Infinite Loop, à 18 h 53 1 commentaire

Ces temps-ci, j'ai mis le nez dans plusieurs formulaires dynamiques où j'ai eu à convertir le vieux code JavaScript vers Prototype (technologie imposée, pour être franc, j'y aurais préféré jQuery). À force de faire du travail répétitif, lorsqu'est venu le temps de tester, je me suis rendu compte que j'avais involontairement fait une erreur d'inattention, la même, qui se retrouvait sur chacun des formulaires. À chaque fois, Firebug annonçait :

J is null
var Prototype={Version:"1.6.0.3",Browser...totype,Enumerable);Element.addMethods();

Comme j'utilisais une version compressée de la librairie (YUI Compressor), l'erreur était détectée à la ligne 1. Je n'ai pas vraiment eu le choix d'exécuter mon code à nouveau en utilisant la version décompressée, ce qui me permit d'obtenir la ligne exacte où ça plantait.

Ligne 3936
element is null
if (element._prototypeEventID) return element._prototypeEventID[0];


Quel indice est-ce que ça me donnait ? Firebug me disait que ça plantait dans la librairie Prototype, et pourtant pas dans mes appels. Après avoir essayé de figurer où pouvait être le bogue, j'en ai déduit que ça se produisait alors que j'essayais d'attacher un événement à un élément inexistant. En effet, j'avais fait la gaffe de placer un attribut name plutôt que id, alors Prototype a réagit comme si le champ n'existait pas, d'où le message element is null.

Si vous rencontrez cette erreur, assurez-vous que vous avez bien identifié tous vos éléments avec un ID. Et si vous développez un formulaire qui sera traité par un langage dynamique (comme PHP), n'oubliez pas que chaque champ doit aussi porter un nom (name) car celui-ci sera utilisé du côté serveur pour récupérer les données une fois le formulaire soumis.

En passant, après plus d'un an d'attente, une lueur d'espoir... La prochaine version de Prototype 1.6.1 est présentement en Release Candidate (RC2). Les ajouts serviront à fixer quelques bogues, entre autre ceux liés à IE 8, et de bonifier avec quelques fonctionnalités mineures supplémentaires. Au moins, le projet est encore en vie.


Tags: JavaScript

mercredi 1 avril 2009

Poisson d'avril 2009

Publié par Infinite Loop, à 08 h 44 0 commentaire

Mon premier réflexe ce matin a été de fouiller pour trouver quelques poissons d'avril originaux sur le web :

  • Gmail Autopilot by CADIE
  • Curiosités ThinkGeek (après le 1er avril, la page sera archivée ici)
  • Naviguez sur le web avec Google Chrome 3D!
  • YouTube a inversé le contenu de toutes ses pages de vidéos à l'envers (vidéo et même le texte)
Mise à jour - 13h25
  • Reddit a clôné l'apparence de Digg
  • Expedia offre un voyage vers Mars pour 99$
  • Sur la page d'accueil de RDS, dans la colonne de gauche (Résultats), on apprend que ce soir, les Nordiques vont jouer contre les Jets... et plusieurs noms d'équipes sont traduits : Insulaires, Capitales, Moqueurs roux, Volants, Feuilles d'érable, Diablotins, Manchots, Bleus, Éperviers...
Mise à jour - 18:20
  • Le journal The New Zealand Herald annonce que Microsoft achète Apple
  • Selon Smashing Magazine, Internet Explorer 8.1 supportera les extensions Firefox, et sera doté d'un "Server-side code decompiler". Haha!
  • Warner Bros. se porte acquéreur de The Pirate Bay!
  • SitePoint : Internet rebootera aujourd'hui à 11h59


Tags: Curiosités

Messages plus récents Messages plus anciens Accueil
S'abonner à : Messages (Atom)
    Suivre @code18 sur Twitter

    Catégories

    • Apache (21)
    • Citations (167)
    • Club Vidéo (24)
    • Coffre à outils (56)
    • CSS (8)
    • Curiosités (117)
    • Design Pattern (2)
    • Drupal (8)
    • Easter Eggs (22)
    • Extensions Firefox (20)
    • GIMP (7)
    • Histoire (21)
    • HTML (32)
    • Humour (57)
    • Intégration (34)
    • iPod (12)
    • JavaScript (110)
    • Jeu de combat (6)
    • Le coin du geek (128)
    • Liens (12)
    • Linux (56)
    • Livres (78)
    • Lois et principes (46)
    • Marché des saveurs (26)
    • Mathématique (18)
    • Mobile (5)
    • Montréal (32)
    • Musique (112)
    • Pancartes et écriteaux (16)
    • Perl (8)
    • Pérou (1)
    • PHP (130)
    • PostgreSQL (44)
    • Programmation (105)
    • Saviez-vous que (55)
    • Sécurité (22)
    • SEO (5)
    • SQL Server (22)
    • Vieilles publicités (6)
    • Virtualisation (8)
    • Voyages (1)
    • Zend Framework (26)

    Divers

    Archives

    • ►  2015 (6)
      • ►  août 2015 (1)
      • ►  juillet 2015 (1)
      • ►  février 2015 (3)
      • ►  janvier 2015 (1)
    • ►  2014 (8)
      • ►  décembre 2014 (1)
      • ►  novembre 2014 (1)
      • ►  octobre 2014 (1)
      • ►  août 2014 (2)
      • ►  juillet 2014 (2)
      • ►  janvier 2014 (1)
    • ►  2013 (53)
      • ►  décembre 2013 (2)
      • ►  novembre 2013 (1)
      • ►  octobre 2013 (3)
      • ►  septembre 2013 (2)
      • ►  août 2013 (5)
      • ►  juillet 2013 (3)
      • ►  juin 2013 (5)
      • ►  mai 2013 (3)
      • ►  avril 2013 (7)
      • ►  mars 2013 (7)
      • ►  février 2013 (11)
      • ►  janvier 2013 (4)
    • ►  2012 (105)
      • ►  décembre 2012 (8)
      • ►  novembre 2012 (5)
      • ►  octobre 2012 (4)
      • ►  septembre 2012 (1)
      • ►  août 2012 (8)
      • ►  juillet 2012 (7)
      • ►  juin 2012 (7)
      • ►  mai 2012 (10)
      • ►  avril 2012 (13)
      • ►  mars 2012 (15)
      • ►  février 2012 (15)
      • ►  janvier 2012 (12)
    • ►  2011 (146)
      • ►  décembre 2011 (14)
      • ►  novembre 2011 (11)
      • ►  octobre 2011 (12)
      • ►  septembre 2011 (13)
      • ►  août 2011 (15)
      • ►  juillet 2011 (17)
      • ►  juin 2011 (18)
      • ►  mai 2011 (15)
      • ►  avril 2011 (9)
      • ►  mars 2011 (7)
      • ►  février 2011 (3)
      • ►  janvier 2011 (12)
    • ►  2010 (398)
      • ►  décembre 2010 (29)
      • ►  novembre 2010 (28)
      • ►  octobre 2010 (32)
      • ►  septembre 2010 (34)
      • ►  août 2010 (22)
      • ►  juillet 2010 (35)
      • ►  juin 2010 (42)
      • ►  mai 2010 (36)
      • ►  avril 2010 (37)
      • ►  mars 2010 (34)
      • ►  février 2010 (32)
      • ►  janvier 2010 (37)
    • ▼  2009 (430)
      • ►  décembre 2009 (32)
      • ►  novembre 2009 (34)
      • ►  octobre 2009 (33)
      • ►  septembre 2009 (37)
      • ►  août 2009 (37)
      • ►  juillet 2009 (39)
      • ►  juin 2009 (38)
      • ►  mai 2009 (37)
      • ▼  avril 2009 (35)
        • Coeur de pirate
        • Photos des développeurs d'OpenOffice
        • Audiotool, studio musical en Flash
        • Compresser TinyMCE
        • Compression HTTP avec GZip
        • Citation no. 27 sur les geeks
        • Compter sur ses doigts
        • Disque dur externe
        • Cocher toutes les cases d'un formulaire
        • Plugin mp3 Fluendo sur Fedora
        • Ajouter du support mp3 à Fedora 10
        • Injection sur PostgreSQL
        • Préférer la balise button à un bouton de formulaire
        • Citation no. 26 sur les logiciels gratuits
        • Écrire du texte à l'envers
        • Amazon = rapide !
        • Amazon.ca en français
        • Centrer un objet dans la page en CSS
        • La fonction PHP la moins utilisée
        • PHP CLI
        • Filtrer une liste de fichiers avec glob
        • Image style Polaroid avec ImageMagick
        • Citation no. 25 sur le langage de programmation pa...
        • Plugin jQuery pour upload de fichiers multiples
        • Comparer des fichiers avec Dreamweaver
        • Compatibilité Internet Explorer 8
        • Temps de chargement d'une page web
        • Installer APC sur Windows
        • Encore une fois Vidéotron...
        • Shazam et SnapTell
        • Citation no. 24 sur le parachutisme
        • Définir une séquence PostgreSQL
        • Pro PHP
        • J is null dans Prototype
        • Poisson d'avril 2009
      • ►  mars 2009 (37)
      • ►  février 2009 (32)
      • ►  janvier 2009 (39)
    • ►  2008 (84)
      • ►  décembre 2008 (34)
      • ►  novembre 2008 (39)
      • ►  octobre 2008 (11)

    Abonnés

Copyright © All Rights Reserved. Code 18 | Converted into Blogger Templates by Theme Craft