PageRenderTime 60ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins-dist/svp/inc/svp_phraser.php

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