You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

477 lines
21 KiB

<?php
/**
* Ce fichier contient l'API N-Core de gestion des types de noisette qui consiste à stocker les descriptions
* dans un espace à accès rapide et à permettre leur lecture et leur mise à jour.
*
* @package SPIP\NCORE\TYPE_NOISETTE\API
*/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
/**
* Charge ou recharge les descriptions des types de noisette à partir des fichiers YAML.
* La fonction optimise le chargement en effectuant uniquement les traitements nécessaires
* en fonction des modifications, ajouts et suppressions des types de noisette identifiés
* en comparant les md5 des fichiers YAML.
*
* @api
*
* @uses ncore_type_noisette_initialiser_dossier()
* @uses ncore_type_noisette_lister()
* @uses ncore_type_noisette_completer_description()
* @uses ncore_type_noisette_stocker()
*
* @param string $plugin Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
* ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
* @param bool $recharger Si `true` force le rechargement de tous les types de noisette, sinon le chargement se base sur le
* md5 des fichiers YAML. Par défaut vaut `false`.
* @param string $stockage Identifiant du service de stockage à utiliser si précisé.
*
* @return bool `false` si une erreur s'est produite, `true` sinon.
*/
function type_noisette_charger($plugin, $recharger = false, $stockage = '') {
// Retour de la fonction
$retour = true;
// On charge l'API de stockage de N-Core.
// Ce sont ces fonctions qui aiguillent ou pas vers une éventuelle fonction spécifique de stockage.
include_spip('ncore/ncore');
// On récupère la configuration du dossier relatif où chercher les types de noisettes à charger.
$dossier = ncore_type_noisette_initialiser_dossier($plugin);
// On recherche les types de noisette directement par leur fichier YAML de configuration car il est
// obligatoire. La recherche s'effectue dans le path en utilisant le dossier relatif fourni.
if ($fichiers = find_all_in_path($dossier, '.+[.]yaml$')) {
// On s'assure que la noisette conteneur fournie par N-Core soit bien dans la liste ce qui peut ne pas être
// le cas si le dossier relatif des types de noisette du plugin appelant est différent de celui de N-Core.
$dossier_ncore = ncore_type_noisette_initialiser_dossier('ncore');
if ($dossier != $dossier_ncore) {
$fichiers['conteneur.yaml'] = find_in_path("${dossier_ncore}conteneur.yaml");
}
// Initialisation des tableaux de types de noisette.
$types_noisette_a_ajouter = $types_noisette_a_changer = $types_noisette_a_effacer = array();
// Récupération de la description complète des types de noisette déjà enregistrés de façon :
// - à gérer l'activité des types en fin de chargement
// - de comparer les signatures md5 des noisettes déjà enregistrées. Si on force le rechargement il est inutile
// de gérer les signatures et les noisettes modifiées ou obsolètes.
$types_noisettes_existantes = ncore_type_noisette_lister($plugin, '', $stockage);
$signatures = array();
if (!$recharger) {
$signatures = array_column($types_noisettes_existantes, 'signature', 'type_noisette');
// On initialise la liste des types de noisette à supprimer avec l'ensemble des types de noisette déjà stockés.
$types_noisette_a_effacer = $signatures ? array_keys($signatures) : array();
}
foreach ($fichiers as $_squelette => $_chemin) {
$type_noisette = basename($_squelette, '.yaml');
// Si on a forcé le rechargement ou si aucun md5 n'est encore stocké pour le type de noisette
// on positionne la valeur du md5 stocké à chaine vide.
// De cette façon, on force la lecture du fichier YAML du type de noisette.
$md5_stocke = (isset($signatures[$type_noisette]) and !$recharger)
? $signatures[$type_noisette]
: '';
// Initialisation de la description par défaut du type de noisette
// -- on y inclut le plugin appelant et la signature
$description_defaut = array(
'type_noisette' => $type_noisette,
'nom' => $type_noisette,
'description' => '',
'icon' => 'noisette-24.png',
'necessite' => array(),
'actif' => 'oui',
'conteneur' => 'non',
'contexte' => array(),
'ajax' => 'defaut',
'inclusion' => 'defaut',
'parametres' => array(),
'groupes' => array(),
'css_saisies' => array(),
'categories' => array('defaut'),
'plugin' => $plugin,
'signature' => '',
);
// On vérifie que le md5 du fichier YAML est bien différent de celui stocké avant de charger
// le contenu. Sinon, on passe au fichier suivant.
$md5 = md5_file($_chemin);
if ($md5 != $md5_stocke) {
include_spip('inc/yaml');
$description = yaml_decode_file($_chemin, array('include' => true));
// TODO : ne faudrait-il pas "valider" le fichier YAML ici ou alors lors du stockage ?
// Traitement des champs pouvant être soit une chaine, soit un tableau
if (!isset($description['necessite'])) {
$description['necessite'] = array();
} elseif (is_string($description['necessite'])) {
$description['necessite'] = array($description['necessite']);
}
if (!isset($description['contexte'])) {
$description['contexte'] = array();
} elseif (is_string($description['contexte'])) {
$description['contexte'] = array($description['contexte']);
}
// Traitements du champ parametres pouvant être vide (null)
if (!isset($description['parametres'])) {
$description['parametres'] = array();
}
// On repère les types de noisette qui nécessitent des plugins explicitement dans leur
// fichier de configuration :
// -- si un plugin nécessité est inactif, on indique le type de noisette comme inactif mais on l'inclut
// dans la liste retournée.
// Rappel: si un type de noisette est incluse dans un plugin non actif elle ne sera pas détectée
// lors du find_all_in_path() puisque le plugin n'est pas dans le path SPIP.
// Ce n'est pas ce cas qui est traité ici.
if (!empty($description['necessite'])) {
foreach ($description['necessite'] as $_plugin_necessite) {
if (!defined('_DIR_PLUGIN_' . strtoupper($_plugin_necessite))) {
$description['actif'] = 'non';
break;
}
}
}
// Mise à jour du md5
$description['signature'] = $md5;
// Complétude de la description avec les valeurs par défaut
$description = array_merge($description_defaut, $description);
// Traitement spécifique d'un type de noisette conteneur : l'ajax et l'inclusion dynamique
// ne sont pas autorisés et le contexte est défini lors de l'encapsulation.
if ($description['conteneur'] === 'oui') {
$description['contexte'] = array('aucun');
$description['ajax'] = 'non';
$description['inclusion'] = 'statique';
}
// Si le contexte est vide alors on le force à env pour éviter de traiter ce cas (contexte vide)
// lors de la compilation.
if (!$description['contexte']) {
$description['contexte'] = array('env');
}
// Traitement des paramètres : on aplatit les saisies pour retrouver une liste continue de paramètres
// et on construit le rangement en groupes (champ "groupes").
$parametres = array();
foreach ($description['parametres'] as $_type => $_saisies) {
if (!is_string($_type)) {
// Pas de groupe, la liste des saisies de paramètres est déjà dans le format souhaité
break;
} else {
// La liste des saisies est organisée en groupes, on l'aplatit et on conserve l'organisation
// dans le champ groupes.
$parametres = array_merge($parametres, $_saisies);
foreach ($_saisies as $_saisie) {
$description['groupes'][$_type][] = $_saisie['options']['nom'];
}
}
}
if ($parametres) {
// On a donc aplatit la liste des saisies, il faut l'enregistrer dans la structure du type de noisette
$description['parametres'] = $parametres;
}
// Traitement des saisies CSS appliquées soit à la capsule soit à la noisette si celle-ci est un
// conteneur. Le service permet à un plugin utilisateur de personnaliser les saisies par défaut.
$description['css_saisies'] = ncore_type_noisette_styler($plugin, $type_noisette, $stockage);
// Sérialisation des champs tableau
$description['necessite'] = serialize($description['necessite']);
$description['contexte'] = serialize($description['contexte']);
$description['parametres'] = serialize($description['parametres']);
$description['groupes'] = serialize($description['groupes']);
$description['css_saisies'] = serialize($description['css_saisies']);
// Implode du champ 'categories' pour faciliter la recherche dans tous les cas de stockage.
// -- il est nécessaire de compléter la liste par des virgules en début et fin de chaine pour
// localiser chaque catégorie en SQL (mySQL et SQLite)
$description['categories'] = ',' . implode(',', $description['categories']) . ',';
// Complément spécifique au plugin utilisateur si nécessaire
$description = ncore_type_noisette_completer_description($plugin, $description, $stockage);
if (!$md5_stocke or $recharger) {
// Le type de noisette est soit nouveau soit on est en mode rechargement forcé:
// => il faut le rajouter.
$types_noisette_a_ajouter[] = $description;
} else {
// La description stockée a été modifiée et le mode ne force pas le rechargement:
// => il faut mettre à jour le type de noisette.
$types_noisette_a_changer[] = $description;
// => et il faut donc le supprimer de la liste de types de noisette obsolètes
$types_noisette_a_effacer = array_diff($types_noisette_a_effacer, array($type_noisette));
}
} else {
// Le type de noisette n'a pas changé et n'a donc pas été rechargé:
// => Il faut donc juste indiquer qu'il n'est pas obsolète.
$types_noisette_a_effacer = array_diff($types_noisette_a_effacer, array($type_noisette));
}
}
// On complète la liste des types de noisette à changer avec les types de noisette dont l'indicateur
// d'activité est modifié suite à l'activation ou à la désactivation d'un plugin (le fichier YAML lui
// n'a pas changé). Il est inutile de le faire si on recharge tout.
// -- on cherche ces types en excluant les pages obsolètes et celles à changer qui ont déjà recalculé
// l'indicateur lors de la lecture du fichier YAML.
if (!$recharger) {
$types_noisette_exclus = $types_noisette_a_changer
? array_merge(array_column($types_noisette_a_changer, 'type_noisette'), $types_noisette_a_effacer)
: $types_noisette_a_effacer;
$types_noisette_a_verifier = $types_noisette_exclus
? array_diff_key($types_noisettes_existantes, array_flip($types_noisette_exclus))
: $types_noisettes_existantes;
if ($types_noisette_a_verifier) {
foreach ($types_noisette_a_verifier as $_type => $_description) {
$actif = 'oui';
$plugins_necessites = unserialize($_description['necessite']);
if ($plugins_necessites) {
foreach ($plugins_necessites as $_plugin_necessite) {
if (!defined('_DIR_PLUGIN_' . strtoupper($_plugin_necessite))) {
$actif = 'non';
break;
}
}
}
if ($actif != $_description['actif']) {
// On stocke la mise à jour dans les types à changer.
$_description['actif'] = $actif;
$types_noisette_a_changer[] = $_description;
}
}
}
}
// Mise à jour du stockage des types de noisette si au moins un des 3 tableaux est non vide et que le chargement forcé
// n'est pas demandé ou si le chargement forcé a été demandé:
// -- Suppression des types de noisettes obsolètes ou de tous les types de noisettes si on est en mode rechargement forcé.
// Pour permettre une optimisation du traitement en mode rechargement forcé on passe toujours le mode.
// -- Update des types de noisette modifiés.
// -- Insertion des nouveaux types de noisette.
if ($recharger
or (!$recharger and ($types_noisette_a_ajouter or $types_noisette_a_effacer or $types_noisette_a_changer))) {
$types_noisette = array('a_ajouter' => $types_noisette_a_ajouter);
if (!$recharger) {
$types_noisette['a_effacer'] = $types_noisette_a_effacer;
$types_noisette['a_changer'] = $types_noisette_a_changer;
}
$retour = ncore_type_noisette_stocker($plugin, $types_noisette, $recharger, $stockage);
}
}
return $retour;
}
/**
* Retourne, pour un type de noisette donné, la description complète ou seulement un champ précis.
* Les champs textuels peuvent subir une traitement typo si demandé.
*
* @api
*
* @uses ncore_type_noisette_decrire()
*
* @param string $plugin Le service permet de distinguer l'appelant qui peut-être un plugin comme le noiZetier ou
* un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
* @param string $type_noisette Identifiant du type de noisette.
* @param string $information Information spécifique à retourner ou vide pour retourner toute la description.
* @param bool $traiter_typo Indique si les données textuelles doivent être retournées brutes ou si elles doivent être traitées
* en utilisant la fonction typo. Par défaut l'indicateur vaut `false`.
* Les champs sérialisés sont eux toujours désérialisés.
* @param string $stockage Identifiant du service de stockage à utiliser si précisé.
*
* @return array|string La description complète ou champ précis demandé pour un type de noisette donné. Les champs
* de type tableau sont systématiquement désérialisés et si demandé, les champs textuels peuvent être
* traités avec la fonction typo().
*/
function type_noisette_lire($plugin, $type_noisette, $information = '', $traiter_typo = false, $stockage = '') {
// On indexe le tableau des descriptions par le plugin appelant en cas d'appel sur le même hit
// par deux plugins différents.
static $description_type_noisette = array();
// Stocker la description de la noisette si besoin
if (!isset($description_type_noisette[$plugin][$traiter_typo][$type_noisette])) {
// On charge l'API de N-Core.
// Ce sont ces fonctions qui aiguillent ou pas vers une fonction spécifique du service.
include_spip('ncore/ncore');
// Lecture de toute la configuration de la noisette: les données retournées sont brutes.
$description = ncore_type_noisette_decrire($plugin, $type_noisette, $stockage);
if ($description) {
// Traitements des champs textuels
if ($traiter_typo) {
$description = ncore_type_noisette_traiter_typo($plugin, $description, $stockage);
}
// Traitements des champs tableaux sérialisés
$description['contexte'] = unserialize($description['contexte']);
$description['necessite'] = unserialize($description['necessite']);
$description['parametres'] = unserialize($description['parametres']);
$description['groupes'] = unserialize($description['groupes']);
$description['css_saisies'] = unserialize($description['css_saisies']);
// Renvoyer un tableau à partir de la liste des catégories formatée comme suit : ,c1,c2,
$description['categories'] = explode(',', trim($description['categories'], ','));
}
// Sauvegarde de la description de la page pour une consultation ultérieure dans le même hit.
$description_type_noisette[$plugin][$traiter_typo][$type_noisette] = $description;
}
if ($information) {
if (isset($description_type_noisette[$plugin][$traiter_typo][$type_noisette][$information])) {
$type_noisette_lu = $description_type_noisette[$plugin][$traiter_typo][$type_noisette][$information];
} else {
$type_noisette_lu = '';
}
} else {
$type_noisette_lu = $description_type_noisette[$plugin][$traiter_typo][$type_noisette];
}
return $type_noisette_lu;
}
/**
* Renvoie une liste de descriptions de types de noisette éventuellement filtrée sur certains champs
* fournis en argument.
*
* @api
*
* @uses ncore_type_noisette_lister()
*
* @param string $plugin Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
* ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
* @param array $filtres Tableau associatif `[champ] = valeur` de critères de filtres sur les descriptions de types de noisette.
* Le seul opérateur possible est l'égalité.
* @param string $stockage Identifiant du service de stockage à utiliser si précisé.
*
* @return array Tableau des descriptions des types de noisette trouvés indexé par le type de noisette.
*/
function type_noisette_repertorier($plugin, $filtres = array(), $stockage = '') {
// On indexe le tableau des types de noisette par le plugin appelant en cas d'appel sur le même hit
// par deux plugins différents.
static $types_noisette = array();
if (!isset($types_noisette[$plugin])) {
// On charge l'API de N-Core.
// Ce sont ces fonctions qui aiguillent ou pas vers une fonction spécifique du service.
include_spip('ncore/ncore');
// On récupère la description complète de tous les types de noisettes détectés par le plugin appelant
$types_noisette[$plugin] = ncore_type_noisette_lister($plugin, '', $stockage);
}
// Application des filtres éventuellement demandés en argument de la fonction
$types_noisette_filtres = $types_noisette[$plugin];
if ($filtres) {
foreach ($types_noisette_filtres as $_type_noisette => $_description) {
foreach ($filtres as $_critere => $_valeur) {
if (isset($_description[$_critere]) and ($_description[$_critere] != $_valeur)) {
unset($types_noisette_filtres[$_type_noisette]);
break;
}
}
}
}
return $types_noisette_filtres;
}
/**
* Supprime tout ou partie des caches liés à la compilation uniquement.
* Ces caches sont entièrement gérés par N-Core sans appel à des services surchargeables.
*
* @api
*
* @param string $plugin Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
* ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
* @param array|string $fonctions Identifiants des fonctions permettant de nommer le cache : les valeurs possibles sont `ajax`, `inclusions`
* et `contextes`. Il est possible de fournir un tableau vide pour supprimer tous les caches
* ou juste une fonction sous forme de chaine.
*
* @return bool `false` si une erreur de fonction s'est produite, `true` sinon.
*/
function type_noisette_decacher($plugin, $fonctions = array()) {
// Retour de la fonction
$retour = false;
// Liste des fonctions valides pour cette API.
static $fonctions_autorisees = array('ajax', 'inclusions', 'contextes');
// Identification des caches à supprimer.
// -- si vide on supprime tous les caches.
// -- si une seule fonction est passée en chaine on la transforme en tableau.
// -- si une liste on vérifie que les fonctions sont valides.
if (!$fonctions) {
$fonctions = $fonctions_autorisees;
} elseif (is_string($fonctions)) {
$fonctions = array($fonctions);
} elseif (is_array($fonctions)) {
$fonctions = array_intersect($fonctions, $fonctions_autorisees);
} else {
$fonctions = array();
}
if ($fonctions) {
// Initialisation de l'identifiant du cache
include_spip('inc/ezcache_cache');
$cache = array(
'sous_dossier' => $plugin,
'objet' => 'type_noisette'
);
foreach ($fonctions as $_fonction) {
// Complétude de l'identifiant du cache
$cache['fonction'] = $_fonction;
// Suppression du cache spécifié.
cache_supprimer('ncore', 'stockage', $cache);
}
$retour = true;
}
return $retour;
}
/**
* Renvoie la liste des catégories de type de noisette et leur description.
* Il est possible de demander toutes les catégories ou juste une seule désignée par son identifiant.
*
* @api
*
* @param string $plugin Identifiant qui permet de distinguer le module appelant qui peut-être un plugin comme le noiZetier
* ou un script. Pour un plugin, le plus pertinent est d'utiliser le préfixe.
* @param string $id_categorie Identifiant de la catégorie ou chaine vide pour toutes les catégories.
*
* @return array Liste des descriptions de catégorie.
*/
function type_noisette_repertorier_categories($plugin, $id_categorie = '') {
// Stocker la liste descriptions de catégorie indexées par l'identifiant pour éviter le recalcul sur le même hit.
static $categories = array();
if (isset($categories[$plugin])) {
// On charge l'API de N-Core.
// Ce sont ces fonctions qui aiguillent ou pas vers une fonction spécifique du service.
include_spip('ncore/ncore');
// Chargement de la liste des catégories: celles par défaut de N-Core et les compléments par pipeline.
$categories[$plugin] = ncore_type_noisette_lister_categories($plugin);
}
if (
$id_categorie
and isset($categories[$plugin][$id_categorie])
) {
$categories_repertoriees = $categories[$plugin][$id_categorie];
} else {
$categories_repertoriees = $categories[$plugin];
}
return $categories_repertoriees;
}