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

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

Petite correction aux grosses conséquences : un test était inversé et ne permettait pas de charger les pages simples sans le plugin Compositions. On avait donc souvent aucune page de chargée par défaut.
Ajout d'une fonction de duplication d'une noisette sur d'autres pages (dans le même bloc).
Correction d'un YAML qui dupliquait un élément ce qui est invalide.

  • Property svn:eol-style set to native
File size: 18.8 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_lister_defaut();
28
29        // Choisir le bon répertoire des pages
30        $options['repertoire_pages'] = noizetier_page_initialiser_dossier();
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        // Initialiser 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_extraire_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 chaîne 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_lister_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_lister_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_extraire_type($page) {
252
253        $type = explode('-', $page, 2);
254        $type = $type[0];
255
256        return $type;
257}
258
259/**
260 * Détermine, à partir de son identifiant, la composition d'une page si elle existe.
261 *
262 * @api
263 *
264 * @param string $page
265 *              L'identifiant de la page.
266 *
267 * @return string
268 *      La composition de la page choisie, à savoir, le mot suivant le tiret,
269 *              ou la chaine vide sinon.
270 */
271function noizetier_page_extraire_composition($page) {
272
273        $composition = explode('-', $page, 2);
274        $composition = isset($composition[1]) ? $composition[1] : '';
275
276        return $composition;
277}
278
279/**
280 * Détermine si les compositions sont possibles sur un type de page.
281 *
282 * @api
283 *
284 * @param string $type
285 *              Identifiant du type de page.
286 *
287 * @return boolean
288 *              True si les compositions sont autorisées, false sinon.
289 */
290function noizetier_page_composition_activee($type) {
291
292        $est_activee = false;
293
294        if (defined('_DIR_PLUGIN_COMPOSITIONS')) {
295                include_spip('compositions_fonctions');
296                if (in_array($type, compositions_objets_actives())) {
297                        $est_activee = true;
298                }
299        }
300
301        return $est_activee;
302}
303
304/**
305 * Déterminer le répertoire dans lequel le NoiZetier peut lister les pages pouvant supporter
306 * l'insertion de noisettes.
307 *
308 * @api
309 *
310 * @return string
311 *              Le répertoire des pages sous la forme dossier/.
312 */
313function noizetier_page_initialiser_dossier() {
314
315        if (defined('_NOIZETIER_REPERTOIRE_PAGES')) {
316                $repertoire_pages = _NOIZETIER_REPERTOIRE_PAGES;
317        } elseif (isset($GLOBALS['z_blocs'])) {
318                $premier_bloc = reset($GLOBALS['z_blocs']);
319                $repertoire_pages = "$premier_bloc/";
320        } else {
321                $repertoire_pages = 'contenu/';
322        }
323
324        return $repertoire_pages;
325}
326
327/**
328 * Détermine, pour une page donnée, la liste des blocs ayant des noisettes incluses et renvoie leur nombre.
329 *
330 * @api
331 *
332 * @param string $page
333 *            L'identifiant de la page ou de la composition.
334 *
335 * @return array
336 *             Tableau des nombre de noisettes incluses par bloc de la forme [bloc] = nombre de noisettes.
337 */
338function noizetier_page_compter_noisettes($page) {
339
340        static $blocs_compteur = array();
341
342        if (!isset($blocs_compteur[$page])) {
343                // Initialisation des compteurs par bloc
344                $nb_noisettes = array();
345
346                // Le nombre de noisettes par bloc doit être calculé par une lecture de la table spip_noisettes.
347                $from = array('spip_noisettes');
348                $select = array('bloc', "count(type_noisette) as 'noisettes'");
349                // -- Construction du where identifiant précisément le type et la composition de la page
350                $where = array(
351                        'plugin=' . sql_quote('noizetier'),
352                        'type=' . sql_quote(noizetier_page_extraire_type($page)),
353                        'composition=' . sql_quote(noizetier_page_extraire_composition($page))
354                );
355                $group = array('bloc');
356                $blocs_non_vides = sql_allfetsel($select, $from, $where, $group);
357                if ($blocs_non_vides) {
358                        // On formate le tableau [bloc] = nb noisettes
359                        $nb_noisettes = array_column($blocs_non_vides, 'noisettes', 'bloc');
360                }
361
362                // Sauvegarde des compteurs pour les blocs concernés.
363                $blocs_compteur[$page] = $nb_noisettes;
364        }
365
366        return $blocs_compteur[$page];
367}
368
369/**
370 * Phrase le fichier XML ou YAML des pages et compositions configurables par le noiZetier et renvoie
371 * un tableau des caractéristiques complètes.
372 *
373 * @internal
374 *
375 * @uses noizetier_page_initialiser_dossier()
376 * @uses noizetier_bloc_lister_defaut()
377 *
378 * @param       $page
379 * @param array $options
380 *
381 * @return array
382 */
383function page_phraser_fichier($page, $options = array()) {
384
385        // Initialisation de la description
386        $description = array();
387
388        // Choisir le bon répertoire des pages
389        if (empty($options['repertoire_pages'])) {
390                $options['repertoire_pages'] = noizetier_page_initialiser_dossier();
391        }
392
393        // Initialiser les blocs par défaut
394        if (empty($options['blocs_defaut'])) {
395                include_spip('inc/noizetier_bloc');
396                $options['blocs_defaut'] = noizetier_bloc_lister_defaut();
397        }
398
399        // Initialiser le contexte de chargment
400        if (!isset($options['recharger'])) {
401                $options['recharger'] = false;
402        }
403        if (!isset($options['md5']) or $options['recharger']) {
404                $options['md5'] = '';
405        }
406
407        // Initialiser les composants de l'identifiant de la page:
408        // - type-composition si la page est une composition
409        // - type sinon
410        // On gère aussi le cas de Zpip v1 où page-xxxx désigne une page et non une composition.
411        // Dans ce cas, on doit donc obtenir type = xxxx et composition vide.
412        $identifiants = explode('-', $page);
413        if (!isset($identifiants[1])) {
414                $identifiants[1] = '';
415        } elseif ($identifiants[0] == 'page') {
416                $identifiants[0] = $identifiants[1];
417                $identifiants[1] = '';
418        }
419
420        // Initialisation de la description par défaut de la page
421        $description_defaut = array(
422                'page'           => $page,
423                'type'           => $identifiants[0],
424                'composition'    => $identifiants[1],
425                'nom'            => $page,
426                'description'    => '',
427                'icon'           => 'page-24.png',
428                'blocs_exclus'   => array(),
429                'necessite'      => array(),
430                'est_active'     => 'oui',
431                'branche'        => array(),
432                'est_virtuelle'  => 'non',
433                'est_page_objet' => 'non',
434                'signature'      => '',
435        );
436
437        // Recherche des pages ou compositions explicites suivant le processus :
438        // a- Le fichier YAML est recherché en premier,
439        // b- ensuite le fichier XML pour compatibilité ascendante.
440        // c- enfin, si il n'y a ni YAML, ni XML et que le mode le permet, on renvoie une description standard minimale
441        //    basée sur le fichier HTML uniquement
442        $md5 = '';
443        if ($fichier = find_in_path("{$options['repertoire_pages']}${page}.yaml")) {
444                // 1a- il y a un fichier YAML de configuration, on vérifie le md5 avant de charger le contenu.
445                //     Un YAML de page ne peut pas contenir d'inclusion YAML.
446                $md5 = md5_file($fichier);
447                if ($md5 != $options['md5']) {
448                        include_spip('inc/yaml');
449                        $description = yaml_decode_file($fichier);
450                }
451        } elseif ($fichier = find_in_path("{$options['repertoire_pages']}${page}.xml")) {
452                // 1b- il y a un fichier XML de configuration, on vérifie le md5 avant de charger le contenu.
453                //     on extrait et on parse le XML de configuration en tenant compte que ce peut être
454                //     celui d'une page ou d'une composition, ce qui change la balise englobante.
455                $md5 = md5_file($fichier);
456                if ($md5 != $options['md5']) {
457                        include_spip('inc/xml');
458                        if ($xml = spip_xml_load($fichier, false)
459                        and (isset($xml['page']) or isset($xml['composition']))) {
460                                $xml = isset($xml['page']) ? reset($xml['page']) : reset($xml['composition']);
461                                // Titre (nom), description et icone
462                                if (isset($xml['nom'])) {
463                                        $description['nom'] = spip_xml_aplatit($xml['nom']);
464                                }
465                                if (isset($xml['description'])) {
466                                        $description['description'] = spip_xml_aplatit($xml['description']);
467                                }
468                                if (isset($xml['icon'])) {
469                                        $description['icon'] = reset($xml['icon']);
470                                }
471
472                                // Liste des blocs autorisés pour la page. On vérifie que les blocs configurés sont bien dans
473                                // la liste des blocs par défaut et on calcule les blocs exclus qui sont les seuls insérés en base.
474                                $blocs_inclus = array();
475                                if (spip_xml_match_nodes(',^bloc,', $xml, $blocs)) {
476                                        foreach (array_keys($blocs) as $_bloc) {
477                                                list(, $attributs) = spip_xml_decompose_tag($_bloc);
478                                                $blocs_inclus[] = $attributs['id'];
479                                        }
480                                }
481                                if ($blocs_inclus) {
482                                        $description['blocs_exclus'] = array_diff($options['blocs_defaut'], array_intersect($options['blocs_defaut'], $blocs_inclus));
483                                }
484
485                                // Liste des plugins nécessaires pour utiliser la page
486                                if (spip_xml_match_nodes(',^necessite,', $xml, $necessites)) {
487                                        $description['necessite'] = array();
488                                        foreach (array_keys($necessites) as $_necessite) {
489                                                list(, $attributs) = spip_xml_decompose_tag($_necessite);
490                                                $description['necessite'][] = $attributs['id'];
491                                        }
492                                }
493
494                                // Liste des héritages
495                                if (spip_xml_match_nodes(',^branche,', $xml, $branches)) {
496                                        $description['branche'] = array();
497                                        foreach (array_keys($branches) as $_branche) {
498                                                list(, $attributs) = spip_xml_decompose_tag($_branche);
499                                                $description['branche'][$attributs['type']] = $attributs['composition'];
500                                        }
501                                }
502                        }
503                }
504        } elseif (defined('_NOIZETIER_LISTER_PAGES_SANS_XML') ? _NOIZETIER_LISTER_PAGES_SANS_XML : true) {
505                // 1c- il est autorisé de ne pas avoir de fichier XML de configuration.
506                // Ces pages sans XML ne sont chargées qu'une fois, la première. Ensuite, aucune mise à jour n'est nécessaire.
507                if (!$options['md5']) {
508                        $description['icon'] = 'page_noxml-24.png';
509                        $md5 = md5('_NOIZETIER_LISTER_PAGES_SANS_XML');
510                }
511        }
512
513        // Si la description est remplie c'est que le chargement a correctement eu lieu.
514        // Sinon, si la page n'a pas changée on renvoie une description limitée à un indicateur d'identité pour
515        // distinguer ce cas avec une erreur de chargement qui renvoie une description vide.
516        if ($description) {
517                // Mise à jour du md5
518                $description['signature'] = $md5;
519                // Identifie si la page est celle d'un objet SPIP
520                include_spip('base/objets');
521                $tables_objets = array_keys(lister_tables_objets_sql());
522                $description['est_page_objet'] = in_array(table_objet_sql($description_defaut['type']), $tables_objets) ? 'oui' : 'non';
523                // Complétude de la description avec les valeurs par défaut
524                $description = array_merge($description_defaut, $description);
525                // Traitement des necessite pour identifier l'activité de la page
526                $description['est_active'] = 'oui';
527                if ($description['necessite']) {
528                        foreach ($description['necessite'] as $_plugin_necessite) {
529                                if (!defined('_DIR_PLUGIN_' . strtoupper($_plugin_necessite))) {
530                                        $description['est_active'] = 'non';
531                                        break;
532                                }
533                        }
534                }
535                // Sérialisation des champs blocs_exclus, necessite et branche qui sont des tableaux
536                $description['blocs_exclus'] = serialize($description['blocs_exclus']);
537                $description['necessite'] = serialize($description['necessite']);
538                $description['branche'] = serialize($description['branche']);
539        } elseif ($md5 == $options['md5']) {
540                $description['identique'] = true;
541        }
542
543        return $description;
544}
Note: See TracBrowser for help on using the repository browser.