From f6a2b0d7ebd9276fceb0bbe9a96fde00365b61cd Mon Sep 17 00:00:00 2001 From: philippe tcheriatinsky <philippe.tcherniatinsky@inrae.fr> Date: Mon, 25 Nov 2024 13:59:28 +0100 Subject: [PATCH 1/2] ajout section submission dans le yaml acbb --- .../domain/data/deposit/DataImporter.java | 3 ++ .../resources/data/acbb/acbb_openAdom_V2.yaml | 30 ++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/inra/oresing/domain/data/deposit/DataImporter.java b/src/main/java/fr/inra/oresing/domain/data/deposit/DataImporter.java index 7c91477..21a7d85 100644 --- a/src/main/java/fr/inra/oresing/domain/data/deposit/DataImporter.java +++ b/src/main/java/fr/inra/oresing/domain/data/deposit/DataImporter.java @@ -473,6 +473,9 @@ public class DataImporter { private fr.inra.oresing.domain.Authorization getLineAuthorization(DataDatum referenceDatum, long lineNumber, ReportErrors errors) { final Authorization authorization = dataImporterContext.getAuthorization(); + if(authorization==null){ + return new fr.inra.oresing.domain.Authorization(); + } BinaryFileDataset binaryFileDataset = Optional.ofNullable(dataImporterContext.getPublishContextBuilder()) .map(PublishContext.PublishContextBuilder::build) diff --git a/src/test/resources/data/acbb/acbb_openAdom_V2.yaml b/src/test/resources/data/acbb/acbb_openAdom_V2.yaml index 9c0f38d..46f89c0 100644 --- a/src/test/resources/data/acbb/acbb_openAdom_V2.yaml +++ b/src/test/resources/data/acbb/acbb_openAdom_V2.yaml @@ -695,7 +695,35 @@ OA_data: } OA_components: #optional - flx_fc_gf_value #optional - + OA_submission: #optional + OA_strategy: OA_VERSIONING #optional + OA_submissionScope: #mandatory + OA_referenceScopes: #optional + - #optional + OA_component: sit_key #mandatory + OA_reference: tr_sites_sit #optional + OA_i18n: #mandatory + OA_title: #optional + fr: Site + en: Site + OA_description: #optional + fr: Référentiel des Sites + en: Site repository + OA_exportHeader: #mandatory + OA_title: #optional + fr: Site + en: Site + OA_description: #optional + fr: Référentiel des Sites + en: Site repository + OA_timeScope: #optional + OA_component: flx_date #mandatory + OA_fileName: #optional + OA_filePattern: (.*)_(.*)_(.*).csv #mandatory + OA_matchPatternScopes: #optional + - sit_key #optional + - __START_DATE__ #optional + - __END_DATE__ #optional OA_computedComponents: flx_date: OA_exportHeader: -- GitLab From 4b0fe20820cc3f78532952365f6fd66c9278a145 Mon Sep 17 00:00:00 2001 From: philippe tcheriatinsky <philippe.tcherniatinsky@inrae.fr> Date: Tue, 26 Nov 2024 11:26:20 +0100 Subject: [PATCH 2/2] =?UTF-8?q?correction=20submission=20-=20requ=C3=AAte?= =?UTF-8?q?=20getNodes()=20-=20ajout=20dans=20dataResult=20des=20Reference?= =?UTF-8?q?Scopes=20-=20ajout=20section=20submission=20yaml=20acbb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oresing/rest/AuthorizationService.java | 2 +- .../fr/inra/oresing/rest/OreSiResources.java | 7 ++- .../fr/inra/oresing/rest/OreSiService.java | 5 +++ .../authorization/AuthorizationsResult.java | 3 +- .../rest/model/data/GetDataResult.java | 4 +- .../migration/application/V1__init_schema.sql | 17 ++++--- src/main/resources/migration/first_roles.sql | 45 ++++++++++++++----- .../resources/data/acbb/acbb_openAdom_V2.yaml | 17 ++++++- 8 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java index 9a737e9..f6782a8 100644 --- a/src/main/java/fr/inra/oresing/rest/AuthorizationService.java +++ b/src/main/java/fr/inra/oresing/rest/AuthorizationService.java @@ -556,7 +556,7 @@ public class AuthorizationService implements fr.inra.oresing.domain.services.aut .toList(); } - private Map<String, List<GetGrantableResult.ReferenceScope>> getAuthorizationScopes(final Application application, final MenuType menuType) { + public Map<String, List<GetGrantableResult.ReferenceScope>> getAuthorizationScopes(final Application application, final MenuType menuType) { Map<ReferenceScope.Context, List<ReferenceScope.TreeNode>> nodesByContext = repository.getRepository(application).data() .getNodesForMenu(menuType) diff --git a/src/main/java/fr/inra/oresing/rest/OreSiResources.java b/src/main/java/fr/inra/oresing/rest/OreSiResources.java index 2d72658..0c9d473 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiResources.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiResources.java @@ -18,6 +18,7 @@ import fr.inra.oresing.domain.checker.type.ReferenceType; import fr.inra.oresing.domain.data.DataValue; import fr.inra.oresing.domain.data.RefsLinkedToValue; import fr.inra.oresing.domain.data.deposit.validation.CsvRowValidationCheckResult; +import fr.inra.oresing.domain.data.menu.MenuType; import fr.inra.oresing.domain.data.read.ouput.KeepAliveZipOutputStream; import fr.inra.oresing.domain.data.read.query.DownloadDatasetQueryOnlyMetadata; import fr.inra.oresing.domain.exceptions.SiOreIllegalArgumentException; @@ -41,6 +42,7 @@ import fr.inra.oresing.rest.filesenderclient.BuildBundleReport; import fr.inra.oresing.rest.model.additionalfiles.CreateAdditionalFileRequest; import fr.inra.oresing.rest.model.additionalfiles.exceptions.BadAdditionalFileParamsSearchException; import fr.inra.oresing.rest.model.application.ApplicationResult; +import fr.inra.oresing.rest.model.authorization.GetGrantableResult; import fr.inra.oresing.rest.model.data.*; import fr.inra.oresing.rest.model.data.query.DownloadDatasetQuery; import fr.inra.oresing.rest.model.reference.GetReferenceResult; @@ -918,14 +920,15 @@ public class OreSiResources { ) ) .orElseGet(LinkedHashMap::new); - + Map<String, List<GetGrantableResult.ReferenceScope>> referenceScopes = service.getAuthorizationScopes(application, MenuType.submission); return ResponseEntity.ok(new GetDataResult( variables, dataRowResults, totalRows, checkedFormatcomponents, - referenceTypeForReferencingColumns)); + referenceTypeForReferencingColumns, + referenceScopes)); } /** diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java index 7b1a971..2ddc3c3 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiService.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java @@ -21,6 +21,7 @@ import fr.inra.oresing.domain.checker.LineChecker; import fr.inra.oresing.domain.checker.type.*; import fr.inra.oresing.domain.data.*; import fr.inra.oresing.domain.data.deposit.PublishContext; +import fr.inra.oresing.domain.data.menu.MenuType; import fr.inra.oresing.domain.data.read.query.*; import fr.inra.oresing.domain.exceptions.OreSiTechnicalException; import fr.inra.oresing.domain.exceptions.SiOreIllegalArgumentException; @@ -1312,4 +1313,8 @@ public class OreSiService { public DataRepositoryWithBuffer getNewDataRepositoryWithBuffer(Application application) { return new DataRepositoryWithBuffer(application, repository.getRepository(application).data()); } + + public Map<String, List<GetGrantableResult.ReferenceScope>> getAuthorizationScopes(Application application, MenuType menuType) { + return authorizationService.getAuthorizationScopes(application, menuType); + } } diff --git a/src/main/java/fr/inra/oresing/rest/model/authorization/AuthorizationsResult.java b/src/main/java/fr/inra/oresing/rest/model/authorization/AuthorizationsResult.java index 2900b81..29615d1 100644 --- a/src/main/java/fr/inra/oresing/rest/model/authorization/AuthorizationsResult.java +++ b/src/main/java/fr/inra/oresing/rest/model/authorization/AuthorizationsResult.java @@ -12,7 +12,8 @@ public record AuthorizationsResult( Boolean applicationManager, Boolean isAdministrator, Boolean userManager, - Boolean applicationUser, Boolean activeApplicationUser + Boolean applicationUser, + Boolean activeApplicationUser ) { public AuthorizationsResult(Map<String, List<AuthorizationParsed>> userAuthorization, Map<String, AuthorizationParsed> publicAuthorization, String applicationName, Boolean applicationCreator, Boolean applicationManager, Boolean userManager, Boolean applicationUser, Boolean activeApplicationUser) { this(userAuthorization, publicAuthorization, applicationName, applicationCreator, applicationManager, applicationCreator||applicationManager, userManager, applicationUser, activeApplicationUser); diff --git a/src/main/java/fr/inra/oresing/rest/model/data/GetDataResult.java b/src/main/java/fr/inra/oresing/rest/model/data/GetDataResult.java index 4ea09aa..aadb455 100644 --- a/src/main/java/fr/inra/oresing/rest/model/data/GetDataResult.java +++ b/src/main/java/fr/inra/oresing/rest/model/data/GetDataResult.java @@ -5,6 +5,7 @@ import fr.inra.oresing.domain.checker.LineChecker; import fr.inra.oresing.domain.checker.type.FieldType; import fr.inra.oresing.domain.checker.type.ListType; import fr.inra.oresing.domain.data.DataColumn; +import fr.inra.oresing.rest.model.authorization.GetGrantableResult; import java.util.List; import java.util.Map; @@ -14,5 +15,6 @@ import java.util.Set; public record GetDataResult(Set<String> variables, List<DataRowResult> rows, Long totalRows, Map<String, Map<String, fr.inra.oresing.rest.model.data.LineCheckerResult>> checkedFormatComponents, - Map<String, String> referenceTypeForReferencingColumns) { + Map<String, String> referenceTypeForReferencingColumns, + Map<String, List<GetGrantableResult.ReferenceScope>> referenceScopes) { } \ No newline at end of file diff --git a/src/main/resources/migration/application/V1__init_schema.sql b/src/main/resources/migration/application/V1__init_schema.sql index 38739ca..4a6441c 100644 --- a/src/main/resources/migration/application/V1__init_schema.sql +++ b/src/main/resources/migration/application/V1__init_schema.sql @@ -157,18 +157,21 @@ with recursive d(key, "data") as ( treetype, key, authorizationscope.component, - Coalesce(("configuration" #> array['i18n', 'data', key, 'components'] #>> array[authorizationscope.component,'exportheader', 'fr'] ),authorizationscope.component) exportheader_fr, - Coalesce(("configuration" #> array['i18n', 'data', key, 'components'] #>> array[authorizationscope.component,'exportheader', 'en'] ),authorizationscope.component) exportheader_en, - authorizationscope.data reference, + Coalesce(("configuration" #> array['i18n', 'data', key, 'components'] #>> array[authorizationscope.component,'exportheader', 'title', 'fr'] ),authorizationscope.component) exportheader_fr, + Coalesce(("configuration" #> array['i18n', 'data', key, 'components'] #>> array[authorizationscope.component,'exportheader', 'title', 'en'] ),authorizationscope.component) exportheader_en, + CASE + WHEN treetype = 'authorization' then authorizationscope.data + WHEN treetype = 'submission' then authorizationscope.reference + end reference, "configuration" from d, jsonb_to_recordset( CASE WHEN treetype = 'authorization' then "configuration"#> array['datadescription', key, 'authorization', 'authorizationscope'] - WHEN treetype = 'submission' then "configuration"#> array['datadescription', key, 'authorization', 'authorizationscope'] + WHEN treetype = 'submission' then "configuration"#> array['datadescription', key, 'submission', 'submissionscope', 'referencescopes'] end - ) as authorizationscope(component text, "data" text) + ) as authorizationscope(component text, reference text, "data" text) ), nodes as ( select @@ -196,8 +199,8 @@ with recursive d(key, "data") as ( 'value', node[2], 'node_key', rv2.hierarchicalkey, 'node_NK', rv2.naturalkey, - 'fr', coalesce(rv2.refvalues ->> '__display___display_fr',rv2.refvalues ->> '__display___display_default') , - 'en', coalesce(rv2.refvalues ->> '__display___display_en',rv2.refvalues ->> '__display___display_default') + 'fr', coalesce(rv2.refvalues ->> '__display_fr',rv2.refvalues ->> '__display_default') , + 'en', coalesce(rv2.refvalues ->> '__display_en',rv2.refvalues ->> '__display_default') ) )::node node from diff --git a/src/main/resources/migration/first_roles.sql b/src/main/resources/migration/first_roles.sql index bcaa0b3..cc06c99 100644 --- a/src/main/resources/migration/first_roles.sql +++ b/src/main/resources/migration/first_roles.sql @@ -1,17 +1,42 @@ INSERT INTO public.oresiuser (id, login, password, authorizations, accountstate, email) -VALUES ('35157557-616a-46b8-aee3-487d7450ec23'::uuid,'philippe', '$2a$12$2RXZnc/w9K2rlevPIXUfNeXfTzQGD3GWCWh7Noe8aoFyF2935PRA2', '{.*}', 'active', 'philippe.tcherniatinsky@inrae.fr'), +VALUES + ('5a4dbd41-3fc9-4b3e-b593-a46bc888a7f9'::uuid,'openadom', '$2a$12$eF88cLz.KdQDUvWR7ztaiOeN3fstIAqqrDFHLdem0Kq/we1bmxUM2', '{}', 'active', 'openadom@inrae.fr'), + ('35157557-616a-46b8-aee3-487d7450ec23'::uuid,'philippe', '$2a$12$2RXZnc/w9K2rlevPIXUfNeXfTzQGD3GWCWh7Noe8aoFyF2935PRA2', '{.*}', 'active', 'philippe.tcherniatinsky@inrae.fr'), ('31bd5755-3433-49f1-856e-7e99f3aef5f4'::uuid,'dmaurice', '$2a$12$VLGZZglGbZfpAHK41.TRyOO7DwhGsXU7Ts2rJGPuxb7SDf.aCcGH6', '{.*}', 'active', 'damien.maurice@inrae.fr'), ('3477ed15-15b1-4fee-ad45-6f2a94a3d66f'::uuid,'gmonet', '$2a$12$zyuSWKjmf2ZViwyv7o7P.Ohsdhb73p9.QC067KFXlAkOF0IOsspFq', '{.*}', 'active', 'ghislaine.monet@inrae.fr'), ('73de78e0-c16c-4a55-a176-1a1feb0dd290'::uuid,'lvarloteaux', '$2a$12$hFi9JFLQ24BxT0Ht/NokmuaMTHp5oc7YOrg9TgpwSLjeAQBR2/qMy', '{.*}', 'active', 'lucile.varloteaux@inrae.fr'), ('45b08ce0-f83b-44b5-8d6a-ec55ebf62548'::uuid,'ryahiaoui', '$2a$12$C/CpmXGxN22OJXUqvXY/DuCzA7PUaS6YbVMPYIFOe7iLKs7Go6XoC', '{.*}', 'active', 'rachid.yahiaoui@inra.fr'), ('893dd0f7-a369-4ca2-af3b-21c11719d350'::uuid,'vkoyao', '$2a$12$.nC/NJlNI.eZnyLNN49CXerwwprIk/UHN9mYpQ0172zixwmZGiExS', '{.*}', 'active', 'vivianne.koyao-yayende@inrae.fr'), ('7f7dec88-9c0a-44ed-82fa-3ec0362b9889'::uuid, 'hboukir', '$2a$12$YVqXsLZ5FkD0MnIjskvJduUWO91BnLU7X188TVgOfiEZcFpXSyEXS', '{.*}', 'active', 'hakima.boukir@inrae.fr'), - ('af63dfbd-54eb-4c11-8e3e-b5a00bb27a4f'::uuid,'afioca', '$2a$12$47CBQ4yxhxfvnwJ5gyh9o.4caLTbPTuy.zQUA7Mx/MFgK1aJwArvC', '{.*}', 'active', 'amelie.fiocca@inrae.fr'); -CREATE ROLE "35157557-616a-46b8-aee3-487d7450ec23";COMMENT ON ROLE "35157557-616a-46b8-aee3-487d7450ec23" IS 'Philippe'; GRANT "openAdomAdmin" TO "35157557-616a-46b8-aee3-487d7450ec23" WITH INHERIT TRUE; GRANT "35157557-616a-46b8-aee3-487d7450ec23" TO "openAdomTechUser" WITH INHERIT TRUE; -CREATE ROLE "31bd5755-3433-49f1-856e-7e99f3aef5f4";COMMENT ON ROLE "31bd5755-3433-49f1-856e-7e99f3aef5f4" IS 'Damien'; GRANT "openAdomAdmin" TO "31bd5755-3433-49f1-856e-7e99f3aef5f4" WITH INHERIT TRUE; GRANT "31bd5755-3433-49f1-856e-7e99f3aef5f4" TO "openAdomTechUser" WITH INHERIT TRUE; -CREATE ROLE "3477ed15-15b1-4fee-ad45-6f2a94a3d66f";COMMENT ON ROLE "3477ed15-15b1-4fee-ad45-6f2a94a3d66f" IS 'Ghislaine'; GRANT "openAdomAdmin" TO "3477ed15-15b1-4fee-ad45-6f2a94a3d66f" WITH INHERIT TRUE; GRANT "3477ed15-15b1-4fee-ad45-6f2a94a3d66f" TO "openAdomTechUser" WITH INHERIT TRUE; -CREATE ROLE "73de78e0-c16c-4a55-a176-1a1feb0dd290";COMMENT ON ROLE "73de78e0-c16c-4a55-a176-1a1feb0dd290" IS 'Lucile'; GRANT "openAdomAdmin" TO "73de78e0-c16c-4a55-a176-1a1feb0dd290" WITH INHERIT TRUE; GRANT "73de78e0-c16c-4a55-a176-1a1feb0dd290" TO "openAdomTechUser" WITH INHERIT TRUE; -CREATE ROLE "45b08ce0-f83b-44b5-8d6a-ec55ebf62548";COMMENT ON ROLE "45b08ce0-f83b-44b5-8d6a-ec55ebf62548" IS 'Rachid'; GRANT "openAdomAdmin" TO "45b08ce0-f83b-44b5-8d6a-ec55ebf62548" WITH INHERIT TRUE; GRANT "45b08ce0-f83b-44b5-8d6a-ec55ebf62548" TO "openAdomTechUser" WITH INHERIT TRUE; -CREATE ROLE "893dd0f7-a369-4ca2-af3b-21c11719d350";COMMENT ON ROLE "893dd0f7-a369-4ca2-af3b-21c11719d350" IS 'Viviane'; GRANT "openAdomAdmin" TO "893dd0f7-a369-4ca2-af3b-21c11719d350" WITH INHERIT TRUE; GRANT "893dd0f7-a369-4ca2-af3b-21c11719d350" TO "openAdomTechUser" WITH INHERIT TRUE; -CREATE ROLE "7f7dec88-9c0a-44ed-82fa-3ec0362b9889";COMMENT ON ROLE "7f7dec88-9c0a-44ed-82fa-3ec0362b9889" IS 'Hackima'; GRANT "openAdomAdmin" TO "7f7dec88-9c0a-44ed-82fa-3ec0362b9889" WITH INHERIT TRUE; GRANT "7f7dec88-9c0a-44ed-82fa-3ec0362b9889" TO "openAdomTechUser" WITH INHERIT TRUE; -CREATE ROLE "af63dfbd-54eb-4c11-8e3e-b5a00bb27a4f";COMMENT ON ROLE "af63dfbd-54eb-4c11-8e3e-b5a00bb27a4f" IS 'Amélie'; GRANT "openAdomAdmin" TO "af63dfbd-54eb-4c11-8e3e-b5a00bb27a4f" WITH INHERIT TRUE; GRANT "af63dfbd-54eb-4c11-8e3e-b5a00bb27a4f" TO "openAdomTechUser" WITH INHERIT TRUE; \ No newline at end of file + ('af63dfbd-54eb-4c11-8e3e-b5a00bb27a4f'::uuid,'afioca', '$2a$12$47CBQ4yxhxfvnwJ5gyh9o.4caLTbPTuy.zQUA7Mx/MFgK1aJwArvC', '{.*}', 'active', 'amelie.fiocca@inrae.fr') +ON CONFLICT DO NOTHING; + + + +-- Fonction pour créer un rôle et accorder des privilèges +CREATE OR REPLACE FUNCTION create_role_and_grant(role_id UUID, role_name TEXT) +RETURNS VOID AS $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = role_id::text) THEN + EXECUTE format('CREATE ROLE "%s"', role_id); +EXECUTE format('COMMENT ON ROLE "%s" IS %L', role_id, role_name); +EXECUTE format('GRANT "openAdomAdmin" TO "%s" WITH INHERIT TRUE', role_id); +EXECUTE format('GRANT "%s" TO "openAdomTechUser" WITH INHERIT TRUE', role_id); +END IF; +END; +$$ +LANGUAGE plpgsql; + +-- Création des rôles et octroi des privilèges +SELECT create_role_and_grant('5a4dbd41-3fc9-4b3e-b593-a46bc888a7f9'::uuid, 'openadom'); +SELECT create_role_and_grant('35157557-616a-46b8-aee3-487d7450ec23'::uuid, 'Philippe'); +SELECT create_role_and_grant('31bd5755-3433-49f1-856e-7e99f3aef5f4'::uuid, 'Damien'); +SELECT create_role_and_grant('3477ed15-15b1-4fee-ad45-6f2a94a3d66f'::uuid, 'Ghislaine'); +SELECT create_role_and_grant('73de78e0-c16c-4a55-a176-1a1feb0dd290'::uuid, 'Lucile'); +SELECT create_role_and_grant('45b08ce0-f83b-44b5-8d6a-ec55ebf62548'::uuid, 'Rachid'); +SELECT create_role_and_grant('893dd0f7-a369-4ca2-af3b-21c11719d350'::uuid, 'Viviane'); +SELECT create_role_and_grant('7f7dec88-9c0a-44ed-82fa-3ec0362b9889'::uuid, 'Hackima'); +SELECT create_role_and_grant('af63dfbd-54eb-4c11-8e3e-b5a00bb27a4f'::uuid, 'Amélie'); + +-- Suppression de la fonction temporaire +DROP FUNCTION create_role_and_grant(UUID, TEXT); \ No newline at end of file diff --git a/src/test/resources/data/acbb/acbb_openAdom_V2.yaml b/src/test/resources/data/acbb/acbb_openAdom_V2.yaml index 46f89c0..c65ada5 100644 --- a/src/test/resources/data/acbb/acbb_openAdom_V2.yaml +++ b/src/test/resources/data/acbb/acbb_openAdom_V2.yaml @@ -10,7 +10,7 @@ OA_application: en: "Agroecosystems, Biogeochemical Cycles and Biodiversity" OA_comment: "L'application ACBB V2" OA_name: acbb_openadom_v2 - OA_version: 1.0.2 + OA_version: 1.0.4 OA_data: tr_agroecosystemes_agr: OA_i18n: @@ -20,6 +20,10 @@ OA_data: OA_description: fr: Les agroécosystèmes représentent des regroupements par type de traitement en: Agroecosystems represent groupings by type of treatment + OA_i18nDisplayPattern: #mandatory + OA_title: #optional + fr: "{agr_agroecosystem_fr}" + en: "{agr_agroecosystem_en}" OA_naturalKey: [agr_agroecosystem_key] OA_basicComponents: agr_agroecosystem_key: @@ -62,6 +66,10 @@ OA_data: OA_description: fr: sites d'etudes en: study sites + OA_i18nDisplayPattern: #mandatory + OA_title: #optional + fr: "{sit_site_fr}" + en: "{sit_site_en}" OA_naturalKey: [sit_site_key] OA_validations: sit_checkDateMiseEnService: @@ -384,6 +392,13 @@ OA_data: OA_description: fr: les sites sont divisés en parcelles sur lesquelles on applique un traitement. Il peut y avoir des blocs de répétitions de parcelles en: Sites are divided into plots on which a treatment is applied. There may be blocks of plot repetitions + OA_i18nDisplayPattern: #mandatory + OA_title: #optional + fr: "{par_parcelle_fr}" + en: "{par_parcelle_en}" + OA_description: #optional + fr: "{par_commentaire_fr}" + en: "{par_commentaire_en}" OA_basicComponents: sit_site: OA_exportHeader: -- GitLab