[PLUGINS] ~maj globale
[lhc/web/www.git] / www / plugins / compositions_v3 / compositions_fonctions.php
index 415e17a..82a20a1 100644 (file)
-<?php\r
-/**\r
- * Plugin Compositions\r
- * (c) 2007-2013 Cedric Morin\r
- * Distribue sous licence GPL\r
- *\r
- * @package SPIP\Compositions\Fonctions\r
- */\r
-\r
-if (!defined("_ECRIRE_INC_VERSION")) return;\r
-\r
-define('_COMPOSITIONS_MATCH','-([^0-9][^.]*)');\r
-$GLOBALS['marqueur_skel'] = (isset($GLOBALS['marqueur_skel'])?$GLOBALS['marqueur_skel']:'').':composition';\r
-\r
-/**\r
- * Lister les objets actives par configuration\r
- *\r
- * @return array\r
- */\r
-function compositions_objets_actives(){\r
-       static $config=null;\r
-       if (is_null($config)){\r
-               // lister les objets dont on a active la composition dans la configuration\r
-               $config = isset($GLOBALS['meta']['compositions']) ? unserialize($GLOBALS['meta']['compositions']) : array();\r
-               $config = (isset($config['objets'])?$config['objets']:array('spip_articles','spip_rubriques'));\r
-               $config = array_map('objet_type',$config);\r
-       }\r
-       return $config;\r
-}\r
-\r
-/**\r
- * Retrouver le nom du dossier ou sont stockees les compositions\r
- * reglage par defaut, ou valeur personalisee via cfg\r
- *\r
- * @return string\r
- */\r
-function compositions_chemin(){\r
-       $config_chemin = 'compositions/';\r
-       if (defined('_DIR_PLUGIN_Z') OR defined('_DIR_PLUGIN_ZCORE'))\r
-               $config_chemin = (isset($GLOBALS['z_blocs'])?reset($GLOBALS['z_blocs']):'contenu').'/';\r
-\r
-       elseif (isset($GLOBALS['meta']['compositions'])){\r
-               $config = unserialize($GLOBALS['meta']['compositions']);\r
-               if (isset ($config['chemin_compositions'])){\r
-                       $config_chemin = rtrim($config['chemin_compositions'],'/').'/';\r
-               }\r
-       }\r
-\r
-       return $config_chemin;\r
-}\r
-\r
-/**\r
- * Tester si la stylisation auto est activee\r
- * @return string\r
- */\r
-function compositions_styliser_auto(){\r
-       $config_styliser = true;\r
-       if (defined('_DIR_PLUGIN_Z') OR defined('_DIR_PLUGIN_ZCORE')){\r
-               $config_styliser = false; // Z s'occupe de styliser les compositions\r
-       }\r
-       elseif (isset($GLOBALS['meta']['compositions'])){\r
-               $config = unserialize($GLOBALS['meta']['compositions']);\r
-               $config_styliser = $config['styliser_auto'] != 'non';\r
-       }\r
-       return $config_styliser?' ':'';\r
-}\r
-\r
-/**\r
- * Lister les compositions disponibles : toutes ou pour un type donne\r
- * Si informer est a false, on ne charge pas les infos du xml\r
- *\r
- * @param string $type\r
- * @param bool $informer\r
- * @return array\r
- */\r
-function compositions_lister_disponibles($type, $informer=true){\r
-       include_spip('inc/compositions');\r
-       $type_match = "";\r
-       if (strlen($type)){\r
-               $type = objet_type($type); // securite\r
-               $type_match = $type;\r
-       }\r
-       else {\r
-               $type_match = "[a-z0-9]+";\r
-       }\r
-\r
-       // rechercher les skel du type article-truc.html\r
-       // truc ne doit pas commencer par un chiffre pour eviter de confondre avec article-12.html\r
-       $match = "($type_match)("._COMPOSITIONS_MATCH.")?[.]html$";\r
-\r
-       // lister les compositions disponibles\r
-       $liste = find_all_in_path(compositions_chemin(),$match);\r
-       $res = array();\r
-       if (count($liste)){\r
-               foreach($liste as $s) {\r
-                       $base = preg_replace(',[.]html$,i','',$s);\r
-                       if (preg_match(",$match,ims",basename($s),$regs)\r
-                         AND ($composition = !$informer\r
-                               OR $composition = compositions_charger_infos($base)))\r
-                               $res[$regs[1]][$regs[3]] = $composition;\r
-                       // retenir les skels qui ont un xml associe\r
-               }\r
-       }\r
-       // Pipeline compositions_lister_disponibles\r
-       $res = pipeline('compositions_lister_disponibles',array(\r
-               'args'=>array('type' => $type,'informer' => $informer),\r
-               'data'=> $res\r
-               )\r
-       );\r
-       return $res;\r
-}\r
-\r
-/**\r
- * Liste les id d'un type donne utilisant une composition donnee\r
- *\r
- * @param string $type\r
- * @param string $composition\r
- * @return array\r
- */\r
-function compositions_lister_utilisations($type,$composition){\r
-       $table_sql = table_objet_sql($type);\r
-       if (!in_array($table_sql, sql_alltable())) return;\r
-       $_id_table_objet = id_table_objet($type);\r
-       return sql_allfetsel("$_id_table_objet as id,titre", $table_sql, "composition=".sql_quote($composition));\r
-}\r
-\r
-/**\r
- * Selectionner le fond en fonction du type et de la composition\r
- * en prenant en compte la configuration pour le chemin\r
- * et le fait que la composition a pu etre supprimee\r
- *\r
- * @param string $composition\r
- * @param string $type\r
- * @param string $defaut\r
- * @param string $ext\r
- * @param bool $fullpath\r
- * @param string $vide\r
- * @return string\r
- */\r
-function compositions_selectionner($composition,$type,$defaut="",$ext="html",$fullpath = false, $vide="composition-vide"){\r
-       if ($type=='syndic') $type='site'; //grml\r
-       $fond = compositions_chemin() . $type;\r
-\r
-       // regarder si compositions/article-xxx est disponible\r
-       if (strlen($composition)\r
-               AND $f = find_in_path("$fond-$composition.$ext"))\r
-               return $fullpath ? $f : $fond . "-$composition";\r
-       else\r
-               // sinon regarder si compositions/article-defaut est disponible\r
-               if (strlen($defaut)\r
-                       AND $f = find_in_path("$fond-$defaut.$ext"))\r
-                       return $fullpath ? $f : $fond . "-$defaut";\r
-\r
-       // se rabattre sur compositions/article si disponible\r
-       if ($f = find_in_path("$fond.$ext"))\r
-               return $fullpath ? $f : $fond;\r
-\r
-       // sinon une composition vide pour ne pas generer d'erreur\r
-       if ($vide AND $f = find_in_path("$vide.$ext"))\r
-               return $fullpath ? $f : $vide;\r
-\r
-       // rien mais ca fera une erreur dans le squelette si appele en filtre\r
-       return '';\r
-}\r
-\r
-/**\r
- * Decrire une composition pour un objet\r
- * @param string $type\r
- * @param string $composition\r
- * @return array|bool|string\r
- */\r
-function compositions_decrire($type, $composition){\r
-       static $compositions = array();\r
-       if (!function_exists('compositions_charger_infos'))\r
-               include_spip('inc/compositions');\r
-       if ($type=='syndic') $type='site'; //grml\r
-       if (isset($compositions[$type][$composition]))\r
-               return $compositions[$type][$composition];\r
-       $ext = "html";\r
-       $fond = compositions_chemin() . $type;\r
-       if (strlen($composition)\r
-               AND $f = find_in_path("$fond-$composition.$ext")\r
-               AND $desc = compositions_charger_infos($f))\r
-               return $compositions[$type][$composition] = $desc;\r
-       return $compositions[$type][$composition] = false;\r
-}\r
-\r
-/**\r
- * Un filtre a utiliser sur [(#COMPOSITION|composition_class{#ENV{type}})]\r
- * pour poser des classes generiques sur le <body>\r
- * si une balise <class>toto</class> est definie dans la composition c'est elle qui est appliquee\r
- * sinon on pose simplement le nom de la composition\r
- *\r
- * @param string $composition\r
- * @param string $type\r
- * @return string\r
- */\r
-function composition_class($composition,$type){\r
-       if ($desc = compositions_decrire($type, $composition)\r
-               AND isset($desc['class'])\r
-               AND strlen($desc['class']))\r
-               return $desc['class'];\r
-       return $composition;\r
-}\r
-\r
-/**\r
- * Liste les types d'objets qui ont une composition ET sont autorises par la configuration\r
- * utilise la valeur en cache meta sauf si demande de recalcul\r
- * ou pas encore definie\r
- *\r
- * @staticvar array $liste\r
- * @return array\r
- */\r
-function compositions_types(){\r
-       static $liste = null;\r
-       if (is_null($liste)) {\r
-               if (_VAR_MODE OR !isset($GLOBALS['meta']['compositions_types'])){\r
-                       include_spip('inc/compositions');\r
-                       compositions_cacher();\r
-               }\r
-               $liste = explode(',',$GLOBALS['meta']['compositions_types']);\r
-       }\r
-       return $liste;\r
-}\r
-\r
-/**\r
- * Renvoie les parametres necessaires pour utiliser l'heritage de composition de façon generique\r
- * recupere les donnes du pipeline compositions_declarer_heritage.\r
- * Si $type n'est pas precise, on renvoie simplement le tableau des objets pouvant heriter.\r
- *\r
- * @param string $type\r
- * @staticvar array $heritages\r
- * @return array\r
- */\r
-function compositions_recuperer_heritage($type=NULL){\r
-       static $heritages = NULL;\r
-       if (is_null($heritages)) // recuperer les heritages declares via le pipeline compositions_declarer_heritage\r
-               $heritages = pipeline('compositions_declarer_heritage', array());\r
-\r
-       if (is_null($type))\r
-               return $heritages;\r
-\r
-       if (array_key_exists($type, $heritages)) {\r
-               $type_parent = $heritages[$type];\r
-               $table_parent =  table_objet_sql($type_parent);\r
-               $nom_id_parent = ($type==$type_parent) ? 'id_parent' : id_table_objet($type_parent); // Recursivite pour les rubriques, nom de l'identifiant du parent dans la table enfant\r
-               $nom_id_table_parent = id_table_objet($type_parent); // Nom de l'identifiant du parent dans la table parent\r
-\r
-               // verifier que table et champs existent...\r
-               $trouver_table = charger_fonction('trouver_table', 'base');\r
-               if (!$type_parent\r
-                       OR !$desc = $trouver_table($table_parent)\r
-                       OR !isset($desc['field']['composition'])\r
-                       OR !isset($desc['field'][$nom_id_parent]))\r
-                       return '';\r
-\r
-               return array(\r
-                       'type_parent' => $type_parent,\r
-                       'table_parent' => $table_parent,\r
-                       'nom_id_parent' => $nom_id_parent,\r
-                       'nom_id_table_parent' => $nom_id_table_parent\r
-               );\r
-       }\r
-       return array();\r
-}\r
-\r
-/**\r
- * Renvoie la composition qui s'applique a un objet\r
- * en tenant compte, le cas echeant, de la composition heritee\r
- * si etoile=true on renvoi directment le champ sql\r
- *\r
- * @param string $type\r
- * @param integer $id\r
- * @param string $serveur\r
- * @param bool $etoile\r
- * @return string\r
- */\r
-function compositions_determiner($type, $id, $serveur='', $etoile = false){\r
-       static $composition = array();\r
-       $id = intval($id);\r
-\r
-       if (isset($composition[$etoile][$serveur][$type][$id]))\r
-               return $composition[$etoile][$serveur][$type][$id];\r
-\r
-       include_spip('base/abstract_sql');\r
-       $table = table_objet($type);\r
-       $table_sql = table_objet_sql($type);\r
-       $_id_table = id_table_objet($type);\r
-\r
-       $retour = '';\r
-\r
-       $trouver_table = charger_fonction('trouver_table', 'base');\r
-       $desc = $trouver_table($table,$serveur);\r
-       if (isset($desc['field']['composition']) AND $id){\r
-               $select = "composition";\r
-\r
-       $heritage = compositions_recuperer_heritage($type);\r
-       if (isset($desc['field'][$heritage['nom_id_parent']]))\r
-               $select .= ', '.$heritage['nom_id_parent'].' as id_parent';\r
-\r
-       $row = sql_fetsel($select, $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);\r
-       if ($row['composition'] != '')\r
-               $retour = $row['composition'];\r
-       elseif (!$etoile\r
-         AND isset($row['id_parent'])\r
-         AND $row['id_parent'])\r
-               $retour = compositions_heriter($type, $id, $row['id_parent'], $serveur);\r
-       }\r
-       return $composition[$etoile][$serveur][$type][$id] = (($retour == '-') ? '' : $retour);\r
-}\r
-\r
-/**\r
- * Renvoie la composition heritee par un objet selon son identifiant.\r
- * Optionnellement, on peut lui transmettre directement l'id du parent s'il a ate calcule.\r
- *\r
- * @param string $type\r
- * @param integer $id\r
- * @param integer $id_parent\r
- * @param string $serveur\r
- * @return string\r
- */\r
-function compositions_heriter($type, $id, $id_parent=NULL, $serveur=''){\r
-       if ($type=='syndic') $type='site'; //grml\r
-       if (intval($id) < 1) return '';\r
-       static $infos = null;\r
-       $compo_parent = '';\r
-\r
-       $heritage = compositions_recuperer_heritage($type);\r
-\r
-       /* Si aucun héritage n'a été défini pour le type d'objet, ce\r
-       * n'est pas la peine d'aller plus loin. */\r
-       if(count($heritage) == 0)\r
-               return '';\r
-\r
-       $type_parent = $heritage['type_parent'];\r
-       $table_parent = $heritage['table_parent'];\r
-       $nom_id_parent = $heritage['nom_id_parent'];\r
-       $nom_id_table_parent = $heritage['nom_id_table_parent'];\r
-\r
-       if (is_null($id_parent))\r
-               $id_parent = sql_getfetsel($nom_id_parent, table_objet_sql($type), id_table_objet($type).'='.intval($id));\r
-\r
-       $heritages = compositions_recuperer_heritage();\r
-\r
-       do {\r
-               $select = 'composition';\r
-               if ($heritages[$type_parent]==$type_parent) // S'il y a recursivite sur le parent\r
-                       $select .= ', id_parent';\r
-               $row = sql_fetsel($select, $table_parent, $nom_id_table_parent.'='.intval($id_parent),'','','','',$serveur);\r
-               if (strlen($row['composition']) AND $row['composition']!='-')\r
-                       $compo_parent = $row['composition'];\r
-               elseif (strlen($row['composition'])==0 AND isset($heritages[$type_parent])) // Si le parent peut heriter, il faut verifier s'il y a heritage\r
-                       $compo_parent = compositions_determiner($type_parent, $id_parent, $serveur='');\r
-\r
-               if (strlen($compo_parent) AND is_null($infos))\r
-                       $infos = compositions_lister_disponibles('');\r
-\r
-       }\r
-       while ($id_parent = $row['id_parent']\r
-               AND\r
-               (!strlen($compo_parent) OR !isset($infos[$type_parent][$compo_parent]['branche'][$type])));\r
-\r
-       if (strlen($compo_parent) AND isset($infos[$type_parent][$compo_parent]['branche'][$type]))\r
-               return $infos[$type_parent][$compo_parent]['branche'][$type];\r
-\r
-       return '';\r
-}\r
-\r
-/**\r
- * #COMPOSITION\r
- * Renvoie la composition s'appliquant a un objet\r
- * en tenant compte, le cas echeant, de l'heritage.\r
- *\r
- * Sans precision, l'objet et son identifiant sont pris\r
- * dans la boucle en cours, mais l'on peut specifier notre recherche\r
- * en passant objet et id_objet en argument de la balise :\r
- * #COMPOSITION{article, 8}\r
- *\r
- * #COMPOSITION* renvoie toujours le champs brut, sans tenir compte de l'heritage\r
- *\r
- * @param array $p     AST au niveau de la balise\r
- * @return array       AST->code modifie pour calculer le nom de la composition\r
- */\r
-function balise_COMPOSITION_dist($p) {\r
-       $_composition = "";\r
-       if ($_objet = interprete_argument_balise(1, $p)) {\r
-               $_id_objet = interprete_argument_balise(2, $p);\r
-       } else {\r
-               $_composition = champ_sql('composition',$p);\r
-               $_id_objet = champ_sql($p->boucles[$p->id_boucle]->primary, $p);\r
-               $_objet = "objet_type('" . $p->boucles[$p->id_boucle]->id_table . "')";\r
-       }\r
-       // si on veut le champ brut, et qu'on l'a sous la main, inutile d'invoquer toute la machinerie\r
-       if ($_composition AND $p->etoile)\r
-               $p->code = $_composition;\r
-       else {\r
-               $connect = $p->boucles[$p->id_boucle]->sql_serveur;\r
-               $p->code = "compositions_determiner($_objet, $_id_objet, '$connect', ".($p->etoile?'true':'false').")";\r
-               // ne declencher l'usine a gaz que si composition est vide ...\r
-               if ($_composition)\r
-                       $p->code = "((\$zc=$_composition)?(\$zc=='-'?'':\$zc):".$p->code.")";\r
-       }\r
-       return $p;\r
-}\r
-\r
-/**\r
- * Indique si la composition d'un objet est verrouillee ou non,\r
- * auquel cas, seul le webmaster peut la modifier\r
- *\r
- * @param string $type\r
- * @param integer $id\r
- * @param string $serveur\r
- * @return string\r
- */\r
-function compositions_verrouiller($type, $id, $serveur=''){\r
-       $config = unserialize($GLOBALS['meta']['compositions']);\r
-       if (isset($config['tout_verrouiller']) AND $config['tout_verrouiller'] == 'oui')\r
-               return true;\r
-\r
-       include_spip('base/abstract_sql');\r
-       $table = table_objet($type);\r
-       $table_sql = table_objet_sql($type);\r
-       $_id_table = id_table_objet($type);\r
-\r
-       $trouver_table = charger_fonction('trouver_table', 'base');\r
-       $desc = $trouver_table($table,$serveur);\r
-       if (isset($desc['field']['composition_lock']) AND $id){\r
-               $lock = sql_getfetsel('composition_lock', $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);\r
-               if ($lock)\r
-                       return true;\r
-               elseif (isset($desc['field']['id_rubrique'])) {\r
-                       $id_rubrique = sql_getfetsel('id_rubrique', $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);\r
-                       return compositions_verrou_branche($id_rubrique, $serveur);\r
-               }\r
-               else\r
-                       return false;\r
-       }\r
-       else return false;\r
-}\r
-\r
-/**\r
- * Indique si les objets d'une branche sont verrouilles\r
- * @param integer $id_rubrique\r
- * @param string $serveur\r
- * @return string\r
- */\r
-function compositions_verrou_branche($id_rubrique, $serveur=''){\r
-\r
-       if (intval($id_rubrique) < 1) return false;\r
-       if($infos_rubrique = sql_fetsel(array('id_parent','composition_branche_lock'),'spip_rubriques','id_rubrique='.intval($id_rubrique),'','','','',$serveur)) {\r
-               if ($infos_rubrique['composition_branche_lock'])\r
-                       return true;\r
-               else\r
-                       return compositions_verrou_branche($infos_rubrique['id_parent'],$serveur);\r
-       }\r
-       return '';\r
-}\r
-?>\r
+<?php
+/**
+ * Plugin Compositions
+ * (c) 2007-2013 Cedric Morin
+ * Distribue sous licence GPL
+ *
+ * @package SPIP\Compositions\Fonctions
+ */
+
+if (!defined('_ECRIRE_INC_VERSION')) {
+       return;
+}
+
+define('_COMPOSITIONS_MATCH', '-([^0-9][^.]*)');
+
+/**
+ * Lister les objets actives par configuration
+ *
+ * @return array
+ */
+function compositions_objets_actives() {
+       static $config=null;
+       if (is_null($config)) {
+               // lister les objets dont on a active la composition dans la configuration
+               $config = isset($GLOBALS['meta']['compositions']) ? unserialize($GLOBALS['meta']['compositions']) : array();
+               $config = (isset($config['objets'])?$config['objets']:array('spip_articles','spip_rubriques'));
+               $config = array_map('objet_type', $config);
+       }
+       return $config;
+}
+
+/**
+ * Retrouver le nom du dossier ou sont stockees les compositions
+ * reglage par defaut, ou valeur personalisee via cfg
+ *
+ * @return string
+ */
+function compositions_chemin() {
+       $config_chemin = 'compositions/';
+       if (defined('_DIR_PLUGIN_Z') or defined('_DIR_PLUGIN_ZCORE')) {
+               $config_chemin = (isset($GLOBALS['z_blocs'])?reset($GLOBALS['z_blocs']):'contenu').'/';
+       } elseif (isset($GLOBALS['meta']['compositions'])) {
+               $config = unserialize($GLOBALS['meta']['compositions']);
+               if (isset($config['chemin_compositions'])) {
+                       $config_chemin = rtrim($config['chemin_compositions'], '/').'/';
+               }
+       }
+
+       return $config_chemin;
+}
+
+/**
+ * Tester si la stylisation auto est activee
+ * @return string
+ */
+function compositions_styliser_auto() {
+       $config_styliser = true;
+       if (defined('_DIR_PLUGIN_Z') or defined('_DIR_PLUGIN_ZCORE')) {
+               $config_styliser = false; // Z s'occupe de styliser les compositions
+       } elseif (isset($GLOBALS['meta']['compositions'])) {
+               $config = unserialize($GLOBALS['meta']['compositions']);
+               $config_styliser = (!isset($config['styliser_auto']) or ($config['styliser_auto'] != 'non'));
+       }
+       return $config_styliser?' ':'';
+}
+
+/**
+ * Lister les compositions disponibles : toutes ou pour un type donne
+ * Si informer est a false, on ne charge pas les infos du xml
+ *
+ * @param string $type
+ * @param bool $informer
+ * @return array
+ */
+function compositions_lister_disponibles($type, $informer = true) {
+       include_spip('inc/compositions');
+       $type_match = '';
+       if (strlen($type)) {
+               $type = objet_type($type); // securite
+               $type_match = $type;
+       } else {
+               // _ pour le cas des groupe_mots
+               $type_match = '[a-z0-9_]+';
+       }
+
+       // rechercher les skel du type article-truc.html
+       // truc ne doit pas commencer par un chiffre pour eviter de confondre avec article-12.html
+       $match = "($type_match)("._COMPOSITIONS_MATCH.')?[.]html$';
+
+       // lister les compositions disponibles
+       $liste = find_all_in_path(compositions_chemin(), $match);
+       $res = array();
+       if (count($liste)) {
+               foreach ($liste as $s) {
+                       $base = preg_replace(',[.]html$,i', '', $s);
+                       if (preg_match(",$match,ims", basename($s), $regs)
+                               and ($composition = !$informer
+                                       or $composition = compositions_charger_infos($base))) {
+                               $regs = array_pad($regs, 4, null);
+                               $res[$regs[1]][$regs[3]] = $composition;
+                               // retenir les skels qui ont un xml associe
+                       }
+               }
+       }
+       // Pipeline compositions_lister_disponibles
+       $res = pipeline('compositions_lister_disponibles', array(
+               'args'=>array('type' => $type,'informer' => $informer),
+               'data'=> $res
+               ));
+       return $res;
+}
+
+/**
+ * Liste les id d'un type donne utilisant une composition donnee
+ *
+ * @param string $type
+ * @param string $composition
+ * @return array
+ */
+function compositions_lister_utilisations($type, $composition) {
+       $table_sql = table_objet_sql($type);
+       if (!in_array($table_sql, sql_alltable())) {
+               return;
+       }
+       $trouver_table = charger_fonction('trouver_table', 'base');
+       $desc = $trouver_table($table_sql);
+       $_id_table_objet = id_table_objet($type);
+       $titre = isset($desc['titre']) ? $desc['titre'] : "'' AS titre";
+       return sql_allfetsel("$_id_table_objet as id, $titre", $table_sql, 'composition='.sql_quote($composition));
+}
+
+/**
+ * Selectionner le fond en fonction du type et de la composition
+ * en prenant en compte la configuration pour le chemin
+ * et le fait que la composition a pu etre supprimee
+ *
+ * @param string $composition
+ * @param string $type
+ * @param string $defaut
+ * @param string $ext
+ * @param bool $fullpath
+ * @param string $vide
+ * @return string
+ */
+function compositions_selectionner($composition, $type, $defaut = '', $ext = 'html', $fullpath = false, $vide = 'composition-vide') {
+       if ($type=='syndic') {
+               $type='site'; //grml
+       }
+       $fond = compositions_chemin() . $type;
+
+       // regarder si compositions/article-xxx est disponible
+       if (strlen($composition)
+               and $f = find_in_path("$fond-$composition.$ext")) {
+               return $fullpath ? $f : $fond . "-$composition";
+       } elseif (strlen($defaut)
+               and $f = find_in_path("$fond-$defaut.$ext")) {
+               // sinon regarder si compositions/article-defaut est disponible
+               return $fullpath ? $f : $fond . "-$defaut";
+       }
+
+       // se rabattre sur compositions/article si disponible
+       if ($f = find_in_path("$fond.$ext")) {
+               return $fullpath ? $f : $fond;
+       }
+
+       // sinon une composition vide pour ne pas generer d'erreur
+       if ($vide and $f = find_in_path("$vide.$ext")) {
+               return $fullpath ? $f : $vide;
+       }
+
+       // rien mais ca fera une erreur dans le squelette si appele en filtre
+       return '';
+}
+
+/**
+ * Decrire une composition pour un objet
+ * @param string $type
+ * @param string $composition
+ * @return array|bool|string
+ */
+function compositions_decrire($type, $composition) {
+       static $compositions = array();
+       if (!function_exists('compositions_charger_infos')) {
+               include_spip('inc/compositions');
+       }
+       if ($type=='syndic') {
+               $type='site'; //grml
+       }
+       if (isset($compositions[$type][$composition])) {
+               return $compositions[$type][$composition];
+       }
+       $ext = 'html';
+       $fond = compositions_chemin() . $type;
+       if (strlen($composition)
+               and $f = find_in_path("$fond-$composition.$ext")
+               and $desc = compositions_charger_infos($f)) {
+               return $compositions[$type][$composition] = $desc;
+       }
+       return $compositions[$type][$composition] = false;
+}
+
+/**
+ * Un filtre a utiliser sur [(#COMPOSITION|composition_class{#ENV{type}})]
+ * pour poser des classes generiques sur le <body>
+ * si une balise <class>toto</class> est definie dans la composition c'est elle qui est appliquee
+ * sinon on pose simplement le nom de la composition
+ *
+ * @param string $composition
+ * @param string $type
+ * @return string
+ */
+function composition_class($composition, $type) {
+       if ($desc = compositions_decrire($type, $composition)
+               and isset($desc['class'])
+               and strlen($desc['class'])) {
+               return $desc['class'];
+       }
+       return $composition;
+}
+
+/**
+ * Liste les types d'objets qui ont une composition ET sont autorises par la configuration
+ * utilise la valeur en cache meta sauf si demande de recalcul
+ * ou pas encore definie
+ *
+ * @staticvar array $liste
+ * @return array
+ */
+function compositions_types() {
+       static $liste = null;
+       if (is_null($liste)) {
+               if (_VAR_MODE or !isset($GLOBALS['meta']['compositions_types'])) {
+                       include_spip('inc/compositions');
+                       compositions_cacher();
+               }
+               $liste = explode(',', $GLOBALS['meta']['compositions_types']);
+       }
+       return $liste;
+}
+
+/**
+ * Renvoie les parametres necessaires pour utiliser l'heritage de composition de façon generique
+ * recupere les donnes du pipeline compositions_declarer_heritage.
+ * Si $type n'est pas precise, on renvoie simplement le tableau des objets pouvant heriter.
+ *
+ * @param string $type
+ * @staticvar array $heritages
+ * @return array
+ */
+function compositions_recuperer_heritage($type = null) {
+       static $heritages = null;
+       if (is_null($heritages)) {
+               // recuperer les heritages declares via le pipeline compositions_declarer_heritage
+               $heritages = pipeline('compositions_declarer_heritage', array());
+       }
+
+       if (is_null($type)) {
+               return $heritages;
+       }
+
+       if (array_key_exists($type, $heritages)) {
+               $type_parent = $heritages[$type];
+               $table_parent =  table_objet_sql($type_parent);
+               $nom_id_parent = ($type==$type_parent) ? 'id_parent' : id_table_objet($type_parent); // Recursivite pour les rubriques, nom de l'identifiant du parent dans la table enfant
+               $nom_id_table_parent = id_table_objet($type_parent); // Nom de l'identifiant du parent dans la table parent
+
+               // verifier que table et champs existent...
+               $trouver_table = charger_fonction('trouver_table', 'base');
+               if (!$type_parent
+                       or !$desc = $trouver_table($table_parent)
+                       or !isset($desc['field']['composition'])
+                       or !isset($desc['field'][$nom_id_parent])) {
+                       return '';
+               }
+
+               return array(
+                       'type_parent' => $type_parent,
+                       'table_parent' => $table_parent,
+                       'nom_id_parent' => $nom_id_parent,
+                       'nom_id_table_parent' => $nom_id_table_parent
+               );
+       }
+       return array();
+}
+
+/**
+ * Renvoie la composition qui s'applique a un objet
+ * en tenant compte, le cas echeant, de la composition heritee
+ * si etoile=true on renvoi directment le champ sql
+ *
+ * @param string $type
+ * @param integer $id
+ * @param string $serveur
+ * @param bool $etoile
+ * @return string
+ */
+function compositions_determiner($type, $id, $serveur = '', $etoile = false) {
+       static $composition = array();
+       $id = intval($id);
+
+       if (isset($composition[$etoile][$serveur][$type][$id])) {
+               return $composition[$etoile][$serveur][$type][$id];
+       }
+
+       include_spip('base/abstract_sql');
+       $table = table_objet($type);
+       $table_sql = table_objet_sql($type);
+       $_id_table = id_table_objet($type);
+
+       $retour = '';
+
+       $trouver_table = charger_fonction('trouver_table', 'base');
+       $desc = $trouver_table($table,$serveur);
+       if (isset($desc['field']['composition']) and $id) {
+               $select = 'composition';
+
+               $heritage = compositions_recuperer_heritage($type);
+               if (isset($desc['field'][$heritage['nom_id_parent']])) {
+                       $select .= ', '.$heritage['nom_id_parent'].' as id_parent';
+               }
+
+               $row = sql_fetsel($select, $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);
+               if ($row['composition'] != '') {
+                       $retour = $row['composition'];
+               } elseif (!$etoile
+                       and isset($row['id_parent'])
+                       and $row['id_parent']) {
+                       $retour = compositions_heriter($type, $id, $row['id_parent'], $serveur);
+               }
+       }
+       return $composition[$etoile][$serveur][$type][$id] = (($retour == '-') ? '' : $retour);
+}
+
+/**
+ * Renvoie la composition heritee par un objet selon son identifiant.
+ * Optionnellement, on peut lui transmettre directement l'id du parent s'il a ate calcule.
+ *
+ * @param string $type
+ * @param integer $id
+ * @param integer $id_parent
+ * @param string $serveur
+ * @return string
+ */
+function compositions_heriter($type, $id, $id_parent = null, $serveur = '') {
+       if ($type=='syndic') {
+               $type='site'; //grml
+       }
+       if (intval($id) < 1) {
+               return '';
+       }
+       static $infos = null;
+       $compo_parent = '';
+
+       $heritage = compositions_recuperer_heritage($type);
+
+       /* Si aucun héritage n'a été défini pour le type d'objet, ce
+       * n'est pas la peine d'aller plus loin. */
+       if (count($heritage) == 0) {
+               return '';
+       }
+
+       $type_parent = $heritage['type_parent'];
+       $table_parent = $heritage['table_parent'];
+       $nom_id_parent = $heritage['nom_id_parent'];
+       $nom_id_table_parent = $heritage['nom_id_table_parent'];
+
+       if (is_null($id_parent)) {
+               $id_parent = sql_getfetsel($nom_id_parent, table_objet_sql($type), id_table_objet($type).'='.intval($id));
+       }
+
+       $heritages = compositions_recuperer_heritage();
+
+       do {
+               $select = 'composition';
+               if ($heritages[$type_parent]==$type_parent) {
+                       // S'il y a recursivite sur le parent
+                       $select .= ', id_parent';
+               }
+               $row = sql_fetsel($select, $table_parent, $nom_id_table_parent.'='.intval($id_parent), '', '', '', '', $serveur);
+               if (strlen($row['composition']) and $row['composition']!='-') {
+                       $compo_parent = $row['composition'];
+               } elseif (strlen($row['composition'])==0 and isset($heritages[$type_parent])) {
+                       // Si le parent peut heriter, il faut verifier s'il y a heritage
+                       $compo_parent = compositions_determiner($type_parent, $id_parent, $serveur = '');
+               }
+
+               if (strlen($compo_parent) and is_null($infos)) {
+                       $infos = compositions_lister_disponibles('');
+               }
+       } while ($id_parent = $row['id_parent']
+               and
+               (!strlen($compo_parent) or !isset($infos[$type_parent][$compo_parent]['branche'][$type])));
+
+       if (strlen($compo_parent) and isset($infos[$type_parent][$compo_parent]['branche'][$type])) {
+               return $infos[$type_parent][$compo_parent]['branche'][$type];
+       }
+
+       return '';
+}
+
+/**
+ * #COMPOSITION
+ * Renvoie la composition s'appliquant a un objet
+ * en tenant compte, le cas echeant, de l'heritage.
+ *
+ * Sans precision, l'objet et son identifiant sont pris
+ * dans la boucle en cours, mais l'on peut specifier notre recherche
+ * en passant objet et id_objet en argument de la balise :
+ * #COMPOSITION{article, 8}
+ *
+ * #COMPOSITION* renvoie toujours le champs brut, sans tenir compte de l'heritage
+ *
+ * @param array $p     AST au niveau de la balise
+ * @return array       AST->code modifie pour calculer le nom de la composition
+ */
+function balise_COMPOSITION_dist($p) {
+       $_composition = '';
+       if ($_objet = interprete_argument_balise(1, $p)) {
+               $_id_objet = interprete_argument_balise(2, $p);
+       } else {
+               $_composition = champ_sql('composition', $p);
+               $_id_objet = champ_sql($p->boucles[$p->id_boucle]->primary, $p);
+               $_objet = "objet_type('" . $p->boucles[$p->id_boucle]->id_table . "')";
+       }
+       // si on veut le champ brut, et qu'on l'a sous la main, inutile d'invoquer toute la machinerie
+       if ($_composition and $p->etoile) {
+               $p->code = $_composition;
+       } else {
+               $connect = $p->boucles[$p->id_boucle]->sql_serveur;
+               $p->code = "compositions_determiner($_objet, $_id_objet, '$connect', ".($p->etoile?'true':'false').")";
+               // ne declencher l'usine a gaz que si composition est vide ...
+               if ($_composition) {
+                       $p->code = "((\$zc=$_composition)?(\$zc=='-'?'':\$zc):".$p->code.")";
+               }
+       }
+       return $p;
+}
+
+/**
+ * Indique si la composition d'un objet est verrouillee ou non,
+ * auquel cas, seul le webmaster peut la modifier
+ *
+ * @param string $type
+ * @param integer $id
+ * @param string $serveur
+ * @return string
+ */
+function compositions_verrouiller($type, $id, $serveur = '') {
+       $config = (isset($GLOBALS['meta']['compositions']) ? unserialize($GLOBALS['meta']['compositions']) : array());
+       if (isset($config['tout_verrouiller']) and $config['tout_verrouiller'] == 'oui') {
+               return true;
+       }
+
+       include_spip('base/abstract_sql');
+       $table = table_objet($type);
+       $table_sql = table_objet_sql($type);
+       $_id_table = id_table_objet($type);
+
+       $trouver_table = charger_fonction('trouver_table', 'base');
+       $desc = $trouver_table($table,$serveur);
+       if (isset($desc['field']['composition_lock']) and $id) {
+               $lock = sql_getfetsel('composition_lock', $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);
+               if ($lock) {
+                       return true;
+               } elseif (isset($desc['field']['id_rubrique'])) {
+                       $id_rubrique = sql_getfetsel('id_rubrique', $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);
+                       return compositions_verrou_branche($id_rubrique, $serveur);
+               } else {
+                       return false;
+               }
+       } else {
+               return false;
+       }
+}
+
+/**
+ * Indique si les objets d'une branche sont verrouilles
+ * @param integer $id_rubrique
+ * @param string $serveur
+ * @return string
+ */
+function compositions_verrou_branche($id_rubrique, $serveur = '') {
+
+       if (intval($id_rubrique) < 1) {
+               return false;
+       }
+       if ($infos_rubrique = sql_fetsel(array('id_parent', 'composition_branche_lock'), 'spip_rubriques', 'id_rubrique='.intval($id_rubrique), '', '', '', '', $serveur)) {
+               if ($infos_rubrique['composition_branche_lock']) {
+                       return true;
+               } else {
+                       return compositions_verrou_branche($infos_rubrique['id_parent'], $serveur);
+               }
+       }
+       return '';
+}