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

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

Amélioration du chargement des pages pour éviter de forcer le mode complet uniquement pour mettre à jour l'activité des pages.
De fait, sur le pipeline affiche_milieu de la page d'admin des plugins on recharge les pages et les types de noisettes normalement.
Par contre, pour les boutons on effectue un rechargement complet.

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