Compare commits

...

1 Commits

@ -626,3 +626,178 @@ function territoire_extra_est_peuple($type, $pays, $type_extra) {
return $est_peuple;
}
/**
* Lit un objet donné connu par son id ou par un identifiant textuel unique et renvoie tout ou partie de sa
* description.
* Il est possible pour un objet donné de fournir la fonction `<objet>_lire_champs` qui renvoie simplement tous les
* champs de l'objet concerné sans aucun autre traitement. Sinon, l'appel SQL est réalisé par l'API.
*
* @param string $objet Type d'objet (comme article ou rubrique)
* @param int|string $valeur_id Valeur du champ identifiant
* @param array $options Tableau d'options dont les index possibles sont:
* - champs : liste des champs à renvoyer. Si absent ou vide la fonction
* renvoie tous les champs.
* - champ_id : nom du champ utilisé comme identifiant de l'objet. Si absent ou vide on
* utilise l'id défini dans la déclaration de l'objet.
* - force : true pour reforcer une lecture en base meme si un cache existe
*
* @return array|string|int|bool
* si champs est non fourni ou au format array
* false : l'objet demande n'existe pas
* array vide : l'objet existe, mais aucun champ demande n'existe
* array non vide : objet avec le ou les champs demandes existants (les champs demandes non existant sont absent)
* si champs est fourni au format string
* false : l'objet demande n'existe pas OU le champs demande n'existe pas
* string|int : valeur du champ demande pour l'objet demande
*/
function objet_lire($objet, $valeur_id, $options = []) {
if (($t = objet_type($objet)) !== $objet) {
spip_log("objet_lire: appel avec type $objet invalide au lieu de $t", 'editer' . _LOG_INFO_IMPORTANTE);
$objet = $t;
}
// tableau du cache des descriptions et des id d'objet (au sens id_xxx).
// Les tableaux sont toujours indexés par le trio [objet][cle][valeur_cle]
static $descriptions = [];
// On détermine le nom du champ id de la table.
include_spip('base/objets');
$primary = id_table_objet($objet);
// On détermine l'id à utiliser.
$champ_id = (!empty($options['champ_id']) ? $options['champ_id'] : $primary);
// Si l'objet n'a pas encore été stocké, il faut récupérer sa description complète.
if (
!isset($descriptions[$objet][$champ_id][$valeur_id])
or (isset($options['force']) and $options['force'])
) {
// Il est possible pour un type d'objet de fournir une fonction de lecture de tous les champs d'un objet.
if (
include_spip('action/editer_' . $objet)
and function_exists($lire = "${objet}_lire_champs")
) {
$valeurs = $lire($objet, $valeur_id, $champ_id);
} else {
// On récupère la table SQL à partir du type d'objet.
$table = table_objet_sql($objet);
// La condition est appliquée sur le champ désigné par l'utilisateur.
$where = [
"${champ_id}=" . sql_quote($valeur_id)
];
// Acquisition de tous les champs de l'objet : si l'accès SQL retourne une erreur on renvoie un tableau vide.
$valeurs = sql_fetsel('*', $table, $where);
}
if (!$valeurs) {
$valeurs = false;
}
$descriptions[$objet][$champ_id][$valeur_id] = $valeurs;
if ($champ_id !== $primary and isset($valeurs[$primary])) {
$descriptions[$objet][$primary][$valeurs[$primary]] = $valeurs;
$descriptions[$objet][$champ_id][$valeur_id] = &$descriptions[$objet][$primary][$valeurs[$primary]];
}
}
$retour = $descriptions[$objet][$champ_id][$valeur_id];
// On ne retourne maintenant que les champs demandés.
// - on détermine les informations à renvoyer.
if ($retour and !empty($options['champs'])) {
$champs = $options['champs'];
// Extraction des seules informations demandées.
// -- si on demande une information unique on renvoie la valeur simple, sinon on renvoie un tableau.
// -- si une information n'est pas un champ valide elle n'est pas renvoyée sans renvoyer d'erreur.
if (is_array($champs)) {
// Tableau des informations valides
$retour = array_intersect_key($retour, array_flip($champs));
} else {
// Valeur unique demandée.
$retour = ($retour[$champs] ?? false);
}
}
return $retour;
}
/**
* Lancer le cron via un hit http sans attendre le resultat
* @param string $url_cron
* @return bool : true si l'url a pu être appelée en asynchrone, false sinon
*/
function queue_lancer_url_http_async($url_cron) {
// methode la plus rapide :
// Si fsockopen est possible, on lance le cron via un socket en asynchrone
// si fsockopen echoue (disponibilite serveur, firewall) on essaye pas cURL
// car on a toutes les chances d'echouer pareil mais sans moyen de le savoir
// mais on renvoie false direct
if (function_exists('fsockopen')) {
$parts = parse_url($url_cron);
switch ($parts['scheme']) {
case 'https':
$scheme = 'ssl://';
$port = 443;
break;
case 'http':
default:
$scheme = '';
$port = 80;
}
$fp = @fsockopen(
$scheme . $parts['host'],
$parts['port'] ?? $port,
$errno,
$errstr,
1
);
if ($fp) {
$host_sent = $parts['host'];
if (isset($parts['port']) and $parts['port'] !== $port) {
$host_sent .= ':' . $parts['port'];
}
$timeout = 200; // ms
stream_set_timeout($fp, 0, $timeout * 1000);
$query = $parts['path'] . ($parts['query'] ? '?' . $parts['query'] : '');
$out = 'GET ' . $query . " HTTP/1.1\r\n";
$out .= 'Host: ' . $host_sent . "\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
spip_timer('read');
$t = 0;
// on lit la reponse si possible pour fermer proprement la connexion
// avec un timeout total de 200ms pour ne pas se bloquer
while (!feof($fp) and $t < $timeout) {
@fgets($fp, 1024);
$t += spip_timer('read', true);
spip_timer('read');
}
fclose($fp);
return true;
}
}
// si fsockopen n'est pas dispo on essaye cURL :
// lancer le cron par un cURL asynchrone si cURL est present
elseif (function_exists('curl_init')) {
//setting the curl parameters.
$ch = curl_init($url_cron);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// cf bug : http://www.php.net/manual/en/function.curl-setopt.php#104597
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
// valeur mini pour que la requete soit lancee
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200);
// lancer
curl_exec($ch);
// fermer
curl_close($ch);
return true;
}
return false;
}

@ -1,10 +1,10 @@
<paquet
prefix="territoires"
categorie="divers"
version="1.2.1"
etat="test"
compatibilite="]3.2.999;4.0.*]"
logo="territoires.svg"
version="0.1.0"
etat="dev"
compatibilite="[3.2.0;3.2.*]"
logo="territoires.png"
documentation="https://contrib.spip.net/Territoires-utilisation-du-plugin"
schema="4"
>
@ -21,6 +21,7 @@
<pipeline nom="post_peupler_territoire" action="" />
<pipeline nom="post_preserver_territoire" action="" />
<pipeline nom="post_depeupler_territoire" action="" />
<pipeline nom="exclure_id_conditionnel" inclure="base/territoires.php" />
<pipeline nom="declarer_tables_objets_sql" inclure="base/territoires.php" />
<pipeline nom="declarer_tables_interfaces" inclure="base/territoires.php" />

@ -243,3 +243,146 @@ function balise_TERRITOIRE_EXTRAS_dist($p) {
return $p;
}
/**
* Compile le critère `{id_?}`
*
* Ajoute automatiquement à la boucle des contraintes (nommées sélections conditionnelles)
* équivalentes à `{id_article ?}{id_rubrique ?}...`, adaptées à la table en cours dutilisation.
*
* Les champs sélectionnés par défaut sont :
* - chaque champ id_xx de la table en cours dutilisation (par exemple id_secteur sur la boucle ARTICLES)
* - un champ 'objet', si cette table en dispose
* - chaque clé primaire des tables des objets éditoriaux, sils sont éditables et liables (par exemple id_mot).
*
* @example
* ```
* <BOUCLE_liste_articles(ARTICLES){id_?}{tout}> ...
* Est équivalent (selon les plugins actifs) à :
* <BOUCLE_liste_articles(ARTICLES){id_article?}{id_rubrique?}{id_secteur?}{id_trad?}{id_mot?}{id_document?} ... {tout}> ...
* ```
*
* @uses lister_champs_id_conditionnel()
* @param string $idb Identifiant de la boucle
* @param array $boucles AST du squelette
* @param Critere $crit Paramètres du critère dans cette boucle
* @return void
*/
function critere_id__dist($idb, &$boucles, $crit) {
/** @var Boucle $boucle */
$boucle = $boucles[$idb];
$champs = lister_champs_id_conditionnel(
$boucle->show['table'],
$boucle->show,
$boucle->sql_serveur
);
// ne pas tenir compte des critères identiques déjà présents.
if (!empty($boucle->modificateur['criteres'])) {
$champs = array_diff($champs, array_keys($boucle->modificateur['criteres']));
}
// nous aider en mode debug.
$boucle->debug[] = 'id_ : ' . implode(', ', $champs);
$boucle->modificateur['id_'] = $champs;
// créer un critère {id_xxx?} de chaque champ retenu
foreach ($champs as $champ) {
$critere_id_table = new Critere();
$critere_id_table->op = $champ;
$critere_id_table->cond = '?';
$critere_id_table->ligne = $crit->ligne;
calculer_critere_DEFAUT_dist($idb, $boucles, $critere_id_table);
}
}
/**
* Liste les champs qui peuvent servir de selection conditionnelle à une table SQL
*
* Retourne, pour la table demandée :
* - chaque champ id_xx de la table en cours dutilisation (par exemple id_secteur sur la boucle ARTICLES)
* - un champ 'objet' si la table le contient (pour les tables avec objet / id_objet par exemple)
* - chaque clé primaire des tables des objets éditoriaux qui peuvent se lier facilement à cette table,
* -- soit parce que sa clé primaire de la table demandée est un champ dans la table principale
* -- soit parce quune table de liaison existe, dun côté ou de lautre
*
* @pipeline_appel exclure_id_conditionnel
* @param string $table Nom de la table SQL
* @param array|null $desc Description de la table SQL, si connu
* @param string $serveur Connecteur sql a utiliser
* @return array Liste de nom de champs (tel que id_article, id_mot, id_parent ...)
*/
function lister_champs_id_conditionnel($table, $desc = null, $serveur = '') {
// calculer la description de la table
if (!is_array($desc)) {
$desc = description_table($table, $serveur);
}
if (!$desc) {
return [];
}
// Les champs id_xx de la table demandée
$champs = array_filter(
array_keys($desc['field']),
function ($champ) {
return strpos($champ, 'id_') === 0 or (in_array($champ, ['objet']));
}
);
// Si le champ id_rubrique appartient à la liste et si id_secteur n'est pas inclus on le rajoute.
if (
in_array('id_rubrique', $champs)
and !in_array('id_secteur', $champs)
) {
$champs[] = 'id_secteur';
}
// On ne fera pas mieux pour les tables dun autre serveur
if ($serveur) {
return $champs;
}
$primary = false;
$associable = false;
include_spip('action/editer_liens');
if (isset($desc['type'])) {
$primary = id_table_objet($desc['type']);
$associable = objet_associable($desc['type']);
}
if (isset($desc['field']['id_objet']) and isset($desc['field']['objet'])) {
$associable = true;
}
// liste de toutes les tables principales, sauf la notre
$tables = lister_tables_objets_sql();
unset($tables[$table]);
foreach ($tables as $_table => $_desc) {
if (
$associable
or ($primary and in_array($primary, array_keys($_desc['field'])))
or objet_associable($_desc['type'])
) {
$champs[] = id_table_objet($_table);
}
}
$champs = array_values(array_unique($champs));
// Exclusions de certains id
$exclusions = pipeline(
'exclure_id_conditionnel',
[
'args' => [
'table' => $table,
'id_table_objet' => $primary,
'associable' => $associable,
],
'data' => [],
]
);
$champs = array_diff($champs, $exclusions);
return $champs;
}

@ -36,10 +36,9 @@ function territoires_affiche_milieu($flux) {
// Insertion des extras de type 'info' chargés pour ce type de territoire
if ($objet === 'territoire') {
// -- acquisition de l'objet
include_spip('action/editer_objet');
include_spip('inc/territoire');
$territoire = objet_lire('territoire', $id_objet);
// -- tester si les extras 'info' sont chargées pour le type de territoire
include_spip('inc/territoire');
$extra_peuple = territoire_extra_est_peuple($territoire['type'], $territoire['iso_pays'], 'info');
if ($extra_peuple) {
$texte .= recuperer_fond(
@ -111,10 +110,9 @@ function territoires_affiche_gauche($flux) {
) {
// Insertion des extras de type 'code' chargés pour ce type de territoire
// -- acquisition de l'objet
include_spip('action/editer_objet');
include_spip('inc/territoire');
$territoire = objet_lire('territoire', $id_objet);
// -- tester si les extras 'code' sont chargées pour le type de territoire
include_spip('inc/territoire');
$extra_peuple = territoire_extra_est_peuple($territoire['type'], $territoire['iso_pays'], 'code');
if ($extra_peuple) {
$texte .= recuperer_fond(

Loading…
Cancel
Save