diff --git a/ezmashup/territoires_stats.php b/ezmashup/territoires_stats.php index ca9405526c886c5d7af5243ddf25e6f021a33cb3..3d014b9677fc0e97a1ba3db768d25498a190c733 100644 --- a/ezmashup/territoires_stats.php +++ b/ezmashup/territoires_stats.php @@ -216,6 +216,7 @@ function territoires_stats_item_completer(array $item, array $feed) : array { $code and isset($codes_to_iso[$code]) ) { + // On ajoute un champ dans l'item pour retrouver le code primaire $item['iso_feed'] = $codes_to_iso[$code]; } else { // Le code primaire est introuvable, on ne retient pas cet item diff --git a/formulaires/creer_feed_territoires.html b/formulaires/creer_feed_territoires.html index a8972f0f0104530daa0f0a02d8c9b9e1e9f26f4b..9c002bca5236c01877b2e20113d78b76b2e5d167 100644 --- a/formulaires/creer_feed_territoires.html +++ b/formulaires/creer_feed_territoires.html @@ -1,5 +1,5 @@ -<div class="formulaire_spip formulaire_editer formulaire_#FORM"> - <h3 class="titrem"><:cartes_territoires:titre_etape_creer{etape=#ENV{_etape}, etapes=#ENV{_etapes}}:></h3> +<div class="formulaire_spip formulaire_#FORM"> + <h3 class="titrem"><:territoires:titre_etape_en_cours{etape=#ENV{_etape}, etapes=#ENV{_etapes}}:></h3> [<p class="reponse_formulaire reponse_formulaire_erreur">(#ENV*{message_erreur})</p>] @@ -13,7 +13,6 @@ label=<:territoires_stats:label_feed_type_territoire:>, data=#ENV{_types_territoire}, defaut=#ENV{_type_territoire_defaut}, - obligatoire=oui, })] <BOUCLE_pays_type(DATA) {source table, #ENV{_pays}}> @@ -43,7 +42,6 @@ type=file, explication=<:territoires_stats:explication_feed_fichier:>, label=<:territoires_stats:label_feed_fichier:>, - obligatoire=oui, conteneur_class=bloc_fichier_source, })] @@ -52,6 +50,16 @@ label=<:territoires_stats:label_feed_url:>, conteneur_class=bloc_url_source, })] + + [(#SAISIE{input, extra, + explication=<:territoires_stats:explication_feed_extra:>, + label=<:territoires_stats:label_feed_extra:>, + })] + + [(#SAISIE{input, titre_extra, + explication=<:territoires_stats:explication_feed_extra_titre:>, + label=<:territoires_stats:label_feed_extra_titre:>, + })] </div> </fieldset> diff --git a/formulaires/creer_feed_territoires.php b/formulaires/creer_feed_territoires.php index c325b4346e22dcb1bdae61739952ada9ada2b834..0d070b9e6c9ce352d46a487f15b117dcc753ddb8 100644 --- a/formulaires/creer_feed_territoires.php +++ b/formulaires/creer_feed_territoires.php @@ -12,6 +12,25 @@ if (!defined('_ECRIRE_INC_VERSION')) { * @return array Tableau des données à charger par le formulaire. */ function formulaires_creer_feed_territoires_charger() : array { + // Etape 1 + // Récupération des saisies éventuelles de l'étape 1 + $valeurs = [ + 'type' => _request('type') ?? '', + 'type_source' => _request('type_source') ?? '', + 'fichier_source' => _request('fichier_source') ?? '', + 'url_source' => _request('url_source') ?? '', + 'extra' => _request('extra') ?? '', + 'titre_extra' => _request('titre_extra') ?? '', + ]; + include_spip('inc/config'); + $types_territoire = lire_config('territoires/types'); + foreach ($types_territoire as $_type) { + if (lire_config("territoires/{$_type}/populated_by_country", false)) { + $variable_pays = 'pays_' . $_type; + $valeurs[$variable_pays] = _request($variable_pays) ?? ''; + } + } + // Initialisation des paramètres de l'unité de peuplement // -- Liste des types de territoire. include_spip('inc/config'); @@ -49,6 +68,27 @@ function formulaires_creer_feed_territoires_charger() : array { ]; $valeurs['_type_source_defaut'] = 'api'; + // Etape 2 + // Initialisation des saisies relatives au mapping et à l'identification du feed + // -- options de décodage : délimiteur si fichier CSV, racine à partir de laquelle paramétrer le mapping + $valeurs['_label_decodage'] = _request('_label_decodage'); + $valeurs['_explication_decodage'] = _request('_explication_decodage'); + // -- Type de code de territoire utilisé dans le jeu de données + $valeurs['_types_code'] = _request('_types_code'); + $valeurs['_type_code_defaut'] = _request('_type_code_defaut'); + // -- Format de la source et feed id par défaut + $valeurs['_format_source'] = _request('_format_source'); + $valeurs['_feed_id_defaut'] = _request('_feed_id_defaut'); + + // Récupération des saisies éventuelles de l'étape 2 + $valeurs['type_code'] = _request('type_code') ?? ''; + $valeurs['decodage'] = _request('decodage') ?? ''; + $valeurs['mapping_code'] = _request('mapping_code') ?? ''; + $valeurs['mapping_valeur'] = _request('mapping_valeur') ?? ''; + $valeurs['feed_id'] = _request('feed_id') ?? ''; + $valeurs['titre'] = _request('titre') ?? ''; + $valeurs['description'] = _request('description') ?? ''; + // Préciser le nombre d'étapes du formulaire $valeurs['_etapes'] = 2; @@ -65,19 +105,158 @@ function formulaires_creer_feed_territoires_verifier_1() : array { // Initialisation des erreurs de vérification. $erreurs = []; + // On collecte les paramètres de l'étape 1 soit pour vérification pour pour élaborer d'autres variables pour l'étape 2. + $type = _request('type'); + $variable_pays = 'pays_' . $type; + $pays = _request($variable_pays) ?? ''; + $type_source = _request('type_source'); + $extra = _request('extra'); + $titre_extra = _request('titre_extra'); + + // Vérification du choix d'un pays pour les types qui le requiert + if ( + include_spip('inc/config') + and lire_config("territoires/{$type}/populated_by_country", false) + and !$pays + ) { + $erreurs[$variable_pays] = _T('info_obligatoire'); + } + + // Vérification du choix d'un fichier valide ou d'une URL valide (on ne teste pas la validité du contenu + if ($type_source === 'api') { + $variable_source = _request('url_source'); + if (empty($variable_source)) { + // Aucune URL saisie. + $erreurs['url_source'] = _T('info_obligatoire'); + } elseif ( + include_spip('inc/distant') + and (valider_url_distante($variable_source) === false) + ) { + // URL distante non valide. + // TODO : voir aussi filter_var(url, FILTER_VALIDATE_URL) + $erreurs['url_source'] = _T('territoires_stats:erreur_feed_url_source'); + } + } else { + $variable_source = 'fichier_source'; + if (empty($_FILES[$variable_source]['name'])) { + // Aucun fichier choisi. + $erreurs[$variable_source] = _T('info_obligatoire'); + } elseif ( + empty($_FILES[$variable_source]['type']) + or (!in_array($_FILES[$variable_source]['type'], ['application/json', 'application/xml', 'text/csv'])) + ) { + // Format de fichier invalide + $erreurs[$variable_source] = _T('territoires_stats:erreur_feed_format_fichier'); + } + } + + // Vérification de la saisie d'un identifiant pour le type de données + if (empty($extra)) { + $erreurs['extra'] = _T('info_obligatoire'); + } elseif (!preg_match('#^[\w]+$#i', $extra)) { + $erreurs['extra'] = _T('territoires_stats:erreur_feed_extra'); + } + + // Vérification de la saisie d'un titre pour le type de données + if (empty($titre_extra)) { + $erreurs['titre_extra'] = _T('info_obligatoire'); + } + + // Constitution des variables et paramètres d'affichage de l'étape 2 + if (!$erreurs) { + // -- Types de code possibles pour les territoires concernés + $types_code['iso_territoire'] = _T('territoire:champ_iso_territoire_label') . ' (iso_territoire)'; + $where = [ + 'type_extra=' . sql_quote('code'), + 'type=' . sql_quote($type), + 'iso_pays=' . sql_quote($pays), + ]; + $ids_code = sql_allfetsel('extra', 'spip_territoires_extras', $where, 'extra'); + if ($ids_code) { + foreach (array_column($ids_code, 'extra') as $_code) { + $types_code[$_code] = _T('territoire_extra:extra_' . $_code) . " ({$_code})"; + } + } + set_request('_types_code', $types_code); + set_request('_type_code_defaut', 'iso_territoire'); + + // -- Option de décodage de la source : pour une api on s'attend toujours à du JSON, pour un fichier à du JSON, XML ou CSV + $format = 'json'; + if ($type_source === 'file') { + // Extraire l'extension pour déterminer l'option de décodage à présenter + [, $format] = explode('/', $_FILES[$variable_source]['type']); + } + set_request('format_source', $format); + if ($format === 'csv') { + set_request('_label_decodage', _T('territoires_stats:label_feed_decodage_delimiteur')); + set_request('_explication_decodage', _T('territoires_stats:explication_feed_decodage_delimiteur')); + } else { + set_request('_label_decodage', _T('territoires_stats:label_feed_decodage_racine')); + set_request('_explication_decodage', _T('territoires_stats:explication_feed_decodage_racine')); + } + + // -- Identifiant proposé pour le feed + include_spip('inc/ezmashup_feed'); + $prefixe = "{$type}_{$pays}_{$extra}"; + foreach (['', '2', '3', '4', '5', '6', '7', '8', '9'] as $_suffixe) { + $id_feed = $prefixe . ($_suffixe ? '_' : '') . $_suffixe; + if (!feed_yaml_existe('territoires_stats', $id_feed)) { + break; + } + } + set_request('_feed_id_defaut', strtolower($id_feed)); + } + return $erreurs; } /** - * Vérification du formulaire :. + * Vérification du formulaire : détection des erreurs de saisie de l'étape 2. * - * @return array Message d'erreur si aucun pays choisi alors que la configuration du type de teritoire l'oblige. - * Sinon, chargement des champs utiles à l'étape 2 : + * @return array Message d'erreur ou vide sinon */ function formulaires_creer_feed_territoires_verifier_2() : array { // Initialisation des erreurs de vérification. $erreurs = []; + // On collecte les paramètres nécessaires de l'étape 1. + $format_source = _request('_format_source'); + $id_feed = _request('feed_id'); + + // On collecte les paramètres de l'étape 2. + $decodage = _request('decodage'); + $mapping_code = _request('mapping_code'); + $mapping_valeur = _request('mapping_valeur'); + + // Vérification de la saisie des mappings qui ne doivent contenir qu'un ou plusieurs mots séparés par des '/' + if (!preg_match('#^[\w/]+$#i', $mapping_code)) { + $erreurs['mapping_code'] = _T('territoires_stats:erreur_feed_mapping_code'); + } + if (!preg_match('#^[\w/]+$#i', $mapping_valeur)) { + $erreurs['mapping_valeur'] = _T('territoires_stats:erreur_feed_mapping_valeur'); + } + + // Vérification du décodage en fonction du type de source + // -- si fichier csv : on attend un délimiteur parmi ',', ';', '|' et '\t' ou vide (la valeur par défaut est la virgule) + // -- sinon : on attend un index comme pour le mapping ou vide + if ($decodage) { + if ($format_source === 'csv') { + if (!in_array($decodage, [',', ';', '|', "\t"])) { + $erreurs['decodage'] = _T('territoires_stats:erreur_feed_decodage'); + } + } elseif (!preg_match('#^[\w/]+$#i', $decodage)) { + $erreurs['decodage'] = _T('territoires_stats:erreur_feed_mapping_code'); + } + } + + // Vérification de la saisie du feed id + include_spip('inc/ezmashup_feed'); + if (!preg_match('#^[\w]+$#i', $id_feed)) { + $erreurs['feed_id'] = _T('territoires_stats:erreur_feed_id'); + } elseif (feed_yaml_existe('territoires_stats', $id_feed)) { + $erreurs['feed_id'] = _T('territoires_stats:erreur_feed_id_existe'); + } + return $erreurs; } diff --git a/formulaires/creer_feed_territoires_2.html b/formulaires/creer_feed_territoires_2.html index 0c1cc7b6dc96d8ac15f49bd71bf7410a7c089572..0d40933b67b3cd89b4180f78af8d8d4f0d638abc 100644 --- a/formulaires/creer_feed_territoires_2.html +++ b/formulaires/creer_feed_territoires_2.html @@ -1,5 +1,5 @@ <div class="formulaire_spip formulaire_editer formulaire_#FORM"> - <h3 class="titrem"><:cartes_territoires:titre_etape_creer{etape=#ENV{_etape}, etapes=#ENV{_etapes}}:></h3> + <h3 class="titrem"><:territoires:titre_etape_en_cours{etape=#ENV{_etape}, etapes=#ENV{_etapes}}:></h3> [<p class="reponse_formulaire reponse_formulaire_erreur">(#ENV*{message_erreur})</p>] @@ -7,136 +7,63 @@ <div> #ACTION_FORMULAIRE{#ENV{action}} <fieldset> - <legend><:territoires_stats:legende_feed_identite:></legend> + <legend><:territoires_stats:legende_feed_mapping:></legend> <div class="editer-groupe"> - [(#SAISIE{input, feed_id, - label=<:territoires_stats:label_feed_id:>, - explication=<:territoires_stats:explication_feed_id:>, - obligatoire=oui, - maxlength=255 - })] - - [(#SAISIE{input, titre, - explication=<:territoires_stats:explication_feed_titre:>, - label=<:territoires_stats:label_feed_titre:>, - obligatoire=oui, + [(#SAISIE{selection, type_code, + label=<:territoires_stats:label_feed_type_code:>, + data=#ENV{_types_code}, + defaut=#ENV{_type_code_defaut}, + cacher_option_intro=oui })] - [(#SAISIE{textarea, description, - explication=<:territoires_stats:explication_feed_description:>, - label=<:territoires_stats:label_feed_description:>, - rows=5, + [(#SAISIE{input, decodage, + explication=#ENV{_explication_decodage}, + label=#ENV{_label_decodage}, })] - </fieldset> - <fieldset> - <legend><:territoires_stats:legende_feed_unite_peuplement:></legend> - <div class="editer-groupe"> - [(#SAISIE{radio, type, - label=<:territoires_stats:label_feed_type_territoire:>, - data=#ENV{_types_territoire}, - defaut=#ENV{_type_territoire_defaut}, + [(#SAISIE{input, mapping_code, + explication=<:territoires_stats:explication_feed_mapping_code:>, + label=<:territoires_stats:label_feed_mapping_code:>, obligatoire=oui, })] - <BOUCLE_pays_type(DATA) {source table, #ENV{_pays}}> - <div id="pays_#CLE" class="editer-pays"> - [(#SAISIE{radio_flex, pays_#CLE, - label=<:territoire:champ_iso_pays_label:>, - data=#VALEUR, - conteneur_class=#ENV{_classe_conteneur/#CLE}, - multi_cols=#ENV{_choix_multi_col/#CLE}, - extraire_multi=oui, - })] - </div> - </BOUCLE_pays_type> - </div> - </fieldset> - - <fieldset> - <legend><:territoires_stats:legende_feed_source:></legend> - <div class="editer-groupe"> - [(#SAISIE{radio, type_source, - label=<:territoires_stats:label_feed_type_source:>, - data=#ENV{_types_source}, - defaut=#ENV{_type_source_defaut} - })] - - [(#SAISIE{input, fichier_source, - type=file, - explication=<:territoires_stats:explication_feed_fichier:>, - label=<:territoires_stats:label_feed_fichier:>, + [(#SAISIE{input, mapping_valeur, + explication=<:territoires_stats:explication_feed_mapping_valeur:>, + label=<:territoires_stats:label_feed_mapping_valeur:>, obligatoire=oui, })] - - [(#SAISIE{input, url_source, - explication=<:territoires_stats:explication_feed_url:>, - label=<:territoires_stats:label_feed_url:>, - })] </div> </fieldset> <fieldset> - <legend><:territoires_stats:legende_feed_mapping:></legend> + <legend><:territoires_stats:legende_feed_identite:></legend> <div class="editer-groupe"> - - [(#SAISIE{input, decodage, - explication=#ENV{_explication_decodage}, - label=#ENV{_label_decodage}, - defaut=#ENV{_decodage_defaut} - })] - [(#SAISIE{selection, type_code, - label=<:territoires_stats:label_feed_type_code:>, - data=#ENV{_types_code}, - defaut=#ENV{_types_code_defaut} + [(#SAISIE{input, feed_id, + label=<:territoires_stats:label_feed_id:>, + explication=<:territoires_stats:explication_feed_id:>, + defaut=#ENV{_feed_id_defaut}, + obligatoire=oui, + maxlength=255 })] - [(#SAISIE{input, extra, - explication=<:territoires_stats:explication_feed_extra:>, - label=<:territoires_stats:label_feed_extra:>, + [(#SAISIE{input, titre, + explication=<:territoires_stats:explication_feed_titre:>, + label=<:territoires_stats:label_feed_titre:>, + obligatoire=oui, })] - [(#SAISIE{input, titre_extra, - explication=<:territoires_stats:explication_feed_extra_titre:>, - label=<:territoires_stats:label_feed_extra_titre:>, + [(#SAISIE{textarea, description, + explication=<:territoires_stats:explication_feed_description:>, + label=<:territoires_stats:label_feed_description:>, + rows=5, })] - </div> </fieldset> <p class="boutons"> - <input type="submit" class="submit" value="<:info_etape_suivante:>" /> + #SET{etape_precedente, #ENV{_etape}|moins{1}} + <input type="submit" class="submit" name="_retour_etape_1" value="<:territoires:info_etape_precedente:>" /> + <input type="submit" class="submit" value="<:bouton_enregistrer:>" /> </p> </div> </form> </div> -<script type="text/javascript"> -//<![CDATA[ - jQuery(document).ready(function() { - // Liste des types associés à un pays - const types_pays = ['subdivision', 'infrasubdivision', 'protected_area']; - - // Afficher le bloc correspondant au types de carte et de territoire passé ou rien sinon. - function afficher_bloc_pays(type_territoire) { - if (types_pays.includes(type_territoire)) { - for (let t of types_pays) { - let id = 'div#pays_' + t; - if (t === type_territoire) { - jQuery(id).show(); - } else { - jQuery(id).hide(); - } - } - } else { - jQuery("div.editer-pays").hide(); - } - } - - afficher_bloc_pays(jQuery("input[name='type']:checked").val()); - - jQuery("input[name='type']").change(function() { - afficher_bloc_pays(jQuery("input[name='type']:checked").val()); - jQuery(this).blur(); - }); - }); -//]]> -</script> diff --git a/lang/territoires_stats_fr.php b/lang/territoires_stats_fr.php index 1e88f7dab667490901bc170cc180f43fd12f8d0e..9e87d20f9c32ab8f60130a87ded4a255b7c1d6f8 100644 --- a/lang/territoires_stats_fr.php +++ b/lang/territoires_stats_fr.php @@ -9,13 +9,17 @@ $GLOBALS[$GLOBALS['idx_lang']] = array( 'bouton_recharger' => 'Recharger la configuration des jeux de données', // E - 'explication_feed_id' => 'L\'identifiant est un mot sans espace composé uniquement de lettres, chiffres et du caractère "_" (tiret bas)', + 'explication_feed_id' => 'L\'identifiant est un mot sans espace composé uniquement de lettres, chiffres et du caractère "_" (tiret bas). Il est initialisé avec une valeur unique non déjà utilisée mais vous pouvez néanmoins la modifier', 'explication_feed_titre' => 'Le titre peut-être écrit en utilisant la balise multilangues', 'explication_feed_description' => 'Le titre peut-être écrit en utilisant la balise multilangues et tous les raccourcis SPIP', 'explication_feed_fichier' => 'Le fichier source doit être disponible au format JSON, CSV ou XML', 'explication_feed_url' => 'L\'URL de la requête doit permettre de récupérer en une fois l\'ensemble des données au format JSON', 'explication_feed_extra' => 'L\'identifiant sert à reconnaitre le type de données du jeu. C\'est un mot sans espace composé uniquement de lettres, chiffres et du caractère "_" (tiret bas)', - 'explication_feed_extra_titre' => 'Le titre sert à reconnaitre le type de données du jeu de façon plus lisible', + 'explication_feed_extra_titre' => 'Le titre sert à reconnaitre le type de données du jeu de façon plus lisible. Il peut-être écrit en utilisant la balise multilangues', + 'explication_feed_decodage_delimiteur' => 'Caractère utilisé comme séparateur des données dans le fichier CSV. Si vide, le délimiteur "," (virgule) sera utilisé par défaut', + 'explication_feed_decodage_racine' => 'Cet index racine représente la partie commune qui est appliquée au mapping du code et de la valeur statistique dans le tableau des données source. Il est exprimé sous la forme index1/index2/index3', + 'explication_feed_mapping_code' => 'Cet index racine représente la partie spécifique permettant d\'accéder au code de territoire dans le tableau des données source. Il est exprimé sous la forme index1/index2/index3', + 'explication_feed_mapping_valeur' => 'Cet index racine représente la partie spécifique permettant d\'accéder à la valeur statistique dans le tableau des données source. Il est exprimé sous la forme index1/index2/index3', // I 'icone_creer_feed' => 'Créer un jeu de données', @@ -25,6 +29,8 @@ $GLOBALS[$GLOBALS['idx_lang']] = array( 'info_nb_feed' => '@nb@ jeux de données', // L + 'label_feed_decodage_delimiteur' => 'Délimiteur', + 'label_feed_decodage_racine' => 'Index racine du mapping', 'label_feed_category_territory_data' => 'Données statistiques sur les territoires', 'label_feed_id' => 'Identifiant du jeu de données', 'label_feed_titre' => 'Titre', @@ -34,11 +40,13 @@ $GLOBALS[$GLOBALS['idx_lang']] = array( 'label_feed_type_territoire' => 'Type de territoire', 'label_feed_type_source' => 'Source de données', 'label_feed_type_code' => 'Type d\'identifiant des territoires de la source', - 'label_feed_extra' => 'Identifiant de la donnée', - 'label_feed_extra_titre' => 'Titre de la donnée', + 'label_feed_extra' => 'Identifiant du type de donnée', + 'label_feed_extra_titre' => 'Titre du type de donnée', + 'label_feed_mapping_code' => 'Index du code du territoire', + 'label_feed_mapping_valeur' => 'Index de la valeur', 'legende_feed_identite' => 'Identification du jeu de données', 'legende_feed_unite_peuplement' => 'Territoires concernés', - 'legende_feed_source' => 'Origine des données', + 'legende_feed_source' => 'Origine & signification des données', 'legende_feed_mapping' => 'Interprétation des données', // M