source: spip-zone/_plugins_/noizetier/trunk/inc/noizetier_page.php @ 110127

Last change on this file since 110127 was 110127, checked in by eric@…, 21 months ago

On remplace le filtre propre au noizetier par la balise #CHEMIN_IMAGE.
On continue à améliorer les balises du noizetier afin de se passer de filtres superflus.
On fait un up de z pour identifier cette version qui commence à être bien cohérente.

  • Property svn:eol-style set to native
File size: 18.6 KB
Line 
1<?php
2/**
3 * Ce fichier contient l'API de gestion des pages et compositions configurables par le noiZetier.
4 *
5 * @package SPIP\NOIZETIER\PAGE\API
6 */
7if (!defined('_ECRIRE_INC_VERSION')) {
8        return;
9}
10
11
12/**
13 *
14 * @api
15 *
16 * @param bool $recharger
17 *
18 * @return bool
19 */
20function noizetier_page_charger($recharger = false) {
21
22        // Retour de la fonction
23        $retour = false;
24
25        // Initialiser les blocs par défaut
26        include_spip('inc/noizetier_bloc');
27        $options['blocs_defaut'] = noizetier_bloc_defaut();
28
29        // Choisir le bon répertoire des pages
30        $options['repertoire_pages'] = noizetier_page_repertoire();
31
32        // Initialiser le contexte de rechargement
33        // TODO : en attente de voir si on rajoute un var_mode=vider_noizetier
34        $forcer_chargement = $recharger;
35
36        // Initaliser la table et le where des pages non virtuelles qui sont utilisés plusieurs fois.
37        $from ='spip_noizetier_pages';
38        $where = array('est_virtuelle=' . sql_quote('non'));
39
40        // On recherche les pages et les compositions explicites par le fichier HTML en premier
41        // Si on le trouve, on récupère la configuration du fichier XML ou YAML.
42        if ($fichiers = find_all_in_path($options['repertoire_pages'], '.+[.]html$')) {
43                $pages_nouvelles = $pages_modifiees = $pages_obsoletes = array();
44                // Récupération des signatures md5 des pages déjà enregistrées.
45                // Si on force le rechargement il est inutile de gérer les signatures et les pages modifiées ou obsolètes.
46                $signatures = array();
47                if (!$forcer_chargement) {
48                        $select = array('page', 'signature');
49                        if ($signatures = sql_allfetsel($select, $from, $where)) {
50                                $signatures = array_column($signatures, 'signature', 'page');
51                        }
52                        // On initialise la liste des pages à supprimer avec l'ensemble des pages non virtuelles
53                        $pages_obsoletes = $signatures ? array_keys($signatures) : array();
54                }
55
56                foreach ($fichiers as $_squelette => $_chemin) {
57                        $page = basename($_squelette, '.html');
58                        $dossier = dirname($_chemin);
59                        $est_composition = noizetier_page_est_composition($page);
60                        // Exclure certaines pages :
61                        // -- celles du privé situes dans prive/contenu
62                        // -- page liée au plugin Zpip en v1
63                        // -- z_apl liée aux plugins Zpip v1 et Zcore
64                        // -- les compositions explicites si le plugin Compositions n'est pas activé
65                        if ((substr($dossier, -13) != 'prive/contenu')
66                        and (($page != 'page') or !defined('_DIR_PLUGIN_Z'))
67                        and (($page != 'z_apl') or (!defined('_DIR_PLUGIN_Z') and !defined('_DIR_PLUGIN_ZCORE')))
68                        and (!$est_composition or ($est_composition     and defined('_DIR_PLUGIN_COMPOSITIONS')))) {
69                                // On passe le md5 de la page si il existe sinon la chaine vide. Cela permet de déterminer
70                                // si on doit ajouter la page ou la mettre à jour.
71                                // Si le md5 est le même et qu'il n'est donc pas utile de recharger la page, la configuration
72                                // retournée est vide.
73                                $options['md5'] = isset($signatures[$page]) ? $signatures[$page] : '';
74                                $options['recharger'] = $forcer_chargement;
75                                if ($configuration = page_phraser_fichier($page, $options)) {
76                                        if (empty($configuration['identique'])) {
77                                                // La page a été chargée (nouvelle) ou rechargée (modifiée).
78                                                // Néanmoins, on n'inclue cette page que si les plugins qu'elle nécessite explicitement dans son
79                                                // fichier de configuration sont bien tous activés.
80                                                // Rappel: si une page est incluse dans un plugin non actif elle ne sera pas détectée
81                                                //         lors du find_all_in_path() puisque le plugin n'est pas dans le path SPIP.
82                                                //         Ce n'est pas ce cas qui est traité ici.
83                                                $page_a_garder = true;
84                                                $necessite = unserialize($configuration['necessite']);
85                                                if (!empty($necessite)) {
86                                                        foreach ($necessite as $plugin) {
87                                                                if (!defined('_DIR_PLUGIN_'.strtoupper($plugin))) {
88                                                                        $page_a_garder = false;
89                                                                        break;
90                                                                }
91                                                        }
92                                                }
93
94                                                // Si la page est à garder on détermine si elle est nouvelle ou modifiée.
95                                                // En mode rechargement forcé toute page est considérée comme nouvelle.
96                                                // Sinon, la page doit être retirée de la base car un plugin qu'elle nécessite a été désactivée:
97                                                // => il suffit pour cela de la laisser dans la liste des pages obsolètes.
98                                                if ($page_a_garder) {
99                                                        if (!$options['md5'] or $forcer_chargement) {
100                                                                // La page est soit nouvelle soit on est en mode rechargement forcé:
101                                                                // => il faut la rajouter dans la table.
102                                                                $pages_nouvelles[] = $configuration;
103                                                        } else {
104                                                                // La configuration stockée dans la table a été modifiée et le mode ne force pas le rechargement:
105                                                                // => il faut mettre à jour la page dans la table.
106                                                                $pages_modifiees[] = $configuration;
107                                                                // => il faut donc la supprimer de la liste des pages obsolètes
108                                                                $pages_obsoletes = array_diff($pages_obsoletes, array($page));
109                                                        }
110                                                }
111                                        } else {
112                                                // La page n'a pas changée et n'a donc pas été réchargée:
113                                                // => Il faut donc juste indiquer qu'elle n'est pas obsolète.
114                                                $pages_obsoletes = array_diff($pages_obsoletes, array($page));
115                                        }
116                                } else {
117                                        // Il y a eu une erreur sur lors du rechargement de la page.
118                                        // Ce peut être en particulier le cas où une page HTML sans XML n'est plus détectée car le
119                                        // paramètre _NOIZETIER_LISTER_PAGES_SANS_XML a été positionné de true à false.
120                                        // => il faut donc ne rien faire pour laisser la page dans les obsolètes
121                                        continue;
122                                }
123                        }
124                }
125
126                // Mise à jour de la table des pages
127                // -- Suppression des pages obsolètes ou de toute les pages non virtuelles si on est en mode
128                //    rechargement forcé.
129                if (sql_preferer_transaction()) {
130                        sql_demarrer_transaction();
131                }
132                if ($pages_obsoletes) {
133                        sql_delete($from, sql_in('page', $pages_obsoletes));
134                } elseif ($forcer_chargement) {
135                        sql_delete($from, $where);
136                }
137                // -- Update des pages modifiées
138                if ($pages_modifiees) {
139                        sql_replace_multi($from, $pages_modifiees);
140                }
141                // -- Insertion des nouvelles pages
142                if ($pages_nouvelles) {
143                        sql_insertq_multi($from, $pages_nouvelles);
144                }
145                if (sql_preferer_transaction()) {
146                        sql_terminer_transaction();
147                }
148
149                $retour = true;
150        }
151
152        return $retour;
153}
154
155/**
156 * Retourne la configuration de la page, de la composition explicite ou de la composition virtuelle demandée.
157 * La configuration est stockée en base de données, certains champs sont recalculés avant d'être fournis.
158 *
159 * @api
160 *
161 * @uses noizetier_bloc_defaut()
162 *
163 * @param string        $page
164 *              Identifiant de la page ou de la composition.
165 * @param boolean       $traitement_typo
166 *      Indique si les données textuelles doivent être retournées brutes ou si elles doivent être traitées
167 *      en utilisant la fonction _T_ou_typo.
168 *              Les champs sérialisés sont toujours désérialisés.
169 *
170 * @return array
171 */
172function noizetier_page_lire($page, $traitement_typo = true) {
173
174        static $description_page = array();
175
176        if (!isset($description_page[$traitement_typo][$page])) {
177                // Chargement de toute la configuration de la page en base de données.
178                $description = sql_fetsel('*', 'spip_noizetier_pages', array('page=' . sql_quote($page)));
179
180                // Sauvegarde de la description de la page pour une consultation ultérieure dans le même hit.
181                if ($description) {
182                        // Traitements des champs textuels
183                        if ($traitement_typo) {
184                                $description['nom'] = _T_ou_typo($description['nom']);
185                                if (isset($description['description'])) {
186                                        $description['description'] = _T_ou_typo($description['description']);
187                                }
188                        }
189                        // Traitements des champs tableaux sérialisés
190                        $description['blocs_exclus'] = unserialize($description['blocs_exclus']);
191                        $description['necessite'] = unserialize($description['necessite']);
192                        $description['branche'] = unserialize($description['branche']);
193                        // Calcul des blocs
194                        $description['blocs'] = noizetier_page_lister_blocs($page, $description['blocs_exclus']);
195                        $description_page[$traitement_typo][$page] = $description;
196                } else {
197                        $description_page[$traitement_typo][$page] = array();
198                }
199        }
200
201        return $description_page[$traitement_typo][$page];
202}
203
204
205
206/**
207 *
208 * @api
209 *
210 * @param       $page
211 * @param array $blocs_exclus
212 *
213 * @return array
214 */
215function noizetier_page_lister_blocs($page, $blocs_exclus = array()) {
216
217        // Initialisation des blocs avec la liste des blocs par défaut
218        include_spip('inc/noizetier_bloc');
219        $blocs = noizetier_bloc_defaut();
220
221        // Si la liste des blocs exclus n'a pas été passé en argument on les cherche dans la configuration
222        // de la page
223        if (!$blocs_exclus) {
224                $where = array('page=' . sql_quote($page));
225                $blocs_exclus = sql_getfetsel('blocs_exclus', 'spip_noizetier_pages', $where);
226                $blocs_exclus = unserialize($blocs_exclus);
227        }
228
229        if ($blocs_exclus) {
230                $blocs = array_diff($blocs, $blocs_exclus);
231                sort($blocs);
232        }
233
234        return $blocs;
235}
236
237
238/**
239 * Renvoie le type d'une page à partir de son identifiant.
240 *
241 * @api
242 *
243 * @param string $page
244 *              L'identifiant de la page.
245 *
246 * @return string
247 *              Le type de la page choisie, c'est-à-dire:
248 *              - soit l'identifiant complet de la page,
249 *              - soit le mot précédent le tiret dans le cas d'une composition.
250 */
251function noizetier_page_type($page) {
252        $type = explode('-', $page, 2);
253
254        return $type[0];
255}
256
257/**
258 * Détermine, à partir de son identifiant, la composition d'une page si elle existe.
259 *
260 * @api
261 *
262 * @param string $page
263 *              L'identifiant de la page.
264 *
265 * @return string
266 *      La composition de la page choisie, à savoir, le mot suivant le tiret,
267 *              ou la chaine vide sinon.
268 */
269function noizetier_page_composition($page) {
270        $composition = explode('-', $page, 2);
271        $composition = isset($composition[1]) ? $composition[1] : '';
272
273        return $composition;
274}
275
276/**
277 * Détermine, à partir de son identifiant, si la page est une composition.
278 *
279 * @api
280 *
281 * @param string $page
282 *              L'identifiant de la page.
283 *
284 * @return boolean
285 *      `true` si la page est une composition, `false` sinon.
286 */
287function noizetier_page_est_composition($page) {
288        $est_composition = false;
289        if (strpos($page, '-') !== false) {
290                $est_composition = true;
291        }
292
293        return $est_composition;
294}
295
296/**
297 * Détermine si les compositions sont possibles sur un type de page.
298 *
299 * @api
300 *
301 * @param string $type
302 *              Identifiant du type de page.
303 *
304 * @return boolean
305 *              True si les compositions sont autorisées, false sinon.
306 */
307function noizetier_page_composition_activee($type) {
308
309        $est_activee = false;
310
311        if (defined('_DIR_PLUGIN_COMPOSITIONS')) {
312                include_spip('compositions_fonctions');
313                if (in_array($type, compositions_objets_actives())) {
314                        $est_activee = true;
315                }
316        }
317
318        return $est_activee;
319}
320
321/**
322 * Déterminer le répertoire dans lequel le NoiZetier peut lister les pages pouvant supporter
323 * l'insertion de noisettes.
324 *
325 * @api
326 *
327 * @return string
328 *              Le répertoire des pages sous la forme dossier/.
329 */
330function noizetier_page_repertoire() {
331
332        if (defined('_NOIZETIER_REPERTOIRE_PAGES')) {
333                $repertoire_pages = _NOIZETIER_REPERTOIRE_PAGES;
334        } elseif (isset($GLOBALS['z_blocs'])) {
335                $premier_bloc = reset($GLOBALS['z_blocs']);
336                $repertoire_pages = "$premier_bloc/";
337        } else {
338                $repertoire_pages = 'contenu/';
339        }
340
341        return $repertoire_pages;
342}
343
344/**
345 * Détermine, pour une page donnée, la liste des blocs ayant des noisettes incluses et renvoie leur nombre.
346 *
347 * @api
348 *
349 * @param string $page
350 *            L'identifiant de la page ou de la composition.
351 *
352 * @return array
353 *             Tableau des nombre de noisettes incluses par bloc de la forme [bloc] = nombre de noisettes.
354 */
355function noizetier_page_compter_noisettes($page) {
356
357        static $blocs_compteur = array();
358
359        if (!isset($blocs_compteur[$page])) {
360                // Initialisation des compteurs par bloc
361                $nb_noisettes = array();
362
363                // Le nombre de noisettes par bloc doit être calculé par une lecture de la table spip_noisettes.
364                $from = array('spip_noisettes');
365                $select = array('bloc', "count(type_noisette) as 'noisettes'");
366                // -- Construction du where identifiant précisément le type et la composition de la page
367                $where = array(
368                        'plugin=' . sql_quote('noizetier'),
369                        'type=' . sql_quote(noizetier_page_type($page)),
370                        'composition=' . sql_quote(noizetier_page_composition($page))
371                );
372                $group = array('bloc');
373                $blocs_non_vides = sql_allfetsel($select, $from, $where, $group);
374                if ($blocs_non_vides) {
375                        // On formate le tableau [bloc] = nb noisettes
376                        $nb_noisettes = array_column($blocs_non_vides, 'noisettes', 'bloc');
377                }
378
379                // Sauvegarde des compteurs pour les blocs concernés.
380                $blocs_compteur[$page] = $nb_noisettes;
381        }
382
383        return $blocs_compteur[$page];
384}
385
386/**
387 * Phrase le fichier XML ou YAML des pages et compositions configurables par le noiZetier et renvoie
388 * un tableau des caractéristiques complètes.
389 *
390 * @internal
391 *
392 * @uses noizetier_page_repertoire()
393 * @uses noizetier_bloc_defaut()
394 *
395 * @param       $page
396 * @param array $options
397 *
398 * @return array
399 */
400function page_phraser_fichier($page, $options = array()) {
401
402        // Initialisation de la description
403        $description = array();
404
405        // Choisir le bon répertoire des pages
406        if (empty($options['repertoire_pages'])) {
407                $options['repertoire_pages'] = noizetier_page_repertoire();
408        }
409
410        // Initialiser les blocs par défaut
411        if (empty($options['blocs_defaut'])) {
412                include_spip('inc/noizetier_bloc');
413                $options['blocs_defaut'] = noizetier_bloc_defaut();
414        }
415
416        // Initialiser le contexte de chargment
417        if (!isset($options['recharger'])) {
418                $options['recharger'] = false;
419        }
420        if (!isset($options['md5']) or $options['recharger']) {
421                $options['md5'] = '';
422        }
423
424        // Initialiser les composants de l'identifiant de la page:
425        // - type-composition si la page est une composition
426        // - type sinon
427        // On gère aussi le cas de Zpip v1 où page-xxxx désigne une page et non une composition.
428        // Dans ce cas, on doit donc obtenir type = xxxx et composition vide.
429        $identifiants = explode('-', $page);
430        if (!isset($identifiants[1])) {
431                $identifiants[1] = '';
432        } elseif ($identifiants[0] == 'page') {
433                $identifiants[0] = $identifiants[1];
434                $identifiants[1] = '';
435        }
436
437        // Initialisation de la description par défaut de la page
438        $description_defaut = array(
439                'page'           => $page,
440                'type'           => $identifiants[0],
441                'composition'    => $identifiants[1],
442                'nom'            => $page,
443                'description'    => '',
444                'icon'           => 'page-24.png',
445                'blocs_exclus'   => array(),
446                'necessite'      => array(),
447                'branche'        => array(),
448                'est_virtuelle'  => 'non',
449                'est_page_objet' => 'non',
450                'signature'      => '',
451        );
452
453        // Recherche des pages ou compositions explicites suivant le processus :
454        // a- Le fichier YAML est recherché en premier,
455        // b- ensuite le fichier XML pour compatibilité ascendante.
456        // c- enfin, si il n'y a ni YAML, ni XML et que le mode le permet, on renvoie une description standard minimale
457        //    basée sur le fichier HTML uniquement
458        $md5 = '';
459        if ($fichier = find_in_path("{$options['repertoire_pages']}${page}.yaml")) {
460                // 1a- il y a un fichier YAML de configuration, on vérifie le md5 avant de charger le contenu.
461                $md5 = md5_file($fichier);
462                if ($md5 != $options['md5']) {
463                        include_spip('inc/yaml');
464                        $description = yaml_charger_inclusions(yaml_decode_file($fichier));
465                }
466        } elseif ($fichier = find_in_path("{$options['repertoire_pages']}${page}.xml")) {
467                // 1b- il y a un fichier XML de configuration, on vérifie le md5 avant de charger le contenu.
468                //     on extrait et on parse le XML de configuration en tenant compte que ce peut être
469                //     celui d'une page ou d'une composition, ce qui change la balise englobante.
470                $md5 = md5_file($fichier);
471                if ($md5 != $options['md5']) {
472                        include_spip('inc/xml');
473                        if ($xml = spip_xml_load($fichier, false)
474                        and (isset($xml['page']) or isset($xml['composition']))) {
475                                $xml = isset($xml['page']) ? reset($xml['page']) : reset($xml['composition']);
476                                // Titre (nom), description et icone
477                                if (isset($xml['nom'])) {
478                                        $description['nom'] = spip_xml_aplatit($xml['nom']);
479                                }
480                                if (isset($xml['description'])) {
481                                        $description['description'] = spip_xml_aplatit($xml['description']);
482                                }
483                                if (isset($xml['icon'])) {
484                                        $description['icon'] = reset($xml['icon']);
485                                }
486
487                                // Liste des blocs autorisés pour la page. On vérifie que les blocs configurés sont bien dans
488                                // la liste des blocs par défaut et on calcule les blocs exclus qui sont les seuls insérés en base.
489                                $blocs_inclus = array();
490                                if (spip_xml_match_nodes(',^bloc,', $xml, $blocs)) {
491                                        foreach (array_keys($blocs) as $_bloc) {
492                                                list(, $attributs) = spip_xml_decompose_tag($_bloc);
493                                                $blocs_inclus[] = $attributs['id'];
494                                        }
495                                }
496                                if ($blocs_inclus) {
497                                        $description['blocs_exclus'] = array_diff($options['blocs_defaut'], array_intersect($options['blocs_defaut'], $blocs_inclus));
498                                }
499
500                                // Liste des plugins nécessaires pour utiliser la page
501                                if (spip_xml_match_nodes(',^necessite,', $xml, $necessites)) {
502                                        $description['necessite'] = array();
503                                        foreach (array_keys($necessites) as $_necessite) {
504                                                list(, $attributs) = spip_xml_decompose_tag($_necessite);
505                                                $description['necessite'][] = $attributs['id'];
506                                        }
507                                }
508
509                                // Liste des héritages
510                                if (spip_xml_match_nodes(',^branche,', $xml, $branches)) {
511                                        $description['branche'] = array();
512                                        foreach (array_keys($branches) as $_branche) {
513                                                list(, $attributs) = spip_xml_decompose_tag($_branche);
514                                                $description['branche'][$attributs['type']] = $attributs['composition'];
515                                        }
516                                }
517                        }
518                }
519        } elseif (defined('_NOIZETIER_LISTER_PAGES_SANS_XML') ? _NOIZETIER_LISTER_PAGES_SANS_XML : false) {
520                // 1c- il est autorisé de ne pas avoir de fichier XML de configuration.
521                // Ces pages sans XML ne sont chargées qu'une fois, la première. Ensuite, aucune mise à jour n'est nécessaire.
522                if (!$options['md5']) {
523                        $description['icon'] = 'page_noxml-24.png';
524                        $md5 = md5('_NOIZETIER_LISTER_PAGES_SANS_XML');
525                }
526        }
527
528        // Si la description est remplie c'est que le chargement a correctement eu lieu.
529        // Sinon, si la page n'a pas changée on renvoie une description limitée à un indicateur d'identité pour
530        // distinguer ce cas avec une erreur de chargement qui renvoie une description vide.
531        if ($description) {
532                // Mise à jour du md5
533                $description['signature'] = $md5;
534                // Identifie si la page est celle d'un objet SPIP
535                include_spip('base/objets');
536                $tables_objets = array_keys(lister_tables_objets_sql());
537                $description['est_page_objet'] = in_array(table_objet_sql($description_defaut['type']), $tables_objets) ? 'oui' : 'non';
538                // Complétude de la description avec les valeurs par défaut
539                $description = array_merge($description_defaut, $description);
540                // Sérialisation des champs blocs_exclus, necessite et branche qui sont des tableaux
541                $description['blocs_exclus'] = serialize($description['blocs_exclus']);
542                $description['necessite'] = serialize($description['necessite']);
543                $description['branche'] = serialize($description['branche']);
544        } elseif ($md5 == $options['md5']) {
545                $description['identique'] = true;
546        }
547
548        return $description;
549}
Note: See TracBrowser for help on using the repository browser.