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/territoires_utils.php

399 lines
14 KiB
PHP

<?php
/**
* Fonctions utilitaires du plugin Territoires appelées par les API.
*
* @package SPIP\TERRITOIRES\OUTILS
*/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
if (!defined('_TERRITOIRE_URL_BASE_ISOCODE')) {
/**
* Endpoint de l'API REST hébergeant les données de Nomenclatures.
*/
define('_TERRITOIRE_URL_BASE_ISOCODE', 'https://contrib.spip.net/http.api/ezrest');
}
/**
* Acquiert les données de territoires disponibles dans Nomenclatures.
* La fonction utilise l'API REST de Nomenclatures.
*
* @internal
*
* @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 Permet de demander l'exclusion (`exclure`) de certains index si ceux-ci sont inutilisés
* ou de ne retourner qu'un seul index (`index`).
*
* @return array
*/
function territoires_acquerir($type, $pays = '', $options = array()) {
// Initialiser les territoires à vide pour gérer une éventuelle erreur de type.
$territoires = array();
// Déterminer la collection et les conditions à appliquer.
$filtres = array();
$collection = array();
if ($type === 'zone') {
$collection = 'zones';
} elseif ($type === 'country') {
$collection = 'pays';
} elseif (
include_spip('inc/territoires_utils')
and type_est_subdivision($type)
) {
$collection = "${type}s";
if ($pays) {
$filtres = array('pays' => $pays);
}
}
// Collectionner les territoires avec l'API REST de Nomenclatures
if ($collection) {
// Ajouter les exclusions si nécessaire
if (!empty($options['exclure'])) {
$filtres['exclure'] = $options['exclure'];
}
// Appel à l'API REST de Nomenclatures
$requeter = charger_fonction('requeter_isocode', 'inc');
if ($data = $requeter(_TERRITOIRE_URL_BASE_ISOCODE, $collection, $filtres)) {
$territoires = $data['donnees'];
}
// Si on a demandé un seul index on le renvoie seul sinon on renvoie le tableau complet.
if (
!empty($options['index'])
and isset($territoires[$options['index']])
) {
$territoires = $territoires[$options['index']];
}
}
return $territoires;
}
/**
* Extrait, pour un les régions, les pays ou les subdivisions d'un pays, la liste des territoires ayant fait l'objet
* d'une modification manuelle (descriptif) et la liste associations vers ses mêmes territoires.
* Les extras ne sont pas sauvegardés car il ne sont ni modifiables ni indexés par un id mais par un code invariant.
*
* @internal
*
* @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.
*
* @return array
*/
function territoires_preserver($type, $pays = '') {
// Initialisation de la table et de la condition de base sur le type et éventuellement le pays.
$territoires = array();
$from = 'spip_territoires';
$where = array(
'type=' . sql_quote($type),
);
if (type_est_subdivision($type)) {
$where[] = 'iso_pays=' . sql_quote($pays);
}
// Extraction des liens vers les territoires du pays
$where_lien = array(
sql_in_select('id_territoire', 'id_territoire', $from, $where)
);
$territoires['liens'] = sql_allfetsel('*', 'spip_territoires_liens', $where_lien);
// Extraction des liens de logos vers les territoires du pays
$where_logo = array(
'objet=' . sql_quote('territoire'),
sql_in_select('id_objet', 'id_territoire', $from, $where)
);
$territoires['logos'] = sql_allfetsel('*', 'spip_documents_liens', $where_logo);
// Extraction de la correspondance id-iso pour les liens et les logos
$territoires['ids'] = array();
$select = array('id_territoire', 'iso_territoire');
// -- les liens
$where_ids = array(
sql_in('id_territoire', array_column($territoires['liens'], 'id_territoire'))
);
if ($ids = sql_allfetsel($select, $from, $where_ids)) {
$territoires['ids'] = array_column($ids, 'id_territoire', 'iso_territoire');
}
// -- les logos
$where_ids = array(
sql_in('id_territoire', array_column($territoires['logos'], 'id_objet'))
);
if ($ids = sql_allfetsel($select, $from, $where_ids)) {
$ids = array_column($ids, 'id_territoire', 'iso_territoire');
$territoires['ids'] = array_merge($territoires['ids'], $ids);
}
// Extraction des territoires éditées.
// -- détermination de la liste des champs éditables.
$territoires['editions'] = array();
include_spip('base/objets');
$description_table = lister_tables_objets_sql($from);
// -- pour le select, les champs éditables sont complétés par le code ISO.
$select = array_merge($description_table['champs_editables'], array('iso_territoire'));
$where[] = 'edite=' . sql_quote('oui');
if ($editions = sql_allfetsel($select, $from, $where)) {
// -- indexer le tableau par le code iso de façon à simplifier la réintégration.
$territoires['editions'] = array_column($editions, null, 'iso_territoire');
}
// Permettre à d'autres plugins de compléter la sauvegarde (principalement pour les liens).
// -- par exemple le plugin Contours sauvegarde les liens vers les contours GIS (spip_liens_gis).
$flux = array(
'args' => array(
'type' => $type,
'pays' => $pays,
),
'data' => $territoires
);
$territoires = pipeline('post_preserver_territoire', $flux);
return $territoires;
}
/**
* Fusionne les traductions d'une balise `<multi>` avec celles d'une autre balise `<multi>`.
* L'une des balise est considérée comme prioritaire ce qui permet de régler le cas où la même
* langue est présente dans les deux balises.
* Si on ne trouve pas de balise `<multi>` dans l'un ou l'autre des paramètres, on considère que
* le texte est tout même formaté de la façon suivante : texte0[langue1]texte1[langue2]texte2...
*
* @internal
*
* @param string $multi_prioritaire Balise multi considérée comme prioritaire en cas de conflit sur une langue.
* @param string $multi_non_prioritaire Balise multi considérée comme non prioritaire en cas de conflit sur une langue.
*
* @return string
* La chaine construite est toujours une balise `<multi>` complète ou une chaine vide sinon.
*/
function traduction_fusionner($multi_prioritaire, $multi_non_prioritaire) {
$multi_merge = '';
// On extrait le contenu de la balise <multi> si elle existe.
$multi_prioritaire = trim($multi_prioritaire);
$multi_non_prioritaire = trim($multi_non_prioritaire);
// Si les deux balises sont identiques ou si la balise non prioritaire est vide on sort directement
// avec le multi prioritaire ce qui améliore les performances.
if (
($multi_prioritaire == $multi_non_prioritaire)
or !$multi_non_prioritaire
) {
$multi_merge = $multi_prioritaire;
} else {
include_spip('inc/filtres');
if (preg_match(_EXTRAIRE_MULTI, $multi_prioritaire, $match)) {
$multi_prioritaire = trim($match[1]);
}
if (preg_match(_EXTRAIRE_MULTI, $multi_non_prioritaire, $match)) {
$multi_non_prioritaire = trim($match[1]);
}
if ($multi_prioritaire) {
if ($multi_non_prioritaire) {
// On extrait les traductions sous forme de tableau langue=>traduction.
$traductions_prioritaires = extraire_trads($multi_prioritaire);
$traductions_non_prioritaires = extraire_trads($multi_non_prioritaire);
// On complète les traductions prioritaires avec les traductions non prioritaires dont la langue n'est pas
// présente dans les traductions prioritaires.
foreach ($traductions_non_prioritaires as $_lang => $_traduction) {
if (!array_key_exists($_lang, $traductions_prioritaires)) {
$traductions_prioritaires[$_lang] = $_traduction;
}
}
// On construit le contenu de la balise <multi> mergé à partir des traductions prioritaires mises à jour.
// Les traductions vides sont ignorées.
ksort($traductions_prioritaires);
foreach ($traductions_prioritaires as $_lang => $_traduction) {
if ($_traduction) {
$multi_merge .= ($_lang ? '[' . $_lang . ']' : '') . trim($_traduction);
}
}
} else {
$multi_merge = $multi_prioritaire;
}
} else {
$multi_merge = $multi_non_prioritaire;
}
// Si le contenu est non vide on l'insère dans une balise <multi>
if ($multi_merge) {
$multi_merge = '<multi>' . $multi_merge . '</multi>';
}
}
return $multi_merge;
}
/**
* Initialise les champs de l'objet territoire à partir d'un élément reçu de l'API REST et en utilisant la configuration
* du type concerné et complète avec le type, le nom d'usage, le descriptif et le parent.
*
* @param array $territoire
* @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.
*
* @internal
*
* @return array
*/
function enregistrement_initialiser($territoire, $type, $pays = '') {
// Traduire les champs de Isocode en champs pour Territoires
include_spip('inc/config');
$enregistrement = array();
$champs = lire_config("territoires/${type}/champs/base");
foreach ($champs as $_champ_api => $_champ_territoire) {
$enregistrement[$_champ_territoire] = $territoire[$_champ_api];
}
// Compléter systématiquement avec le type, le nom d'usage qui pour l'instant n'est pas fourni et le descriptif
// TODO : pour l'instant Nomenclatures ne fournit pas de nom d'usage ni de descriptif.
$enregistrement['type'] = $type;
$enregistrement['nom_usage'] = preg_replace("#\s+\([^)]*\)#", '', $enregistrement['iso_titre']);
$enregistrement['descriptif'] = '';
// Gestion des parentés inter-types : on remplit systématiquement le champ parent pour créer une hiérarchie complète
// inter-type et ce que le type parent soit peuplé ou pas.
// Cela revient :
// -- à ajouter le pays d'appartenance des subdivisions de plus haut niveau
if (
($type === 'subdivision')
and (!$enregistrement['iso_parent'])
) {
// Le pays d'appartenance est toujours inclus dans le champ iso_pays
$enregistrement['iso_parent'] = $enregistrement['iso_pays'];
}
// -- à ajouter la région d'appartenance des pays (pas de hiérarchie dans les pays) et dupliquer le code du pays
// dans le champ iso_pays pour faciliter les filtres.
if ($type === 'country') {
// La région d'appartenance est toujours inclus dans le champ code_num_region fourni par Nomenclatures
$enregistrement['iso_parent'] = $territoire['code_num_region'];
$enregistrement['iso_pays'] = $enregistrement['iso_territoire'];
}
return $enregistrement;
}
/**
* Compile, en fonction du mode de récupération (extra avec les données de territoire ou à part), les données
* extra à insérer dans la table `spip_territoires_extras`.
*
* @internal
*
* @param string $mode
* @param array $types_extras
* @param array $source
* @param array $configuration
* @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 $meta_extras
*
* @return array
*/
function extra_compiler($mode, $types_extras, $source, $configuration, $type, $pays, &$meta_extras) {
$extras = array();
foreach ($types_extras as $_type_extra) {
// Vérifier qu'il existe des extras pour le type de territoire
// -- on traite les deux modes d'extraction : interne à un objet territoire ou un bloc externe
if (
(
($mode === 'interne')
and !empty($configuration['champs']['extras'][$_type_extra])
)
or (
($mode !== 'interne')
and !empty($configuration['extras'][$_type_extra])
and isset($source[$configuration['extras'][$_type_extra]['index']])
)
) {
// On initialise les données communes de chaque extra
$extra_defaut = array(
'type_extra' => $_type_extra,
'type' => $type,
'iso_pays' => $pays,
);
// Suivant le mode on enregistre chaque extra détecté:
// - mode interne : chaque champ extra du territoire devient un objet extra de la table spip_territoires_extras
// - mode externe : chaque élément du bloc idoine de la collection devient un objet extra
$source_extras = ($mode === 'interne')
? $configuration['champs']['extras'][$_type_extra]
: $source[$configuration['extras'][$_type_extra]['index']];
foreach ($source_extras as $_cle => $_valeur) {
$extra = $extra_defaut;
$extra['iso_territoire'] = ($mode === 'interne')
? $source['iso_territoire']
: $_valeur[$configuration['extras'][$_type_extra]['cle_iso']];
$extra['extra'] = ($mode === 'interne')
? $_valeur
: (substr($configuration['extras'][$_type_extra]['champs']['extra'], 0, 1) === '#'
? ltrim($configuration['extras'][$_type_extra]['champs']['extra'], '#')
: $_valeur[$configuration['extras'][$_type_extra]['champs']['extra']]);
$extra['valeur'] = ($mode === 'interne')
? $source[$_cle]
: $_valeur[$configuration['extras'][$_type_extra]['champs']['valeur']];
$extras[] = $extra;
}
// Enregistrer le type d'extra pour la meta de consignation
if (!in_array($_type_extra, $meta_extras)) {
$meta_extras[] = $_type_extra;
}
}
}
return $extras;
}
/**
* Vérifie si le type de territoire est `subdivision` ou `infra-subdivision`.
*
* @internal
*
* @param string $type Type de territoires. Prends les valeurs `zone`, `country`, `subdivision` ou `infrasubdivision`.
*
* @return bool `true` si le couple (type, pays) est valide, `false` sinon.
*/
function type_est_subdivision($type) {
$est_subdivision = false;
// On récupère le sha de la table dans les metas si il existe (ie. la table a été chargée)
if (
($type === 'subdivision')
or ($type === 'infrasubdivision')
) {
$est_subdivision = true;
}
return $est_subdivision;
}
/**
* Identifie la variable de configuration à partir du type de territoire et éventuellement du pays pour les
* subdivisions.
*
* @internal
*
* @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.
*
* @return string
*/
function consigne_identifier($type, $pays = '') {
return (type_est_subdivision($type))
? "${type}/${pays}"
: $type;
}