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

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

Correction de la liste des blocs exclus qui recevait dans certains cas la page complète et non juste les blocs exclus (real3t).

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