From 9a7add7181b8c5d1cb9b45d689231e7411221406 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Georges=20Dup=C3=A9ron?= <jahvascriptmaniac+github@free.fr>
Date: Sun, 3 Oct 2010 23:18:46 +0200
Subject: [PATCH] =?UTF-8?q?Brouillon=20de=20l'impl=C3=A9mentation=20de=20l?=
 =?UTF-8?q?a=20nouvelle=20structure.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 __cms__/code/classes.txt               | 203 -------------------------
 cms2/code/bdd.php                      |  70 +++++++++
 cms2/code/configuration.php            |  30 ++++
 cms2/code/debug.php                    |   7 +
 cms2/code/document.php                 | 105 +++++++++++++
 cms2/code/include.php                  |  12 ++
 cms2/code/index.php                    |  13 ++
 cms2/code/main.php                     |  10 ++
 cms2/code/page.php                     | 101 ++++++++++++
 cms2/code/stockage_fichiers.php        |  14 ++
 cms2/code/util.php                     | 129 ++++++++++++++++
 cms2/config.php                        |  27 ++++
 cms2/index.php                         |   5 +
 cms2/modules/galerie/galerie-index.php |  41 +++++
 cms2/modules/galerie/include.php       |   5 +
 cms2/modules/include.php               |   9 ++
 cms2/modules/squelette/include.php     |   5 +
 cms2/modules/squelette/squelette.php   |  13 ++
 18 files changed, 596 insertions(+), 203 deletions(-)
 delete mode 100644 __cms__/code/classes.txt
 create mode 100644 cms2/code/bdd.php
 create mode 100644 cms2/code/configuration.php
 create mode 100644 cms2/code/debug.php
 create mode 100644 cms2/code/document.php
 create mode 100644 cms2/code/include.php
 create mode 100644 cms2/code/index.php
 create mode 100644 cms2/code/main.php
 create mode 100644 cms2/code/page.php
 create mode 100644 cms2/code/stockage_fichiers.php
 create mode 100644 cms2/code/util.php
 create mode 100644 cms2/config.php
 create mode 100644 cms2/index.php
 create mode 100644 cms2/modules/galerie/galerie-index.php
 create mode 100644 cms2/modules/galerie/include.php
 create mode 100644 cms2/modules/include.php
 create mode 100644 cms2/modules/squelette/include.php
 create mode 100644 cms2/modules/squelette/squelette.php

diff --git a/__cms__/code/classes.txt b/__cms__/code/classes.txt
deleted file mode 100644
index 9de83f4..0000000
--- a/__cms__/code/classes.txt
+++ /dev/null
@@ -1,203 +0,0 @@
-
-/******\
-| TODO |
-\******/
-
-Comment pseudo-insérer des images dans la BDD ? Est-ce la classe de la page qui décide de les déplacer dans /data et de mettre juste le nom dans la BDD ?
-(Et c'est à ce moment-là que la miniature est faite). Ou bien, un "trigger" (get et set) pour faire la même chose ?
-
-
-=== Page ===
-->rendu() -> Document
-->parent
-->url // toute l'url
-->urlComponent // juste la fin (le composant de l'url qui désigne cette page).
-->select(requête) -> CollectionPages
-->set_prop() {
-	s'il y a un setter (trigger), on l'appelle, sinon on appelle set_prop_direct();
-	le setter fait ce qu'il veut, puis appelle set_prop_direct();
-}
-
-// TODO : gestion du renomage (pseudo-réécriture d'URL).
-
-
-=== CollectionPages ===
-__construct()
-__call() // appelle la fonction sur tous les objets, et renvoie les résultats dans un tableau. si les résultats sont des CollectionPages, ils sont concaténés dans la même collection.
-__get() // renvoie un tableau contenant le champ pour chaque objet.
-__set() // affecte la valeur sur chaque objet.
-
-=== Document ===
-
-->header -> HTMLElement
-->footer -> HTMLElement
-->nav -> HTMLElement
-->article[] -> HTMLElement // ?
-
-->titre
-->url
-
-->xhtml5()
-->html5()
-
-=== HTMLElement ===
-// Chaque type d'élément est une sous-classe de HTMLElement, et impléménte uniquement les méthodes de création qui respectent les règles d'imbrication des éléments.
-// Pour les éléments dont les enfants possibles dépendent du parent (par ex. <a>), on restreindra les enfants et (parents) possibles à quelque chose de sensé.
-// Plutôt que d'avoir plein de sous-classes, HTMLElement a une méthode __call(), qui vérifie ce qu'on peut appeller en fonction du type de l'élément.
-->_type
-->espace_CSS($class) // définit l'espace CSS de cet élément et de ses descendants.
-->div()
-->ul()
-->li()
-->p()
-...
-->_titre($select) // renvoie un <h2> ou un <input> selon les droits
-->_richText($select) // similaire
-->_field($select) // ...
-// Peut-être que _field peut détecter automatiquement s'il faut traiter un champ de la BDD (par ex. pour le richText) en fonction d'une info "type" dans la classe correspondant à la page de ce champ ?
-->_list($select, function_formattage_elements)
-
-=== GeleriePériode extends Page ===
-// TODO : nommer différemment les ressources de type HTML et les autres. Cela permer de ne pas appeller les méthodes ->header etc. sur une image !
-// Par ex res_h_defaut, res_h_miniature, res_i_image, res_i_miniature (h = html, i=image, c=css, j=javascript, ...)
-
-class GaleriePériode extends Page {
-	ressources_statiques = ["icône_nouvelle_page:image/jpeg", "css:text/css"];
-	ressources_dynamiques = ["page:Document", "miniature:Document", "mini_miniature:Document"];
-
-	defauts = [
-		"titre" => "Galerie",
-		"description" => ""
-	];
-
-	enfants = "GalerieÉvènement"; // Pour SiteIndex, c'est "*" (ou true ou un truc du genre).
-	
-	ressource_icône_nouvelle_page() {
-		return ...;
-	}
-	ressource_css() {
-	}
-	ressource_page() {
-		$d = new Document();
-		$d->heading->standard();
-		$l = $d->article(1)->append->liste(select(...), function($e) {...});
-		$l->append->...
-	}
-	ressource_miniature() {
-		return $this->miniature_image() . "blabla";
-	}
-	ressource_miniature_image() {
-		// Prendre le 1er par ordre décroissant sur la date, ou bien :
-		// TODO : prendre l'élément ayant la propriété "aperçu" à true (s'il y en a un, sinon date).
-		return this->select("./*", order=date desc, limit=1)->mini_miniature;
-	}
-}
-register_css(GaleriePériode); // Insère la ressource "ressource_c_css" dans le CSS principal
-register_js(GaleriePériode);  // Ajoute le script "ressource_j_js" au <head> de la page.
-                              // Ou bien, lors de l'inclusion des pages, les scripts de chacune sont remontés tout en haut. (meilleure solution).
-// Dans tous les cas, les deux devraient être faits par :
-type_page(GaleriePériode, "galerie-periode");
-
-// Fin GaleriePériode
-
-class CSS extends Page {
-	ressources_dynamiques = ["css:text/css"];
-	ressource_css() {
-		mettre bout à bout tous les CSS ?
-	}
-}
-
-
-
-
-class Document {
-	function ul() {}
-	function li() {}
-	function title() {}
-	...
-	
-	function standard() {
-		$d = new Document();
-		$d->append->titre($this->select("titre"));
-		$d->append->description($this->select("description"));
-		return $d;
-	}
-	
-	function liste($elts, $format) {
-		$d = new Document();
-		$ul = $d->append->ul();
-		foreach ($elts as $k => $e) {
-			$ul->append($format($e));
-		}
-		return $d;
-	}
-	
-	function bouton($texte, $page_callback, $action_callback) {
-		// afficher un input
-		// lors du clic, appeller $action_callback sur $page_callback ?
-	}
-}
-
-abstract class elementDocument() {
-	private _espaceCss = null;
-	function espaceCss($class) {
-		$this->_espaceCss = $class;
-	}
-}
-
-class ul extends elementDocument() {
-}
-
-
-
-=== Base de données ===
-table page (id autoincrement primary key, nomSysteme, composantUrl, parent)
-  Racine : (?,'racine','',NULL)
-  NomSysteme -> dans propriétés ?
-  composantUrl -> dnas propriétés ?
-table propriétés(id auto pk, bool système, nom, valeur)
-  (?,true,dateCréation,?)
-  (?,true,dateModification,?)
-  (?,true(false?),publier,?)
-
-TODO : lors d'une requête, ne renvoyer que les éléments que l'utilisateur a
-le droit de voir. Filtrage après la requête (attention au LIMIT et OFFSET !) ?
-ou y a-t-il moyen d'exprimer ça directement dans la requête ?
-
-class DB extends Selectable {
-	function connect(){
-	}
-}
-
-class Selectable {
-	// TODO : comment faire un select sur la table page ?
-	// TODO : comment créer une nouvelle page ?
-	function select(qw champs, qw ordre, int limit, int offset) {
-		// Retourne un selectable, qui a une méthode run() (la méthode run renvoie la liste d'éléments. Chaque élément a une méthode set()).
-	}
-
-	function set(valeur, qw champs, qw ordre, int limit, int offset) {
-		// Appelle set(valeur) sur chaque élément de $this->select(champs, ordre, limit, offset)->run()
-	}
-
-	function set(valeur, uid) {
-		// Appelle set(valeur) sur la ligne de la base de données avec cet uid.
-		// Les <form> des pages ont des champ <input type="hidden" name"_uid_"> et un bouton submit.
-		// Lorsqu'on active ce submit, les lignes correspondant aux _uid_ dans la base de données reçoivent la bonne valeur.
-	}
-
-	function sql() {
-	}
-
-	function add_setter(condition, fonction_setter) {
-		// Si on fait un set qui remplit la condition, la fonction_setter est appellée au lieu de modifier directement la bdd.
-		// Condition peut être :
-		// true                       // toujours appellé (pour les permissions, les dates de modification etc.).
-		// type de page parente       // lorsqu'on récupère la liste des enfants d'une page de ce type.
-		// type de page, nom attribut // lorsqu'on récupère cet attribut d'une page de ce type.
-	}
-	
-	function add_getter(condition, fonction_getter) {
-		// Comme add_setter().
-	}
-}
diff --git a/cms2/code/bdd.php b/cms2/code/bdd.php
new file mode 100644
index 0000000..5728fbc
--- /dev/null
+++ b/cms2/code/bdd.php
@@ -0,0 +1,70 @@
+<?php
+
+// TODO : Comment pseudo-insérer des images dans la BDD ? Est-ce la classe de la page qui décide de les déplacer dans /data et de mettre juste le nom dans la BDD ?
+//        (Et c'est à ce moment-là que la miniature est faite). Ou bien, un "trigger" (get et set) pour faire la même chose ?
+
+// TODO : Pouvoir stocker des collections de propriétés avec le même nom... Par ex. une photo peut avoir une collection de personnes.
+
+/*
+ Base de données :
+ table page (uid autoincrement primary key, nomSysteme, composantUrl, parent)
+   Racine : (?,'racine','',NULL)
+ table propriétés(uid auto pk, fk uid_page, bool systeme, nom, valeur)
+   (?,true,dateCréation,?)
+   (?,true,dateModification,?)
+   (?,false,publier,?)
+   (?,true,nomSysteme,?)
+   (?,true,composantUrl,?)
+*/
+
+// Lors d'une requête, ne renvoyer que les éléments que l'utilisateur a
+// le droit de voir. Filtrage après la requête (attention au LIMIT et OFFSET !) ?
+// ou y a-t-il moyen d'exprimer ça directement dans la requête ?
+
+class DB extends Selectable {
+	private static $handle = null;
+	public function __construct() {
+		if (self::$handle === null) {
+			niy("Connexion à la BDD");
+		} else {
+			return self::$handle;
+		}
+	}
+}
+
+class Selectable {
+	// TODO : comment faire un select sur la table page ?
+	// TODO : comment créer une nouvelle page ?
+	function select($qw_champs, $qw_ordre = "", $limit = 0, $offset = 0) {
+		// Retourne un selectable, qui a une méthode run() (la méthode run renvoie la liste d'éléments. Chaque élément a une méthode set()).
+		// Une limit de 0 signifie qu'on prend tous les éléments à partir de $offset.
+	}
+
+	function set($valeur, $qw_champs) {
+		// Appelle set(valeur) sur chaque élément de $this->select(champs, ordre, 0, 0)->run()
+	}
+
+	function set_with_uid($valeur, int $uid) {
+		// Appelle set(valeur) sur la ligne de la base de données avec cet uid.
+		// Les <form> des pages ont des champ <input type="hidden" name"_uid_"> et un bouton submit.
+		// Lorsqu'on active ce submit, les lignes correspondant aux _uid_ dans la base de données reçoivent la bonne valeur.
+	}
+
+	function sql() {
+	}
+
+	function add_setter($condition, $fonction_setter) {
+		// Si on fait un set qui remplit la condition, la fonction_setter est appellée au lieu de modifier directement la bdd.
+		// Condition peut être :
+		// true                       // toujours appellé (pour les permissions, les dates de modification etc.).
+		// type de page parente       // lorsqu'on récupère la liste des enfants d'une page de ce type.
+		// type de page, nom attribut // lorsqu'on récupère cet attribut d'une page de ce type.
+	}
+	
+	function add_getter($condition, $fonction_getter) {
+		// Comme add_setter().
+	}
+}
+
+
+?>
\ No newline at end of file
diff --git a/cms2/code/configuration.php b/cms2/code/configuration.php
new file mode 100644
index 0000000..dda92f0
--- /dev/null
+++ b/cms2/code/configuration.php
@@ -0,0 +1,30 @@
+<?php
+
+  /***************************************
+   * La configuration n'est pas ici,     *
+   * elle est dans le fichier config.php *
+   ***************************************/
+
+class Config {
+	private static $config = array();
+	
+	public static function get($nom) {
+		if (!isset(self::$config[$nom])) return null;
+		return self::$config[$nom];
+	}
+	
+	public static function set($nom, $valeur) {
+		self::$config[$nom] = $valeur;
+	}
+}
+
+require_once(dirname(__FILE__) . "/util.php"); // class Path.
+require_once(dirname(__FILE__) . "/../config.php");
+
+if (Config::get('courriel_admin') === null) {
+	echo "Vous devez indiquer le courriel de l'administrateur dans le fichier config.php.";
+	exit;
+}
+
+
+?>
\ No newline at end of file
diff --git a/cms2/code/debug.php b/cms2/code/debug.php
new file mode 100644
index 0000000..31b5a8b
--- /dev/null
+++ b/cms2/code/debug.php
@@ -0,0 +1,7 @@
+<?php
+
+function niy($name) {
+	echo "Not implemented yet : $name";
+}
+
+?>
\ No newline at end of file
diff --git a/cms2/code/document.php b/cms2/code/document.php
new file mode 100644
index 0000000..9aa170c
--- /dev/null
+++ b/cms2/code/document.php
@@ -0,0 +1,105 @@
+<?php
+
+// Chaque type d'élément est une sous-classe de ElementDocument, et impléménte uniquement les méthodes de création qui respectent les règles d'imbrication des éléments.
+// Pour les éléments dont les enfants possibles dépendent du parent (par ex. <a>), on restreindra les enfants et (parents) possibles à quelque chose de sensé.
+// Plutôt que d'avoir plein de sous-classes, ElementDocument a une méthode __call(), qui vérifie ce qu'on peut appeller en fonction du type de l'élément.
+
+// Propriété "url" pour le document ? Probablement innutile, pusiqu'on bosse principalement avec des uid (le href d'un <a> est un uid).
+
+class ElementDocument {
+	public $espaceCss = null;
+	private static $enfantsÉléments = array();
+	private static $attributsÉléments = array();
+	private $type = null;
+	private $enfants = array();
+	
+	public static function ajouter_type_élément($type, $typesEnfants, $attributs = "") {
+		self::$enfantsÉléments[$type] = qw($typesEnfants);
+		self::$attributsÉléments[$type] = qw($attributs);
+	}
+
+	public static function ajouter_widget($nom, $callback) {
+		niy("ajouter_widget");
+	}
+
+	public function inclure($elem) {
+		// Tente de fusionner $elem avec $this
+		niy("inclure");
+	}
+
+	public function to_XHTML_5() {
+		niy("to_XHTML_5");
+	}
+
+	public function to_HTML_5() {
+		niy("to_HTML_5");
+	}
+
+	public function to_HTML_4_01() {
+		niy("to_HTML_4_01");
+	}
+
+	public function to_XHTML_1_1() {
+		niy("to_XHTML_1_1");
+	}
+
+	public function __construct($type = "document") {
+		$this->type = $type;
+	}
+
+	public function __call($fn, $args) {
+		if (array_key_exists($this->type, self::$enfantsÉléments)
+			&& in_array($fn, self::$enfantsÉléments[$this->type])) {
+			$elem = new self($fn);
+			$this->enfants[] = $elem;
+			return $elem;
+		} else {
+			return null;
+		}
+	}
+}
+
+ElementDocument::ajouter_type_élément("document", "header footer nav article script style");
+ElementDocument::ajouter_type_élément("header", "title");
+ElementDocument::ajouter_type_élément("footer", "");
+ElementDocument::ajouter_type_élément("nav", "ul");
+ElementDocument::ajouter_type_élément("article", "ul p form");
+ElementDocument::ajouter_type_élément("script", "", "src");
+ElementDocument::ajouter_type_élément("style", "", "src");
+ElementDocument::ajouter_type_élément("ul", "li");
+ElementDocument::ajouter_type_élément("li", "text a strong em");
+ElementDocument::ajouter_type_élément("form", "input_text_line input_text_multi input_text_rich input_file");
+ElementDocument::ajouter_type_élément("a", "text strong em", "href");
+// ElementDocument::ajouter_type_élément("", "");
+
+//ElementDocument::ajouter_widget("titre", function($select){}); // renvoie un <h2> ou un <input> selon les droits
+//ElementDocument::ajouter_widget("richText", function($select){}); // similaire
+//ElementDocument::ajouter_widget("field", function($select){}); // ...
+// Peut-être que _field peut détecter automatiquement s'il faut traiter un champ de la BDD
+// (par ex. pour le richText) en fonction d'une info "type" dans la classe correspondant à la page de ce champ ?
+//ElementDocument::ajouter_widget("liste", function($select, $function_formattage_elements));
+
+/* Widgets :
+	function en_tete() {
+		$d = new Document();
+		$d->append->titre($this->select("titre"));
+		$d->append->description($this->select("description"));
+		return $d;
+	}
+	
+	function liste($elts, $format) {
+		$d = new Document();
+		$ul = $d->append->ul();
+		foreach ($elts as $k => $e) {
+			$ul->append($format($e));
+		}
+		return $d;
+	}
+	
+	function bouton($texte, $page_callback, $action_callback) {
+		// afficher un input
+		// lors du clic, appeller $action_callback sur $page_callback ?
+	}
+*/
+
+?>
\ No newline at end of file
diff --git a/cms2/code/include.php b/cms2/code/include.php
new file mode 100644
index 0000000..3ac5938
--- /dev/null
+++ b/cms2/code/include.php
@@ -0,0 +1,12 @@
+<?php
+
+require_once(dirname(__FILE__) . "/debug.php");
+require_once(dirname(__FILE__) . "/util.php");
+require_once(dirname(__FILE__) . "/page.php");
+require_once(dirname(__FILE__) . "/bdd.php");
+require_once(dirname(__FILE__) . "/document.php");
+require_once(dirname(__FILE__) . "/main.php");
+
+require_once(dirname(__FILE__) . "/../modules/include.php");
+
+?>
\ No newline at end of file
diff --git a/cms2/code/index.php b/cms2/code/index.php
new file mode 100644
index 0000000..04c0305
--- /dev/null
+++ b/cms2/code/index.php
@@ -0,0 +1,13 @@
+<?php
+
+error_reporting(E_ALL | E_STRICT);
+// display_errors ne s'appliquera pas au fichier courant,
+// alors gardons-le aussi court que possible !
+ini_set("display_errors", 1);
+
+require_once(dirname(__FILE__) . "/configuration.php");
+require_once(dirname(__FILE__) . "/include.php");
+
+main();
+
+?>
\ No newline at end of file
diff --git a/cms2/code/main.php b/cms2/code/main.php
new file mode 100644
index 0000000..45ad4e6
--- /dev/null
+++ b/cms2/code/main.php
@@ -0,0 +1,10 @@
+<?php
+
+function main() {
+	echo "<pre>";
+	$g = new GalerieIndex();
+	var_dump($g);
+	echo "</pre>";
+}
+
+?>
\ No newline at end of file
diff --git a/cms2/code/page.php b/cms2/code/page.php
new file mode 100644
index 0000000..9e89ceb
--- /dev/null
+++ b/cms2/code/page.php
@@ -0,0 +1,101 @@
+<?php
+
+// TODO : gestion du renomage (pseudo-réécriture d'URL).
+
+class Page {
+	// Convention de nommage :
+	// res_h_xxx = html, res_i_xxx = image, res_c_xxx = css, res_j_xxx = javascript
+	protected static $ressources_statiques = array();
+	protected static $ressources_dynamiques = array();
+	protected static $attributs = array(
+		"date_creation" => 0,
+		"date_modification" => 0,
+		"publier" => false,
+		"nom_systeme" => "",
+		"composant_url" => "page",
+	);
+	protected static $enfants = true; // Type des enfants. True pour tout autoriser.
+
+	public static function ajouter_type($type) {
+		niy("Page::ajouter_type($type);");
+		// Insérer la ressource "res_c_style" dans le CSS principal
+	}
+	
+	private $parent = null;
+	public function parent() {
+		return $this->parent;
+	}
+	
+	public function rendu() {
+		// Renvoie un document (classe ElementDocument).
+		niy("rendu");
+	}
+	
+	public function url() {
+		// Renvoie toute l'url
+		niy("url");
+	}
+	
+	public function composant_url() {
+		// renvoie juste la fin de l'url (le composant de l'url qui désigne cette page).
+		niy("composant_url");
+	}
+
+	private $uid = -1;
+	public function uid() {
+		// Renvoie l'uid de la page dans la base de données.
+		niy("uid");
+	}
+	
+	public function select($requête) {
+		// Renvoie un objet de la classe CollectionPages.
+		niy("select");
+	}
+	
+	public function __get($nom) {
+		// s'il y a un getter (trigger), on l'appelle, sinon on appelle get_prop_direct();
+		// le getter fait ce qu'il veut, puis appelle set_prop_direct();
+		niy("get $name");
+	}
+
+	private function get_prop_direct($nom) {
+		// Récupère l'attribut "$nom" depuis la BDD.
+		niy("get direct $name");
+	}
+	
+	public function __set($nom, $val) {
+		// s'il y a un setter (trigger), on l'appelle, sinon on appelle set_prop_direct();
+		// le setter fait ce qu'il veut, puis appelle set_prop_direct();
+		niy("set $name = $val");
+	}
+	
+	public function set_prop_direct($nom, $val) {
+		// Modifie l'attribut "$nom" dans la BDD.
+		niy("set direct $name = $val");
+	}
+}
+
+class CollectionPages {
+	function __construct() {
+		niy("CollectionPages");
+	}
+	
+	function __call($fn, $args) {
+		// appelle la fonction sur tous les objets, et renvoie les résultats
+		// dans un tableau. si les résultats sont des CollectionPages, ils
+		// sont concaténés dans la même collection.
+		niy("CollectionPages");
+	}
+	
+	function __get($name) {
+		// renvoie un tableau contenant le champ pour chaque objet.
+		niy("CollectionPages");
+	}
+	
+	function __set($name, $val) {
+		// affecte la valeur sur chaque objet.
+		niy("CollectionPages");
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/cms2/code/stockage_fichiers.php b/cms2/code/stockage_fichiers.php
new file mode 100644
index 0000000..80687c0
--- /dev/null
+++ b/cms2/code/stockage_fichiers.php
@@ -0,0 +1,14 @@
+<?php
+
+class StockageFichiers {
+	public static function stocker_fichier($fichier, $uid) {
+		// Stocker $fichier avec le nom $uid dans Config::get('chemin_base_stockage')
+		niy("stocker fichier");
+	}
+	public static function stocker_upload($fichier, $id) {
+		// Stocker $fichier avec le nom $uid dans Config::get('chemin_base_stockage')
+		// Utiliser move_uploaded_file().
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/cms2/code/util.php b/cms2/code/util.php
new file mode 100644
index 0000000..ed8a142
--- /dev/null
+++ b/cms2/code/util.php
@@ -0,0 +1,129 @@
+<?php
+
+function qw($arg, $sep = " ") {
+	if (is_array($arg)) return $arg;
+	$ret = array();
+	foreach(explode($sep, $arg) as $v) {
+		if ($v !== "") array_push($ret, $v);
+	}
+	return $ret;
+}
+
+/**** Début PATH ****/
+
+// http://www.liranuna.com/php-path-resolution-class-relative-paths-made-easy/
+// Licence : WTFPL
+/**
+ * @class Path
+ *
+ * @brief Utility class that handles file and directory pathes
+ *
+ * This class handles basic important operations done to file system paths.
+ * It safely renders relative pathes and removes all ambiguity from a relative path.
+ *
+ * @author Liran Nuna
+ */
+final class Path
+{
+	/**
+	 * Returns the parent path of this path.
+	 * "/path/to/directory" will return "/path/to"
+	 *
+	 * @arg $path	The path to retrieve the parent path from
+	 */
+	public static function dirname($path) {
+		return dirname(self::normalize($path));
+	}
+ 
+	/**
+	 * Returns the last item on the path.
+	 * "/path/to/directory" will return "directory"
+	 *
+	 * @arg $path	The path to retrieve the base from
+	 */
+	public static function basename($path) {
+		return basename(self::normalize($path));
+	}
+ 
+	/**
+	 * Normalizes the path for safe usage
+	 * This function does several operations to the given path:
+	 *   * Removes unnecessary slashes (///path//to/////directory////)
+	 *   * Removes current directory references (/path/././to/./directory/./././)
+	 *   * Renders relative pathes (/path/from/../to/somewhere/in/../../directory)
+	 *
+	 * @arg $path	The path to normalize
+	 */
+	public static function normalize($path) {
+		return array_reduce(explode('/', $path), create_function('$a, $b', '
+			if($a === 0)
+				$a = "/";
+ 
+			if($b === "" || $b === ".")
+				return $a;
+ 
+			if($b === "..")
+				return dirname($a);
+ 
+			return preg_replace("/\/+/", "/", "$a/$b");
+		'), 0);
+	}
+	
+	// Ajout par js jahvascriptmaniac+github@gmail.com
+	public static function realpath($path) {
+		return self::normalize(realpath($path));
+	}
+ 
+	/**
+	 * Combines a list of pathes to one safe path
+	 *
+	 * @arg $root	The path or array with values to combine into a single path
+	 * @arg ...		Relative pathes to root or arrays
+	 *
+	 * @note		This function works with multi-dimentional arrays recursively.
+	 */
+	public static function combine($root, $rel1) {
+		$arguments = func_get_args();
+		return self::normalize(array_reduce($arguments, create_function('$a,$b', '
+			if(is_array($a))
+				$a = array_reduce($a, "Path::combine");
+			if(is_array($b))
+				$b = array_reduce($b, "Path::combine");
+ 
+			return "$a/$b";
+		')));
+	}
+
+	// Ajout par js jahvascriptmaniac+github@gmail.com
+	// Depuis le dossier $a, construire un chemin relatif vers $b.
+	public static function relative($a, $b) {
+		$a = explode('/', self::normalize($a));
+		$b = explode('/', self::normalize($b));
+
+		// Zapper la partie commune
+		for ($i = 0; $i < count($a) && $i < count($b); $i++) {
+			if (! ($a[$i] == $b[$i])) break;
+		}
+		
+		$rel = ".";
+		for ($j = $i; $j < count($a); $j++) {
+			$rel .= "/..";
+		}
+		for ($j = $i; $j < count($b); $j++) {
+			$rel .= '/' . $b[$j];
+		}
+		
+		return $rel;
+	}
+	
+	/**
+	 * Empty, private constructor, to prevent instantiation
+	 */
+	private function __construct() {
+		// Prevents instantiation
+	}
+}
+
+/**** Fin PATH ****/
+
+?>
\ No newline at end of file
diff --git a/cms2/config.php b/cms2/config.php
new file mode 100644
index 0000000..932ca68
--- /dev/null
+++ b/cms2/config.php
@@ -0,0 +1,27 @@
+<?php
+
+// ========== CONFIGURATION =========
+
+// Addresse de courriel de l'administrateur.
+Config::set('courriel_admin', "jahvascriptmaniac+github@free.fr");
+
+// URL de la racine du site. Ex: http://www.monsite.com/cms/
+// Doit se terminer par '/'.
+Config::set('url_base', "http://127.0.0.1/2010-moteur-site-simple/");
+
+// Chemin absolu vers le dossier '__cms__'.
+// dirname(__FILE__) peut retourner un chemin relatif (PHP < 4.0.2),
+// donc utiliser realpath si on s'en sert.
+Config::set('chemin_base', Path::realpath(dirname(__FILE__)));
+
+// Chemin vers le stockage interne des données.
+// En général, c'est le chemin ..../__cms__/modele
+Config::set('chemin_base_stockage', Path::combine(Config::get('chemin_base'), "/../__donnees__"));
+
+// Chemin vers la partie visible du site.
+// En général, c'est le chemin vers le dossier contenant __cms__
+Config::set('chemin_base_public', Path::combine(Config::get('chemin_base'), "/.."));
+
+// ======== FIN CONFIGURATION =======
+
+?>
\ No newline at end of file
diff --git a/cms2/index.php b/cms2/index.php
new file mode 100644
index 0000000..9d4443f
--- /dev/null
+++ b/cms2/index.php
@@ -0,0 +1,5 @@
+<?php
+
+require_once(dirname(__FILE__) . "/code/index.php");
+
+?>
\ No newline at end of file
diff --git a/cms2/modules/galerie/galerie-index.php b/cms2/modules/galerie/galerie-index.php
new file mode 100644
index 0000000..515bfea
--- /dev/null
+++ b/cms2/modules/galerie/galerie-index.php
@@ -0,0 +1,41 @@
+<?php
+
+class GalerieIndex extends Page {
+	protected static $ressources_statiques = array("i_icône_nouvelle_période image/jpeg", "c_style text/css");
+	protected static $ressources_dynamiques = array("h_page Document", "h_miniature Document", "h_mini_miniature Document");
+	protected static $attributs = array(
+		"titre" => "Galerie",
+		"description" => ""
+	);
+	protected static $enfants = "GalerieÉvènement";
+	
+	public function res_i_icône_nouvelle_période() {
+		niy("res_i_icône_nouvelle_période");
+	}
+	
+	public function res_c_style() {
+		niy("res_c_style");
+	}
+	
+	public function res_h_page() {
+		$d = new Document();
+		$d->heading->standard();
+		$l = $d->article(1)->append->liste(select(/*todo*/), function($e) {/*todo*/});
+		// todo $l->append->...
+	}
+	
+	public function res_h_miniature() {
+		return $this->res_h_miniature_image();
+		// todo : ajouter le titre etc.;
+	}
+	
+	public function res_h_miniature_image() {
+		// Prendre le 1er par ordre décroissant sur la date, ou bien :
+		// TODO : prendre l'élément ayant la propriété "aperçu" à true (s'il y en a un, sinon date).
+		return $this->select("./*", "date desc", 1)->mini_miniature;
+	}
+}
+
+Page::ajouter_type("GalerieIndex");
+
+?>
\ No newline at end of file
diff --git a/cms2/modules/galerie/include.php b/cms2/modules/galerie/include.php
new file mode 100644
index 0000000..30eec1f
--- /dev/null
+++ b/cms2/modules/galerie/include.php
@@ -0,0 +1,5 @@
+<?php
+
+require_once(dirname(__FILE__) . "/galerie-index.php");
+
+?>
\ No newline at end of file
diff --git a/cms2/modules/include.php b/cms2/modules/include.php
new file mode 100644
index 0000000..66d1103
--- /dev/null
+++ b/cms2/modules/include.php
@@ -0,0 +1,9 @@
+<?php
+
+// Dépendance commune à tous les modules :
+require_once(dirname(__FILE__) . "/../code/page.php");
+
+require_once(dirname(__FILE__) . "/galerie/include.php");
+require_once(dirname(__FILE__) . "/squelette/include.php");
+
+?>
\ No newline at end of file
diff --git a/cms2/modules/squelette/include.php b/cms2/modules/squelette/include.php
new file mode 100644
index 0000000..d97fe7d
--- /dev/null
+++ b/cms2/modules/squelette/include.php
@@ -0,0 +1,5 @@
+<?php
+
+require_once(dirname(__FILE__) . "/squelette.php");
+
+?>
\ No newline at end of file
diff --git a/cms2/modules/squelette/squelette.php b/cms2/modules/squelette/squelette.php
new file mode 100644
index 0000000..4d978af
--- /dev/null
+++ b/cms2/modules/squelette/squelette.php
@@ -0,0 +1,13 @@
+<?php
+
+class mSquelette extends Page {
+	// Trouver un moyen pour que mSquelette soit appellé après avoir généré la page, pour qu'il puisse l'emballer.
+	
+	protected static $ressources_dynamiques = array("c_css_principal text/css");
+	public function res_c_css_principal() {
+		// mettre bout à bout tous les CSS ?
+		niy("res_c_css_principal");
+	}
+}
+
+?>
\ No newline at end of file