Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Matthieu Napoli

blog technique

Le Responsive Web Design - Exemple

English version of that article: The responsive web design by example.

L'expression Responsive Web Design veut dire en gros : le design de ce site web s'adapte à la taille de l'écran. Et par "s'adapte", il ne s'agit pas seulement d'utiliser des tailles horizontales en % de la taille de l'écran. Il s'agit parfois de changer complètement l'organisation visuelle de la page affichée.

Techniquement, c'est rendu possible par les media queries introduites dans CSS 3. Elles permettent de restreindre l'exécution de règles CSS en fonction de conditions, notamment la taille de l'écran.

Pour aborder concrètement le sujet par un exemple, je vais présenter la refonte graphique que j'ai opéré sur un projet en cours : Awespot. Instant publicité : il s'agit d'un site de partage et découverte de lieux à visiter dans le monde (utile pour préparer votre prochain voyage).

Cette refonte graphique s'est appuyée sur la mise en place du framework HTML/CSS/Javascript Bootstrap (créé par Twitter), qui propose justement des classes CSS de base pour mettre en place du Responsive Web Design rapidement. Au passage, j'en profite pour recommander (très) chaudement ce framework: mise en place très rapide (même sur de l'existant), gains immédiats et maintenabilité accrue du code HTML/CSS. Et surtout, c'est un plaisir de développer avec.

Exemple

Ci-dessous l'exemple en question. Il s'agit d'une comparaison de 2 versions d'Awespot : une avec et une sans le Responsive Web Design.

Excusez les différences annexes : la refonte graphique du site est passée par là entre les 2 versions.

Page d'accueil

La page d'accueil est une carte présentant les lieux à visiter dans le monde. Elle est très simple et épurée pour intriguer et favoriser le "play" plutôt que l'effort de compréhension. Son affichage est crucial pour ne pas perdre un nouveau visiteur : si elle ne s'affiche pas correctement sur les tablettes ou téléphones, c'est autant de visiteurs perdus.

Écran large

Sans Responsive Web Design :


Avec Responsive Web Design :

On remarque que la version initiale était prévue en largeur fixe plus petite : c'était un compromis pour être visible sur les écrans plus petits. Avec le Responsive Web Design on peut s'autoriser une largeur maximum plus grande (même si pour l'instant elle ne s'étant pas beaucoup plus).

Tablette (ou petit écran)

Sans Responsive Web Design :

Le cadre rouge délimite la partie visible dans le navigateur.

Avec Responsive Web Design :


Là la barre de menu de gauche (ajout d'un spot) a disparu, laissant entièrement la place à la carte (celle-ci n'est pas centrée car je l'ai décalée involontairement).

Pour le menu principal (en haut de page), il est rétréci. Le logo l'est également, pour laisser de la place au menu.

Téléphone / Smartphone

Sans Responsive Web Design :


Encore une fois le cadre rouge délimite la partie visible dans le navigateur.

Avec Responsive Web Design :


La différence est sans appel. Alors que le site est complètement inutilisable dans le premier cas, il s'épure et prend presque la forme d'une application mobile dans le deuxième.

Notez également le menu du haut qui a encore changé : le logo a énormément rétréci pour laisser la place au menu, plus petit et décalé à gauche. Quelques marges ont également rétrécies.

Page de spot

La page d'un spot présente un lieu à visiter. Elle regroupe beaucoup d'informations, donc sa réorganisation n'est pas simple en soit.

Écran large

Sans Responsive Web Design :


Avec Responsive Web Design :


(ignorons les données du jeu de test)

Ce format représente l'affichage prévu de la page. Les différences entre les 2 versions sont dues à la refonte graphique (rendue possible si facilement grâce à Bootstrap, je me répète).

Tablette (ou petit écran)

Sans Responsive Web Design :


Avec Responsive Web Design :


Très grosse différence pour ce cas : les différents blocs (note et boutons, texte, carte, photos...) se sont complètement réorganisés automatiquement. Ils sont maintenant empilés pour rendre la lecture très facile et agréable.

Notez que le site n'est pas défiguré pour autant, c'est très appréciable. J'ai du procéder à quelques ajustements spécifiques, notamment l'affichage en ligne des boutons bleus. De plus, tous les cadres d'images vides (cadres gris) ont disparus (ils participent au joli rendu sur la page entière, mais à ce format ils prennent de la place pour rien).

Téléphone / Smartphone

Sans Responsive Web Design :


On imagine que le site est quasiment inutilisable sur téléphone dans cet état.

Avec Responsive Web Design :


Le site est là encore plus vertical, mais toujours pas défiguré. Le menu principal s'est encore plus compacté (comme vu plus haut).

Démo

Pour voir la démo de tout ça en temps réel, allez directement sur awespot.org et redimensionnez l'écran.

Conclusion

La mise en place de Responsive Web Design est une bonne alternative à un site web dédié aux mobiles. C'est surtout plus rapide, plus facile, moins cher et beaucoup plus maintenable. Mais le principal avantage réside dans la souplesse du résultat : aujourd'hui, la frontière entre PC et mobile n'existe plus. Les smartphones sont de plus en plus grand, certains avec des écrans à haute résolution, sans compter les tablettes.

Un peu de technique

Tout a été réalisé entièrement en CSS (pas de Javascript). Bootstrap a été très utile dans le processus de mise en place, je vous invite à lire plus d'informations ici : Boostrap Responsive Design.

Voici un exemple de media-query en CSS :

@media (max-width: 480px) {
   #logoSmall img {
     max-height: 70px;
   }
}

Quel langage de templating utiliser avec PHP ?

C'est une question qui revient souvent, et auquel le nombre non négligeable de moteurs de templates pour PHP complique la réponse.

De mon point de vue, pas besoin de chercher trop loin : PHP est parfait pour ça.

PHP est à la base un langage de templating. Il fournit la majorité des outils nécessaires, pour ceux qui manquent, il n'y a qu'à les développer (principe de View Helper par exemple).

La liste des avantages de PHP vis-à-vis des autres langages est sans appel :

  • Pas d'apprentissage d'un nouveau langage
  • Auto-complétion et validation dans l'IDE (bien que pour des langages basés sur XML il est possible d'avoir ces fonctionnalités aussi)
  • Possibilités d'extensions/fonctionnalités "illimitées"
  • Rapide (un langage interprété par du code PHP est forcément plus lent que d'utiliser directement du PHP)
  • Pas de limitations lors de l'utilisation d'objets (peu de langages de templating permettent d'utiliser les attributs ou getters d'un objet)

Des réponses aux critiques souvent faites à PHP pour du templating :

  • « PHP est verbeux »

Je prends l'exemple de Twig qui propose, à la place de ce code PHP : <?php echo $var ?>, la notation suivante : {{ var }} car beaucoup plus courte.

Ce qu'ils ont passé sous silence dans leur exemple, c'est le short tag pour echo... Autant avant PHP 5.4, son utilisation était un vrai choix à faire, car cela nécessitait d'activer tous les shorts tags dans la configuration PHP (désactivés par défaut). Mais depuis cette nouvelle version, c'est reconnu comme faisant 100% partie du langage car le short tag suivant est toujours actif, peut importe que les "autres" short tags soient activés :

<?=$var?>

Ça n'est plus très verbeux. Dans le même genre <?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8') ?> peut être très facilement (et je le recommande) remplacé par un helper : <?=$this->escape($var)?>.

Au contraire, je trouve que PHP rend le code moins verbeux. Par exemple (Fluid de FLOW3) :

<f:if condition="{post} == {currentBlogPosting}">
    <f:then>
        <div class="post selected"></div>
    </f:then>
    <f:else>
        <div class="post"></div>
    </f:else>
</f:if>

Alors qu'il suffit de :

<?php
$selected = "";
if ($post == $currentBlogPosting) {
    $selected = " selected";
}
?>
<div class="post<?=$selected?>"></div>

(sans compter la duplication de code évitée)

  • « Un langage de templating permet à des non-développeurs de lire/travailler sur le template »

Mais dans ces non-développeurs, qui n'a pas un minimum de rudiments de PHP ? Si un webdesigner doit intégrer un design dans un template, on s’attend généralement à ce qu'il ait la base en PHP (j'entends si c'est à lui d'intégrer le design dans le template).

Et si ça n'est pas le cas, pour afficher une variable, écrire un if ou un for, le langage de PHP n'est pas plus compliqué qu'un autre langage de templating. Et ça lui sera beaucoup plus utile d'apprendre PHP que d'apprendre un nouveau langage de template pour chaque projet/client.

Et avec la syntaxe alternative pour les blocs, c'est très lisible et la signification coule de source, même pour un novice :

<?php if ($a == 5) : ?>
     <strong>A égal 5</strong>
<?php endif; ?>
  • « Un langage de templating empêche d'avoir de la logique métier dans le template »

Ce point là est un peu plus intéressant, car il est vrai, on peut se retrouver avec du code métier dans le template. En interdisant PHP, on est sur de ne conserver que l'élémentaire (boucles, branchements conditionnels, formatage...).

Mais ce point seul ne justifie pas l'effort à mes yeux. Sinon, en suivant cette logique, autant avoir un langage séparé pour chaque couche de l'application pour garantir encore plus leur séparation.

Cette solution traite les conséquences du problème et non pas la source : bien coder (tout simplement :p). En réalité, ce problème n'existe pas avec des développeurs qui comprennent le principe de séparation des couches. Leur enlever cette réflexion (ce code appartient-il bien à cette couche) avec des verrous, c'est leur demander de moins y réfléchir, de moins se soucier de ça, et au final alimenter le problème (mélange des couches ailleurs, nécessité de mise en place d'autres verrous, etc...).


Pour conclure ce billet, je ne peux que souligner les regrets de voir FLOW3 utiliser un langage de templating. C'est bien un des rares défaut que je vois à ce framework tellement prometteur (ce mot est-il encore valide maintenant qu'une version stable est sortie ?).

D'autre part, je suis conscient qu'il manque quelques petits plus à PHP pour augmenter son usabilité, mais cela reste la meilleure solution à mes yeux.

Google Maps - InfoWindow.getPosition() ne marche pas, contournement

Comment récupérer la position (LatLng) d'une InfoWindow Google Maps ?

En utilisant InfoWindow.getPosition() me direz-vous. Mais cette méthode, pourtant bien présente dans la documentation officielle, ne marche pas (en tout cas chez moi, et visiblement pas que) : elle renvoie toujours null.

Ce problème a été remonté depuis Février 2011 à Google via un rapport de bug, mais en attendant qu'elle soit fixée, voici ma solution de contournement.

  • Lier l'InfoWindow à un marker (même si l'InfoWindow apparait au-dessus d'un marker, elle n'y est pas lié par défaut)
  • Récupérer la position du marker lié à l'InfoWindow grâce à Marker.getPosition()

Pour la première étape, on va ajouter ce lien au prototype InfoWindow :

(function () {
    google.maps.InfoWindow.prototype.marker = null;
    google.maps.InfoWindow.prototype.setMarker = function(marker) {
        this.marker = marker;
    };
    google.maps.InfoWindow.prototype.getMarker = function() {
        return this.marker;
    };
})();

Pour ceux qui ne comprennent pas, on ajoute des méthodes à la classe InfoWindow de Google Maps. Il suffit de placer ce code une fois dans la page pour que cela fonctionne.

Donc, lors de la création (ou l'affichage) de notre InfoWindow, on va la lier au marker correspondant :

var infowindow = new google.maps.InfoWindow();
infowindow.setMarker(marker);

Ensuite, pour la dernière étape, on récupère la position du marker :

var position = infoWindow.getMarker().getPosition();

Supprimer un élément d'un tableau Javascript

Cette question est mainte fois abordée sur le web, mais on trouve rarement une version satisfaisante. Alors pour les impatients qui sont tombés sur cette page après une recherche Google, voici directement le code :

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
    var rest = this.slice((to || from) + 1 || this.length);
    this.length = from < 0 ? this.length + from : from;
    return this.push.apply(this, rest);
}
;

Pour l'utiliser :

// Supprimer le premier élément du tableau
array.remove(0);

// Supprimer l'avant dernier élément du tableau
array.remove(-1);

Comme vous le remarquerez, c'est pompé directement d'ici. C'est à ce jour la meilleur version d'un code de suppression d'un tableau que j'ai pu trouver.

Le principe : on modifie le type Array (son prototype) afin de lui rajouter une méthode remove. Ensuite on peut appeler cette méthode sur n'importe quel tableau Javascript.

Les avantages (pas rapport aux autres méthodes) : c'est clean (méthode ajoutée au type Array), complet (la suppression de l'élément du tableau va bien décaler tous les éléments suivants) et puissant (possibilité de supprimer l'avant dernier élément par exemple).

N'hésitez pas à aller consulter l'article original pour comprendre le détail du code de la méthode remove.

Outil de bilan carbone de voyage

Bonjour à tous,

Je vous présente aujourd'hui un outil que j'ai développé en HTML5 + Javascript, utilisant Google Maps, et open source : la calculette carbone voyage.

L'objectif est de vulgariser au maximum la notion d'empreinte carbone pour les transports. La transparence sur les calculs est complète, les sources sont disponibles sur github, et les détails des calculs sont publics.

C'est un peu dommage que le site devienne disponible à la fin des vacances, mais les projets perso prennent toujours un peu de temps :).

N'hésitez pas à faire des retours dans les commentaires.


Attaque de Celeonet

Je viens de recevoir un mail de mon hébergeur, Celeonet, m'informant d'une attaque sur leurs infrastructure.

C'est sur qu'à lire leur blog, ça parait pas grand chose :

Consécutivement à ce piratage, une liste d’identifiants utilisateurs Celeonet a été dévoilée et a pu être librement consultée.
Le fait qu’une liste d’identifiants utilisateurs ait été dévoilée ne met en aucun cas en danger les comptes et les sites rattachés.

Super, mais alors pourquoi dans la foulée je reçois un mail qui m'explique que tous mes mots de passe ont été changés immédiatement ?

Je pense que ça aurait été utile de le préciser sur leur blog, au lieu de jouer à "tout va bien", et "c'est la faute du webmaster, pas de l'hébergeur".

Surtout qu'on a pas le droit à un mot d'excuse, pas plus d'explications, est-ce qu'il est nécessaire de modifier nos mots de passe mail par exemple, y'a t'il des conséquences sur nos sites à nous... Si il a été nécessaire de modifier tous les mots de passe de tous les clients, on peut s'attendre à une meilleure communication que ça !

Le plus drôle dans cette histoire, c'était que tout ça était conséquence d'une attaque sur le site de Marine Le Pen.

Merci Celeonet.

Google Maps v3 : l'évènement "bounds_changed" se déclenche en continu!

Bonjour à tous,

Si vous aussi vous travaillez avec Google Maps v3, et que vous avez besoin par exemple de rafraichir les marqueurs qui se trouvent sur la carte lorsqu'on zoom ou on se déplace, vous avez peut être remarqué que la doc conseille d'utiliser l'évènement "bounds_changed".

Seulement cet évènement est déclenché en continu quand on déplace la carte. On se retrouve donc avec une quantité énorme de requêtes sur le serveur (et rafraichir les marqueurs de la carte plusieurs fois par seconde, ça n'a pas trop d'intérêt).

Bref, après que plusieurs personnes aient râlé, Google a proposé un bugfix sous la forme de l'évènement "idle". Cet évènement est déclenché après un certain temps sans action de l'utilisateur. Pratique, mais j'aurais quand même préféré un simple évènement qui se déclenche quand l'utilisateur fini de déplacer la carte (un mouseUp).

Voici tout simplement comment l'utiliser :

google.maps.event.addListener(map, 'idle', function() {
});

Ils devraient quand même le préciser dans la doc, car la solution se trouve dans le rapport de bug de google :

http://code.google.com/p/gmaps-api-issues/issues/detail?id=1371

Plugin pour PowerPoint 2010 : AutoSummary

Petite présentation d'un plugin développé par un ami, pour la dernière version de PowerPoint (du pack Office 2010), il s'agit d'AutoSummary.

Il permet d'insérer automatiquement :

  • des diapositives de sommaire
  • des diapositives de progression
  • des rappels de progression dans toutes les diapos (titre du chapitre en cours)
  • numérotation des diapositives

Rien de bien compliqué en plus :


Bref, assez pratique pour gagner du temps sur ces taches répétitives.

Si ça vous intéresse, voici le site web : AutoSummary pour PowerPoint 2010.

Alerte Growl de risque d'aurores boréales

Si vous aussi vous habitez en Islande (ou Canada, Suède, Norvège, USA...), vous n'avez pas envie de passer à côté d'une aurore boréale parce que vous êtes en train de faire le geek.

Les prévisions par heure peuvent être trouvées ici. C'est présenté sous forme de graphique, mais il est possible de récupérer directement les données sous format texte.

Voici un script shell tout simple, qui va vérifier les dernières prévisions pour la prochaine heure. En cas d'alerte supérieure à 3 (configurable, selon votre latitude), vous obtiendrez une alerte Growl. Ce script est donc spécialisé pour Mac, mais vous pourrez facilement remplacer l'appel à growlnotify par un envoi de mail par exemple.

    #!/bin/sh
### Northern Lights alert ###

# The script will stop if a variable was not initialized
set -u

# Treeshold level
ALERT_LEVEL=3.0

# Get data
wget -q -O aurores.txt http://www.swpc.noaa.gov/wingkp/wingkp_list.txt || exit 1;

# Get next hour prevision
nextLevel=`tail -n1 aurores.txt | awk '{print $10}'`

# Comparison
if [ $(bc <<< "$ALERT_LEVEL <= $nextLevel") -eq 1 ]
then
growlnotify -p 1 -m "Northern lights alert ! Next Kp level is : $nextLevel, in 1 hour." -a iPhoto
fi

Il n'y a plus qu'à ajouter l'exécution de ce script à votre Crontab toutes les demi-heures.

Grâce à ce script, j'ai déjà réussi à en capturer quelques unes :

Aurore boréale Aurore boréale

SVN : Forcer un message de commit

Quand on travaille en équipe avec SVN (Subversion), il est recommandé de préciser les modifications faites à chaque commit. Vous pouvez aussi prendre les devants, et interdire un commit sans message.

Pour cela, il faut utiliser un hook pre-commit. Utilisez donc le contenu suivant pour le fichier repositories/XXX/hooks/pre-commit :

# Message de commit obligatoire
SVNLOOK=/usr/bin/svnlook
$SVNLOOK log -t "$TXN" "$REPOS" | grep "[a-zA-Z0-9]" > /dev/null
RESULT=$?
if [ $RESULT -eq "0" ];
then
# c'est bon, on peut sortir au statut 0
exit 0
else
# c'est pas bon, on lance un message et on sort au statut 1
# ce qui va bloquer la propagation du commit
MESSAGE="Message de log obligatoire !"
echo $MESSAGE 1>&2
exit 1
fi
Microsoft Word 2008 [Mac] Erreur à l'insertion d'image

Vous voulez insérer une image dans Word 2008 (version pour Mac Os X), mais vous obtenez systématiquement le message :
The application cannot open this file. This file is an unsupported graphic format or may be damaged. Try opening the graphic in another application.

Je me suis moi aussi tiré les cheveux la dessus, en croyant à un histoire d'encodage, png/gif/jpeg, mais que nenni, il suffit de fermer la boite "Styles" de la fenêtre de formatage. Et hop, ça marche, allez comprendre.

Ce billet (en plus de permettre à d'autres personnes de perdre moins de temps quand ça leur arrivera), est surtout un coup de gueule contre Microsoft et son Word pour Mac bien plus que décevant. Et malheureusement, ce n'est pas Open Office qui va dire le contraire, quand on est dans une boite qui utilise le docx (ben oui, sous Windows ça marche très bien, et c'est très joli...). Parce que Open Office et le docx, c'est pas encore ça.

En plus, moi qui ai un clavier reconfiguré pour Dvorak, je n'ai pas de touche "z", ni en minuscule, ni en majuscule. C'est drôle non Angry ? Obligé de faire des copier/coller...

Ôôô monde cruel !

MySql, les index UNIQUE et les valeurs NULL

Bonjour à tous,

Voici un comportement peu intuitif que je viens de remarquer dans MySql.

Un exemple est bien plus explicite qu'un long texte, alors voici :

create table tb (
  a int,
  b int,
  c int,
  unique index (a,b,c)
);

insert into tb(a,b,c) values (null,null,null); -- ok
insert into tb(a,b,c) values (null,null,null); -- ok
insert into tb(a,b,c) values (null,null,null); -- ok
insert into tb(a,b,c) values (1,null,null); -- ok
insert into tb(a,b,c) values (1,2,null); -- ok
insert into tb(a,b,c) values (1,2,3); -- ok
insert into tb(a,b,c) values (1,null,null); -- ok (contrairement à ce qu'on pourrait penser)
insert into tb(a,b,c) values (1,2,null); -- ok (contrairement à ce qu'on pourrait penser)
insert into tb(a,b,c) values (1,2,3); -- echec

Ceci me trouble un peu, au final une valeur NULL se comporte comme un "joker", qui annule la contrainte d'unicité.

En tout cas, c'est à savoir !

Simuler des classes Friend en PHP

PHP, malgré la sortie de sa version 5.3 cet été, a toujours quelques lacunes. Parmi l'une d'elle, les classes Friend seraient bien utile.

Visiblement, ce n'est pas au programme, car comme il est élégamment expliqué ici, "long ago, we decided against 'friend' declarations".

Le principe de classes Friend consiste à permettre à une classe d'accéder aux attributs et fonctions privées d'une autre. Ceci est par exemple utile quand on veut implémenter une classe Data Mapper qui fait le lien entre une classe du modèle métier et le modèle de données. Pour sauvegarder un objet en base de données (ou le charger), elle a besoin de définir ses attributs, même ceux privés.

Une manière de contourner cette limitation est peu élégante mais relativement efficace : utiliser les méthodes magiques __get, __set et __call de PHP.

Ainsi, si B essaye d'accéder à un attribut privé de A, la méthode A::__get() sera appelée avec comme paramètre le nom de l'attribut demandé. Il est alors nécessaire de savoir qui demande l'accès à cet attribut : est-ce une classe "Friend" ou pas.

Pour cela, on utilise debug_backtrace() (beurk, oui), qui nous permet de connaitre la classe à l'origine de l'appel. Une petite vérification sur le nom de la classe et on renvoie la valeur de l'attribut demandé.

Voici un exemple sur __get et __set :

public function __get($attribut)
{
    // Accès aux variables privées autorisé pour les Data Mapper
    // Note : une variable privée commence par '_'

    if ($attribut[0] == '_') {
        $trace = debug_backtrace();
        if (isset($trace[1]['class'])) {
            $classname = $trace[1]['class'];
            if (strpos($classname, 'Mapper_')) {
                return $this->$attribut;
            }
        }
    }
    // Accès à un attribut non autorisé
    throw new Exception('Accès à un attribut non autorisé ou inexistant : '
                        . get_class() . '::' . $attribut);
}

public function __set($attribut, $valeur)
{
    // Accès aux variables privées autorisé pour les Data Mapper
    // Note : une variable privée commence par '_'

    if ($attribut[0] == '_') {
        $trace = debug_backtrace();
        if (isset($trace[1]['class'])) {
            $classname = $trace[1]['class'];
            if (strpos($classname, 'Mapper_')) {
                return $this->$attribut = $valeur;
            }
        }
    }
    // Accès à un attribut non autorisé
    throw new Exception('Modification d\un attribut non autorisé ou inexistant : '
                        . get_class() . '::' . $attribut);
}

Cet exemple autorise l'accès aux variables privées et protégées de cette classe à n'importe quelle classe contenant "Mapper_" dans son nom (i.e. Data Mapper). Par contre ce code suppose que vous avez nommé vos variables privées en respectant les conventions de nommage PEAR.

Alors, à quand les classes Friend dans PHP ? Le polymorphisme ? Et l'héritage multiple :-) ?

Générer un graphique de l'évolution des révisions d'un SVN

Rien de bien constructif dans cet article, c'est sur. Mais c'est toujours marrant de faire des statistiques sur un peu tout et n'importe quoi.

Alors comment construire un graphique qui va nous montrer l'évolution du nombre de révision d'un repository svn en fonction du temps ?

Graphique SVN

Déjà, la source des données :

svn log /home/matthieu/myrepository | grep -E "^r[0-9]+" | awk '{ print $5, $1 }' | sed 's/r//' > log.txt

Ceci va générer un fichier texte contenant les dates des différentes revisions.

Ensuite, à partir de cette source de données, pour générer un graphique, nous allons utiliser gnuplot. Sous Linux, ou Mac Os X (avec Fink installé), vous pouvez le récupérer directement :

apt-get install gnuplot

Créez le fichier qui sera utilisé pour génerer le graphique avec ce contenu (script dont je ne suis pas l'auteur bien entendu) :

set xdata time
# The format of time found in the file
set timefmt "%Y-%m-%d"

set term png
set output "log.png"

set xlabel "Date"
set ylabel "Revision"
set title "Revisions du svn"

# The time format actually shown on the x-axis
set format x "%Y"

# It's tricky to get the tics right in time mode, as you have to specify increments in seconds.
# I find it easier to just specify what dates I want shown
set xtics ("2009-1-1", "2010-1-1")
set nokey

plot "log.txt" using 1:2 with lines

Ensuite il ne reste plus qu'à lancer la génération du graphique :

gnuplot graphesvn.gnuplot

(avec "graphesvn.gnuplot" le nom du fichier qu'on a créé)

Le résultat est simple, mais suffisant non ?

(source en anglais)

Classe Singleton en PHP 5.3

PHP 5.3 est enfin sorti en version finale ! Et c'est pas trop tôt.

Le principal défaut qu'il vient corriger, à mes yeux, est le problème du Late Static Binding. Maintenant, il est possible de créer des vraies classes Singleton et d'utiliser ce design pattern grâce à l'héritage.

Voici un exemple de classe Singleton, qui ne fonctionne bien entendu que sous PHP 5.3.

<?php

/**
 * Classe Singleton abstraite
 */

abstract class Singleton
{

    /**
     * Renvoie l'instance Singleton de la classe
     */

    public static function getInstance()
    {
        // Un tableau statique contenant les instances de
        // toutes les classes filles

        static $_instances = array();
        // Récupère le nom de la classe appelée (PHP 5.3, Late Static Binding)
        $classname = get_called_class();
        // Vérifie si l'instance a déjà été chargée
        if (! isset($_instances[$classname])) {
            // Si l'instance n'existe pas on la charge
            $_instances[$classname] = new $classname();
        }
        return $_instances[$classname];
    }

    /**
     * Le constructeur peut être redéclaré dans les classes filles
     * mais sera en protected pour éviter qu'il soit possible de faire
     * $o = new ClasseFille()   (on sera obligé d'utiliser getInstance())
     */

    protected function __construct() {}

    /**
     * On déclare cette méchode en final private pour interdire son
     * utilisation par des classes filles
     */

    final private function __clone() {}

}

Maintenant il est possible de créer des classes qui héritent de cette classe Singleton, et aucune méthode (ni attribut) n'est à redéclarer. Il sera possible d'accéder à l'instance d'une classe fille uniquement par la méthode :

$instance_fille = ClasseFille::getInstance();

Écrire un script Shell propre

Les scripts Shell pour Unix, quelle galère. C'est très puissant, mais il faut parfois s'accrocher pour trouver quelques fonctionnalités classiques.


Par exemple, un script bien construit voudrait qu'en cas d'échec d'une commande, l'exécution du script s'arrête, plutôt que de continuer avec des erreurs. Pour parvenir à cela, il suffit d'activer une option qui ordonne à bash de stopper l'exécution du script si une commande retourne une erreur.

Pour l'activer : set -e

Pour la désactiver : set +e

Il est donc recommandé d'inclure cette ligne au début du script.

Une autre façon de procéder est d'utiliser le schéma suivant :

command || { echo "Erreur"; exit 1; }


Une autre source d'erreur concerne les variables. Comment vérifier simplement si une variable utilisée n'est pas définie ou initialisée, par exemple dans le cas où vous utiliser un paramètre du script qui n'a pas été renseigné.

Cela est possible grâce à la commande : set -u

Cette commande arrêtera l'exécution du script si une variable est utilisée sans avoir été initialisée.


Ce sont des petites astuces, mais qui permettent d'obtenir des scripts bien plus fiables.

Transformer une adresse en coordonnées géographiques avec le service Geocoding de Google Maps

Google Maps propose une API Javascript largement utilisée aujourd'hui par de nombreux sites. Cet API permet d'intégrer des cartes facilement.

Google Maps propose également un autre service de géocoding, cette fois ci non pas en Javascript mais sous la forme d'un service web REST. Son utilisation est simplissime, et d'une puissance incroyable. Un exemple tout simple consiste à récupérer les coordonnées géographiques (latitude, longitude) correspondant à une adresse.

Voici un exemple en PHP. Il requiert une clé Google Maps pour fonctionner.

/**
* Recherche les coordonnées d'une adresse avec le service Google Maps
* @param adresse string
* @return array(id_result => array(adresse_complete, latitude, longitude))
*/

function get_coordonees_from_adresse($adresse)
{
  $adresse = urlencode($adresse);
  $url = 'http://maps.google.com/maps/geo?q=' . $adresse . '&output=xml&oe=utf8&gl=fr&sensor=false&key=' . GMAP_KEY;
  $page = file_get_contents($url);
  // Parse le résultat XML
  $xml_result = new SimpleXMLElement($page);
  // Vérifie que la requête a réussi
  if ($xml_result->Response->Status->code != 200) return array();
  // Charge les adresses
  $adresses = array();
  foreach ($xml_result->Response->Placemark as $place) {
    list($longitude, $latitude, $altitude) = explode(',', $place->Point->coordinates);
    // Ajoute au tableau
    $adresses[] = array('adresse_complete' => utf8_decode($place->address),
             'latitude' => $latitude,
             'longitude' => $longitude);
  }
  return $adresses;
}

Cette fonction renvoie un tableau contenant, pour chaque résultat (il peut y'en avoir plusieurs en cas d'ambiguité) l'adresse trouvée (pratique pour différencier plusieurs résultats) et la latitude/longitude.

Plugin Firefox pour modifier l'apparence de n'importe quel site web

Je dois dire qu'il est souvent très lourd, quand on visite un site quotidiennement, de retrouver dans ses pages web des services que l'on sait que l'on utilisera pas. Pour cacher les parties d'un site qui ne me servent pas, j'utilise un plugin firefox.

Voici un exemple avec la page d'accueil de vbfrance :

[Aperçu non disponible]

(cliquez pour agrandir)

Épurée non ? Big Smile Pour comparaison, l'originale.

Ces modifications sont possibles grâce à l'extension Platypus qui vous permettra de redesigner tous les sites web. Avec une interface relativement intuitive, vous pourrez supprimer toutes les parties d'un site qui ne vous intéresse pas.

Une fois la boucherie finie, vous enregistrez le travail sous forme d'un script GreaseMonkey (également très bonne extension, à installer pour que ça fonctionne). Tout est intégré automatiquement, rien de compliqué, et quand on rafraichit la page, les modifications restent. Magnifique !

Ceci dit, j'espère que les développeurs ne m'en voudront pas de tronquer tant leur site web. Grâce à cette extension, on sent encore plus le web sous sa forme "service", et cet aspect me séduit beaucoup.

Car, par exemple les rubriques Livre, Emploi, Comparateur de prix, blog, sondages, sponsors, IT, formations vidéos, Appels d'offres, Logiciels, photothèque, news IT, news .Net et VIDÉOS DROLES (sérieux ?)

Y'a un moment ou l'aspect programmation se perd un peu dans ce fouillis, et le reste ne fait que surcharger ma page. C'est bien entendu un avis personnel lié à mon utilisation.

Générateur de graphiques excellent pour sites web (PHP, ASP, HTML, Javascript...)

Bonjour à tous,

Voici une petite présentation de XML/SWF Charts. C'est une API de génération de graphique pour vos sites web très simple, et TRÈS puissante. Gabe me l'a conseillé il y'a peu (j'avais l'habitude d'utiliser Artichow), et c'est sans comparaison.

Voyez dejà le rendu, pour vous faire une idée :

Aperçu non disponible

Voici ses fonctionnalités :

  • Rendu très professionnel
  • Objet Flash, indépendant des données (une fois chargé par le navigateur, il n'est plus rechargé même si les données changent)
  • Génération des graphiques à partir d'un fichier XML (très pratique ! ce fichier peu contenir du PHP pour générer les données dynamiquement)
  • Indépendant du langage du site web (PHP, ASP...) car objet Flash utilisant un peu de Javascript, et les données sont en XML
  • Possibilité que le graphe mette à jour ses données en temps réel toutes les X secondes, sans recharger la page (très très bien géré)
  • Possibilité d'interaction avec l'utilisateur sur le graphe de manière complexe (simple à définir)
  • Nombre de fonctionnalités incroyables...
  • Projet Open Source, gratuit

Bref, vous avez peut être saisi mon enthousiasme, je suis plus qu'épaté par les possibilités offertes par cet outil. Je suis passé de graphiques "images" (avec Artichow), statiques, dépendants du PHP, à XML/SWF Charts. Et même si la documentation a tendance à fournir des exemples assez poussés, on s'y fait vite.

L'installation est très simple. Téléchargez l'archive zip ou gzip sur la page "download". Extrayez la et ouvrez le dossier avec votre navigateur. L'exemple fourni (sample.php) affiche des données d'exemple car le fichier sample.xml est vide, mais à vous de le remplir et le personnaliser.

La documentation est votre amie. Bonne chance.

Géolocalisation et approximations (ou comment simplifier le calcul avec des latitudes et longitudes en PHP)

Bonjour à tous,
Aujourd'hui, un petit article sur la manipulation de coordonnées géographiques dans vos applications, notamment ici dans un site web en PHP.

Cet article fait suite au peu de documentation que j'ai constaté en recherchant le web.

1ère méthode : Calculs précis

Le "calcul précis" présenté ici est déjà une approximation, mais c'est la solution qu'on rencontre le plus souvent dans les forums :

define('RAYON_TERRE', 6378137);

/**
* Retourne la distance précise à vol d'oiseau entre 2 coordonnées
* @param double latitude du point 1
* @param double longitude du point 1
* @param double latitude du point 2
* @param double longitude du point 2
* @return double Distance entre les points 1 et 2
*/

function get_distance_precis($lat1, $long1, $lat2, $long2)
{
  $rlo1 = deg2rad($long1);
  $rla1 = deg2rad($lat1);
  $rlo2 = deg2rad($long2);
  $rla2 = deg2rad($lat2);

  $dlo = ($rlo2 - $rlo1) / 2;
  $dla = ($rla2 - $rla1) / 2;
  $a = (sin($dla) * sin($dla)) + cos($rla1) * cos($rla2) * (sin($dlo) * sin($dlo));
  $d = 2 * atan2(sqrt($a), sqrt(1 - $a));

  return (RAYON_TERRE * $d);
}

Bilan :

  • Calcul compliqué et lent
  • Propre à PHP, s'implémente difficilement dans une requête SQL (pour faire un SELECT en prenant en compte la distance par exemple)

2ème méthode : Approximation

On remarquera que le calcul suivant renvoie un résultat linéaire :

echo get_distance_precis(0.0, 0.0, 0.001, 0);
echo get_distance_precis(0.0, 0.0, 0.01, 0);
echo get_distance_precis(0.0, 0.0, 0.1, 0);
echo get_distance_precis(0.0, 0.0, 1., 0.);

111.31949079327
1113.1949079327
11131.949079327
111319.49079327

On peut donc utiliser le facteur proportionnel get_distance_precis(0.0, 0.0, 1., 0.) ce qui donne la fonction suivante :

define('CONVERSION_LATLONG_METRES', 111319.49079327);

/**
* Retourne la distance approximative à vol d'oiseau entre 2 coordonnées
* @param double latitude du point 1
* @param double longitude du point 1
* @param double latitude du point 2
* @param double longitude du point 2
* @return double Distance entre les points 1 et 2
*/

function get_distance_approx($lat1, $long1, $lat2, $long2)
{
  $delta_lat = $lat2 - $lat1;
  $delta_long = $long2 - $long1;
  $dist_lat = $delta_lat * CONVERSION_LATLONG_METRES;
  $dist_long = $delta_long * CONVERSION_LATLONG_METRES;
  $distance = sqrt($dist_lat * $dist_lat + $dist_long * $dist_long);
  return $distance;
}

Vérification des résultats :

echo get_distance_precis(0.0, 0.0, 0.001, 0);
echo get_distance_precis(0.0, 0.0, 0.01, 0);
echo get_distance_precis(0.0, 0.0, 0.1, 0);
echo get_distance_precis(0.0, 0.0, 1., 0.);
echo get_distance_approx(0.0, 0.0, 0.001, 0);
echo get_distance_approx(0.0, 0.0, 0.01, 0);
echo get_distance_approx(0.0, 0.0, 0.1, 0);
echo get_distance_approx(0.0, 0.0, 1., 0.);

111.31949079327
1113.1949079327
11131.949079327
111319.49079327

111.31949079327
1113.1949079327
11131.949079327
111319.49079327

Bilan :

  • Calcul simple et rapide
  • Peut s'implémenter facilement dans une requête SQL en utilisant le facteur de conversion.

Bonus : Faire l'inverse, décaler une latitude/longitude avec une distance

Au lieu de calculer la distance entre 2 points, on peut vouloir décaler une coordonnées (pour par exemple faire un carré autour d'un point).
Pour cela voici la fonction suivante :

define('CONVERSION_LATLONG_METRES', 111319.49079327);

/**
* Décale la latitude ou la longitude de la distance spécifiée
* @return angle de décalage
*/

function get_angle_decalage_geo($distance)
{
  return $distance / CONVERSION_LATLONG_METRES;
}

Cette fonction renvoie le décalage à appliquer à une latitude ou une longitude pour obtenir un point situé à la distance précisée.

Vérification des résultats :

echo get_angle_decalage_geo(100);
echo get_distance_approx(0.0, 0.0, get_angle_decalage_geo(100), 0.);

0.00089831528411955
100

Plus de Messages Page suivante »


Les 10 derniers blogs postés

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01