:-y

GIF, APNG, WebP versus Canvas

vendredi 25 mai 2012 - 23:58

Je vous avais préparé un petit article pour vous parler d'images animées. C'est dommage mais l'article n'était pas encore tout à fait prêt quand je me suis rendu compte que j'aurai pu le publier pour l'anniversaire du GIF animé (Korben). Je n'ai jamais utilisé de javascript dans un flux RSS, je ne sais donc pas ce que ça va donner. Si cela ne s'affiche pas correctement, n'hésitez pas à venir directement sur le site.

Si seulement je n'avais pas eu tous ces partiels et soutenances à préparer...

On m'a demandé quand est-ce que je mettrais en place sur ce site quelques images pour faire des illustrations... Je compte intégrer l'ajout d'images à kriss blog, mais en attendant, voilà en avant-première une petite alternative aux formats GIF, APNG, WebP et autres formats qui permettent/permettront d'animer une image.

Pourquoi une alternative ?

Tout d'abord, il faut savoir qu'à la base GIF est un format propriétaire qui ne satisfaisait pas vraiment tout le monde. Maintenant ce format est vieux et les brevets associés ne sont plus vraiment gênants...

D'autres solutions ont été proposées comme alternatives à ce format comme le format APNG ou le format de Google WebP qui devrait supporter les animations dans ses futures versions. Ces formats ne sont malheureusement pas supportés par tous les navigateurs, et pour une fois pas seulement Internet Explorer, mais le format APNG n'est pas non plus supporté par Chrome et Safari et le format WebP n'est supporté que par Chrome et Opera. Bravo Opera qui supporte tous ces formats nativement !

Mais alors pourquoi une alternative ?

Tout d'abord, je pense que c'est bien d'avoir le choix et je pense qu'il peut être intéressant d'utiliser la balise canvas comme une alternative qui sera compatible avec tous les navigateurs (récents) et qui devrait permettre un paramétrage plus avancé des animations.

Des solutions existent déjà :
- afficher successivement plusieurs images png à l'aide d'un script : exemple (solution qui ne m'a pas vraiment convaincue)
- utiliser des sprites et un script pour animer tout ça exemple 1, exemple 2.
J'ai trouvé ces solutions intéressantes, mais elles sont "compliquées" à mettre en place et ne permettent pas vraiment de configurer l'animation. Je me suis donc inspiré et j'ai adapté tout ça.

Une image vaut mille mots alors voici un petit exemple :

(source : Wikipedia : Balle.gif)

- on convertit le gif en png avec des sprites :
montage Balle.gif -tile 1x -geometry +0+0 balle.png (merci ImageMagick)
- on met un background transparent (pas obligatoire) :
convert balle.png -transparent white balle.png (merci ImageMagick)
- on obtient cette image :

- on met les images à animer dans un canvas, un petit script et voilà le résultat
<script src="//tontof.net/js/acanvas.js"></script>
<canvas height="50" width="50"><img src="//tontof.net/img/balle.png" width="50" height="50"></canvas>


- à partir de la même image, il est possible de modifier l'animation


Je ferai un autre article pour présenter les différentes options, car je n'ai pas encore tout développé ce que j'ai en tête, et je trouve cet article bien assez long :-)
Je vous laisse regarder le code source si vous ne voulez pas attendre le prochain article consacré à acanvas.

Avantages :
- simple d'utilisation : un simple script js à inclure, une balise canvas qui encadre l'image avec les dimensions du sprite et c'est tout
- compatible sur tous les navigateurs récents ? (juste testé sur firefox et sur le navigateur mobile android 2.3.5 pour le moment)
- hautement paramétrable grâce à javascript et permet d'animer également une image fixe sans sprite
- peut réduire la taille de l'image (2361 octets pour l'image gif, 1549 octets pour l'image png, soit 35% de réduction)... dépend bien évidemment du type d'image, l'exemple étant clairement avantageux pour png.

Inconvénients :
- nécessite javascript, sinon affiche l'image à la place toute réduite.
- ne gère pas encore les gif où seulement des parties changent un peu comme les Cinemagraph qui n'encodent que les parties qui bougent dans le gif.
- doit être plus gourmand en ressources qu'un simple fichier image, même si je pense avoir un peu limité en utilisant un setTimeout dans le code à la place de setInterval.

Linux vs python turtle !

vendredi 11 mai 2012 - 10:08

J'aime linux !

Imaginez que vous donniez des cours à la fac depuis quelques temps et que votre administrateur réseau vous demande gentiment de prévoir vos besoins en logiciels en mai/juin alors que bien souvent vous ne connaissez vos enseignements qu'en juillet, voire septembre... quand ce n'est pas janvier...

Difficile en effet. Heureusement d'une année sur l'autre vous avez plus ou moins les mêmes enseignements, et du coup, les mêmes besoins en logiciels. Ouf on est sauvé !

Cela fait 2 ans que j'enseigne python (qui est une option) à des premières années en licence. Ils n'ont bien souvent jamais programmé, certains ne sont même pas destinés à faire de l'informatique plus tard. Mon but est donc de faire un cours introductif à la programmation sans qu'il ne soit trop barbant. Difficile en seulement 20h...

Heureusement python est intuitif, simple et grâce au module turtle, il est possible de faire des choses simples et sympathiques comme quand on était en primaire et qu'on faisait de la programmation logo... comment ? on me dit que cela ne se fait plus ? dommage je pense que c'était un bon moyen de faire réfléchir les plus jeunes... Encore faut-il qu'on veuille qu'ils réfléchissent...

Passons, la personne qui avait fait du python les autres années (coucou Laurent) avait également utilisé turtle. Je n'ai donc pas eu de soucis la première année et tout marchait très bien. Cette année, je n'ai donc pas demandé à installer de logiciel particulier. Sauf que voilà au moment de faire le TP, les étudiants me disent que import turtle ne marche pas. Je me rappelle qu'il était installé sur le serveur spécifique à la filière informatique et leur dit donc de faire un ssh sur ce serveur. Sauf que non, la machine a été mise à jour et cela ne marche tout simplement plus :-(

ImportError: No module named _tkinter, please install the python-tk package

Oh mon dieu comment je vais faire ? Quand j'ai eu l'audace de demander en milieu d'année à ce que inkscape soit installé, on m'a de nouveau gentiment dit que ce n'était pas possible, les demandes d'installation de logiciels ne peuvent pas être fait quand on veut c'est trop compliqué à gérer... Sur le serveur, cela doit revenir à faire une ligne de commande c'est ça ?

Bon ben c'est parti, google install the python-tk package

Il faut donc commencer par installer tcl puis tk

On commence par tcl :
tar xvfz tcl8.5.11-src.tar.gz
cd tcl8.5.11/unix/
./configure --prefix=/home/login/L1/Python/local
make
make install

On recommence pour tk :
tar xvzf tk8.5.11-src.tar.gz
cd tk8.5.11/unix/
./configure --prefix=/home/login/L1/Python/local
make
make install

On fixe les variables d'environnement qui vont bien :
setenv C_INCLUDE_PATH /home/login/L1/Python/local/include
setenv LIBRARY_PATH /home/login/L1/Python/local/lib
setenv LD_LIBRARY_PATH /home/login/L1/Python/local/lib
ou
export C_INCLUDE_PATH=/home/login/L1/Python/local/include
export LIBRARY_PATH=/home/login/L1/Python/local/lib
export LD_LIBRARY_PATH=/home/login/L1/Python/local/lib
selon votre $SHELL

On va chercher python.
Tiens on va prendre la version 2.7.3 ou 3.2.3, ça changera des versions 2.5 et 2.6 de la fac...
(Attention à la syntaxe de programmation qui est différente entre python2 et python3 !)
tar xvfz Python-3.2.3.tgz
cd Python-3.2.3
./configure --prefix=/home/login/L1/Python/local
make
make install

Et pour finir :
cd /home/login/L1/Python/local/bin
./python3
>>> import turtle

Enjoy !

Bon écoutez moi tous, pour le tp, ne lancer pas python mais /home/login/L1/Python/local/bin/python3

Être root aurait été plus simple, mais y a pas à dire,
j'aime linux !

Kriss blog (version 3)

lundi 07 mai 2012 - 18:29

Je pense avoir terminé, dans un premier temps, ce que je voulais ajouter à picoblog.
Dans cette version 3 de kriss blog, il est maintenant possible :
- d'ajouter des articles privés (utile pour l'édition de plusieurs articles en parallèle) ;
- de publier des articles dans le futur (il suffit de fixer la date à laquelle on veut que l'article apparaisse (ne fonctionne pas encore si le cache est activé)) ;
- de modérer les commentaires avec du BBCode (si vous dîtes trop de choses méchantes, je pourrais les modérer);
- de lire les articles plus facilement sur les mobiles (HTML5 et CSS simplifié : avec un joli score à gtmetrix Page speed : 100% et Yslow : 98%) ;
- d'activer l'auto sauvegarde pendant l'édition d'article pour éviter de perdre un article en cours d'édition (je stocke dans le localStorage du navigateur).

J'avais partagé sur les liens publics du hollandais volant, un outil permettant de sauvegarder automatiquement les données de formulaire quand Timo avait parlé du problème de perte d'un article en cours d'édition. Ce lien avait été repris par Sebsauvage. C'est peut-être un détail pour vous, mais cela avait flatté mon égo et je m'étais dit à l'époque qu'il fallait vraiment que je commence à penser à faire moi aussi un blog pour que je puisse partager des choses qui je l'espère seront utiles pour vous.

Au final, je ne sais pas comment Timo a résolu son problème pour blogotext, mais pour mes besoins de sauvegarde, j'ai juste écrit 2 petites fonctions javascript légèrement inspirées de howtocreate. Un petit setInterval de 1 min et si l'utilisateur perd son article, il suffit de charger les données sauvegardées.

(pour mettre un peu de couleur, j'ai utilisé highlight_string de php, mais il s'agit effectivement de javascript... Il faudrait que j'ajoute dans les TODOs la coloration syntaxique ?) :

<?php 
var id="edit_form";
function 
saveForm(){
  var 
document.getElementById(id);
  var as;

  for (var 
i=0fO={};f.elements[i]; i++){
    var 
f.elements[i];
    if (
e.type){
      var 
e.type.toLowerCase();
      if( 
== "text"
        
|| == "textarea"
        
|| == "password"
        
|| == "datetime"
        
|| == "datetime-local"
        
|| == "date"
        
|| == "month"
        
|| == "week"
        
|| == "time"
        
|| == "number"
        
|| == "range"
        
|| == "email"
        
|| == "url" ) {
        
fO[e.name]=e.value;
      } else if(( 
== "radio" || == "checkbox" )
                   && 
e.checked) {
        
fO[e.name]=e.value;
      }
    }
  }
  
localStorage.toform=JSON.stringify(fO);
}

function 
loadForm(){
  if (
typeof(localStorage.toform) != "undefined"
      
&& localStorage.toform !== null) {
    var 
document.getElementById(id);

    for (var 
i=0fI=JSON.parse(localStorage.toform);
           
f.elements[i];
           
i++) {
      var 
f.elements[i];
      if (
e.type) {
        var 
e.type.toLowerCase();
        if( 
== "text"
          
|| == "textarea"
          
|| == "password"
          
|| == "datetime"
          
|| == "datetime-local"
          
|| == "date"
          
|| == "month"
          
|| == "week"
          
|| == "time"
          
|| == "number"
          
|| == "range"
          
|| == "email"
          
|| == "url" ) {
          
e.value fI[e.name];
        } else if(( 
== "radio"
                      
|| == "checkbox" )) {
          if (
fI.hasOwnProperty(e.name)
              && 
e.value==fI[e.name]) {
            
e.checked true;  
          }
        }
      }
    }
  }
}
 
?>


Maintenant que kriss blog est pleinement fonctionnel, je vais pouvoir commencer à vous parler un peu d'autres choses, alors à la prochaine :-)

Un blog statique

mercredi 25 avril 2012 - 17:46

Avant de me lancer dans ce blog, j'ai longtemps hésité entre les différentes méthodes et moteurs de blogs existants. Dans l'optique de l'auto-hébergement, il me fallait un outil qui soit simple et efficace. Dans mes recherches, je me suis donc intéressé aux moteurs de blogs statiques et il en existe beaucoup (32 static website generator et d'autres).

J'ai été surpris de voir qu'il en existait très peu (pas) en php (php s'utilise aussi en ligne de commande après tout). Python est sans doute le langage de programmation qui ressort le plus. Seulement voilà, je voulais aussi avoir la possibilité de générer des pages dynamiques comme pour ajouter des commentaires. Le problème, c'est que ces outils utilisent des services externes, comme disqus... Oui, mais moi je ne voulais pas.

La solution que j'ai choisie est l'utilisation d'un cache local. Le principe est tout simple. Les pages statiques sont générées en fonction des besoins. Quand un utilisateur veut accéder à une page, si la page existe dans le cache local, elle est appelée, sinon elle est générée et stockée dans le cache. C'est une solution intermédiaire entre statique et dynamique.

Bien sûr, tout n'a pas été aussi simple et je ne garantis pas qu'il n'y ait pas de bogue. Un détail qui m'avait échappé est le problème des captchas dans les commentaires. Un captcha dans un fichier du cache n'a aucun sens, aussi, j'ai dû ajouter un bouton qui au lieu d'envoyer directement le commentaire, génère un captcha avant. Une étape de plus avant de pouvoir commenter. Cette étape n'est présente que si l'option du cache est activée dans la configuration. Je ne l'ai pas encore activée, j'avais peur que cela vous empêche de commenter :-p

Dans les news en plus du cache, j'ai ajouté la possibilité d'autoriser ou pas les commentaires pour chaque article. Il est possible d'arrêter temporairement l'ajout de commentaires, sans perdre ceux qui ont déjà été postés.

Dans les prochaines améliorations de kriss blog, je vais penser à enregistrer automatiquement les articles en cours d'édition. Pourquoi ? Tout simplement, parce qu'après une heure d'inactivité, la session est perdue. Sauf que je n'y avais pas pensé, mais rédiger un article ça peut prendre facilement bien plus qu'une heure. Et oui, vous ne le saviez pas, mais il m'est déjà arrivé de perdre un article. Celui sur les captchas, j'ai dû le faire deux fois et la deuxième fois il était forcément moins bien. Ça m'apprendra, j'aurai dû faire un copier-coller avant de l'envoyer.

Kriss blog

vendredi 20 avril 2012 - 20:47

Bienvenue à toi petit Mathis !

Ce n'était pas prévu pour tout de suite, mais je suis tontof pour la deuxième fois depuis hier soir. Au moins ce ne sera pas compliqué pour retenir les anniversaires.

Chose promise chose due, voici le code de kriss blog. Le nom est sans surprise inspiré du principe de programmation KISS. J'ai hésité à changer de nom, mais même si je compte garder le principe minimaliste de picoblog proposé par BohwaZ, les fonctionnalités que j'ai et que je compte ajouter vont rendre kriss blog différent du projet original.

La TODO list est encore très sommaire, mais j'envisage de :
- convertir en html5
- sauvegarder automatiquement l'édition d'articles
- coder en utilisant la syntaxe PEAR
- pouvoir poster des messages futurs automatiquement

J'ai déjà fait quelques modifications importantes :
- mise en place d'une procédure d'installation
- ajout d'un titre par article
- ajout des commentaires
- ajout d'un captcha

Pour le captcha, une petite classe php et le tour est joué. J'ai cherché des polices ASCII pour m'en créer une petite perso :
 _   _   __  _   __  __  __     ___ ___         _ _ 
|_| |_) | | \ |__ |_ / _ |_| | | |_/ | |||
| | |_) |__ |_/ |__ | \_/ | | _|_ _| | \ |__ | |
__   _   _   _   _   _  ___                     ___ 
| | / \ |_| | | |_| (_ | | | \ / \ / \_/ |_| /
| | \_/ | |_\ | \ _) | |_| v w / \ _| /__
 _       _   _       _   _   __  _   _  
|/| /| _| _| |_| |_ |_ | |_| |_|
|_| | |_ _| | _| |_| / |_| _|


Il est possible de définir sa propre police, il suffit juste que les caractères soient de la même largeur. Par défaut, le constructeur utilise la police ci-dessus. Et pour finir, voici cette petite classe qui permet de générer une chaîne automatiquement et de l'encoder facilement pour obtenir un captcha dans une balise pre.


<?php 
/**
 * Captcha management class
 * 
 * Features:
 * - Use an ASCII font by default but can be customized
 *   (letter width should be the same)
 * - generate random strings with 5 letters
 * - convert string into captcha using the ASCII font
 */

class Captcha
{
    public 
$alphabet="";
    public 
$alphabet_font;
    public 
$col_font 0;
    public 
$row_font 0;

    public function 
__construct($alpha_font=array(
                                    
'A'=>" _ |_|| |",
                                    
'B'=>" _ |_)|_)",
                                    
'C'=>" __|  |__",
                                    
'D'=>" _ | \\|_/",
                                    
'E'=>" __|__|__",
                                    
'F'=>" __|_ |  ",
                                    
'G'=>" __/ _\\_/",
                                    
'H'=>"   |_|| |",
                                    
'I'=>"___ | _|_",
                                    
'J'=>"___ | _| ",
                                    
'K'=>"   |_/| \\",
                                    
'L'=>"   |  |__",
                                    
'M'=>"_ _|||| |",
                                    
'N'=>"__ | || |",
                                    
'O'=>" _ / \\\\_/",
                                    
'P'=>" _ |_||  ",
                                    
'Q'=>" _ | ||_\\",
                                    
'R'=>" _ |_|| \\",
                                    
'S'=>" _ (_  _)",
                                    
'T'=>"___ |  | ",
                                    
'U'=>"   | ||_|",
                                    
'V'=>"   \\ / v ",
                                    
'W'=>"   \\ / w ",
                                    
'X'=>"   \\_// \\",
                                    
'Y'=>"   |_| _|",
                                    
'Z'=>"___ / /__",
                                    
'0'=>" _ |/||_|",
                                    
'1'=>"    /|  |",
                                    
'2'=>" _  _||_ ",
                                    
'3'=>" _  _| _|",
                                    
'4'=>"   |_|  |",
                                    
'5'=>" _ |_  _|",
                                    
'6'=>" _ |_ |_|",
                                    
'7'=>" __  | / ",
                                    
'8'=>" _ |_||_|",
                                    
'9'=>" _ |_| _|"),
                                
$row_font=3){
        
$this->alphabet_font $alpha_font;

        
$keys array_keys($this->alphabet_font);

        foreach (
$keys as $k){
            
$this->alphabet .= $k;
        }

        if (
$keys[0]){
            
$this->row_font $row_font;
            
$this->col_font =
                (int)
strlen($this->alphabet_font[$keys[0]])/$this->row_font;
        }
    }

    public function 
generateString($len=5){
        
$i=0;
        
$str='';
        while (
$i<$len){
            
$str.=$this->alphabet[mt_rand(0,strlen($this->alphabet)-1)];
            
$i++;
        }
        return 
$str;
    }

    public function 
convertString($str_in){
        
$str_out="\n";
        
$str_out.='<pre>';
        
$str_out.="\n";
        
$i=0;
        while(
$i<$this->row_font){
            
$j=0;
            while(
$j<strlen($str_in)){
                
$str_out.= substr($this->alphabet_font[$str_in[$j]],
                                  
$i*$this->col_font,
                                  
$this->col_font)." ";
                
$j++;
            }
            
$str_out.= "\n";
            
$i++;
        }
        
$str_out.='</pre>';
        return 
$str_out;
    }
}
 
?>

Joyeux anniversaire

mercredi 18 avril 2012 - 19:00

Cela fait 2 ans jour pour jour, 2 ans déjà que Tontof existe. Aujourd'hui était donc le jour idéal pour se lancer.

Depuis quelques temps l'idée me trottait dans la tête. Et puis, j'ai hésité à me lancer dans l'auto-hébergement, et j'ai finalement choisi l'option de facilité en prenant un hébergeur... ce n'est que partie remise.

Qu'est-ce que vous trouverez sur tontof.net ? Un peu de tout mais surtout des choses qui concernent le libre, linux, l'informatique et bien sûr d'autres activités diverses et variées que je vous laisserai découvrir. Je cherche à m'émanciper et à quitter les services privés qui je sais ne font pas bon usage de toutes nos informations. Et puis j'en ai marre de devoir accepter qu'une application ait besoin d'accéder à la liste de mes contacts pour une boussole. Alors je partagerai avec vous les codes que je développe pour répondre à des besoins précis sans avoir à baisser son...

Bref, vous avez compris.

Alors pour commencer, il a fallu que je me trouve un joli moteur pour faire mon blog. J'ai pensé à blogotext de Timo que je suis régulièrement, mais cela me semblait un peu trop compliqué et je cherchais un outil encore plus minimaliste. Dans sa liste de moteurs de blog, je me suis très vite orienté vers picoblog . J'ai regardé le code, il m'a bien plu et j'ai commencé à ajouter les choses qui me manquaient :
- une installation automatique
- une gestion de session sécurisée
- des commentaires

Il me reste encore beaucoup d'autres choses à faire, mais je ne voulais pas attendre pour vous rejoindre. Je partagerai évidemment avec vous ce code, mais pour vous faire patienter, je vous livre la gestion de la session que j'ai réalisée grâce au code de Sebsauvage.
Comme je ne voulais pas plusieurs fichiers et que je voulais ajouter d'autres possibilités, j'ai fait un mixte avec shaarli pour créer une classe Session qui permet de gérer facilement les sessions.

J'espère que cela vous plaira. N'hésitez pas à faire des critiques dans les commentaires et à m'ajouter dans vos amis facebook, google+ et twitter. Ah ben non je suis bête j'ai pas de compte. Mais vous pouvez maintenant me suivre grâce à mon flux RSS :-)

Au fait, j'allais oublier. Très bon anniversaire Noé !

Edit : 2013-08-31 : voir http://tontof.net/kriss/php5/session pour une version à jour

<?php 
/**
 * Session management class
 * http://www.developpez.net/forums/d51943/php/langage/sessions/
 * http://sebsauvage.net/wiki/doku.php?id=php:session
 * http://sebsauvage.net/wiki/doku.php?id=php:shaarli
 *
 * Features:
 * - Everything is stored on server-side (we do not trust client-side data,
 *   such as cookie expiration)
 * - IP addresses + user agent are checked on each access to prevent session
 *   cookie hijacking (such as Firesheep)
 * - Session expires on user inactivity (Session expiration date is
 *   automatically updated everytime the user accesses a page.)
 * - A unique secret key is generated on server-side for this session
 *   (and never sent over the wire) which can be used
 *   to sign forms (HMAC) (See $_SESSION['uid'] )
 * - Token management to prevent XSRF attacks.
 * 
 * TODO:
 * - log login fail
 * - prevent brute force (ban IP)
 *
 * HOWTOUSE:
 * - Just call Session::init(); to initialize session and
 *   check if connected with Session::isLogged()
 */

class Session
{  
    
// If the user does not access any page within this time,
    // his/her session is considered expired (in seconds).
    
public static $inactivity_timeout 3600;
    private static 
$_instance;
 
    
// constructor
    
private function __construct()
    {
        
// Use cookies to store session.
        
ini_set('session.use_cookies'1);
        
// Force cookies for session  (phpsessionID forbidden in URL)
        
ini_set('session.use_only_cookies'1);
        if (!
session_id()){
            
// Prevent php to use sessionID in URL if cookies are disabled.
            
ini_set('session.use_trans_sid'false);
            
session_start(); 
        }
    } 

    
// initialize session
    
public static function init()
    {
        if (!isset(
self::$_instance)) {
            
self::$_instance = new Session();
        }
    }

    
// Returns the IP address, user agent and language of the client
    // (Used to prevent session cookie hijacking.)
    
private static function _allInfos()
    {
        
$infos $_SERVER["REMOTE_ADDR"];
        if (isset(
$_SERVER['HTTP_X_FORWARDED_FOR'])) {
            
$infos.=$_SERVER['HTTP_X_FORWARDED_FOR'];
        }
        if (isset(
$_SERVER['HTTP_CLIENT_IP'])) {
            
$infos.='_'.$_SERVER['HTTP_CLIENT_IP'];
        }
        
$infos.='_'.$_SERVER['HTTP_USER_AGENT'];
        
$infos.='_'.$_SERVER['HTTP_ACCEPT_LANGUAGE'];
        return 
sha1($infos);
    }
 
    
// Check that user/password is correct and init some SESSION variables.
    
public static function login($login,$password,$login_test,$password_test,
                                 
$pValues = array())
    {
        foreach (
$pValues as $key => $value) { 
            
$_SESSION[$key] = $value
        } 
        if (
$login==$login_test && $password==$password_test){
            
// generate unique random number to sign forms (HMAC)
            
$_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand());
            
$_SESSION['info']=Session::_allInfos(); 
            
$_SESSION['username']=$login;
            
// Set session expiration.
            
$_SESSION['expires_on']=time()+Session::$inactivity_timeout;
            return 
true;
        }
        return 
false;
    }
 
    
// Force logout
    
public static function logout()
    {
        unset(
$_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on']);
    } 

    
// Make sure user is logged in.
    
public static function isLogged()
    {
        if (!isset (
$_SESSION['uid'])
            || 
$_SESSION['info']!=Session::_allInfos()
            || 
time()>=$_SESSION['expires_on']){
            
Session::logout();
            return 
false;
        }
        
// User accessed a page : Update his/her session expiration date.
        
$_SESSION['expires_on']=time()+Session::$inactivity_timeout;  
        return 
true;
    }

    
// Returns a token.
    
public static function getToken()
    {
        if (!isset(
$_SESSION['tokens'])){
            
$_SESSION['tokens']=array();
        }
        
// We generate a random string and store it on the server side.
        
$rnd sha1(uniqid('',true).'_'.mt_rand());
        
$_SESSION['tokens'][$rnd]=1;  
        return 
$rnd;
    }

    
// Tells if a token is ok. Using this function will destroy the token.
    // return true if token is ok.
    
public static function isToken($token)
    {
        if (isset(
$_SESSION['tokens'][$token]))
        {
            unset(
$_SESSION['tokens'][$token]); // Token is used: destroy it.
            
return true// Token is ok.
        
}
        return 
false// Wrong token, or already used.
    
}
}
 
?>