Brouillon de l'implémentation de la nouvelle structure.

This commit is contained in:
Georges Dupéron 2010-10-03 23:18:46 +02:00
parent ca40793f22
commit 9a7add7181
18 changed files with 596 additions and 203 deletions

View File

@ -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().
}
}

70
cms2/code/bdd.php Normal file
View File

@ -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().
}
}
?>

View File

@ -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;
}
?>

7
cms2/code/debug.php Normal file
View File

@ -0,0 +1,7 @@
<?php
function niy($name) {
echo "Not implemented yet : $name";
}
?>

105
cms2/code/document.php Normal file
View File

@ -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 ?
}
*/
?>

12
cms2/code/include.php Normal file
View File

@ -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");
?>

13
cms2/code/index.php Normal file
View File

@ -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();
?>

10
cms2/code/main.php Normal file
View File

@ -0,0 +1,10 @@
<?php
function main() {
echo "<pre>";
$g = new GalerieIndex();
var_dump($g);
echo "</pre>";
}
?>

101
cms2/code/page.php Normal file
View File

@ -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");
}
}
?>

View File

@ -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().
}
}
?>

129
cms2/code/util.php Normal file
View File

@ -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 ****/
?>

27
cms2/config.php Normal file
View File

@ -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 =======
?>

5
cms2/index.php Normal file
View File

@ -0,0 +1,5 @@
<?php
require_once(dirname(__FILE__) . "/code/index.php");
?>

View File

@ -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");
?>

View File

@ -0,0 +1,5 @@
<?php
require_once(dirname(__FILE__) . "/galerie-index.php");
?>

9
cms2/modules/include.php Normal file
View File

@ -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");
?>

View File

@ -0,0 +1,5 @@
<?php
require_once(dirname(__FILE__) . "/squelette.php");
?>

View File

@ -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");
}
}
?>