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