:-y

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.
    
}
}
 
?>