Web-dev

Quand « lightweight » signifie « fat and no features » : les CMS file-based

Note préalable

Ceci est plus un billet d'humeur qu'un comparatif objectif. Il a été écrit pendant une nuit où je cherchais désespéremment un CMS simple, après avoir goûté aux déceptions que sont Grav et Phile.

Oh, et j'entends bien entendu par backend toute interface d'administration permettant de gérer le contenu affiché en frontend. Il s'agit ici plus d'éviter les longues répétitions.

Préambule

J'adore quand quelque chose de simple devient complexe… Les CMS file-based, j'aime bien. C'est facile à mettre en place, ça s'installe partout, et les sauvegardes se font rapidement.

Habituellement, j'utilise pour ça PluXML. C'est une valeur sûre. Pour les sites statiques, sans blog par exemple, j'utilise plutôt GetSimple que je trouve plus rapide à appréhender pour un client (ajouter une page se fait en un clic, on modifie le menu en glissant-déposant, là où il faut une manipulation tirée d'un autre âge pour PluXML sur ce point).

Mais j'ai voulu voir un peu la concurrence, trouver encore plus simple, si possible avec un éditeur Markdown et sans fioritures (si je veux un blog ou un flux RSS, ce doit être un plugin).

Eh bien, c'est un peu le parcours du combattant.

Le but

Disons que je cherche un CMS avec une administration, pour deux raisons.

La première, c'est que si les CMS sans backend sont intéressants dans le cadre d'une utilisation d'un contenu édité en groupe (cas d'une documentation mise sur serveur git, par exemple) ou seul sur une même machine, ils sont bien moins intéressants quand on est sans cesse en déplacement d'une machine à l'autre ou si le groupe en question n'a pas accès au serveur.

Un exemple parlant : lorsque je suis sur une autre machine que la mienne, pour prendre des notes, j'aime bien utiliser mon Shaarli en mode privé. Ainsi, je ne laisse pas de trace chez mon hôte, et j'évite de déconnecter son Facebook/Gmail/etc. pour simplement prendre quelques notes. C'est impossible à réaliser avec un CMS sans backend.

La seconde raison, c'est que si je voulais faire un site sans backend, autant le faire en local, tout en HTML/PHP ou avec les outils qui vont bien (Hugo je pense à toi). Le seul intérêt des CMS flat file sans administration, c'est de mettre l'accent sur le contenu, en simplifiant l'écriture de celui-ci à sa base : un simple écrit dans un fichier texte.

Au cas où, StaticGen liste les générateurs de site statique, et code.makery a écrit un article intéressant sur le sujet : Making Content Editor and Web Developers Happy Again.

Durant les recherches effectuées pour écrire cet article, je suis également tombé sur Sculpin, un générateur de site statique en PHP fonctionnant de manière similaire à Composer. Utile si vous ne voulez pas vous farcir du Ruby, du Node.js, du Python ou du Go…

Comparatif subjectif

xkcd - Standards

Prenons comme base de comparatif PluXML. PHP, 200 fichiers, taille nu 1,1 Mo. Template PHP. Flux RSS, blog, pages statiques, pagination, commentaires, plugins, panneau d'administration, multi-utilisateurs, pages privées, gestionnaire de fichiers, multi-langues, thèmes, sitemap… Bref, assez complet.

Pour info, encore à des fins comparatives, WordPress : PHP + MariaDB, 1330 fichiers, 20,9 Mo. Template PHP. Niveau fonctionnalités, assez proche de PluXML, auquelles on peut ajouter : plus de plugins, plus de thèmes, plus grande communauté, quick-post, et quelques améliorations au niveau typographie…

Note sur la méthodologie

Pourquoi regarder le nombre de fichiers ? Tout simplement parce qu'il est bien plus simple de comprendre comment fonctionne un code qui tient en 8 fichiers qu'en 2500. Si comprendre et pouvoir maintenir un minimum le code que vous faites tourner sur votre serveur ne vous intéresse pas, pour moi ça a son importance.
Pourquoi regarder la taille ? Sur certains hébergements, ça a son importance. Ici, cela va surtout servir à démontrer un point : ce n'est pas en faisant plus de lignes de codes que vous aurez plus de fonctionnalités, il semble même que ce soit l'inverse…
Le template est donné à titre indicatif uniquement. Généralement ils ne sont guère complexes.

Grav

Je commence par Grav, très eye candy, sympathique. PHP, 1540 fichiers, taille nu 8,9 Mo. Template Twig.

Déjà il y a quelque chose qui cloche. Avec un poids pareil, on pourrait s'attendre à trouver plus de fonctionnalités que PluXML. Eh bien… Non. Ça, c'est juste pour parser des fichiers Markdown et les afficher en un site statique (ou un blog, en jouant avec les sous-dossiers).
Vous voulez un flux RSS ? Ajoutez 300 ko.
Vous voulez un sitemap ? Ajoutez 10 ko.
Etc. On se retrouve avec bien 10 Mo pour à peine le tiers des fonctionnalités d'un CMS qui en fait 10 fois moins… Et le meilleur ? Avec tout ça, il n'y a même pas d'administration ! Certes, c'est le principe des flat file CMS, mais on aurait pu s'attendre à ce qu'un plugin soit présent pour justifier tout ça…

Donc je surveillerai Grav, mais pour le moment il me semble bien trop volumineux pour pouvoir clamer être « léger ».

Dropplets

Dropplets est un blog. Rien de plus. PHP, 47 fichiers, 1,4 Mo. Template PHP.

Pas de flux RSS, pas de plugins, mais il a un panel d'administration, lui ! Celui-ci se résume à quelques variables habituelles (mot de passe, nom du site…) et un formulaire d'envoi de fichier. Vous devez donc écrire en local avant d'envoyer via le panel. Concept intéressant, auquel je vois deux bémols :

  1. Il n'y a pas possibilité d'envoyer une image ou un quelconque media par le panel, donc obligation d'utiliser autre chose pour le faire.

  2. Dropplets renomme le fichier envoyé en utilisant la première phrase. Ça m'a valu de ne pas pouvoir supprimer ces fichiers sur mon FTP pour une raison que j'ignore. Il a fallu que je passe par la console SSH pour arriver à faire un rm -rf propre. Pourquoi ne pas utiliser un timestamp, comme tout le monde ?

Donc minimaliste, et aucune possibilité de modification…

Phile

Phile est amusant. Il commence comme un fork de Pico. PHP, 710 fichiers, 2,0 Mo. Template Twig.

Juste un site statique. Comme pour Grav, on peut jouer avec les sous-dossiers pour arriver à faire un blog. Et comme pour Grav, tout se passe dans les plugins. Là où ça devient hilarant, c'est sur le plugin d'administration (PhileAdmin).

D'une, il n'y a que son créateur qui semble arriver à le faire fonctionner. De deux, que l'on essaie de l'installer avec Composer (méthode recommandée par l'auteur) ou manuellement, on se retrouve avec un plugin qui pèse 30 Mo. Oui, vous avez bien lu. 30 Mo pour un backend qui ne sert qu'à ajouter, modifier ou supprimer des pages. Oh, et l'éditeur Markdown pour ce panel n'est même pas inclut, c'est un plugin supplémentaire ! Bien entendu, aucun support pour les images et autres médias en administration… Là je trouve que c'est du grand art. La faute à des dépendances Symfony qui semblent indispensables pour l'auteur, quand du code PHP pur lui donnerait le même résultat en quelques 300 Ko maximum.

Donc Phile, c'est bien, mais encore une fois il faut passer par la case FTP/Git.

Pico

Pico est souvent cité comme une référence. PHP, 909 fichiers, 2,6 Mo. Template Twig.

Site/Blog qui se dit « simple & blazing fast ». Tellement « simple » qu'il faut :

  1. Cloner le dépôt git ou télécharger l'archive,
  2. Télécharger Composer et le lancer,
  3. Envoyer le tout sur le serveur.

Parce que proposer une release zippée avec toutes les dépendances, c'est so-2012

Encore une fois, tout se passe dans les plugins. Et là, pour le coup, rien à dire. Le panel d'admin fonctionne, il fait 1,1 Mo, mais avec ça vous avez un file manager, un upload des images et un éditeur Markdown. M'enfin, si vous voulez, il y a aussi possibilité de passer par Draft.

Kirby

Kirby est génial. PHP, 670 fichiers, 5,6 Mo. Template PHP.

Backend intégré d'office, système de template ultra-simplifié, exactement de quoi tomber amoureux… Si ce n'est qu'il est payant (entre 15 et 79€ selon l'utilisation).

Stacey

Stacey est très léger. PHP, 150 fichiers, 858 Ko. Template perso simple.

Pas de backend, et n'est plus maintenu depuis 2 ans, mais je voulais en parler juste pour montrer que parser du Markdown en un blog avec flux RSS, sitemap, pages statiques, cache, pagination, pouvait se faire en-dessous du mégaoctet.

Yellow

Yellow reste léger. PHP, 47 fichiers, 347 Ko. Template PHP.

Le projet a l'air prometteur, souvent mis à jour, bien qu'un peu limité au départ. De base, parse le Markdown pour en faire des pages, avec possibilité de les éditer directement depuis le site web (malheureusement, juste du texte, aucun support pour les images de ce côté-là). Assez nu, mais est disponible dans 12 langues et les plugins foisonnent (du flux RSS à l'intégration Youtube en passant par SmartyPants ou FontAwesome).

Simple, net, efficace, mais vraiment sec à l'utilisation.

Automad

Automad. PHP, 218 fichiers, 6,6 Mo. Template perso simple à utiliser mais incroyablement profond.

Cache, tags, sitemap, galerie, plugins. Et il y a un backend ultra-simple d'utilisation pour le client lambda (drag&drop des images, par exemple), ainsi que la possibilité d'avoir plusieurs utilisateurs ! Aurais-je trouvé ma perle rare ?

Peu de plugins pour le moment, pratiquement pas de communauté, par défaut les utilisateurs ont tous des droits d'admin. Bien qu'il soit plus complet et léger que Grav, sa taille relativement importante n'est toujours pas justifiée.
EDIT : On réalité, le contenu par défaut prend pas mal de place. Une fois mis à nu, Automad se rapproche plus des 3,9 Mo.

Parvula

Parvula ne semble pas fonctionner. PHP, 85 fichiers, 584 Ko. Template PHP. Enfin, quand je dis qu'il ne semble pas fonctionner, c'est qu'il faut modifier le .htaccess en changeant

RewriteRule ^(.*)$ index.php/$1 [L]

par

RewriteRule . index.php [L]

Ainsi, on évite les Page not found. L'administration est simple mais efficace, divisée en deux parties : à gauche l'éditeur Markdown, à droite son résultat WYSIWYG. Possibilité d'ajouter, de modifier ou de supprimer des pages, mais c'est tout. Vraiment dommage qu'il n'y ait rien pour les images. Ne semble pas gérer les sous-dossiers, donc difficile (mais pas impossible) d'en faire autre chose qu'un CMS pour site statique (et là-dessus, il bat Pico au niveau de la simplicité d'utilisation et de la légèreté). Néanmoins, aucun plugin, aucune communauté.

Oh, et sans pêcher par excès de chauvinisme, le développeur semble être français. À garder sous le coude pour de petits sites très simples.

Et tous les autres

Des CMS sans base de données, il en existe des paquets. J'en ai même codé un, fut un temps, c'est dire comme c'est simple à réaliser.

Ce comparatif n'est pas exhaustif, et n'a pas prétention à l'être. Parmi les outils que j'ai écarté, certains méritent une mention :

  • Nibbleblog est de qualité, très complet, on peut le voir comme le concurrent direct de PluXML ou de DotClear.
  • SiteCake est bien plus un générateur de site statique hébergé. Ça reste une solution simple à mettre en place et rapide à prendre en main par les clients.
  • Singularity tient dans un seul fichier PHP de 40 lignes. C'est minimaliste, mais intéressant.
  • Monstra est vraiment cool et peut remplacer assez simplement un GetSimple.
  • Baun. Après avoir vendu Pico à quelqu'un d'autre, l'auteur a décidé de recommencer un CMS léger sur le même principe, mais cette fois-ci en faisant payer le plugin d'administration (39 $)… PHP, 1300 fichiers, 3,7 Mo. Template Twig.
  • Et pour aller plus loin, l'utilisateur ahadb a dressé une petite liste des CMS flat-file.

Étude de cas

Prenons maintenant un exemple concret.

Pour un projet perso, j'ai un cahier des charges comme suit :

Thème simple HTML5 responsive support mobile

Pas de titre sur les billets

Pages statiques affichées comme un blog sans commentaires

2 utilisateurs différents, aucun des deux n'ayant accès au FTP ou au dépôt Git du serveur

2 styles différents selon l'auteur

2 modes de tri (ascendant/descendant par date)

trois possibilités d'écrire un article :

  • manuellement (Markdown) -> drag & drop ou éditeur
  • envoyer une photo -> drag & drop ou bouton classique
  • whiteboard -> écrire quelque chose au stylet qui est converti en image (vraiment optionnel)

Possibilité de se connecter en cliquant sur un lien Répondre

Syndication RSS/Atom

Donc, grosso modo, un Tumblr à deux collaborateurs. Ça parait bête comme chou, dit comme ça, non ?

  • PluXML peut faire ça, sans aucun problème, mais n'est pas assez simple au niveau de l'utilisation du backend.
  • GetSimple est pareil. Plus simple que PluXML, mais pas encore assez. À la limite, si on se débrouille avec le thème d'administration…
  • Grav demanderait de coder un plugin d'administration pour ça…
  • Dropplets ne supporte pas plus d'un utilisateur.
  • Phile nécessite de recoder un plugin d'administration…
  • Pico aurait besoin de lourdes modification sur le plugin d'administration.
  • Kirby est payant.
  • Stacey n'est plus vraiment maintenu et n'a pas d'administration.
  • Yellow aurait besoin de plus de modifications encore que Pico.
  • Automad irait parfaitement, si j'arrivais à trouver un moyen pour limiter les droits des utilisateurs, simplifier encore plus l'envoi des photos, et activer la syndication.
  • Parvula aurait besoin d'une bonne modification du backend.

On se retrouve donc avec des CMS qui font entre 1 et 2 Mo capables de réaliser ce que d'autres pesant entre 3 et 9 Mo sont incapables de produire. Le problème étant que les premiers ne sont pas aussi simples d'utilisation que les seconds.

Conclusion

Simple, les CMS file-based ? C'est mitigé. Ceux qui stockent leurs données en XML, plus anciens, semblent être bien plus complets et rapides à déployer, lorsque ceux basés sur du Markdown se concentrent sur la vitesse au détriment de fonctionnalités de base.

Si tout ce dont vous avez besoin, c'est d'un éditeur Markdown, quitte à devoir coder vos propres plugins, regardez du côté de Pico, Parvula et Automad.

Si au contraire vous avez besoin de fonctions « exotiques » telles qu'une barre de recherche, une syndication RSS, une pagination ou même le support des hébergements sans mod_rewrite, tournez-vous vers PluXML, Nibbleblog, GetSimple.

Maintenant, pour aller plus loin, il serait intéressant de jeter un œil aux divers scripts PHP qui, tenant en un seul fichier, permettent l'envoi de fichiers et leur gestion sur l'hébergement qui les accueillent. Je suis certain qu'en travaillant un peu là-dessus, on pourrait arriver à une interface d'administration flexible et portable d'un CMS à l'autre. Il ne manque qu'un dossier admin potable à Pico, Phile, Grav, Dropplets, Stacey, pour qu'on puisse réellement les considérer comme des CMS modernes. En somme, one backend to rules them all.

Note finale

Cet article a été écrit avec l'éditeur Markdown de Parvula, je n'ai eu qu'à récupérer le HTML généré pour le coller dans PluXML. Je pense l'adopter pour de petits projets, lui au moins il est léger…

Blink en CSS3

À l'époque d'HTML4, il y avait la balise <blink>. Elle n'était pas très populaire, au vu de sa sur-utilisation sur des sites bardés de gif de mauvais goût. Et aussi parce que, il faut l'avouer, un texte qui clignote, ça donne mal aux yeux.

Mais il arrive qu'on veuille tout de même recréer cet effet sous HTML5. Pour cela, rien de plus simple avec CSS3 !


.blink {
	animation-duration: 1s;
	animation-name: blink;
	animation-iteration-count: infinite;
	animation-timing-function: steps(2, start);
}

@keyframes blink {
	80% { visibility: hidden; }
}

On peut ainsi s'amuser à faire un prompt :

guillaume@yomli ~ $

Ou bien reproduire l'exemple de l'image :

Schroedinger's Cat

is NOT dead.

Chiffrer un message avec OpenPGP.js

Pour ma page de contact, je souhaitais faciliter l'envoi de messages chiffrés avec ma clé publique PGP. Dans l'idéal, une simple case à cocher permettrait de chiffrer le message avant l'envoi au serveur, puis de là dans ma boite mail. C'est pourquoi je me suis penché sur plusieurs implémentations d'OpenPGP en Javascript.

La première, Hanewinkel, était assez légère une fois minifiée (30 ko). Néanmoins, le chiffrement de messages contenant des caractères accentués posait problème. J'aurais pu m'en contenter, mais c'est toujours bon d'arriver à lire des messages directement. Si toutefois ça vous intéresse, voici un petit snippet à intégrer :


function encryptPGP(key, message){
	var pu = new getPublicKey(key);
 	if(pu.vers == -1) 
 		return;

	var keytyp = 0;      // 0=RSA, 1=Elgamal
	var keyid = pu.keyid;
	var pubkey = pu.pkey.replace(/\n/g,'');
	return doEncrypt(keyid, keytyp, pubkey, message);
}

$("#contact form").on("submit", function() {

	// Comme on va peut-être avoir besoin de
	// chiffrer le message, on n'utilise pas
	// le .serialize() habituel d'Ajax, mais
	// un tableau.
	var donnees = $(this).serializeArray();

	// Si la checkbox est cochée
	if($("#contact-crypted")[0].checked) {
		// On récupère la clé publique affichée
		// dans la page
		var key = $("#pgp-key").html();
		// On récupère le message
		var message = $("#contact-message").val();
		// On le chiffre
		message = encryptPGP(key, message);
		// Et on affiche le message chiffré
		$("#contact-message").val(message);

		// Enfin, on remplace le message par
		// son équivalent chiffré dans le tableau
		for(var i = 0; i < donnees.length; i++) {
			if(donnees[i].name == "contact-message")
				donnees[i].value = message;
		}
	}

	// On transforme notre tableau en une suite
	// de paramètres dont Ajax va se charger
	donnees = jQuery.param(donnees);

	// Et on envoie tout ça par Ajax pour
	// éviter un rechargement de la page
	$.ajax({
		url: $(this).attr("action"),
		type: $(this).attr("method"), 
		data: donnees,
		success: function(html) { 
			alert(html);
		},
		error: function(html) {
			alert(html);
		}
	});

    return false;
});

Je suis donc allé voir du côté d'openpgpjs, une solution plus lourde (250 ko), mais qui cette fois fonctionne avec les caractères accentués. Le problème principal qu'on a ici, c'est que openpgp est une promise. C'est cool pour un tas de choses, mais là on aimerait bien attendre la fin du chiffrement avant d'envoyer notre message au serveur. Oh, et au cas où vous vous le demanderiez, openpgp.js embarque un polyfill pour assurer la compatibilité avec les navigateurs qui ne tiennent pas leurs promesses…

Donc je passerai sur mes envies de meurtre lorsque j'ai compris que je devrais refaire tout le code et vous le présente tel quel :


$("#contact form").on("submit", function() {

	// Si la checkbox est cochée
	if($("#contact-crypted")[0].checked) {
		
		var contact = $("#contact form");
	
		// Comme on va peut-être avoir besoin de
		// chiffrer le message, on n'utilise pas
		// le .serialize() habituel d'Ajax, mais
		// un tableau.
		var donnees = $(contact).serializeArray();
	
		// On récupère le message
		var message = $("#contact-message").val();

		// On récupère la clé publique affichée
		// dans la page et on la prépare
		var pubkey = openpgp.key.readArmored($("#pgp-key").html());

		// Enfin, on lance le chiffrement
		openpgp.encryptMessage(pubkey.keys, message).then(function(pgpMessage){
			
			// On récupère le message chiffré
			message = pgpMessage;
			
			// Et on l'affiche
			$("#contact-message").val(message);

			// Enfin, on remplace le message par
			// son équivalent chiffré dans le tableau
			for(var i = 0; i < donnees.length; i++) {
				if(donnees[i].name == "contact-message")
				donnees[i].value = message;
			}

			// On transforme notre tableau en une suite
			// de paramètres dont Ajax va se charger
			donnees = jQuery.param(donnees);

			// Et on envoie tout ça par Ajax pour
			// éviter un rechargement de la page
			$.ajax({
				url: $(contact).attr("action"),
				type: $(contact).attr("method"), 
				data: donnees,
				success: function(html) { 
					alert(html);
				},
				error: function(html) {
					alert(html);
				}
			});
		}).catch(function(error){
			alert(error);
		});
	}
	// Et là la méthode classique,
	// sans chiffrement
	else {
		$.ajax({
			url: $(this).attr("action"),
			type: $(this).attr("method"), 
			data: $(this).serialize(),
			success: function(html) { 
				alert(html);
			},
			error: function(html) {
				alert(html);
			}
		});
	}	

	return false;
});

Note sur la sécurité : ne déployez pas cette solution autrement que pour un petit formulaire de contact. Il s'agit plus d'un proof of concept. J'entends par là qu'elle se base sur ce qu'on appelle dans le milieu host based security, autrement dit aucune véritable sécurité. Il suffit à quelqu'un (que ce soit l'hébergeur, le FAI, le VPN, le navigateur, etc.) de changer la clé publique, le fichier openpgp.js ou le code javascript pour qu'il soit capable de lire le message. Plus d'informations sur cet article.

PluXML : faire un plan de site

La page du wiki officiel ayant été effacée, et parce que c'est tout de même une fonction bien pratique à insérer sur un site, voici comment faire un plan de site sous PluXML 5+.

Il suffit de créer une nouvelle page statique et d'y insérer le contenu ci-dessous :


<?php
# Page statique Pluxml : liste de tous les articles par catégories par stephane@pluxml.org
# révision 2.0 par Yomli :
#	- compatibilité pluxml 5+ 

global $plxShow;
#=======PARAMÈTRES ========
# format de la date
$format_date = '#num_day/#num_month/#num_year(4)';
 
# liste des catégories à afficher
# exemple: $catList = ''; = articles de toutes les catégories, 
# exemple: $catList = '001|003'; = articles des catégories 001 et 003 uniquement
$catList = ''; 
# nombre d'articles à afficher par catégorie, mettre 0 pour lister tous les articles
$artsByCategory = 0; 
#==========================
?>

<h4>Pages statiques</h4>
<ul class="alt">
	<?php $plxShow->staticList('Accueil','<li id="#static_id"><a href="#static_url" class="#static_status" title="#static_name">#static_name</a></li>'); ?>
</ul>

<h4>Catégories</h4>
<ul class="alt">
	<?php $plxShow->catList('','<li id="#cat_id" class="#cat_status"><a href="#cat_url" title="#cat_name">#cat_name</a></li>'); ?>
</ul>

<h4>Articles</h4>
<?php
$plx_arts = array();
$plxGlob_arts = plxGlob::getInstance(PLX_ROOT.$plxShow->plxMotor->aConf['racine_articles']);
$aFiles = $plxGlob_arts->query('/[0-9]{4}.[home|0-9,]*'.$catList.'[0-9,]*.[0-9]{3}.[0-9]{12}.[a-z0-9-]+.xml$/','art','rsort',0,false,'before');
$aCatList = explode('|', $catList);
if(is_array($aFiles)) { # On a des fichiers
    while(list($k,$v) = each($aFiles)) { # On parcourt tous les fichiers
        $temp = $plxShow->plxMotor->parseArticle(PLX_ROOT.$plxShow->plxMotor->aConf['racine_articles'].$v);
		$cats = explode(',', $temp['categorie']);
		foreach($cats as $cat) {
			if($catList=='' OR in_array($cat, $aCatList)) {
				if(!isset($plx_arts[$cat]))
					$plx_arts[$cat][] = $temp;
				elseif(intval(sizeof($plx_arts[$cat])) < $artsByCategory OR $artsByCategory == 0)
					$plx_arts[$cat][] = $temp;
			}
		}
    }
    if($plx_arts) { # On a des articles
		
		# tri en fonction de l'ordre d'affiche des catégories
		uksort($plx_arts, create_function('$a, $b', 'global $plxShow; return strcmp(array_search($a, array_keys($plxShow->plxMotor->aCats)), array_search($b, array_keys($plxShow->plxMotor->aCats)));'));
			
        # On boucle sur nos articles
		foreach ($plx_arts as $k => $v) {

			$cat_num = $k;
		
			# on trie en fonction de l'ordre d'affichage des articles dans la catégorie
			if($cat_num=='home') {
				if ($plxShow->plxMotor->aConf['tri']=='asc') 
					usort($v, create_function('$a, $b', 'return strcmp($a["date"], $b["date"]);'));
				else
					usort($v, create_function('$a, $b', 'return strcmp($b["date"], $a["date"]);'));
				echo '<h5><a href="'.$plxShow->plxMotor->aConf['racine'].'">Accueil</a></h5>';
			}
			elseif(!isset($plxShow->plxMotor->aCats[$cat_num])) {
				if ($plxShow->plxMotor->aConf['tri']=='asc') 
					usort($v, create_function('$a, $b', 'return strcmp($a["date"], $b["date"]);'));
				else
					usort($v, create_function('$a, $b', 'return strcmp($b["date"], $a["date"]);'));
				echo '<h5>Non classé</h5>';
			} 
			else {
				if ($plxShow->plxMotor->aCats[$cat_num]['tri'] == 'asc')
					usort($v, create_function('$a, $b', 'return strcmp($a["date"], $b["date"]);'));
				else
					usort($v, create_function('$a, $b', 'return strcmp($b["date"], $a["date"]);'));
				$cat_name = plxUtils::strCheck($plxShow->plxMotor->aCats[ $cat_num ]['name']);
				$cat_url = $plxShow->plxMotor->aCats[ $cat_num ]['url'];
				echo '';				
			}
			echo "<h5>".$cat_name."</h5><ul class='alt'>";
			# On boucle sur les articles de la categories
			while(list($null, $art) = each($v)) {
				$art_num = intval($art['numero']);
				$art_url = plxUtils::strCheck(($art['url']));
				$art_title = plxUtils::strCheck(($art['title']));
				$art_date = plxDate::formatDate($art['date'], $format_date);
				echo '<li>'.$art_date.' : <a href="'.$plxShow->plxMotor->aConf['racine'].'?article'.$art_num.'/'.$art_url.'">'.$art_title.'</a></li>'; 
			}
			echo "</ul>";
        }

    }
}
?>

CipherSaber.js

Monsieur Urvoas souhaite pouvoir empêcher le chiffrement, à ce qu'il en ressort des débats pour le ‪#‎PJLRenseignement‬. Le problème, c'est qu'il est impossible d'arrêter le chiffrement de données.

CipherSaber en est l'exemple parfait. Cet algorithme est si simple que n'importe quel développeur amateur peut en faire une implémentation de tête. Il se base sur l'idée qu'un Jedi devait fabriquer son propre sabre laser avant d'être admis comme chevalier, le rendant indépendant de tout Empire totalitaire. Voici une implémentation en javascript de mon cru, très basique, que vous pouvez tester sur JSFiddle ou bien en suivant le lien de la démo.

Dépôt github : https://github.com/yomli/ciphersaber.js/

Un lien « Retour en haut » dynamique

Les petits boutons « Retour en haut », on les aime tous. Généralement, il s'agit d'une simple div en position: fixed. En voici un qui n'apparait qu'arrivé à un certain pourcentage de la page, et qui s'arrêtre gracieusement juste au-dessus du footer.

Le code HTML est on ne peut plus simple :


<div class="relative">
	<div class="toTop">
		<a href="#top" title="Retour en haut">
			Retour en haut
		</a>
	</div>
</div>
<footer id="footer">
…
</footer>

Le code CSS :


.relative {
	position:relative;
}

.toTop {
	display:none;
	position:fixed;
	right:20px;
	bottom:20px;
	z-index:1000;
}

Voilà, nous avons un lien, mais il n'apparait pas pour le moment. On ajoute un peu de jQuery :


function toTop(element,footer){
	var scroll = $(window).scrollTop();
	var maxScroll = $(window).height() * 0.4;

	if(scroll > maxScroll)
		$(element).show();
	else
		$(element).hide();


	if($(element).offset().top + $(element).height() >= $(footer).offset().top - 10)
		$(element).css('position', 'absolute');
	if($(document).scrollTop() + window.innerHeight < $(footer).offset().top)
		$(element).css('position', 'fixed');
}	

$(window).scroll(function() {
	toTop('.toTop',"#footer");
});

$(window).resize(function() {
	toTop('.toTop',"#footer");
});

Scrolling doux vers une ancre

Un petit bout de jQuery intéressant pour animer le scrolling vers une ancre (ce que les anglophones appellent smooth scroll) :


$('a[href*=#]').on('click', function(event){     
	event.preventDefault();
	$('html,body').animate({scrollTop:$(this.hash).offset().top}, 700);
});	
	

Oubliez votre lien d'administration avec Javascript !

Parfois, il faut quelque chose de simple. Accéder à l'interface d'administration d'un CMS devrait être aussi simple que de cliquer sur un bouton. En fait, c'est déjà le cas dans plusieurs CMS, il suffit effectivement de cliquer sur le beau lien Administration.

Mais c'est peu élégant. C'est pourquoi beaucoup de gens décident de garder le lien dans leurs favoris. C'est bien, mais pour peu que vous changiez de navigateur ou décidiez de poster depuis l'ordinateur de votre belle-tante, retrouver le lien d'administration devient compliqué.

C'est pourquoi ce petit bout de Javascript vous permettra d'accéder à l'administration en tapant simplement trois fois d'affilée la touche Entrée. Il s'agit de Javascript vanilla, mais j'ai mis en commentaire la ligne à changer pour le rendre jQuery :


function keyboardAuth(redirection) {
	var enterCount = 0;
	
	//$(document).keypress(function(event){
	document.addEventListener("keypress", function(event) {
		var keycode = (event.keyCode ? event.keyCode : event.which);
		if(keycode == '13'){
			enterCount++;
			if(enterCount >= 3){
				enterCount = 0;
				window.location = redirection;
			}
		setTimeout(function(){ enterCount = 0; },2000);
		}

	});
}
	

Notez que vous n'aurez que 2 secondes pour appuyer trois fois sur la touche Entrée, ceci pour éviter que quelqu'un écrivant un commentaire ne soit redirigé vers la page d'administration pour avoir simplement voulu mettre en page son commentaire…

Tout ce que vous aurez à faire ensuite, c'est appeler cette fonction avec le lien de votre interface d'administration :


keyboardAuth("/admin/");
	

Effet parallax avec jQuery

Un gros bout de code en jQuery pour faire un effet parallax sur un élément donné. Je le colle là sans trop d'explications, j'ai beaucoup trop de choses à faire pour tout commenter.


/*
* Adds a parallax effect to a background image
* (position:fixed;no-repeat;background-size:42%;)
*
* element : the element with a background image
* percent : percent of the background-size (0.42)
* height : height of the background image
* width : width of the background image
* factor : factor of speed (the lower the faster)
* reference : the element bottom-placed to get the total
*             height of the page
*
* author : Guillaume Litaudon  <guillaume.litaudon@gmail.com>
*/

function parallax(element,percent,height,width,factor,reference){
	var winWidth = $(window).width();
	var winHeight = $(window).height();

	var sizePercent = percent == 1 ? height : ((winWidth*percent)/width)*height;

	var maxScroll = -(sizePercent - winHeight);
	var speed = factor*($(reference).offset().top / winHeight);
	var yPos = -($(window).scrollTop() / speed);
	yPos = yPos >= 0 ? 0 : yPos;
	yPos = yPos >= maxScroll ? yPos : maxScroll;
	var coords = '0 '+ yPos + 'px';

	$(element).css({ backgroundPosition: coords });
}


function goGoParallaxEffect(){
	/* La taille dépend de si l'on est en-dessous
	* de pictureWidth ou non
	*/
	var pictureHeight = 1080;
	var pictureWidth = 388;

	if($(window).width() > pictureWidth)
		parallax('#page',0.42,pictureWidth,pictureHeight,5,footer);
	else
		parallax('#page',1,pictureWidth,pictureHeight,5,footer);
}

$(window).scroll(function() {
	goGoParallaxEffect();
});

$(window).resize(function() {
	goGoParallaxEffect();
});	

Picoloblog 1.2 beta

On passe à la 1.2 beta du Picoloblog, une version qui sort avant la maturité de la 2.0 que je prépare. Soyez patient !

Au menu :
  • Icônes et nouveaux boutons dans la barre d'outils
  • Moteur de recherche
  • Le bookmarklet prend en compte la sélection
  • Poster automatiquement vers les réseaux sociaux (Twitter, Identi.ca et Facebook pour le moment)
  • Correction du bug de l'encodage en UTF8 du flux RSS
  • Validité W3C

Et ça se télécharge ici.
Mais il va falloir patienter un peu pour la version 2.0, qui contiendra notamment un système de plugins permettant l'ajout simplement de commentaires, de boutons Like ou Tweet, une lightbox sur les photos et vidéos, voire le classement des billets en catégories (photos, vidéos, écrits). C'est un petit peu la version 1.8 de Minecraft : on l'attends impatiemment et elle fini par arriver.

Haut de page