source: spip-zone/_core_/plugins/svp/inc/svp_phraser.php

Last change on this file was 115696, checked in by eric@…, 6 months ago

SVP Typologie gère maintenant toute la typologie des plugins, soit les catégories et les tags. Aussi, contrairement à ce qui a été prévu dans les commits précédents on supprime toute manipulation des catégories et des tags dans SVP :

  • les colonnes tags et categorie sont supprimées de spip_plugins
  • le chargement du XML considère encore l'attribut ou la balise categorie par souci de compatibilité mais ne le renvoie pas.
  • il ne reste plus que les API d'affichage qui utilisent la catégorie comme filtre mais qui fonctionnent très bien sans et ne sont utilisés que par Plugins SPIP. Ces API seront revus dans un prochain commit.

Cette version de SVP qui inaugure une branche 2.0 est totalement compatible avec les sites de production fonctionnant en mode runtime mais est incomptaible avec Plugins SPIP.

  • Property svn:eol-style set to native
File size: 15.6 KB
Line 
1<?php
2
3/**
4 * Fichier permettant de phraser les XML des fichiers paquet.xml et plugin.xml
5 * ainsi que des fichiers décrivant le contenu d'un dépot de paquets.
6 *
7 * @plugin SVP pour SPIP
8 * @license GPL
9 * @package SPIP\SVP\Plugins
10 **/
11
12if (!defined("_ECRIRE_INC_VERSION")) {
13        return;
14}
15
16include_spip('inc/xml');
17include_spip('inc/config');
18
19if (!defined('_SVP_MODE_RUNTIME')) {
20        if (defined('_DEV_VERSION_SPIP_COMPAT')) {
21                /**
22                 * Mode d'utilisation de SVP runtime ou pas :
23                 * - En mode runtime (true), on ne charge que les plugins compatibles avec la version courante
24                 * - En mode non runtime (false) on charge tous les plugins : cas du site Plugins SPIP
25                 * Runtime est le mode par defaut
26                 *
27                 * @var bool
28                 */
29                define('_SVP_MODE_RUNTIME', false);
30        } else {
31                define('_SVP_MODE_RUNTIME', (lire_config('svp/mode_runtime', 'oui') == 'oui' ? true : false));
32        }
33}
34
35
36// Type parseur XML à appliquer pour récupérer les infos du plugin
37/** @var string  Phraseur à utiliser pour un XML de plugin.xml */
38define('_SVP_DTD_PLUGIN', 'plugin');
39/** @var string  Phraseur à utiliser pour un XML de paquet.xml */
40define('_SVP_DTD_PAQUET', 'paquet');
41
42// Regexp de recherche des balises principales de archives.xml
43define('_SVP_REGEXP_BALISE_DEPOT', '#<depot[^>]*>(.*)</depot>#Uims');
44define('_SVP_REGEXP_BALISE_ARCHIVES', '#<archives[^>]*>(.*)</archives>#Uims');
45define('_SVP_REGEXP_BALISE_ARCHIVE', '#<archive[^s][^>]*>(.*)</archive>#Uims');
46define('_SVP_REGEXP_BALISE_ZIP', '#<zip[^>]*>(.*)</zip>#Uims');
47define('_SVP_REGEXP_BALISE_TRADUCTIONS', '#<traductions[^>]*>(.*)</traductions>#Uims');
48define('_SVP_REGEXP_BALISE_PLUGIN', '#<plugin[^>]*>(.*)</plugin>#Uims');
49define('_SVP_REGEXP_BALISE_PAQUET', '#<paquet[^>]*>(.*)</paquet>#Uims');
50define('_SVP_REGEXP_BALISE_MULTIS', '#<multis[^>]*>(.*)</multis>#Uims');
51
52
53/** Liste des balises techniques autorisées dans la balise <spip> */
54$GLOBALS['balises_techniques'] = array(
55        'menu',
56        'chemin',
57        'lib',
58        'necessite',
59        'onglet',
60        'procure',
61        'pipeline',
62        'utilise',
63        'options',
64        'fonctions',
65        'install'
66);
67# define('_BALISES_TECHNIQUES', serialize($balises_techniques));
68
69/** Liste des balises autorisant une traduction */
70$GLOBALS['balises_multis'] = array(
71        'nom',
72        'slogan',
73        'description'
74);
75# define('_BALISES_MULTIS', serialize($balises_multis));
76
77
78/**
79 * Phrase un fichier décrivant un dépot, dont le chemin local est donné
80 *
81 * Le fichier est au format XML et contient deux balises principales :
82 * - <depot>...</depot> : informations de description du depot (facultatif)
83 * - <archives>...</archives> : liste des informations sur chaque archive (obligatoire)
84 *
85 * La fonction met en cache le résultat du phrasage de chaque archive et ne
86 * rephrase que les archives ayant changées.
87 *
88 * @uses  svp_aplatir_balises()
89 * @uses  svp_phraser_archives()
90 * @param string $fichier_xml
91 *     Chemin local du fichier XML de description du dépot
92 * @return array|bool
93 *     false si erreur,
94 *     Tableau de 2 index sinon :
95 *     - depot : description du dépot
96 *     - paquets :
97 */
98function svp_phraser_depot($fichier_xml) {
99
100        // le fichier xml fournit sous forme de fichier
101        lire_fichier($fichier_xml, $xml);
102
103        // Initialisation du tableau des informations
104        // -- Si aucun bloc depot n'est trouve le titre et le type prennent une valeur par defaut
105        $infos = array(
106                'depot' => array(
107                        'titre' => _T('svp:titre_nouveau_depot'),
108                        'type' => 'manuel'
109                ),
110                'paquets' => array()
111        );
112
113
114        // Extraction et phrasage du bloc depot si il existe
115        // -- Si le bloc <depot> n'est pas renseigne on ne considere pas cela comme une erreur
116        $balises_depot = array('titre', 'descriptif', 'type', 'url_serveur', 'url_brouteur', 'url_archives', 'url_commits');
117        if (preg_match(_SVP_REGEXP_BALISE_DEPOT, $xml, $matches)) {
118                if (is_array($arbre_depot = spip_xml_parse($matches[1]))) {
119                        $infos['depot'] = svp_aplatir_balises($balises_depot, $arbre_depot, 'nonvide', $infos['depot']);
120                }
121        }
122
123        // Extraction et phrasage du bloc des archives si il existe
124        // -- Le bloc <archives> peut etre une chaine de grande taille et provoquer une erreur
125        // sur une recherche de regexp. On ne teste donc pas l'existence de cette balise
126        // -- Si aucun bloc <archive> c'est aussi une erreur
127        if (!preg_match_all(_SVP_REGEXP_BALISE_ARCHIVE, $xml, $matches)) {
128                return false;
129        }
130
131        // lire le cache des md5 pour ne parser que ce qui a change
132        $fichier_xml_md5 = $fichier_xml . ".md5.txt";
133        lire_fichier($fichier_xml_md5, $cache_md5);
134        if (!$cache_md5
135                or !$cache_md5 = unserialize($cache_md5)
136        ) {
137                $cache_md5 = array();
138        }
139
140        $infos['paquets'] = svp_phraser_archives($matches[0], $cache_md5);
141        ecrire_fichier($fichier_xml_md5, serialize($cache_md5));
142
143        // -- Si aucun paquet extrait c'est aussi une erreur
144        if (!$infos['paquets']) {
145                return false;
146        }
147
148        return $infos;
149}
150
151
152/**
153 * Phrase la liste des balises <archive>
154 *
155 * Chaque bloc XML est constitue de 3 sous-blocs principaux :
156 * - <zip> : contient les balises d'information sur le zip (obligatoire)
157 * - <traductions> : contient la compilation des informations de traduction (facultatif)
158 * - <plugin> ou <paquet> suivant la DTD : le contenu du fichier plugin.xml ou paquet.xml (facultatif)
159 *
160 * @uses  svp_phraser_zip()
161 * @uses  svp_phraser_traductions()
162 * @uses  svp_phraser_plugin()
163 * @uses  plugin_version_compatible()
164 * @param array $archives
165 *     Tableau de la liste des archives trouvées dans la description d'un dépot
166 * @param array $md5_cache
167 *     Tableau des descriptions d'archives déjà connues : on supprime
168 *     à la fin celles qui ne font plus parties du dépot.
169 * @return array
170 *     Tableau décrivant chaque archive, avec en index l'url de l'archive.
171 *     Tableau (url => Tableau de description de l'archive)
172 */
173function svp_phraser_archives($archives, &$md5_cache = array()) {
174        include_spip('inc/plugin');
175        $seen = array();
176
177        $paquets = array();
178        $version_spip = $GLOBALS['spip_version_branche'] . "." . $GLOBALS['spip_version_code'];
179
180        // On verifie qu'il existe au moins une archive
181        if (!$archives) {
182                return $paquets;
183        }
184
185        // On phrase chacune des archives
186        // Seul le bloc <zip> est obligatoire
187        foreach ($archives as $_cle => $_archive) {
188                // quand version spip ou mode runtime changent,
189                // il faut mettre le xml a jour pour voir les plugins compatibles ou non
190                $md5 = md5($_archive . ":$version_spip:" . _SVP_MODE_RUNTIME);
191                if (isset($md5_cache[$md5])) {
192                        if (is_array($p = $md5_cache[$md5])) {
193                                $paquets[$p['file']] = $p;
194                        } // ce paquet est connu
195                        $seen[] = $md5;
196                } elseif (preg_match(_SVP_REGEXP_BALISE_ZIP, $_archive, $matches)) {
197
198                        // Extraction de la balise <zip>
199                        $zip = svp_phraser_zip($matches[1]);
200
201                        if ($zip) {
202
203                                // Extraction de la balise traductions
204                                $traductions = array();
205                                if (preg_match(_SVP_REGEXP_BALISE_TRADUCTIONS, $_archive, $matches)) {
206                                        $traductions = svp_phraser_traductions($matches[1]);
207                                }
208
209
210                                // La balise <archive> peut posseder un attribut qui precise la DTD utilisee pour les plugins (plugin ou paquet)
211                                // Sinon, c'est la DTD plugin qui est utilisee
212                                list($tag, $attributs) = spip_xml_decompose_tag($_archive);
213                                // -- On stocke la DTD d'extraction des infos du plugin
214                                $dtd = (isset($attributs['dtd']) and $attributs['dtd']) ? $attributs['dtd'] : _SVP_DTD_PLUGIN;
215
216                                // Extraction *des balises* plugin ou *de la balise* paquet suivant la DTD et la version SPIP
217                                // -- DTD : si on utilise plugin.xml on extrait la balise <plugin> sinon la balise <paquet>
218                                $xml = svp_phraser_plugin($dtd, $_archive);
219
220                                // Si on est en mode runtime, on est seulement interesse par les plugins compatibles avec
221                                // la version courant de SPIP. On ne stocke donc pas les autres plugins.
222                                // Si on est pas en mode runtime on prend tout !
223                                if (!_SVP_MODE_RUNTIME
224                                        or (_SVP_MODE_RUNTIME and isset($xml['compatibilite']) and plugin_version_compatible($xml['compatibilite'],
225                                                        $version_spip, 'spip'))
226                                ) {
227                                        $paquets[$zip['file']] = $zip;
228                                        $paquets[$zip['file']]['traductions'] = $traductions;
229                                        $paquets[$zip['file']]['dtd'] = $dtd;
230                                        $paquets[$zip['file']]['plugin'] = $xml;
231                                        $paquets[$zip['file']]['md5'] = $md5;
232                                        $md5_cache[$md5] = $paquets[$zip['file']];
233                                        $seen[] = $md5;
234                                } else {
235                                        $md5_cache[$md5] = $zip['file'];
236                                        $seen[] = $md5;
237                                }
238                        }
239                }
240        }
241
242        // supprimer du cache les zip qui ne sont pas dans le nouveau $archives
243        $oldies = array_diff(array_keys($md5_cache), $seen);
244        foreach ($oldies as $old_md5) {
245                unset($md5_cache[$old_md5]);
246        }
247
248        return $paquets;
249}
250
251
252/**
253 * Phrase le contenu du XML décrivant une archive suivant une DTD
254 * de plugin.xml ou de paquet.xml donnée
255 *
256 * La fonction peut-être appelée via archives.xml ou via un xml de plugin.
257 * Elle phrase la balise <multi> dans le cas d'une DTD paquet qui contient
258 * les traductions du nom, slogan et description
259 *
260 * @uses svp_aplatir_balises()
261 *
262 * @global $balises_multis
263 * @static array $informer
264 *
265 * @param string $dtd
266 *     Nom du type de dtd : plugin ou paquet (pour phraser un plugin.xml ou un paquet.xml)
267 * @param string $contenu
268 *     Contenu XML à phraser
269 * @return array
270 *     Description du plugin
271 **/
272function svp_phraser_plugin($dtd, $contenu) {
273        global $balises_multis;
274        static $informer = array();
275
276        $plugin = array();
277
278        // On initialise les informations du plugin avec le contenu du plugin.xml ou paquet.xml
279        $regexp = ($dtd == 'plugin') ? _SVP_REGEXP_BALISE_PLUGIN : _SVP_REGEXP_BALISE_PAQUET;
280        if ($nb_balises = preg_match_all($regexp, $contenu, $matches)) {
281                $plugins = array();
282                // Pour chacune des occurences de la balise on extrait les infos
283                foreach ($matches[0] as $_balise_plugin) {
284                        // Extraction des informations du plugin suivant le standard SPIP
285                        if (!isset($informer[$dtd])) {
286                                $informer[$dtd] = charger_fonction('infos_' . $dtd, 'plugins');
287                        }
288                        $plugins[] = $informer[$dtd]($_balise_plugin);
289                }
290
291                // On appelle systematiquement une fonction de mise a jour de la structure de donnees du plugin :
292                // -- Si DTD plugin et que le nombre de balises plugin > 1 ou si DTD paquet avec une presence de balise spip
293                //    alors on fusionne donc les informations recoltees
294                // -- sinon on arrange la structure pour deplacer le contenu des balises dites techniques dans un sous tableau
295                //    d'index 0 par similitude avec la structure fusionnee
296                $fusionner = charger_fonction('fusion_' . $dtd, 'plugins');
297                if ($dtd == 'plugin') {
298                        $plugin = $fusionner($plugins);
299                } else {
300                        $plugin = $fusionner($plugins[0]);
301                }
302
303                // Durant la période où les XML contiennent encore l'attribut ou la balise categorie il faut la traiter
304                // lors du phrasage mais la supprimer après la fusion afin d'éviter une erreur SQL lors de l'insertion.
305                if (isset($plugin['categorie'])) {
306                        unset($plugin['categorie']);
307                }
308
309                // Pour la DTD paquet, les traductions du nom, slogan et description sont compilees dans une balise
310                // du fichier archives.xml. Il faut donc completer les informations precedentes avec cette balise
311                if (($dtd == _SVP_DTD_PAQUET) and (preg_match(_SVP_REGEXP_BALISE_MULTIS, $contenu, $matches))) {
312                        $multis = array();
313                        if (is_array($arbre = spip_xml_parse($matches[1]))) {
314                                $multis = svp_aplatir_balises($balises_multis, $arbre);
315                        }
316                        // Le nom peut etre traduit ou pas, il faut donc le tester
317                        if ($multis['nom']) {
318                                $plugin['nom'] = $multis['nom'];
319                        }
320                        // Slogan et description sont forcement des items de langue
321                        $plugin['slogan'] = $multis['slogan'];
322                        $plugin['description'] = $multis['description'];
323                }
324        }
325
326        return $plugin;
327}
328
329
330/**
331 * Phrase le contenu de la balise <zip>
332 *
333 * Extrait du XML les informations du zip
334 *
335 * @uses svp_aplatir_balises()
336 *
337 * @param string $contenu
338 *     Description XML de l'archive
339 * @return array
340 *     Description du zip.
341 *     - Index 'file' : nom du zip
342 *     - Index 'size' : taille
343 *     - Index 'date' : date de création
344 *     - Index 'last_commit' : date du dernier commit
345 *     - Index 'source' : arborescence relative des sources
346 */
347function svp_phraser_zip($contenu) {
348        static $balises_zip = array('file', 'size', 'date', 'source', 'last_commit');
349
350        $zip = array();
351        if (is_array($arbre = spip_xml_parse($contenu))) {
352                $zip = svp_aplatir_balises($balises_zip, $arbre);
353        }
354
355        return $zip;
356}
357
358
359/**
360 * Phrase le contenu d'une balise <traductions> en un tableau plus
361 * facilement utilisable
362 *
363 * @param string $contenu
364 *     Contenu XML de la balise <traductions>
365 * @return array
366 *     Tableau complexe avec pour index les noms des modules de langue et pour
367 *     valeur leur description. Chaque description contient dedans 3 index :
368 *     - reference : la langue de référence
369 *     - gestionnaire : quel logiciel à servi à gérer les traductions
370 *     - langues : tableau classé par langue puis par traducteurs, qui indique
371 *       l'ensemble des traducteurs pour chacune des langues présentes
372 */
373function svp_phraser_traductions($contenu) {
374
375        $traductions = array();
376        if (is_array($arbre = spip_xml_parse($contenu))) {
377                foreach ($arbre as $_tag => $_langues) {
378                        // On commence par les balises <traduction> et leurs attributs
379                        list($tag, $attributs_traduction) = spip_xml_decompose_tag($_tag);
380                        $traductions[$attributs_traduction['module']]['reference'] = $attributs_traduction['reference'];
381                        $traductions[$attributs_traduction['module']]['gestionnaire'] = isset($attributs_traduction['gestionnaire']) ? $attributs_traduction['gestionnaire'] : '';
382
383                        // On continue par les balises <langue> qui donnent le code en attribut
384                        // et les balises <traducteur> qui donnent uniquement le nom en attribut
385                        if (is_array($_langues[0])) {
386                                foreach ($_langues[0] as $_tag_trad => $_traducteurs) {
387                                        list($tag, $attributs_langue) = spip_xml_decompose_tag($_tag_trad);
388                                        $traducteurs = array();
389                                        if (is_array($_traducteurs[0])) {
390                                                foreach ($_traducteurs[0] as $_tag_lang => $_vide) {
391                                                        list($tag, $attributs_traducteur) = spip_xml_decompose_tag($_tag_lang);
392                                                        $traducteurs[] = $attributs_traducteur;
393                                                }
394                                        }
395                                        $traductions[$attributs_traduction['module']]['langues'][$attributs_langue['code']] = $traducteurs;
396                                }
397                        }
398                }
399        }
400
401        return $traductions;
402}
403
404
405/**
406 * Aplatit plusieurs clés d'un arbre xml dans un tableau
407 *
408 * Effectue un trim() de la valeur trouvée dans l'arbre
409 *
410 * @uses  spip_xml_aplatit()
411 *
412 * @param array $balises
413 *     Liste de noms de balises XML.
414 *     Peut aussi être un tableau indiquant un renommage d'une balise
415 *     au passage tel que 'x' => 'y' qui cherchera x dans l'arbre XML et
416 *     l'applatira dans y.
417 * @param array $arbre_xml
418 *     Un arbre issu de spip_xml_parse()
419 * @param string $mode
420 *     Mode d'affectation des valeurs trouvées
421 *     - 'vide_et_nonvide' : Affecte une chaine vide si la balise n'est
422 *       pas trouvée dans l'arbre et affecte la valeur de la balise sinon.
423 *     - 'nonvide' : Si la balise n'est pas trouvée dans l'arbre ou si son
424 *       contenu est vide, affecte la valeur du tableau initial concernant
425 *       cette balise si elle est connue.
426 * @param array $tableau_initial
427 *     Tableau initial pouvant contenir des valeurs par défaut à affecter
428 *     à chaque balise avec 'x' => 'valeur'
429 */
430function svp_aplatir_balises($balises, $arbre_xml, $mode = 'vide_et_nonvide', $tableau_initial = array()) {
431        $tableau_aplati = array();
432
433        if (!$balises) {
434                return $tableau_initial;
435        }
436
437        foreach ($balises as $_cle => $_valeur) {
438                $tag = (is_string($_cle)) ? $_cle : $_valeur;
439                $valeur_aplatie = '';
440                if (isset($arbre_xml[$tag])) {
441                        $valeur_aplatie = trim(spip_xml_aplatit($arbre_xml[$tag]));
442                }
443                if (($mode == 'vide_et_nonvide')
444                        or (($mode == 'nonvide') and $valeur_aplatie)
445                ) {
446                        $tableau_aplati[$_valeur] = $valeur_aplatie;
447                } else {
448                        $tableau_aplati[$_valeur] = isset($tableau_initial[$_valeur]) ? $tableau_initial[$_valeur] : '';
449                }
450        }
451
452        return $tableau_aplati;
453}
Note: See TracBrowser for help on using the repository browser.