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

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

Renommage dans l'API des blocs.
Mise au point de l'API des objets.
Correction des fonctions réversibles de composition/décomposition des conteneurs pour le cas des objets.

  • Property svn:eol-style set to native
File size: 18.4 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                'branche'        => array(),
431                'est_virtuelle'  => 'non',
432                'est_page_objet' => 'non',
433                'signature'      => '',
434        );
435
436        // Recherche des pages ou compositions explicites suivant le processus :
437        // a- Le fichier YAML est recherché en premier,
438        // b- ensuite le fichier XML pour compatibilité ascendante.
439        // c- enfin, si il n'y a ni YAML, ni XML et que le mode le permet, on renvoie une description standard minimale
440        //    basée sur le fichier HTML uniquement
441        $md5 = '';
442        if ($fichier = find_in_path("{$options['repertoire_pages']}${page}.yaml")) {
443                // 1a- il y a un fichier YAML de configuration, on vérifie le md5 avant de charger le contenu.
444                $md5 = md5_file($fichier);
445                if ($md5 != $options['md5']) {
446                        include_spip('inc/yaml');
447                        $description = yaml_charger_inclusions(yaml_decode_file($fichier));
448                }
449        } elseif ($fichier = find_in_path("{$options['repertoire_pages']}${page}.xml")) {
450                // 1b- il y a un fichier XML de configuration, on vérifie le md5 avant de charger le contenu.
451                //     on extrait et on parse le XML de configuration en tenant compte que ce peut être
452                //     celui d'une page ou d'une composition, ce qui change la balise englobante.
453                $md5 = md5_file($fichier);
454                if ($md5 != $options['md5']) {
455                        include_spip('inc/xml');
456                        if ($xml = spip_xml_load($fichier, false)
457                        and (isset($xml['page']) or isset($xml['composition']))) {
458                                $xml = isset($xml['page']) ? reset($xml['page']) : reset($xml['composition']);
459                                // Titre (nom), description et icone
460                                if (isset($xml['nom'])) {
461                                        $description['nom'] = spip_xml_aplatit($xml['nom']);
462                                }
463                                if (isset($xml['description'])) {
464                                        $description['description'] = spip_xml_aplatit($xml['description']);
465                                }
466                                if (isset($xml['icon'])) {
467                                        $description['icon'] = reset($xml['icon']);
468                                }
469
470                                // Liste des blocs autorisés pour la page. On vérifie que les blocs configurés sont bien dans
471                                // la liste des blocs par défaut et on calcule les blocs exclus qui sont les seuls insérés en base.
472                                $blocs_inclus = array();
473                                if (spip_xml_match_nodes(',^bloc,', $xml, $blocs)) {
474                                        foreach (array_keys($blocs) as $_bloc) {
475                                                list(, $attributs) = spip_xml_decompose_tag($_bloc);
476                                                $blocs_inclus[] = $attributs['id'];
477                                        }
478                                }
479                                if ($blocs_inclus) {
480                                        $description['blocs_exclus'] = array_diff($options['blocs_defaut'], array_intersect($options['blocs_defaut'], $blocs_inclus));
481                                }
482
483                                // Liste des plugins nécessaires pour utiliser la page
484                                if (spip_xml_match_nodes(',^necessite,', $xml, $necessites)) {
485                                        $description['necessite'] = array();
486                                        foreach (array_keys($necessites) as $_necessite) {
487                                                list(, $attributs) = spip_xml_decompose_tag($_necessite);
488                                                $description['necessite'][] = $attributs['id'];
489                                        }
490                                }
491
492                                // Liste des héritages
493                                if (spip_xml_match_nodes(',^branche,', $xml, $branches)) {
494                                        $description['branche'] = array();
495                                        foreach (array_keys($branches) as $_branche) {
496                                                list(, $attributs) = spip_xml_decompose_tag($_branche);
497                                                $description['branche'][$attributs['type']] = $attributs['composition'];
498                                        }
499                                }
500                        }
501                }
502        } elseif (defined('_NOIZETIER_LISTER_PAGES_SANS_XML') ? _NOIZETIER_LISTER_PAGES_SANS_XML : true) {
503                // 1c- il est autorisé de ne pas avoir de fichier XML de configuration.
504                // Ces pages sans XML ne sont chargées qu'une fois, la première. Ensuite, aucune mise à jour n'est nécessaire.
505                if (!$options['md5']) {
506                        $description['icon'] = 'page_noxml-24.png';
507                        $md5 = md5('_NOIZETIER_LISTER_PAGES_SANS_XML');
508                }
509        }
510
511        // Si la description est remplie c'est que le chargement a correctement eu lieu.
512        // Sinon, si la page n'a pas changée on renvoie une description limitée à un indicateur d'identité pour
513        // distinguer ce cas avec une erreur de chargement qui renvoie une description vide.
514        if ($description) {
515                // Mise à jour du md5
516                $description['signature'] = $md5;
517                // Identifie si la page est celle d'un objet SPIP
518                include_spip('base/objets');
519                $tables_objets = array_keys(lister_tables_objets_sql());
520                $description['est_page_objet'] = in_array(table_objet_sql($description_defaut['type']), $tables_objets) ? 'oui' : 'non';
521                // Complétude de la description avec les valeurs par défaut
522                $description = array_merge($description_defaut, $description);
523                // Sérialisation des champs blocs_exclus, necessite et branche qui sont des tableaux
524                $description['blocs_exclus'] = serialize($description['blocs_exclus']);
525                $description['necessite'] = serialize($description['necessite']);
526                $description['branche'] = serialize($description['branche']);
527        } elseif ($md5 == $options['md5']) {
528                $description['identique'] = true;
529        }
530
531        return $description;
532}
Note: See TracBrowser for help on using the repository browser.