source: spip-zone/_plugins_/compositions/trunk/compositions_fonctions.php @ 66150

Last change on this file since 66150 was 66150, checked in by joseph@…, 7 years ago

Reprise des fonctions d'héritage, pas encore assez génériques. Le formulaire d'édition prend bien en compte les héritages.

ATTENTION : les héritages doivent maintenant être explicitement déclarés via le pileline dédié.

Reste la question du verrouillage par branche qui pour le moment reste limité aux rubriques.

File size: 14.8 KB
Line 
1<?php
2/*
3 * Plugin Compositions
4 * (c) 2007-2009 Cedric Morin
5 * Distribue sous licence GPL
6 *
7 */
8
9if (!defined("_ECRIRE_INC_VERSION")) return;
10
11define('_COMPOSITIONS_MATCH','-([^0-9][^.]*)');
12
13/**
14 * Lister les objets actives par configuration
15 *
16 * @return array
17 */
18function compositions_objets_actives(){
19        static $config=null;
20        if (is_null($config)){
21                // lister les objets dont on a active la composition dans la configuration
22                $config = unserialize($GLOBALS['meta']['compositions']);
23                $config = (isset($config['objets'])?$config['objets']:array('spip_articles','spip_rubriques'));
24                $config = array_map('objet_type',$config);
25        }
26        return $config;
27}
28
29/**
30 * Retrouver le nom du dossier ou sont stockees les compositions
31 * reglage par defaut, ou valeur personalisee via cfg
32 *
33 * @return string
34 */
35function compositions_chemin(){
36        $config_chemin = 'compositions/';
37        if (defined('_DIR_PLUGIN_Z') OR defined('_DIR_PLUGIN_ZCORE'))
38                $config_chemin = (isset($GLOBALS['z_blocs'])?reset($GLOBALS['z_blocs']):'contenu').'/';
39
40        elseif (isset($GLOBALS['meta']['compositions'])){
41                $config = unserialize($GLOBALS['meta']['compositions']);
42                if (isset ($config['chemin_compositions'])){
43                        $config_chemin = rtrim($config['chemin_compositions'],'/').'/';
44                }
45        }
46       
47        return $config_chemin;
48}
49
50/**
51 * Tester si la stylisation auto est activee
52 * @return string
53 */
54function compositions_styliser_auto(){
55        $config_styliser = true;
56        if (defined('_DIR_PLUGIN_Z') OR defined('_DIR_PLUGIN_ZCORE')){
57                $config_styliser = false; // Z s'occupe de styliser les compositions
58        }
59        elseif (isset($GLOBALS['meta']['compositions'])){
60                $config = unserialize($GLOBALS['meta']['compositions']);
61                $config_styliser = $config['styliser_auto'] != 'non';
62        }
63        return $config_styliser?' ':'';
64}
65
66/**
67 * Lister les compositions disponibles : toutes ou pour un type donne
68 * Si informer est a false, on ne charge pas les infos du xml
69 *
70 * @param string $type
71 * @param bool $informer
72 * @return array
73 */
74function compositions_lister_disponibles($type, $informer=true){
75        include_spip('inc/compositions');
76        $type_match = "";
77        if (strlen($type)){
78                $type = objet_type($type); // securite
79                $type_match = $type;
80        }
81        else {
82                $type_match = "[a-z0-9]+";
83        }
84
85        // rechercher les skel du type article-truc.html
86        // truc ne doit pas commencer par un chiffre pour eviter de confondre avec article-12.html
87        $match = "/($type_match)("._COMPOSITIONS_MATCH.")?[.]html$";
88
89        // lister les compositions disponibles
90        $liste = find_all_in_path(compositions_chemin(),$match);
91        $res = array();
92        if (count($liste)){
93                foreach($liste as $s) {
94                        $base = preg_replace(',[.]html$,i','',$s);
95                        if (preg_match(",$match,ims",$s,$regs)
96                          AND ($composition = !$informer
97                                OR $composition = compositions_charger_infos($base)))
98                                $res[$regs[1]][$regs[3]] = $composition;
99                        // retenir les skels qui ont un xml associe
100                }
101        }
102        // Pipeline compositions_lister_disponibles
103        $res = pipeline('compositions_lister_disponibles',array(
104                'args'=>array('type' => $type,'informer' => $informer), 
105                'data'=> $res
106                )
107        );
108        return $res;
109}
110
111/**
112 * Liste les id d'un type donne utilisant une composition donnee
113 *
114 * @param string $type
115 * @param string $composition
116 * @return array
117 */
118function compositions_lister_utilisations($type,$composition){
119        $table_sql = table_objet_sql($type);
120        if (!in_array($table_sql, sql_alltable())) return;
121        $_id_table_objet = id_table_objet($type);
122        return sql_allfetsel("$_id_table_objet as id,titre", $table_sql, "composition=".sql_quote($composition));
123}
124
125/**
126 * Selectionner le fond en fonction du type et de la composition
127 * en prenant en compte la configuration pour le chemin
128 * et le fait que la composition a pu etre supprimee
129 *
130 * @param string $composition
131 * @param string $type
132 * @param string $defaut
133 * @param string $ext
134 * @param bool $fullpath
135 * @param string $vide
136 * @return string
137 */
138function compositions_selectionner($composition,$type,$defaut="",$ext="html",$fullpath = false, $vide="composition-vide"){
139        if ($type=='syndic') $type='site'; //grml
140        $fond = compositions_chemin() . $type;
141
142        // regarder si compositions/article-xxx est disponible
143        if (strlen($composition)
144                AND $f = find_in_path("$fond-$composition.$ext"))
145                return $fullpath ? $f : $fond . "-$composition";
146        else
147                // sinon regarder si compositions/article-defaut est disponible
148                if (strlen($defaut)
149                        AND $f = find_in_path("$fond-$defaut.$ext"))
150                        return $fullpath ? $f : $fond . "-$defaut";
151
152        // se rabattre sur compositions/article si disponible
153        if ($f = find_in_path("$fond.$ext"))
154                return $fullpath ? $f : $fond;
155
156        // sinon une composition vide pour ne pas generer d'erreur
157        if ($vide AND $f = find_in_path("$vide.$ext"))
158                return $fullpath ? $f : $vide;
159
160        // rien mais ca fera une erreur dans le squelette si appele en filtre
161        return '';
162}
163
164/**
165 * Decrire une composition pour un objet
166 * @param string $type
167 * @param string $composition
168 * @return array|bool|string
169 */
170function compositions_decrire($type, $composition){
171        static $compositions = array();
172        if (!function_exists('compositions_charger_infos'))
173                include_spip('inc/compositions');
174        if ($type=='syndic') $type='site'; //grml
175        if (isset($compositions[$type][$composition]))
176                return $compositions[$type][$composition];
177        $ext = "html";
178        $fond = compositions_chemin() . $type;
179        if (strlen($composition)
180                AND $f = find_in_path("$fond-$composition.$ext")
181                AND $desc = compositions_charger_infos($f))
182                return $compositions[$type][$composition] = $desc;
183        return $compositions[$type][$composition] = false;
184}
185
186/**
187 * Un filtre a utiliser sur [(#COMPOSITION|composition_class{#ENV{type}})]
188 * pour poser des classes generiques sur le <body>
189 * si une balise <class>toto</class> est definie dans la composition c'est elle qui est appliquee
190 * sinon on pose simplement le nom de la composition
191 *
192 * @param string $composition
193 * @param string $type
194 * @return string
195 */
196function composition_class($composition,$type){
197        if ($desc = compositions_decrire($type, $composition)
198                AND isset($desc['class'])
199                AND strlen($desc['class']))
200                return $desc['class'];
201        return $composition;
202}
203
204/**
205 * Liste les types d'objets qui ont une composition ET sont autorises par la configuration
206 * utilise la valeur en cache meta sauf si demande de recalcul
207 * ou pas encore definie
208 *
209 * @staticvar array $liste
210 * @return array
211 */
212function compositions_types(){
213        static $liste = null;
214        if (is_null($liste)) {
215                if (_VAR_MODE OR !isset($GLOBALS['meta']['compositions_types'])){
216                        include_spip('inc/compositions');
217                        compositions_cacher();
218                }
219                $liste = explode(',',$GLOBALS['meta']['compositions_types']);
220        }
221        return $liste;
222}
223
224/**
225 * Renvoie les parametres necessaires pour utiliser l'heritage de composition de façon generique
226 * recupere les donnes du pipeline compositions_declarer_heritage.
227 * Si $type n'est pas precise, on renvoie simplement le tableau des objets pouvant heriter.
228 *
229 * @param string $type
230 * @staticvar array $heritages
231 * @return array
232 */
233function compositions_recuperer_heritage($type=NULL){
234        static $heritages = NULL;
235        if (is_null($heritages)) // recuperer les heritages declares via le pipeline compositions_declarer_heritage
236                $heritages = pipeline('compositions_declarer_heritage', array());
237       
238        if (is_null($type))
239                return $heritages;
240
241        if (array_key_exists($type, $heritages)) {
242                $type_parent = $heritages[$type];
243                $table_parent =  table_objet_sql($type_parent);
244                $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
245                $nom_id_table_parent = id_table_objet($type_parent); // Nom de l'identifiant du parent dans la table parent
246               
247                // verifier que table et champs existent...
248                $trouver_table = charger_fonction('trouver_table', 'base');
249                if (!$type_parent
250                        OR !$desc = $trouver_table($table_parent,$serveur)
251                        OR !isset($desc['field']['composition'])
252                        OR !isset($desc['field'][$nom_id_parent]))
253                        return '';
254
255                return array(
256                        'type_parent' => $type_parent,
257                        'table_parent' => $table_parent,
258                        'nom_id_parent' => $nom_id_parent,
259                        'nom_id_table_parent' => $nom_id_table_parent
260                );
261        }
262        return array();
263}
264
265/**
266 * Renvoie la composition qui s'applique a un objet
267 * en tenant compte, le cas echeant, de la composition heritee
268 * si etoile=true on renvoi directment le champ sql
269 *
270 * @param string $type
271 * @param integer $id
272 * @param string $serveur
273 * @param bool $etoile
274 * @return string
275 */
276function compositions_determiner($type, $id, $serveur='', $etoile = false){
277        static $composition = array();
278        $id = intval($id);
279       
280        if (isset($composition[$etoile][$serveur][$type][$id]))
281                return $composition[$etoile][$serveur][$type][$id];
282
283        include_spip('base/abstract_sql');
284        $table = table_objet($type);
285        $table_sql = table_objet_sql($type);
286        $_id_table = id_table_objet($type);
287
288        $retour = '';
289
290        $trouver_table = charger_fonction('trouver_table', 'base');
291        $desc = $trouver_table($table,$serveur);
292        if (isset($desc['field']['composition']) AND $id){
293                $select = "composition";
294
295        $heritage = compositions_recuperer_heritage($type);
296        if (isset($desc['field'][$heritage['nom_id_parent']]))
297                $select .= ', '.$heritage['nom_id_parent'].' as id_parent';
298
299        $row = sql_fetsel($select, $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);
300        if ($row['composition'] != '')
301                $retour = $row['composition'];
302        elseif (!$etoile
303          AND isset($row['id_parent'])
304          AND $row['id_parent'])
305                $retour = compositions_heriter($type, $id, $row['id_parent'], $serveur);
306        }
307        return $composition[$etoile][$serveur][$type][$id] = (($retour == '-') ? '' : $retour);
308}
309
310/**
311 * Renvoie la composition heritee par un objet selon son identifiant.
312 * Optionnellement, on peut lui transmettre directement l'id du parent s'il a ate calcule.
313 *
314 * @param string $type
315 * @param integer $id
316 * @param integer $id_parent
317 * @param string $serveur
318 * @return string
319 */
320function compositions_heriter($type, $id, $id_parent=NULL, $serveur=''){
321        if ($type=='syndic') $type='site'; //grml
322        if (intval($id) < 1) return '';
323        static $infos = null;
324        $compo_parent = '';
325       
326        $heritage = compositions_recuperer_heritage($type);
327        $type_parent = $heritage['type_parent'];
328        $table_parent = $heritage['table_parent'];
329        $nom_id_parent = $heritage['nom_id_parent'];
330        $nom_id_table_parent = $heritage['nom_id_table_parent'];
331       
332        if (is_null($id_parent))
333                $id_parent = sql_getfetsel($nom_id_parent, table_objet_sql($type), id_table_objet($type).'='.intval($id));
334       
335        $heritages = compositions_recuperer_heritage();
336       
337        do {
338                $select = 'composition';
339                if ($heritages[$type_parent]==$type_parent) // S'il y a recursivite sur le parent
340                        $select .= ', id_parent';
341                $row = sql_fetsel($select, $table_parent, $nom_id_table_parent.'='.intval($id_parent),'','','','',$serveur);
342                if (strlen($row['composition']) AND $row['composition']!='-')
343                        $compo_parent = $row['composition'];
344                elseif (strlen($row['composition'])==0 AND isset($heritages[$type_parent])) // Si le parent peut heriter, il faut verifier s'il y a heritage
345                        $compo_parent = compositions_determiner($type_parent, $id_parent, $serveur='');
346               
347                if (strlen($compo_parent) AND is_null($infos))
348                        $infos = compositions_lister_disponibles('');
349                       
350        }
351        while ($id_parent = $row['id_parent']
352                AND
353                (!strlen($compo_parent) OR !isset($infos[$type_parent][$compo_parent]['branche'][$type])));
354
355        if (strlen($compo_parent) AND isset($infos[$type_parent][$compo_parent]['branche'][$type]))
356                return $infos[$type_parent][$compo_parent]['branche'][$type];
357
358        return '';
359}
360
361/**
362 * #COMPOSITION
363 * Renvoie la composition s'appliquant a un objet
364 * en tenant compte, le cas echeant, de l'heritage.
365 *
366 * Sans precision, l'objet et son identifiant sont pris
367 * dans la boucle en cours, mais l'on peut specifier notre recherche
368 * en passant objet et id_objet en argument de la balise :
369 * #COMPOSITION{article, 8}
370 *
371 * #COMPOSITION* renvoie toujours le champs brut, sans tenir compte de l'heritage
372 *
373 * @param array $p      AST au niveau de la balise
374 * @return array        AST->code modifie pour calculer le nom de la composition
375 */
376function balise_COMPOSITION_dist($p) {
377        $_composition = "";
378        if ($_objet = interprete_argument_balise(1, $p)) {
379                $_id_objet = interprete_argument_balise(2, $p);
380        } else {
381                $_composition = champ_sql('composition',$p);
382                $_id_objet = champ_sql($p->boucles[$p->id_boucle]->primary, $p);
383                $_objet = "objet_type('" . $p->boucles[$p->id_boucle]->id_table . "')";
384        }
385        // si on veut le champ brut, et qu'on l'a sous la main, inutile d'invoquer toute la machinerie
386        if ($_composition AND $p->etoile)
387                $p->code = $_composition;
388        else {
389                $connect = $p->boucles[$p->id_boucle]->sql_serveur;
390                $p->code = "compositions_determiner($_objet, $_id_objet, '$connect', ".($p->etoile?'true':'false').")";
391                // ne declencher l'usine a gaz que si composition est vide ...
392                if ($_composition)
393                        $p->code = "((\$zc=$_composition)?(\$zc=='-'?'':\$zc):".$p->code.")";
394        }
395        return $p;
396}
397
398/**
399 * Indique si la composition d'un objet est verrouillee ou non,
400 * auquel cas, seul le webmaster peut la modifier
401 *
402 * @param string $type
403 * @param integer $id
404 * @param string $serveur
405 * @return string
406 */
407function compositions_verrouiller($type, $id, $serveur=''){
408        $config = unserialize($GLOBALS['meta']['compositions']);
409        if ($config['tout_verrouiller'] == 'oui')
410                return true;
411       
412        include_spip('base/abstract_sql');
413        $table = table_objet($type);
414        $table_sql = table_objet_sql($type);
415        $_id_table = id_table_objet($type);
416
417        $trouver_table = charger_fonction('trouver_table', 'base');
418        $desc = $trouver_table($table,$serveur);
419        if (isset($desc['field']['composition_lock']) AND $id){
420                $lock = sql_getfetsel('composition_lock', $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);
421                if ($lock)
422                        return true;
423                elseif (isset($desc['field']['id_rubrique'])) {
424                        $id_rubrique = sql_getfetsel('id_rubrique', $table_sql, "$_id_table=".intval($id), '', '', '', '', $serveur);
425                        return compositions_verrou_branche($id_rubrique, $serveur);
426                }
427                else
428                        return false;
429        }
430        else return false;
431}
432
433/**
434 * Indique si les objets d'une branche sont verrouilles
435 * @param integer $id_rubrique
436 * @param string $serveur
437 * @return string
438 */
439function compositions_verrou_branche($id_rubrique, $serveur=''){
440       
441        if (intval($id_rubrique) < 1) return false;
442        if($infos_rubrique = sql_fetsel(array('id_parent','composition_branche_lock'),'spip_rubriques','id_rubrique='.intval($id_rubrique),'','','','',$serveur)) {
443                if ($infos_rubrique['composition_branche_lock'])
444                        return true;
445                else
446                        return compositions_verrou_branche($infos_rubrique['id_parent'],$serveur);
447        }
448        return '';
449}
450?>
Note: See TracBrowser for help on using the repository browser.