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

524 lines
19 KiB
PHTML

<?php
/**
* API de gestion des objets territoire.
*
3 years ago
* @package SPIP\TERRITOIRES\API
*/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
if (!defined('_TERRITOIRE_ASYNCHRONE_PAR_JOB')) {
/**
* Mode de peuplement asynchrone:
* - `job`: utilisation de job_queue_add
* - `url`: utilisation d'une action.
*/
define('_TERRITOIRE_ASYNCHRONE_PAR_JOB', true);
}
/**
* Peuple soit les régions du monde, soit les pays ou soit les subdivisions d'un pays.
* La fonction utilise les données fournies par Nomenclatures.
*
* @api
*
* @param string $type Type de territoires. Prends les valeurs `zone`, `country`, `subdivision` ou `infrasubdivision`.
* @param string $pays Code ISO 3166-1 alpha2 du pays si le type est `subdivision` ou `infrasubdivision` sinon une chaine vide.
* @param array $options Tableau des options de peuplement:
* - `force` : si `true` force le repeuplement même si le sha est identique (`false` par défaut).
* - `extras`: tableau des types d'extras à peupler soit `code` pour les codes alternatifs
* et `info` pour le caractéristiques (table spip_territoires_extras)
*
* @return array Tableau retour de la fonction permettant de connaitre le résultat du traitement (utile pour l'affichage
* du message dans le formulaire de peuplement).
*/
function territoire_peupler($type, $pays, $options = array()) {
// On initialise le retour à une erreur nok
$retour = array(
'ok' => false,
'arg' => false,
'sha' => false,
'type' => $type,
'pays' => $pays,
'sync' => true
);
// Le peuplement dépend du type :
// - type = zone ou country : on charge l'ensemble des régions du monde ou l'ensemble des pays
// - type = subdivision ou infrasubdivision : il faut préciser le pays (code ISO alpha2) pour lequel on charge toutes les subdivisions
include_spip('inc/territoires_services');
include_spip('inc/territoires_utils');
if (type_pays_est_valide($type, $pays)) {
$timestamp['debut'] = microtime(true);
$erreur = true;
$type_identique = false;
// Lecture de la configuration statique du type de territoire
include_spip('inc/config');
$configuration = lire_config("territoires/${type}");
// Identification de la meta et de la variable de consigne
$meta = 'territoires_peuplement';
$consigne = consigne_identifier($type, $pays);
// On récupère tous les index fournis par la collection et pas uniquement celui des territoires concernés.
// Ainsi, les autres index éventuels restent disponibles.
$collection = territoires_acquerir($type, $pays);
$timestamp['acquisition'] = microtime(true);
if (!empty($collection[$configuration['champs']['index']])) {
// On extrait que l'index correspondant au type demandé
$territoires = $collection[$configuration['champs']['index']];
// Si le type de territoire est déjà chargé il possède un sha. Si le sha des data récupérées de Nomenclatures
// possèdent le même sha alors on ne fait aucun traitement et on indique que la configuration n'a pas
// changée. Dans ce cas, aucun traitement n'a lieu.
$sha_type = sha1(json_encode($territoires));
if (
sha_est_identique($sha_type, $meta, $consigne)
and empty($options['force'])
) {
$type_identique = true;
} else {
// Le sha a changé : il est donc licite de recharger les territoires.
// -- on préserve les éditions manuelles et les liens pour les réinjecter ensuite lors du
// rechargement
$sauvegardes = territoires_preserver($type, $pays);
$timestamp['preservation'] = microtime(true);
// -- on vide les territoires avant de les remettre (inutile de gérer les erreurs
// car l'insertion les détectera).
// On gère aussi les infra subdivisions qui doivent être vidées au préalable si on vide
// les subdivisions parents.
if (
($type === 'subdivision')
and territoire_est_peuple('infrasubdivision', $pays)
) {
territoire_depeupler('infrasubdivision', $pays);
}
territoire_depeupler($type, $pays);
$timestamp['vidage'] = microtime(true);
// -- on insère chaque territoire comme un objet
include_spip('action/editer_objet');
$erreur_insertion = false;
$ids = array();
$extras = array();
$meta_extras = array();
foreach ($territoires as $_territoire) {
// On initialise le territoire avec les noms de champs de la table spip_territoires
$territoire = enregistrement_initialiser($_territoire, $type, $pays);
// Si le code iso_continent est rempli c'est qu'on vient de charger les pays. Comme le code
// du continent est le code alpha2 on le remplace par le code M49 pour être homogène.
// -- la collection pays contient pour cela un bloc 'continents'.
if (!empty($territoire['iso_continent'])) {
$territoire['iso_continent'] = $collection['continents'][$territoire['iso_continent']]['code_num'];
}
// Intégrer les éventuelles modifications sauvegardées
if (isset($sauvegardes['editions'][$territoire['iso_territoire']])) {
// -- descriptif en fusionnant les traductions (priorité aux sauvegardes)
// -- remise à 'oui' de l'indicateur d'édition
$territoire['descriptif'] = traduction_fusionner(
$sauvegardes['editions'][$territoire['iso_territoire']]['descriptif'],
$territoire['descriptif']
);
$territoire['edite'] = 'oui';
}
if ($id = sql_insertq('spip_territoires', $territoire)) {
// On consigne le couple (iso, id) pour rétablir les liens si besoin
$ids[$territoire['iso_territoire']] = $id;
// Si demandé et que la configuration le prévoit préparer le tableau des extras inclus dans
// le bloc principal des territoires
if (!empty($options['extras'])) {
$source = $_territoire;
$source['iso_territoire'] = $territoire['iso_territoire'];
$extras = array_merge(
$extras,
extra_compiler(
'interne',
$options['extras'],
$source,
$configuration,
$type,
$pays,
$meta_extras
)
);
}
} else {
$erreur_insertion = true;
break;
}
}
$timestamp['insertion'] = microtime(true);
if (!$erreur_insertion) {
// Traitements des extras
if (!empty($options['extras'])) {
// Si la configuration le prévoit préparer le tableau des extras inclus dans un bloc annexe
if (!empty($configuration['extras'])) {
$extras = array_merge(
$extras,
extra_compiler(
'externe',
$options['extras'],
$collection,
$configuration,
$type,
$pays,
$meta_extras
)
);
}
// Ajouter tous les extras compilés pour le type de territoire.
if ($extras) {
sql_insertq_multi('spip_territoires_extras', $extras);
}
$timestamp['extras'] = microtime(true);
}
// On rétablit les liens vers les territoires et les logos
// -- les liens avec les autres objets
$config_lien = array(
'table' => 'spip_territoires_liens',
'id_table' => 'id_territoire'
);
liens_retablir('liens', $sauvegardes, $ids, $config_lien);
// -- les liens avec les logos
$config_lien = array(
'table' => 'spip_documents_liens',
'id_table' => 'id_objet'
);
liens_retablir('logos', $sauvegardes, $ids, $config_lien);
$timestamp['retablissement'] = microtime(true);
// Permettre à d'autres plugins de compléter le peuplement.
// -- par exemple le plugin Contours rétablit les liens vers les contours GIS (spip_liens_gis),
// les objets GIS étant conservés si ils existent déjà.
$flux = array(
'args' => array(
'type' => $type,
'pays' => $pays,
'sauvegardes' => $sauvegardes,
'ids_crees' => $ids,
),
'data' => array()
);
pipeline('post_peupler_territoire', $flux);
// On stocke les informations de chargement dans une meta.
$contenu = array(
'sha' => $sha_type,
'nbr' => count($territoires),
'maj' => date('Y-m-d H:i:s'),
'lic' => isset($collection['credits']) ? $collection['credits'] : array(),
'ext' => $meta_extras,
);
ecrire_config("${meta}/${consigne}", $contenu);
$timestamp['fin'] = microtime(true);
$erreur = false;
}
// Log du traitement pour debug
$duree = $timestamp['fin'] - $timestamp['debut'];
spip_log("Les territoires (Type '${type}' - Pays '${pays}') ont été chargées en ${duree} s", 'territoires' . _LOG_INFO_IMPORTANTE);
if (
defined('_LOG_FILTRE_GRAVITE')
and (_LOG_FILTRE_GRAVITE >= _LOG_DEBUG)
) {
$timestamp_debut = $timestamp['debut'];
foreach ($timestamp as $_periode => $_timestamp) {
if ($_periode !== 'debut') {
$timestamp_fin = $_timestamp;
$duree = ($timestamp_fin - $timestamp_debut) * 1000;
$timestamp_debut = $timestamp_fin;
spip_log("Période ${_periode}: ${duree} ms", 'territoires' . _LOG_DEBUG);
}
}
}
}
} else {
spip_log("Aucun territoire pour (Type '${type}' - Pays '${pays}') retourné par Nomenclatures", 'territoires' . _LOG_ERREUR);
}
// Si le territoire est en erreur, on stocke le cas d'erreur.
if ($erreur) {
if ($type_identique) {
$retour['sha'] = true;
spip_log("Les territoires de (Type '${type}' - Pays '${pays}') sont inchangés", 'territoires' . _LOG_AVERTISSEMENT);
} else {
$retour['ok'] = false;
}
} else {
$retour['ok'] = true;
}
} else {
$retour['arg'] = true;
spip_log("Le couple (Type '${type}' - Pays '${pays}') est invalide", 'territoires' . _LOG_ERREUR);
}
return $retour;
}
/**
* Appelle la fonction de peuplement de territoires en asynchrone.
*
* @api
*
* @param string $type Type de territoires. Prends les valeurs `zone`, `country`, `subdivision` ou `infrasubdivision`.
* @param string $pays Code ISO 3166-1 alpha2 du pays si le type est `subdivision` ou `infrasubdivision` sinon une chaine vide.
* @param array $options Tableau des options de peuplement:
* - `force` : si `true` force le repeuplement même si le sha est identique (`false` par défaut).
* - `extras`: tableau des types d'extras à peupler soit `code` pour les codes alternatifs
* et `info` pour le caractéristiques (table spip_territoires_extras)
*
* @return array Tableau retour de la fonction permettant de connaitre le résultat du traitement (utile pour l'affichage
* du message dans le formulaire de peuplement).
*/
function territoire_peupler_asynchrone($type, $pays, $options = array()) {
// On initialise le retour à une ok ce qui est le cas le plus fréquent car on ne fait que créer le job.
$retour = array(
'ok' => true,
'arg' => false,
'sha' => false,
'type' => $type,
'pays' => $pays,
'sync' => false
);
if (_TERRITOIRE_ASYNCHRONE_PAR_JOB) {
// Lancement du peuplement dans un job à déclenchement immédiat et de priorité maximale pour éviter d'attendre.
include_spip('inc/utils');
$arguments = array(
$type,
$pays,
$options
);
$retour['job'] = job_queue_add(
'territoire_peupler',
'Peuplement de territoires',
$arguments,
'inc/territoire',
true
);
if (!$retour['job']) {
$retour['ok'] = false;
}
} else {
// Construction de la chaine des arguments passés à l'action.
// -- type et pays sont toujours fournis même si le pays est la chaine vide.
$arguments = "${type}:${pays}";
// -- ajout des éventuelles options : on passe les options par leur identifiant
if (!empty($options['force'])) {
$arguments .= ":force";
}
if (!empty($options['extras'])) {
$arguments .= ':' . implode(':', $options['extras']);
}
// Génération de l'URL qui lancera le peuplement en asynchrone.
include_spip('inc/actions');
$url = generer_action_auteur('peupler_territoires', $arguments);
// Lancement du peuplement en asynchrone.
include_spip ('inc/queue');
$retour['ok'] = queue_lancer_url_http_async($url);
}
return $retour;
}
/**
* Supprime de la base soit les régions du monde, soit les pays ou soit les subdivisions d'un pays.
*
* @api
*
* @param string $type Type de territoires. Prends les valeurs `zone`, `country`, `subdivision` ou `infrasubdivision`.
* @param string $pays Code ISO 3166-1 alpha2 du pays si le type est `subdivision` ou `infrasubdivision` sinon une chaine vide.
* @param array $options Tableau des options de dépeuplement:
* - `force` : si `true` force le vidage même si la meta n'est pas présente (cas d'erreur
* sur timeout par exemple). La valeur par défaut est `false`.
*
* @return array Tableau retour de la fonction permettant de connaitre le résultat du traitement (utile pour l'affichage
* du message dans le formulaire de peuplement).
*/
function territoire_depeupler($type, $pays = '', $options = array()) {
// On initialise le retour à une erreur nok
$retour = array(
'ok' => false,
'arg' => false,
'sha' => false,
'type' => $type,
'pays' => $pays,
'sync' => true
);
// Le vidage dépend du type :
// - type = zone ou country : on vide l'ensemble des régions du monde ou l'ensemble des pays
// - type = subdivision ou infrasubdivision : il faut préciser le pays (code ISO alpha2) pour lequel on vide toutes les subdivisions
include_spip('inc/territoires_services');
include_spip('inc/territoires_utils');
if (type_pays_est_valide($type, $pays)) {
// Inutile de vider une table vide sauf si l'option de forçage est activée.
if (
territoire_est_peuple($type, $pays)
or !empty($options['force'])
) {
// Identification de la variable de consigne
$consigne = consigne_identifier($type, $pays);
// Avant de vider la table on réserve la liste des id de territoire qui seront supprimés
// de façon à vider ensuite les liens éventuels y compris ceux des logos.
$from = 'spip_territoires';
$where = array(
'type=' . sql_quote($type),
);
if (type_est_subdivision($type)) {
$where[] = 'iso_pays=' . sql_quote($pays);
}
if ($ids = sql_allfetsel('id_territoire', $from, $where)) {
$ids = array_column($ids, 'id_territoire');
$sql_ok = sql_delete($from, $where);
if ($sql_ok !== false) {
// Vider les extras si besoin
$sql_ok = sql_delete('spip_territoires_extras', $where);
if ($sql_ok !== false) {
// Vider les liens éventuels avec les logos (on gère pas d'erreur)
$where_logo = array(
sql_in('id_objet', $ids),
'objet=' . sql_quote('territoire')
);
sql_delete('spip_documents_liens', $where_logo);
// Vider les liens éventuels avec les autres objets (on gère pas d'erreur)
$where_lien = array(
sql_in('id_territoire', $ids),
);
sql_delete('spip_territoires_liens', $where_lien);
// Permettre à d'autres plugins de compléter le dépeuplement.
// -- par exemple le plugin Contours supprime les liens vers les contours GIS (spip_liens_gis),
// mais pas les objets GIS.
$flux = array(
'args' => array(
'type' => $type,
'pays' => $pays,
'ids_territoire' => $ids,
),
'data' => array()
);
pipeline('post_depeupler_territoire', $flux);
// Supprimer la meta propre au pays même si le vidage des liens est en erreur.
include_spip('inc/config');
effacer_config("territoires_peuplement/${consigne}");
}
// Enregistrer le succès ou pas du déchargement de la table
if ($sql_ok !== false) {
// Succès complet
$retour['ok'] = true;
spip_log("Les territoires (Type '${type}' - Pays '${pays}') ont été vidés avec succès ainsi que les extras", 'territoires' . _LOG_DEBUG);
} else {
spip_log("Les territoires (Type '${type}' - Pays '${pays}') ont été vidés avec succès mais erreur lors du vidage des extras", 'territoires' . _LOG_ERREUR);
}
} else {
spip_log("Erreur de vidage des territoires (Type '${type}' - Pays '${pays}')", 'territoires' . _LOG_ERREUR);
}
}
} else {
$retour['sha'] = true;
spip_log("Aucun territoire (Type '${type}' - Pays '${pays}') à vider", 'territoires' . _LOG_AVERTISSEMENT);
}
} else {
$retour['arg'] = true;
spip_log("Le couple (Type '${type}' - Pays '${pays}') est invalide", 'territoires' . _LOG_ERREUR);
}
return $retour;
}
/**
3 years ago
* Teste si un type de territoire est chargé en base.
* La fonction lit la meta de chargement et non la table `spip_territoires`.
*
* @api
*
* @param string $type Type de territoires. Prends les valeurs `zone`, `country`, `subdivision` ou `infrasubdivision`.
* @param array|string $pays Code ISO 3166-1 alpha2 du pays si le type est `subdivision` ou `infrasubdivision` sinon une chaine vide.
*
* @return bool `true` si le territoire est chargé, `false` sinon.
*/
function territoire_est_peuple($type, $pays = '') {
// Initialisation de la liste
$est_peuple = false;
// La liste des territoires chargés est en meta.
include_spip('inc/config');
$peuplement = lire_config('territoires_peuplement', array());
if (
include_spip('inc/territoires_utils')
and type_est_subdivision($type)
) {
// Chaque pays chargé est un index du tableau
if (isset($peuplement[$type])) {
$est_peuple = array_key_exists($pays, $peuplement[$type]);
}
} else {
$est_peuple = isset($peuplement[$type]);
}
return $est_peuple;
}
/**
* Teste si les codes alternatifs ou les caractéristiques additionnelles dun type de territoire sont chargées en base.
* La fonction lit la meta de chargement et non la table `spip_territoires_extras`.
*
* @api
*
* @param string $type Type de territoires. Prends les valeurs `zone`, `country`, `subdivision` ou `infrasubdivision`.
* @param string $pays Code ISO 3166-1 alpha2 du pays si le type est `subdivision` ou `infrasubdivision` sinon une chaine vide.
* @param string $type_extra Type d'extra. Prends les valeurs `code` ou `info`.
*
* @return bool `true` si le territoire est chargé, `false` sinon.
*/
function territoire_extra_est_peuple($type, $pays, $type_extra) {
// Initialisation de la liste
$est_peuple = false;
// La liste des territoires chargés est en meta.
include_spip('inc/config');
$peuplement = lire_config('territoires_peuplement', array());
if (
include_spip('inc/territoires_utils')
and type_est_subdivision($type)
) {
// Chaque pays chargé est un index du tableau
if (isset($peuplement[$type][$pays]['ext'])) {
$est_peuple = in_array($type_extra, $peuplement[$type][$pays]['ext']);
}
} else {
if (isset($peuplement[$type]['ext'])) {
$est_peuple = in_array($type_extra, $peuplement[$type]['ext']);
}
}
return $est_peuple;
}