Modifier le fichier

Documentation K-Sup version master

Bienvenue sur le site de documentation du produit K-Sup pour la version master. Vous pouvez consulter les autres versions ici :

Ce site présente l'ensemble de la documentation. Vous y trouverez :

Cette documentation est vivante. Si jamais vous constatez un manque ou une erreur, il faut :

  • Soit modifier directement la documentation (cf le guide de contribution)
  • Soit créer un ticket doctech

Modifier le fichier

Documentation fonctionnelle

L'organisation de la documentation fonctionnelle reproduit l'organisation de la cartograhie des composants du produit K-Sup (cf cartographie sur Confluence).
La documentation est ainsi organisée en 3 catégories :

  • Les composants Socle : Composant bas-niveau du produit K-Sup
  • Les contenus : Les différentes fiches composant le CMS
  • Les modules : Les composants métiers n'étant pas des fiches

Cartographie des composants

Les composants sont classés en 4 criticité : bloquants, critiques, majeurs, simples

Composants Bloquants

Composants Critiques

Composant Majeurs

Composants Simples

Modifier le fichier

Module de statistiques

Présentation

Le module Statistiques a pour objet de publier des indicateurs afin d'aider au pilotage du produit et des applications déployées. Ces indicateurs transversaux aux différentes fonctionnalités de l'application, permettront de vérifier si un composant est utilisé et comment il l'est. Les données générées par ce module ont pour vocation d'être intégrées dans un système tiers afin de les exploiter (compilation dans le temps, génération de graphiques ...). Ces données doivent être le plus générique possible et ne pas contenir de données personnelles ou trop précises (on mesure qu'un tag est utilisé, pas ce qu'il contient).

Fonctionnalités

Plusieurs indicateurs sont générés :

  • North Star
  • Indicateur d'usage
    • Indicateur de volume : Indicateur statique généré par des requêtes réalisées en base de données (Combien de fiche formations en ligne au mois de mars ?)
    • Indicateur d'action : Indicateur généré par l'écoute en temps réel de certaines méthodes de l'applicatif (Combien d'action de création de fiche formation au mois de mars)
  • Indicateur de performance

Ces données seront extraites dans des fichiers présents par défaut dans le répertoire statistics du storage :

  • Les indicateurs d'usage et North Star sont extraits mensuellement par un traitement planifié dans un fichier horodaté au format jsonl (json ligne à ligne https://jsonlines.org/).
  • L'indicateur de performance est écrit dans un fichier de log (fichier avec rotation)

Cohorte d'utilisateurs

Certains indicateurs indiquent une cohorte d'utilisateurs dans le message généré. Cette cohorte permet d'identifier la typologie d'utilisateur utilisant le produit. Pluisuers cohortes sont identifiées par défaut de la produit :

  • anonyme : Utilisateur non connecté navigant sur l'application.
    • Nom du profil : ANONYMOUS
  • utilisateur connecté : Utilisateur connecté mais n'ayant aucune permission sur l'application.
    • Nom du profil : CONNECTEDUSER
  • contributeur de contenu : Utilisateur ayant des droits sur des fiches (hors fiches de l'extension offre de formation).
    • Nom du profil : CONTENTMANAGER
  • webmaster : Utilisateur avec la permission Webmaster.
    • Nom du profil : WEBMASTER
  • contributeur de site: Utilisateur avec la permission Edition de rubrique (création, modification ou suppression).
    • Nom du profil : SITEMANAGER
  • contributeur de formation : Utilisateur avec la permission de modification sur la fiche formation.
    • Nom du profil : PROGRAMMMANAGER
  • contributeur : Utilisateur avec des permissions sur l'application (sans autorisation sur les fiches).
    • Nom du profil : CONTRIBUTOR

Cette liste de cohortes est extensible et il est possible d'en ajouter pour des extensions ou un besoin spécifique à un projet.

North Star

Indicateur de type action rendant compte de l'utilisation globale du produit. Cet indicateur mesure le taux de rafraîchissement des données visibles d'une application. Cela mesure donc :

  • Le passage en ligne d'une fiche
  • La modification d'une fiche en ligne
  • Le dépublication d'une fiche en ligne

Cet indicateur est disponible par type de fiche et cohorte d'utilisateurs.

Indicateurs d'usage

Chaque composant du produit K-Sup va publier des indicateurs afin de suivre son utilisation. La description de chacun des indicateurs est disponible dans la documentation propre à chaque composant.

Le marqueur HTTP

Présentation

Cet indicateur permet de journaliser tout ou partie des appels HTTP.

Il est composé d'un filtre HTTP qui recueille des informations techniques sur l'exécution de la requête HTTP.

Les informations collectées sont :

  1. le temps total passé pour la génération de la page
  2. le temps total passé pour l'exécution des requêtes SQL
  3. le nombre de requêtes SQL exécutées
  4. le temps de la requête SQL la plus longue

Deux marqueurs différents sont implémentés à partir de ce marqueur HTTP.

Le marqueur HTTP de performance

Marqueur technique permettant de signaler les requêtes anormalement longues de l'application. L'écriture dans le journal de statistiques est déclenchée lorsque deux critères sont respectés :

  1. la requête fait partie des requêtes à auditer
  2. le temps total de la requête HTTP dépasse un seuil de déclenchement
  • Le seuil de déclenchement est porté par la propriété statistics.http.duration.threshold (cf core-statistics.properties). La valeur -1 permet de désactiver le seuil
  • Le filtrage des requêtes est réalisé par un ensemble de filtre (cf com/kosmos/statistics/marker/http/requestfilter) permettant de ne traiter que les URLs des contenus (pages et médias).
    • Un filtre d'exclusion de type "l'URI commence par".
      Par défaut, le filtre refuse les URL commençant par "/static/,/jsp/styles/,/jsp/images/,/jsp/scripts/"
      La propriété statistics.performance.exclude.uri.start.with permet de surcharger le comportement par défaut.
    • Un filtre d'exclusion de type "l'URI NE finit PAS par".
      Par défaut, le filtre refuse les URL finissant par ".js,.css.map,.css,.ttf,.woff,.json,.png"
      La propriété statistics.performance.exclude.uri.end.with permet de surcharger le comportement par défaut.

Les filtres par défaut sont définis dans le fichier core-statistics-http.xml.

Le marqueur HTTP d'usage

Marqueur fonctionnel permettant de tracer certaines URL de l'application. L'écriture dans la table des évènements est déclenchée si l'URL courante correspond à certains motifs prédéfinis:

  • Le filtrage des requêtes est réalisé par un ensemble de filtre (cf com/kosmos/statistics/marker/http/requestfilter) permettant de ne traiter que les URLs des contenus (pages et médias). Les différents filtres sont spécifiques à chaque indicateur.

Les différentes extensions peuvent pousser leurs propres filtres à l'aide d'un bean "ListToAddBean"

Accueil BO

Bandeau alerte

Modifier le fichier

Module Groupes

Modifier le fichier

Statistiques du composant Groupes

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de groupes total{{"marker":"VOLUME_GROUPE","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"GROUPE","payload":{"content":"GroupeTotalCount","count":5}}
Nombre de groupes dynamiques (LDAP) si connecteur ldap activé{"marker":"VOLUME_GROUPE","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"GROUPE","payload":{"content":"GroupeLdapCount","count":1}}
Nombre de groupe dynamiques de structure{"marker":"VOLUME_GROUPE","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"GROUPE","payload":{"content":"GroupeStructureCount","count":2}}

Modifier le fichier

Module Liste dynamique

Modifier le fichier

Statistique du composant Liste dynamique

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Liste dynamique{"marker":"VOLUME_TAG","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"core","payload":{"content":"[traitement;liste_fiches;]","count":2}}

Modifier le fichier

Module Médiathèque

Le module Médiathèque permet de gérer les médias insérés dans les sites K-Sup.

Fonctionnalités

Modifier le fichier

Recherche par URL

La recherche par URL permet de rechercher n'importe quel média inséré dans un site à partir de son URL et de consulter les utilisations de ce média au sein de K-Sup.

Bouton de recherche par URL

L'écran de recherche comporte un unique champ URL du média dans lequel l'utilisateur doit saisir l'URL du média qu'il souhaite rechercher (URL interne ou externe).

Une fois l'URL saisie, l'utilisateur doit cliquer sur le bouton Rechercher pour lancer la recherche.

Fenêtre de recherche par URL

Si le média est trouvé, l'utilisateur est redirigé vers une page affichant les résultats de la recherche.

Cette page affiche la vignette du média et son titre (l'URL du média si le titre n'est pas renseigné). Un bouton Voir les utilisations permet d'afficher la liste des références du média.

Résultat de recherche par URL

La liste des références du média est affichée sous forme de tableau.

Chaque ligne du tableau correspond à une référence du média.

Un lien Modifier permet d'accéder à la page d'édition de la référence. Si la référence est une fiche et que celle ci est en ligne, un lien Voir en ligne permet d'accéder à la page de consultation de la fiche.

Affichage de la liste des références du média

Modifier le fichier

Module Profils

Modifier le fichier

Statistiques du composant Profils

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de profils utilisateurs avec un groupe au moins sur une rubrique invalide{"marker":"VOLUME_PROFIL","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"core","payload":{"content":"ProfileWithGroupAndInvalidSection","count":1}}

Modifier le fichier

Module Rubriques

Modifier le fichier

Statistiques du composant Rubriques

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de rubriques{"marker":"VOLUME_SECTION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"core","payload":{"content":"section","count":164}}

Modifier le fichier

Module Scripts automatisés

Modifier le fichier

Statistiques du composant Scripts automatisés

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de scripts planifiés actifs{"marker":"VOLUME_SCRIPTS_PLANIFIES","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"SCRIPT_AUTOMATISE","payload":{"content":"ScriptsPlanifiesCount","count":5}}

Indicateurs d'actions

Chaque script exécuté sera tracé avec le profil utilisateur renseigné. Le nombre de scripts exécutés manuellement devra être déterminé en compilant toutes les lignes de cette indicateur et en excluant les lignes avec le profil BATCH.

IndicateurMessage type
Evolution du nombre de scripts lancés manuellement (exemple d'indicateur){"marker":"COMPONENT","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"BATCH","component":"core","payload":{"count":1,"action":"RUN","script":"urlRedirectJob","parameters":{"job.datepurge":"auto;90;180;270;360"}}}

Modifier le fichier

Module Services externes

Modifier le fichier

Statistiques du composant Services externes

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de services externes existants{"marker":"VOLUME_EXTERNAL_SERVICES","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"core","payload":{"content":"services_externes","count":3}}

Modifier le fichier

Module Utilisateurs

Modifier le fichier

Statistique du composant Utilisateurs

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de comptes utilisateurs{"marker":"VOLUME_USER","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"UTILISATEUR","payload":{"content":"UtilisateurCount","count":69}}
Nombre de comptes utilisateurs ayant au moins un droit{"marker":"VOLUME_USER","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"UTILISATEUR","payload":{"content":"UtilisateurRolesCount","count":54}}

Modifier le fichier

Module Agenda

Modifier le fichier

Statistique du composant agenda

Ce fichier présente les statistiques du composant agenda (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module actualité{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"agenda","payload":{"extension":"agenda","active":1}}

North Star

IndicateurMessage type

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches ayant le plugin agenda renseigné (avec dates à venir) dans une page en ligne{"marker":"VOLUME_PLUGIN","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"agenda","payload":{"content":"AgendaPlugin","count":0}}
Nombre de tag "agenda" inséré dans des pages en ligne{"marker":"VOLUME_TAG","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"agenda","payload":{"content":"[agendaListeEvenement;]","count":1}}
Nombre de rubriques ayant un accueil de type "Vue agenda" .{"marker":"VOLUME_SECTION_HEAD_TYPE","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"agenda","payload":{"content":"0500","count":2}}

Indicateurs d'actions

IndicateurMessage type
Evolution du nombre d'enregistrements de rubriques avec ajout d'un accueil de type "Vue Agenda"{"marker":"SECTION_HEAD_TYPE","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"agenda","payload":{"count":1,"type":"0500","action":"PUBLISHING"}}
Evolution du nombre de tag "agenda" enregistrés.{"marker":"COMPONENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"agenda","payload":{"action":"CREATE","plugin":"agenda"}}
Evolution de ligne "AgendaEvenement" nouvellement crées lors de l'enregistrement de la page.{"marker":"NEW_TAG","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"agenda","payload":{"count":1,"content":"0008","tag":"[agendaListeEvenement;]"}}

Modifier le fichier

Extension Arbre

Modifier le fichier

Statistique de l'extension Arbre

Ce fichier présente les statistiques de l'extension Arbre

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension Arbre{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"arbre","payload":{"extension":"arbre","active":1}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de tags arbre insérés dans des fiches en ligne{"marker":"VOLUME_TAG","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"arbre","payload":{"content":"[traitement_arbre;","count":0}}

Modifier le fichier

Extension CatalogueLien

Modifier le fichier

Statistique de l'extension CatalogueLien

Ce fichier présente les statistiques de l'extension CatalogueLien

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension CatalogueLien{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"cataloguelien","payload":{"extension":"cataloguelien","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications de Lien (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"cataloguelien","payload":{"count":1,"content":"0018","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches Lien en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"cataloguelien","payload":{"content":"0018","count":7}}

Modifier le fichier

Composant Espaces collaboratifs

Modifier le fichier

Statistique du composant Espaces collaboratifs

Ce fichier présente les statistiques du composant espaces collaboratifs (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module espaces collaboratifs{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"extension":"collaboratif","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications dans les espaces collaboratif (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"collaboratif","payload":{"count":1,"content":"0100","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de tags de type intitule espaces{"marker":"VOLUME_TAG","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"content":"[intitule_espace]","count":0}}
Nombre de tags de type liste espaces{"marker":"VOLUME_TAG","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"content":"[liste_espaces;]","count":1}}
Nombre de tags de type lien espaces{"marker":"VOLUME_TAG","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"content":"[lien_espaces;]","count":0}}
Nombre d'agenda en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"content":"0101","count":2}}
Nombre de document en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"content":"0105","count":2}}
Nombre de forum en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"content":"0102","count":1}}
Nombre de news en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"collaboratif","payload":{"content":"0100","count":1}}

Modifier le fichier

Extension FicheLink - Rebonds

Modifier le fichier

Statistique de l'extension FicheLink

Ce fichier présente les statistiques de l'extension FicheLink

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension fiche link{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"fichelink","payload":{"extension":"fichelink","active":1}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches tous statut de fiche ayant au moins un rebond{"marker":"VOLUME_FICHELINK","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"fichelink","payload":{"content":"fichelink","count":5}}
Nombre de fiches en ligne ayant au moins un rebond{"marker":"VOLUME_ONLINE_FICHELINK","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"fichelink","payload":{"content":"onlineFicheLink","count":2}}

Indicateurs d'actions

IndicateurMessage type
Evolution du nombre de lien créé entre une fiche et un rebond{"marker":"COMPONENT","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"fichelink","payload":{"count":1,"content":"FICHELINK","action":"CREATE"}}

Modifier le fichier

Extension Formulaire

Modifier le fichier

Statistique de l'extension Formulaire

Ce fichier présente les statistiques de l'extension Formulaire.

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension Formulaire{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"formulaire","payload":{"extension":"formulaire","active":1}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de formulaires{"marker":"VOLUME_FORMULAIRE","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"formulaire","payload":{"content":"FormulaireNombre","count":3}}
Taille du dossier des pièces jointes sur l'environnement{"marker":"VOLUME_FORMULAIRE_PIECES_JOINTES","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"formulaire","payload":{"content":"FormulairePoidsAttachments","count":2905843}}
Nombre de tag formulaires insérés dans des pages en ligne{"marker":"VOLUME_TAG","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"formulaire","payload":{"content":"[formulaire;]","count":3}}

Modifier le fichier

Module Import / Export XML

Modifier le fichier

Statistique de l'extension Import / Export XML

Ce fichier présente les statistiques de l'extension Import / Export XML (Gestion électronique des Documents).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension importexport{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"importExport","payload":{"extension":"importExport","active":true}}

Statistique d'usage

Indicateurs d'actions

IndicateurMessage type
Evolution du nombre d'exécutions du script d'import{"marker":"COMPONENT","date":"2023-06","organisation":"DEV-KSUP-67","route":"ksup1","profile":"CONTENTMANAGER","component":"importExport","payload":{"count":1,"action":"RUN","script":"importXmlJob","parameters":{"TRANSFORMATION_XML":"AUCUNE","PATTERN_FICHIERS":"TOUS"}}}

Modifier le fichier

Extension OFIN

Modifier le fichier

Statistique de l'extension OFIN

Ce fichier présente les statistiques de l'extension OFIN.

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension OFIN{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ofin","payload":{"extension":"ofin","active":true}}

Modifier le fichier

Module Supervision

Modifier le fichier

Statistique du composant supervision

Ce fichier présente les statistiques du composant supervision (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module commentaire{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"supervision","payload":{"extension":"supervision","active":true}}

Statistique d'usage

Indicateurs d'actions

Evolution du nombre d'accès à chaque sous menu :

IndicateurMessage type
Contrôle de la configuration des sites{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":1,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=CONFIGURATION_SITE&ACTION=LISTE_SITES"}}
Etat du système{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":1,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=ETAT_SYSTEME&ACTION=SANTE_APPLICATION"}}
Fichiers journaux{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":34,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=FICHIERS_JOURNAUX&ACTION=RECHERCHER"}}
Gestion des caches{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":10,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=GESTION_CACHES&ACTION=RECHERCHER"}}
Gestion de l'envoi de mail{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":1,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=GESTION_MAIL&ACTION=PARAMETRAGE_MAIL"}}
Internationalisation des messages{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":1,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=INTERNATIONALISATION_MESSAGES&ACTION=RECHERCHER"}}
Paramétrage de l'application{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":2,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=PARAMETRAGE_APPLICATION&ACTION=RECHERCHER"}}
Recherche{"marker":"HTTP","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"supervision","payload":{"count":1,"method":"GET","url":"https://dev-ksup-master.sup.rancher.kosmos.fr/servlet/com.jsbsoft.jtf.core.SG?PROC=SEARCH_SUPERVISION&ACTION=SUPERVISION"}}

Modifier le fichier

Import des évènements

Afin de faciliter la création des évènements en masse, une fonctionnalité d'import est disponible. La fonctionnalité est disponible depuis la page de liste des évènements, en cliquant sur le bouton "Import"

Import

Importer un fichier

Le fichier à importer est un fichier ZIP composé de deux fichiers csv, un pour les évènements et un pour les créneaux. Les deux fichiers de données sont des fichiers CSV, avec séparateur ",".

Le fichier des évènements

Le fichier est composé de 21 colonnes, avec une ligne d'entête. Si une donnée contient des sauts de ligne ou des virgules, il est nécessaire d'encadrer la valeur par des guillemets. ex:

valeur 1,"Valeur 2,
avec un saut de ligne",valeur3

Si la donnée contient des guillemets, la valeur doit être encadrée par des guillements et les guillements de texte doublés.

valeur 1,"Valeur 2, avec ""des guillements""",valeur3
ChampDescriptionFormatExemple
CodeCode unique de l'évènement. Ce code sera visible lors de la réservationObligatoire, 50 caractères alphanumériques, pas de caractères spéciaux sauf "-"JS-2022, po-lettres-2023
Nom de l'évènementNom de l'évènementTexte libre sur une seule ligne, 255 caractères maxPortes ouvertes 2023
RubriqueCode de la rubrique de rattachementObligatoire, prendre le code de la rubrique indiqué dans K-Sup
StructureCode de la structure de rattachementFacultatif, prendre le code de la structure indiqué dans K-Sup
CatégorieCode de la catégorie de l'évènement (cf Administration > Libellés)Facultatif, prendre le code du libellé de la catégorie d'évènement indiqué dans K-Sup
TypeCode du type de l'évènement. (cf Administration > Libellés)Facultatif, prendre le code du libellé de du type d'évènement indiqué dans K-Sup
LieuDescription du lieu de l'évènementFacultatif, 4096 caractères
Date de débutDate de début de l'évènementObligatoire, Date au format JJ/MM/AAAA15/02/2023
Heure de débutHeure de début de l'évènementObligatoire, Heure au format HH24:MI:SS08:30:00
Date de finDate de fin de l'évènementOptionnel, Date au format JJ/MM/AAAA22/02/2023
Heure de finHeure de fin de l'évènementOptionnel, Heure au format HH24:MI:SS18:45:00
Date d'ouverture des inscriptionsDate d'ouverture des inscriptionsObligatoire, Date au format JJ/MM/AAAA15/01/2023
Heure d'ouverture des inscriptionsHeure d'ouverture des inscriptionsObligatoire, Heure au format HH24:MI:SS00:00:00
Date de fermeture des inscriptionsDate de fermeture des inscriptionsOptionnel, Date au format JJ/MM/AAAA10/02/2023
Heure de fermeture des inscriptionsHeure de fermeture des inscriptionsOptionnel, Heure au format HH24:MI:SS23:59:59
DescriptionTexte de présentation de l'évènement.Facultatif, 4096 caractères
Intitulé de la place / articleIntitulé de la placeObligatoire,
Nombre de places / articles par inscriptionNombre de place maximum par inscriptionNumérique, facultatif
Texte de confirmationTexte de confirmation d'inscription. Ce texte est affiché à l'écran à la fin de la reservation et dans le mail de confirmation d'inscription.
Adresse de l'expéditeurAdresse électronique à utiliser pour l'émetteur des courrielsemail
Attacher le fichier calendaireFacultatif
Adresse mail de contactAdresse électronique à utiliser pour obtenir des informations sur l'évènementemail

Exemple :

Code,Nom de l'évènement,Rubrique,Structure,Catégorie,Type,Lieu,Date de début,Heure de début,Date de fin,Heure de fin,Date d'ouverture des inscriptions,Heure d'ouverture des inscriptions,Date de fermeture des inscriptions,Heure de fermeture des inscriptions,Description,Intitulé de la place / article,Nombre de places / articles par inscription,Texte de confirmation,Adresse de l'expéditeur,Attacher le fichier calendaire,Adresse mail de contact KSMS-191022-A,Découerte du module inscription,RUBRIQUE1,,KSUP,WEBINAR,"Kosmos - 8 rue Kervegan - 44000 Nantes Rendez-vous place du Commerce, 15 à 30 minutes avant le début",01/11/2022,10:00:00,01/11/2022,12:00:00,15/10/2022,08:00:00,,,,Présentation du module inscription,2,"Vous êtes bien inscrit à la présentation du module inscription. Rendez-vous place du Commerce, 15 à 30 minutes avant le début. Merci de vous désinscrire si vous ne pouvez pas venir.",evenement@kosmos.fr,,jpatin@kosmos.fr

Le fichier des créneaux

Le fichier est composé de 16 colonnes, avec une ligne d'entête.

ChampDescriptionFormatExemple
Code de l'évènement de rattachementCode de l'évènement auquel rattacher ce créneauObligatoire, utiliser un des code évènement
Code du créneauCode du créneauObligatoire, 50 caractères alphanumériques, pas de caractères spéciaux sauf "-"
Intitulé du créneauTexte libre sur une seule ligne, 255 caractères max
StructureCode de la structure de rattachementFacultatif, prendre le code de la structure indiqué dans K-Sup
CatégorieCode de la catégorie du créneau (cf Administration > Libellés)Facultatif, prendre le code du libellé de la catégorie de créneau indiqué dans K-Sup
TypeCode du type du créneau. (cf Administration > Libellés)Facultatif, prendre le code du libellé de du type de créneau indiqué dans K-Sup
Date de débutDate de début de l'évènementObligatoire, Date au format JJ/MM/AAAA15/02/2023
Heure de débutHeure de début du créneauObligatoire, Heure au format HH24:MI:SS08:30:00
Date de finDate de fin du créneauOptionnel, Date au format JJ/MM/AAAA22/02/2023
Heure de finHeure de fin du créneauOptionnel, Heure au format HH24:MI:SS18:45:00
LieuDescription du lieu du créneauFacultatif, 4096 caractères
DescriptionTexte de présentation du créneau.Facultatif, 4096 caractères
Nombre de places disponiblesNombre de place disponibles pour le créneauNumérique, obligatoire
Gestionnaire 1Code utilisateur du premier getionnaireFacultatif, (cf Admisnistration > Utilisateurs)
Gestionnaire 2Code utilisateur du second getionnaireFacultatif, (cf Admisnistration > Utilisateurs)
Gestionnaire 3Code utilisateur du troisième getionnaire Facultatif, (cf Admisnistration > Utilisateurs)Facultatif, (cf Admisnistration > Utilisateurs)

Exemple :

Code de l'évènement de rattachement,Code du créneau,Intitulé du créneau,Structure,Catégorie,Type,Date de début,Heure de début,Date de fin,Heure de fin,Lieu,Description,Nombre de places disponibles,Gestionnaire 1,Gestionnaire 2,Gestionnaire 3 KSMS-191022-A, KSMS-1,Créneau 1234,,,,01/11/2022,08:00:00,01/11/2022,09:00:00,,,5,jpatin,,

Fichier d'exemple

Import

Modifier le fichier

Formulaires personnalisés

Lors de son inscription à un évènement, il est possible de demander des informations complémentaires au participant. C'est la notion de "Question" dans pretix.

Création d'un nouveau formulaire

Les formulaires sont des fichiers JSON contenant la définition des questions.

Le fichier de définition doit respecter la syntaxe exacte de pretix.
En effet, le fichier est envoyé directement via l'API sur le endpoint pretix dédié.

Actuellement, le moyen le plus simple de générer un nouveau formulaire est d'utiliser l'IHM pretix native. Une fois le formulaire construit, il suffit de l'appeler via le endpoint d'API GET /api/v1/organizers/(organizer)/events/(event)/questions/ Dans la réponse, extraire la section results et la copier dans un fichier ".json"

HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [{
        "id": 1,
        "question": {"en": "T-Shirt size"},
        "help_text": {"en": "Choose your preferred t-shirt-size"},
        "type": "C",
        ...
        },{
        ...
    }]
}

Selon le besoin, le fichier généré peut être stocké dans trois emplacements différents:

  1. Dans les ressources de l'extension inscription v2
    C'est le cas des questionnaires "produit" qui seront communs à tous les clients. Le fichier doit etre déposé dans le répertoire src/main/resources/form/templates de l'extension.
  2. Dans les ressources du projet
    C'est le cas des questionnaires "projet" spécifiques à un client. Le fichier doit etre déposé dans le répertoire src/main/resources/form/templates du projet.
  3. Dans le répertoire /inscription/templates/ du storage
    C'est le cas des questionnaires "projet" urgents, spécifiques à un client. En déposant le formulaire dans le storage il est immédiatement disponible, sans avoir besoin de procéder à une livraison et un déploiement.

Les fichiers issus du storage sont toujours prioritaires par rapport aux fichiers embarqués.
Si un fichier existe dans le storage et dans les ressources, c'est le fichier du storage qui fera foi.

Afin d'activer un formulaire pour le rendre disponible dans la création /modification d'un évènement, le fichier doit être associé à un libellé K-Sup dont le code est le nom (hors extension) du fichier JSON. Par exemple, pour un fichier de question nommé lorem.json, il faut créer un libellé de type "(Évènement) Type de formulaire" dont le code et "lorem".

Cela implique que les formulaires produit ou projet doivent être accompagnés d'un script flyway pour créer le libellé associé.

Modifier le fichier

Extension Lieu

Modifier le fichier

Statistique du module Cartographie

Ce fichier présente les statistiques du module Cartographie

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de la cartographie Accueil Rubrique{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"cartographie","payload":{"module":"cartographieAccueilRubrique","active":1}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de rubriques ayant comme accueil le type "plan interactif"{"marker":"VOLUME_SECTION_HEAD_TYPE","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"cartographie","payload":{"content":"9999","count":1}}

Modifier le fichier

Article

La fiche "Article" est la fiche la plus simple. Elle sert à présenter un contenu en différents chapitre, mettre en place des guides pratiques, des procédures, diffuser des brèves...

"Contenus" > "Article"

Tous les champs ne sont pas obligatoires, seuls ceux précédés d'un astérisque (*) doivent obligatoirement être renseignés. Les champs non renseignés n'apparaîtront pas dans l'article sur le site.

La fiche article est découpée en 7 onglets :

Onglet Contenu principal

  • Langue : Sélectionnez une langue dans le menu déroulant.
  • Titre : Champ libre.
  • Sous-titre : Champ libre.
  • Date de l'article : format JJ/MM/AAAA
  • Rubrique : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arborescence la rubrique de rattachement de l'article. Le rattachement à une rubrique permet de positionner une page dans l'arborescence des contenus et de définir un contexte de navigation.
  • Structure : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arbre des structures. Le rattachement à une structure permet de définir des autorisations de modification sur la fiche en fonction des droits des utilisateurs, de rechercher une fiche ou de la faire remonter dans une liste de fiches.
  • Thématique : Liste mutlivaluée. Les libellés sont paramétrables depuis le menu "Administration" du module d'adminsitration
  • Résumé : Texte simple, qui sera affiché dans les listes d'articles si le style d'affichage sélectionné est par exemple : titre + sous-titre + résumé
  • Description : Contenu de l'article. Dans ce champ vous pouvez copier/coller du texte et insérer des liens et des médias.
  • Photo : Cliquez sur Parcourir pour aller chercher une image dans la médiathèque ou ajouter une nouvelle image. La photo sera affichée dans les listes d'articles, si le style d'affichage sélectionné est par exemple : date + titre + sous-titre + vignette + résumé

Onglet Informations complémentaires

  • Ordre d'affichage : Saisissez un nombre entre 1 et 100 pour forcer l'ordre d'affichage des articles dans une liste de fiches.
  • Fichier(s) joint(s) : Pour proposer en téléchargement un visuel ainsi que des fichiers. Attention : l'image et les fichiers ne seront pas intégrés dans la médiathèque il seront enregistrés sur la fiche article.
  • Forum : Activer ou Fermer un forum de discussion.

Onglet Encadré

La zone d'encadré s'affiche habituellement à droite dans la fiche. Elle permet de faire des zooms et/ou des rebonds vers d'autres contenus. Le rédacteur peut y insérer des documents à télécharger, des liens vers des pages internes ou vers des sites externes... Pour insérer un nouvel encadré de fiche, cliquez sur "Modèles" situé dans l'éditeur de contenus.

Modifier le fichier

Statistique du composant Article

Ce fichier présente les statistiques du composant article (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module article{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"article","payload":{"extension":"article","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches article (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"article","payload":{"count":1,"content":"0015","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiche article en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"article","payload":{"content":"0015","count":131}}

Modifier le fichier

Page Libre

Créez des pages d'accueil de rubrique, des pages éditoriale

Le contributeur peut organiser une page libre en plusieurs "blocs de contenus" et y appliquer les "modèles" souhaités. En cela, elle est idéale pour créer des pages d'accueil de rubriques, des pages éditoriales par exemple.

"Contenus" > "Page Libre"

La page libre permet de faire des mises en page de façon souple et illimitée :

  • d'ajouter autant de blocs de contenus que l'on souhaite
  • d'organiser ces blocs et de les répartir librement
  • exemple : double colonage, triple colonage...
  • d'appliquer des modèles graphiques qui ne sont pas laisser à disposition dans les autres types de fiches
  • d'appliquer des encadrés de recherche sur les contenus souhaités (Exemple : encadré de recherche dans l'annuaire)

Organisation des zones de contenus

Un bloc de contenus est caractérisé par :

  • Une ligne
  • Une colonne
  • Une largeur (en %)

L'administration de ces 3 critères permet d'avoir une mise en page spécifique à sa page.

Les onglets de la fiche

Tous les champs ne sont pas obligatoires, seuls ceux précédés d'un astérisque (*) doivent obligatoirement être renseignés. Les champs non renseignés n'apparaîtront pas dans le site.

Comme les autres fiches, la page libre est découpée en 6 onglets.

Onglet Contenu Principal

  • Langue : Sélectionnez une langue dans le menu déroulant.
  • Titre : Champ libre.
  • Rubrique : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arborescence la rubrique de rattachement de la page. Le rattachement à une rubrique permet de positionner une page dans l'arborescence des contenus et permet de définir un contexte de navigation.
  • Structure : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arbre des structures. Le rattachement à une structure permet de définir des autorisations de modification sur la fiche en fonction des droits des utilisateurs.
  • Bloc de contenu : Correspond à un paragraphe de votre page. Dans ce champ vous pouvez copier/coller du texte et insérer des liens, des médias...

Afin d'organiser votre page vous pouvez ajouter des blocs de contenu, changer l'ordre des blocs de contenu dans la page en modifiant le numéro de ligne, définir des colonnes.

Onglet Informations complémentaires

Informations complémentaires : Ajoutez ici les informations complémentaires qui apparaîtront généralement en bas de votre page libre

Onglet Encadré

Encadré de fiche

La zone d'encadré s'affiche habituellement à droite dans la fiche. Elle permet de faire des zooms et/ou des rebonds vers d'autres contenus. Le rédacteur peut y insérer des documents à télécharger, des liens vers des pages internes ou vers des sites externes... Pour insérer un nouvel encadré de fiche, cliquez sur "Modèles" dans l'éditeur de contenus.

Encadré de recherche

Vous pouvez ajouter deux formulaires de recherche de fiches au choix :

  • Encadré recherche : sélectionnez un type dans le menu déroulant
  • Encadré recherche (2) : sélectionnez un type dans le menu déroulant

Modifier le fichier

Module Accueil

Modifier le fichier

Statistique du composant accueil

Ce fichier présente les statistiques du composant accueil (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module accueil{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"accueil","payload":{"extension":"accueil","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Accueil (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"accueil","payload":{"count":1,"content":"0111","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiche accueil en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"accueil","payload":{"content":"0111","count":24}}

Modifier le fichier

Module Actualite

Modifier le fichier

Documentation Fonctionnelle : Carte "Dernières actualités"

Description

La carte "Dernières actualités" est un composant d'interface utilisateur qui permet d'afficher les actualités récentes et à venir, organisées par type d'actualité. Cette carte peut être intégrée dans différentes pages du portail pour offrir aux utilisateurs un accès rapide aux informations récentes.

Fonctionnalités

Affichage des actualités par type

  • La carte affiche les actualités regroupées par type (événements, annonces, communiqués, etc.)
  • Les types d'actualités sont présentés sous forme d'onglets, permettant à l'utilisateur de naviguer facilement entre les différentes catégories
  • Un onglet "Dernières actualités" est toujours présent et affiche toutes les actualités, tous types confondus

Chargement dynamique du contenu

  • Le contenu des actualités est chargé dynamiquement lorsque l'utilisateur clique sur un onglet
  • Pendant le chargement, un indicateur visuel (skeleton loader) est affiché pour informer l'utilisateur que le contenu est en cours de chargement
  • L'onglet "Dernières actualités" est chargé automatiquement à l'ouverture de la page

Filtrage des actualités

  • La carte affiche uniquement les actualités actuelles et à venir (les actualités passées ne sont pas affichées)
  • Le nombre d'actualités affichées par onglet est configurable
  • Les types d'actualités affichés sont configurables

Lien vers toutes les actualités

  • Un lien optionnel "Voir toutes les actualités" peut être affiché en bas de la carte, permettant à l'utilisateur d'accéder à la page complète des actualités

Configuration de la carte

La carte "Dernières actualités" peut être configurée par les administrateurs du portail avec les options suivantes :

Options de configuration

  • Titre : Le titre principal de la carte
  • Sous-titre : Un texte descriptif optionnel affiché sous le titre
  • Types d'actualité : Sélection des types d'actualités à afficher dans la carte
  • Nombre d'actualités par onglet : Définit combien d'actualités sont affichées dans chaque onglet
  • Style d'affichage : Définit le style de liste à utiliser pour l'affichage dans la carte et dans les résultats "voir toutes les actualités"
  • Contexte d'affichage de la recherche : Permet de filtrer les actualités selon un contexte spécifique (par exemple, une rubrique particulière)
  • Intitulé du lien "voir toutes les actualités" : Personnalise le texte du lien vers la page complète des actualités

Cas d'utilisation

Pour les visiteurs du portail

  • Consulter rapidement les dernières actualités de l'établissement
  • Filtrer les actualités par type pour trouver rapidement l'information recherchée
  • Accéder à la page complète des actualités pour voir plus de détails

Pour les administrateurs du portail

  • Mettre en avant les actualités importantes sur la page d'accueil ou d'autres pages du portail
  • Configurer les types d'actualités à afficher selon les besoins spécifiques
  • Personnaliser l'apparence et le comportement de la carte pour s'adapter au contexte d'utilisation

Modifier le fichier

Statistique du composant actualité

Ce fichier présente les statistiques du composant actualité (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module actualité{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"actualite","payload":{"extension":"actualite","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches actualités (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"actualite","payload":{"count":1,"content":"0008","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches actualité en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"actualite","payload":{"content":"0008","count":0}}

Indicateurs d'actions

IndicateurMessage type

Modifier le fichier

Module AnnuaireSup

Modifier le fichier

Statistique du composant annuairesup

Ce fichier présente les statistiques du composant annuairesup (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module annuairesup{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"annuairesup","payload":{"extension":"annuairesup","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Annuaire (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"annuairesup","payload":{"count":1,"content":"0006","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiche annuaire en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"annuairesup","payload":{"content":"0006","count":1}}

Indicateurs d'actions

IndicateurMessage type

Modifier le fichier

Fiche structure

Recherche

Une fiche structure est recherchable en front office si :

  • elle est au statut "En ligne"
  • elle est publiée ou multipubliée dans une rubrique d'un site actif OU est utilisée en page de tête d'une rubrique visible
  • la case à cocher "Ne pas afficher dans le moteur de recherche" n'est pas cochée dans l'onglet "Publication" de saisie de la fiche
  • la case "Visible en front-office" est cochée dans l'onglet "Suivi" de saisie de la fiche

Modifier le fichier

Statistique du composant structure

Ce fichier présente les statistiques du composant structure (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module structure{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"structure","payload":{"module":"annuairesupObjetStructure","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications de structure (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"structure","payload":{"count":1,"content":"0001","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches structures "en ligne"{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"structure","payload":{"content":"0001","count":0}}

Indicateurs d'actions

IndicateurMessage type

Modifier le fichier

Module Application

La fiche "Application" sert à présenter une application, un service, un outil, un logiciel, une application mobile...

"Etablissement" > "Application"

Tous les champs ne sont pas obligatoires, seuls ceux précédés d'un astérisque (*) doivent obligatoirement être renseignés.

La fiche application est découpée en 6 onglets :

Onglet Contenu principal

  • Code : Champ libre.
  • Titre : Champ libre.
  • Sous-titre : Champ libre.
  • Fiche consultable : Case à cocher.
  • Structure : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arbre des structures. Le rattachement à une structure permet de définir des autorisations de modification sur la fiche en fonction des droits des utilisateurs, de rechercher une fiche ou de la faire remonter dans une liste de fiches.
  • Application : Radio bouton Externe ou Interne. Lorsque Externe est sélectionné, un champ URL est affiché pour saisir l'url de l'application.
  • Catégories : Liste multivaluée. Les libellés sont paramétrables depuis le menu "Administration" du module d'adminsitration
  • Types : Liste multivaluée. Les libellés sont paramétrables depuis le menu "Administration" du module d'adminsitration
  • Description : Toolbox. Dans ce champ vous pouvez copier/coller du texte et insérer des liens et des médias.
  • Photo : Cliquez sur Parcourir pour aller chercher une image dans la médiathèque ou ajouter une nouvelle image. La photo sera affichée lors de la consultation de la fiche application.

Recherche elastic

La fiche remonte dans les résultats de recherche.

Si la case "Fiche consultable" est cochée, le lien du résultat de recherche renvoie vers la fiche application. Sinon, le lien renvoie vers l'URL de l'application.

Affichage front

  • Les catégories sont affichées en haut de la fiche application sous la forme de cartouches.
  • La date de publication est affichée sous les catégories. Si la fiche a été modifiée, la date de modification est également affichée.
  • Si l'application est interne, un bouton "Accéder" est affiché pour ouvrir l'application dans un nouvel onglet. Si l'application est externe, un bouton "Accéder" est affiché pour ouvrir l'URL de l'application dans un nouvel onglet.

Carte Application

Carte permettant d'afficher une mosaïque des applications

Cette carte contient les champs de saisie suivants :

NomTypeDescription
TitreTexte simpleLe titre
Sous-titreTexte simpleLe sous-titre
CatégorieSélection multipleChoix des catégories affichées

La carte application affiche :

  • Un titre
  • Un sous-titre
  • Les catégories d'applications
    • Les 3 premières catégories d'applications
    • Un menu "Plus d'apps" permettant de sélectionner une autre catégorie d'application
  • Le filtre "rechercher une application" qui permet de filtrer les applications de la catégorie courante directement dans l'onglet (filtre sur le titre ou le sous-titre ou l'url de l'application)
  • Les aperçus des applications accessibles à l'utilisateur (pas de restriction d'accès) avec pour chaque appli :
    • Le sous-titre de l'application : affiché sous la forme d'un texte d'aide
    • La photo de l'application
    • Le titre de l'application

Au clic sur le bloc application : l'utilisateur est redirigé vers l'url de l'application ou vers la fiche application (selon ce qui a été défini dans la fiche application) => en V1 si le front de la fiche application n'existe pas on redirigera vers l'URL Lorsque l'utilisateur est connecté

Utilisateur connecté

Si l'utilisateur est connecté il voit apparaître un onglet "Mes apps" et il a la possibilité :

  • Depuis les onglets catégories :
    • D'épingler des applications dans les différentes catégories qui seront ensuite disponibles dans "Mes apps" => les applications déjà ajoutées présentent une épingle de couleur
    • De supprimer l'appli de "Mes apps" depuis l'onglet de la catégorie
  • Depuis "Mes apps"
    • De retrouver toutes les applis épinglées
    • De supprimer les applis épinglées de l'onglet "Mes apps"

Modifier le fichier

Statistique du composant Application

Ce fichier présente les statistiques du composant application.

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module application{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"application","payload":{"extension":"application","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches application (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"application","payload":{"count":1,"content":"0030","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiche application en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"application","payload":{"content":"0030","count":131}}

Modifier le fichier

Extension Blog

Modifier le fichier

Statistique de l'extension Blog

Ce fichier présente les statistiques de l'extension blog (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension blog{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"blog","payload":{"extension":"blog","active":true}}

Modifier le fichier

Module Article de blog

Modifier le fichier

Statistique du composant Article de blog

Ce fichier présente les statistiques du composant article de blog (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type

North Star

IndicateurMessage type
Evolution du nombre de publications d'article de blog (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"blog","payload":{"count":1,"content":"9130","action":"MODIFYING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre d'article de blog en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"blog","payload":{"content":"9130","count":4}}

Modifier le fichier

Module Blog

Modifier le fichier

Statistique du composant Blog

Ce fichier présente les statistiques du composant blog (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type

North Star

IndicateurMessage type
Evolution du nombre de publications des blogs (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"blog","payload":{"count":1,"content":"9120","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de blog en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"blog","payload":{"content":"9120","count":3}}

Modifier le fichier

Module Commentaire

Modifier le fichier

Statistique du composant commentaire

Ce fichier présente les statistiques du composant commentaire (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module commentaire{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"commentaire","payload":{"extension":"commentaire","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Commentaire (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"commentaire","payload":{"count":2,"content":"0010","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches commentaire en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"commentaire","payload":{"content":"0010","count":7}}

Indicateurs d'actions

IndicateurMessage type

Modifier le fichier

Extension étudiant et alumni

Modifier le fichier

Statistique de l'extension étudiant alumni

Ce fichier présente les statistiques de l'extension étudiant alumni (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension étudiant alumni{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"etudiantetalumni","payload":{"extension":"etudiantetalumni","active":1}}

Modifier le fichier

Module Annuaire Anciens Etudiants

Modifier le fichier

Statistique du composant annuaire anciens etudiants

Ce fichier présente les statistiques du composant annuaire anciens etudiants (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module annuaire anciens étudiants{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"etudiantetalumni","payload":{"module":"etudiantetalumniObjetAnnuaireanciensetudiants","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Ancien Etudiant (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"etudiantetalumni","payload":{"count":1,"content":"0013","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches ancien étudiant en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"etudiantetalumni","payload":{"content":"0013","count":0}}

Indicateurs d'actions

IndicateurMessage type

Module étudiant

Modifier le fichier

Statistique du composant annuaire étudiants

Ce fichier présente les statistiques du composant annuaire étudiants (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module annuaire étudiants{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"etudiantetalumni","payload":{"module":"etudiantetalumniObjetAnnuaireetudiants","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Etudiant (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"etudiantetalumni","payload":{"count":1,"content":"0007","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches étudiant en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"etudiantetalumni","payload":{"content":"0007","count":0}}

Indicateurs d'actions

IndicateurMessage type

Modifier le fichier

Module association étudiant

Modifier le fichier

Statistique du composant association étudiant

Ce fichier présente les statistiques du composant association étudiant (cf ).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module association étudiant{"marker":"COMPONENT_ACTIVATION","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"etudiantetalumni","payload":{"module":"etudiantetalumniObjetAssociationetudiant","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches association étudiant (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"etudiantetalumni","payload":{"count":1,"content":"0012","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches association étudiant en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-04","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"etudiantetalumni","payload":{"content":"0012","count":0}}

Indicateurs d'actions

IndicateurMessage type

Modifier le fichier

Extension GED

Modifier le fichier

Statistique de l'extension GED

Ce fichier présente les statistiques de l'extension GED (Gestion électronique des Documents).

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension GED{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ged","payload":{"extension":"ged","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Document (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"ged","payload":{"count":1,"content":"0017","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches Document en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ged","payload":{"content":"0017","count":6}}
Nombre de fiches document uniquement en téléchargement{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ged","payload":{"content":"0017-FICHIERGW","count":2}}

Modifier le fichier

Messages

Diffusion de messages ciblés aux utilisateurs de K-Sup

"Gestion éditoriale" > Message"

Les onglets de saisie de message

Tous les champs ne sont pas obligatoires, seuls ceux précédés d'un astérisque (*) doivent obligatoirement être renseignés.

La saisie du message est découpée en 3 onglets.

Onglet Contenu Principal

  • Etat : Case à cocher permettant d'activer ou de désactiver le message.
  • Intitulé : Champ texte. L'intitulé du message dans le back office.
  • Rubrique : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arborescence la rubrique de rattachement de la page. Le rattachement à une rubrique permet de positionner une page dans l'arborescence des contenus et permet de définir un contexte de navigation.
  • Structure : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arbre des structures. Le rattachement à une structure permet de définir des autorisations de modification sur la fiche en fonction des droits des utilisateurs.
  • Langue : Sélectionnez une langue dans le menu déroulant.
  • Date de début : Sélectionner une date de début d'affichage du message dans le date-picker.
  • Date de fin : Sélectionner une date de fin d'affichage du message dans le date-picker.
  • Heure de début : Saisir une heure de début d'affichage du message (format HH:mm).
  • Heure de début : Saisir une heure de fin d'affichage du message (format HH:mm).
  • Fermeture : Sélectionner "oui" ou "non" pour définir si le message peut être fermé.
  • Notification : Sélectionner "oui" ou "non" pour définir si le message est doublé d'une notification.
  • Ordre : Champ texte. Permet de définir l'ordre d'affichage du message.

Onglet Périmètres d'affichage

Application à une rubrique:

  • Rubrique : Cliquez sur le bouton "Sélectionner" et recherchez dans l'arborescence la rubrique de périmètre.
  • Appliquer aux sous-rubriques : Sélectionner la case à cocher pour que les sous-rubriques de la rubrique sélectionnée soit prises en compte dans le périmètre.
  • Groupes : Sélectionner les groupes du périmètre dans l'arborescence des groupes.

Application à une page

  • Type de fiche : Sélectionner dans la liste le type de fiche à appliquer au périmètre.
  • Groupes : Sélectionner les groupes du périmètre dans l'arborescence des groupes.

Onglet Contenu

  • Choix du type de message : Sélectionner le type d'affichage du message.
  • Titre : Champ libre. Titre du message affiché en front.
  • Contenu : Toolbox. Contenu du message affiché en front.

Modifier le fichier

Règles de gestion de visibilité des messages par date

RG1

Un message est visible uniquement s'il est actif.

RG2

Un message est visible si la date de début est dans le passé ou égale à la date courante.

RG3

Si une heure de début est définie. Un message est visible si l'heure de début est dans le passé ou égale à l'heure courante (l'heure de début s'applique sur la date de début).

RG4

Un message est visible s'il n'y a pas de date de fin ou si la date de fin est dans le futur.

RG5

Si une heure de fin est définie. Un message est visible si l'heure de fin est dans le futur (l'heure de fin s'applique sur la date de fin).

Modifier le fichier

Règles de gestion de visibilité des messages par périmètre

RG1

Un message est visible s'il n'a pas de périmètre d'affichage.

RG2

Si un périmètre de rubrique est renseigné. Un message est visible si la rubrique du périmètre est la rubrique du contexte courant.

RG3

Si un périmètre de rubrique est renseigné. Si les sous-rubriques sont incluses dans le périmètre. Un message est visible si la rubrique du contexte courant est la rubrique du périmètre ou une de ses sous-rubriques.

RG4

Si un groupe est présent sur le périmètre. Si l'utilisateur ne possède pas de groupe, le message n'est pas visible.

RG5

Si un groupe est présent sur le périmètre. Si l'utilisateur possède un ou plusieurs groupes, le message est visible si le groupe du périmètre est présent dans les groupes de l'utilisateur.

RG6

Si plusieurs groupes sont présents sur le périmètre. Si l'utilisateur possède un ou plusieurs groupes, le message est visible si un des groupes du périmètre est présent dans les groupes de l'utilisateur.

RG7

Si un groupe est présent sur le périmètre. Si l'utilisateur possède un ou plusieurs groupes, le message est visible si un des sous-groupes des groupes du périmètre est présent dans les groupes de l'utilisateur.

RG8

Si un périmètre d'objet est renseigné. Un message est visible si l'objet présent dans le contexte courant est l'objet défini sur le périmètre.

Modifier le fichier

Module Formation

Modifier le fichier

Statistique du composant Formation

Ce fichier présente les statistiques du composant Formation

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module Formation{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ofin","payload":{"module":"offreformationObjetFormation","active":false}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches formation (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"ofin","payload":{"count":1,"content":"0002","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches formations en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ofin","payload":{"content":"0002","count":6}}
Nombre de fiches formations importées{"marker":"VOLUME_FORMATIONS_IMPORTEES","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ofin","payload":{"content":"FormationsImportees","count":1}}
Nombre de fiches formations saisies manuellement{"marker":"VOLUME_FORMATIONS_SAISIES","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ofin","payload":{"content":"FormationsSaisies","count":6}}

Modifier le fichier

Module Parcours

Modifier le fichier

Statistique du composant Parcours

Ce fichier présente les statistiques du composant Parcours.

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module parcours{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"parcours","payload":{"module":"offreformationObjetParcours","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Parcours (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"parcours","payload":{"count":1,"content":"0005","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches Parcours en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"parcours","payload":{"content":"0005","count":146}}
Nombre de liens entre les fiches Parcours Amont <> Aval{"marker":"VOLUME_PARCOURS_TO_PARCOURS","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"constructionparcours","payload":{"content":"ParcoursToParcours","count":191}}

Modifier le fichier

Module Elément Pédagogique

Modifier le fichier

Statistique du composant Element Pédagogique

Ce fichier présente les statistiques du composant Element Pédagogique.

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module élément pédagogique{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ueup","payload":{"module":"offreformationObjetUeup","active":false}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches élément pédagogique (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"ueup","payload":{"count":1,"content":"0003","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches association étudiant en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"ueup","payload":{"content":"0003","count":1}}

Modifier le fichier

Module Cours

Modifier le fichier

Statistique du composant Cours

Ce fichier présente les statistiques du composant Cours.

Indication d'installation / d'utilisation

IndicateurMessage type
Statut du module cours{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"cours","payload":{"module":"offreformationObjetCours","active":false}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Cours (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"cours","payload":{"count":1,"content":"0004","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches Cours en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"cours","payload":{"content":"0004","count":0}}

Modifier le fichier

Extension Offre Stage Emploi

Modifier le fichier

Statistique de l'extension OffreStageEmploi

Ce fichier présente les statistiques de l'extension OffreStageEmploi

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension fiche link{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"offrestageemploi","payload":{"extension":"offrestageemploi","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches stage / emploi (passage à l'état En Ligne){"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"offrestageemploi","payload":{"content":"0011","count":9}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Mode de saisie anonyme (Paramétrage "offresstagesemplois.anonyme"){"marker":"VOLUME_OFFRESTAGEEMPLOI_ANONYME","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"offrestageemploi","payload":{"module":"OffreStageEmploiAnonyme","active":"0"}}
Répartition des fiches en lignes par Type de stage / emploi : stage{"marker":"VOLUME_OFFRESTAGEEMPLOI_TYPE_OFFER","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"offrestageemploi","payload":{"content":"OffreStageEmploiStage","count":1}}
Répartition des fiches en lignes par Type de stage / emploi : job{"marker":"VOLUME_OFFRESTAGEEMPLOI_TYPE_OFFER","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"offrestageemploi","payload":{"content":"OffreStageEmploiJob","count":7}}
Répartition des fiches en lignes par Type de stage / emploi : emploi{"marker":"VOLUME_OFFRESTAGEEMPLOI_TYPE_OFFER","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"offrestageemploi","payload":{"content":"OffreStageEmploiEmploi","count":1}}

Modifier le fichier

Extension Petite Annonce

Modifier le fichier

Statistique de l'extension Petite Annonce

Ce fichier présente les statistiques de l'extension Petite Annonce

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension petite annonce{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"petiteannonce","payload":{"extension":"petiteannonce","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications des fiches Petite Annonce (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"petiteannonce","payload":{"count":1,"content":"9010","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches petites annonces en ligne avec une date de mise à jour inférieure à 6 mois{"marker":"VOLUME_PETITE_ANNONCE","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"petiteannonce","payload":{"content":"petiteannonce","count":1}}

Modifier le fichier

Extension Recherche et Labo

Modifier le fichier

Statistique de l'extension Recherche et Labo

Ce fichier présente les statistiques de l'extension Recherche et Labo

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension Recherche et Labo{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"rechercheetlabo","payload":{"extension":"rechercheetlabo","active":true}}

North Star

IndicateurMessage type
Evolution du nombre de publications de Laboratoire (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"DEFAULT","profile":"WEBMASTER","component":"rechercheetlabo","payload":{"count":1,"content":"0020","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches Laboratoire en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-MASTER","route":"#N/A","profile":"BATCH","component":"rechercheetlabo","payload":{"content":"0020","count":14}}

Modifier le fichier

Extension Lieu

Modifier le fichier

Statistique de l'extension Lieu

Ce fichier présente les statistiques de l'extension Lieu

Indication d'installation / d'utilisation

IndicateurMessage type
Statut de l'extension Lieu{"marker":"COMPONENT_ACTIVATION","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"lieu","payload":{"extension":"lieu","active":1}}

North Star

IndicateurMessage type
Evolution du nombre de publications de Lieu (passage à l'état En Ligne){"marker":"NORTHSTAR","date":"2023-06","organisation":"DEV-KSUP-MASTER","route":"ksup1","profile":"WEBMASTER","component":"lieu","payload":{"count":2,"content":"9100","action":"PUBLISHING"}}

Statistique d'usage

Indicateur de type compteurs

IndicateurMessage type
Nombre de fiches Lieu en ligne{"marker":"VOLUME_ONLINE_CONTENT","date":"2023-05","organisation":"DEV-KSUP-67","route":"#N/A","profile":"BATCH","component":"lieu","payload":{"content":"9100","count":3}}

Modifier le fichier

Documentation technique

La documentation technique décrit le fonctionnement de l'ensemble du produit K-Sup.
Celle-ci est organisée en modules techniques (calqués sur l'organisation du code, c'est à dire par repository git).
Si nécessaire, vous pourrez trouver pour chaque module :

  • Une description du fonctionnement
  • La liste des propriétés exposées pour configurer le module
  • Un guide à destination des intégrateurs pour implémenter le module sur le projet

Modifier le fichier

Paramétrage des messages d'alerte

Le paramétrage des messages d'alerte est réalisé dans le fichiers core.properties et dans Core_fr.properties/Core_en.properties.

Surcharge

La surcharge des propriétés peut se faire dans le fichier :

  • application_xxx.properties dans les sources d'un projet
  • env.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)
  • Message_xxx.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir, xxx étant la langue)

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
BO.HEADER.MESSAGEContenu du texte du messageChaine de caractères internationaliséeServeur de production (fr)/Production server (en)
BO.HEADER.MESSAGE.COLOR.TEXTCouleur du texte du messageCouleur CSS¹#56040a
BO.HEADER.MESSAGE.COLOR.BACKGROUNDCouleur du fond du messageCouleur CSS¹#ffd6d0
BO.HEADER.SHOW_MESSAGEActivation du messagetrue/falsetrue

1 les couleurs CSS s'expriment de plusieurs manières : documentatiton Mozilla sur les couleurs en css

Modifier le fichier

Les bandeaux d'actions

Un bandeau d’action permet d’afficher un ensemble de boutons regroupés dans une zone dédiée, généralement positionnés côte à côte. Chaque bouton représente une action utilisateur (par exemple : ajout aux favoris, export PDF, notification...).

La configuration des actions du bandeau est entièrement personnalisable :

  • Une action peut être désactivée via une propriété dans le projet.
  • L’ordre d’affichage des actions est configurable.
  • De nouvelles actions peuvent être ajoutées directement depuis le projet, sans modifier le code de l’extension d’origine.

Définition du bandeau

Voici un exemple de définition de bandeau :

<bean id="defaultActionsBandeauFicheViewPreparer" class="com.kosmos.actionsbandeau.bandeau.ActionsBandeauFicheViewPreparer">
<property name="type" value="bandeau-action-fiche" />
<property name="view" value="/WEB-INF/jsp/actionsbandeau/actionsBandeau.jsp" />
<property name="expectedViewTypes">
<list>
<value>action-fiche</value>
</list>
</property>
</bean>

Tout bandeau doit étendre AbstractActionsBandeauViewPreparer. Pour utiliser ce bandeau au sein d'une fiche, il est nécessaire, dans la page en question de rajouter le type du preparer dans les expectedViewTypes.

Le bandeau prépare ici des actions de type "action-fiche".

Actions de bandeau

Voici un exemple d'action, ici un bouton d'ajout/retrait des favoris, qui sera ajouté au bandeau si son preparer l'accepte et que l'action est active (non désactivé).

<bean id="favorisActionFicheViewPreparer" class="com.kosmos.panier.actionsbandeau.preparer.FavorisActionFicheViewPreparer">
<property name="type" value="action-fiche" />
<property name="view" value="/extensions/panier/WEB-INF/jsp/buttonContentPanier.jsp" />
<property name="actionActive" value="${front.actionbandeaufiche.favoris.active:true}" />
<property name="actionPosition" value="${front.actionbandeaufiche.favoris.order:3}" />
<property name="typeFicheAutorise" value="${panier.FAVORIS.included.contents:}"/>
<property name="typeFicheExclu" value="${panier.FAVORIS.excluded.contents:}"/>
</bean>

Tout préparer d'action de bandeau doit étendre AbstractActionViewPreparer.

@startuml skinparam class { BackgroundColor White BorderColor #2C3E50 FontColor #2C3E50 FontSize 14 FontName Arial AttributeFontColor #34495E AttributeFontSize 12 AttributeFontName Arial StereotypeFontColor #8E44AD StereotypeFontSize 12 StereotypeFontName Arial } abstract class AbstractActionViewPreparer{ -actionActive -actionPosition +accept(frontContext : FrontContext) : Boolean +setActionActive() : Void +setActionPosition() Void }

class ActionsBandeauViewModel{ }

abstract class AbstractActionsBandeauViewPreparer { -typeAction //On acepte queles actions actionFiche ou actioncard ou ... +accept(frontContext : FrontContext) : Boolean +prepare(frontContext : FrontContext, Map<String, preparers : List>) : V //Prepare toutes les actions du type typeAction +setTypeAction() : Void } AbstractActionsBandeauViewPreparer --* ActionsBandeauViewModel abstract class AbstractActionViewModel{ -icon : String -label : String -order : Integer }

class ActionsBandeauFicheViewPreparer extends AbstractActionsBandeauViewPreparer{ } class AccueilActionsBandeauFicheViewPreparer extends ActionsBandeauFicheViewPreparer{ +accept(frontContext : FrontContext) : Boolean +prepare(frontContext : FrontContext, Map<String, preparers : List>) : V }

note left of ActionsBandeauFicheViewPreparer action-fiche

 main.jsp va attendre un bandeau-action-fiche

end note

class ActionsBandeauCardViewPreparer extends AbstractActionsBandeauViewPreparer{ }

note right of ActionsBandeauCardViewPreparer action-card

FavorisPanierCardViewBuilder va avoir une liste de ActionsBandeauCardViewPreparer.
Il va rechercher un preparer de type bandeau-action-card. et en preparer un pour chaque card.

end note

ActionsBandeauCardViewPreparer --* ActionsBandeauViewModel

abstract class AbstractFavorisActionViewPreparer { +prepare(frontContext : FrontContext, Map<String, preparers : List>) : V }

AbstractFavorisActionViewPreparer -right-|> AbstractActionViewPreparer class FavorisActionViewModel extends AbstractActionViewModel{ }

class FavorisActionFicheViewPreparer extends AbstractFavorisActionViewPreparer{

} note bottom of FavorisActionFicheViewPreparer Dans le core-front-sitetemplates, le preparer : end note

FavorisActionFicheViewPreparer --* FavorisActionViewModel

class FavorisActionCardViewPreparer extends AbstractFavorisActionViewPreparer{ } note bottom of FavorisActionCardViewPreparer Dans le core-front-sitetemplates, le preparer : end note

FavorisActionCardViewPreparer --* FavorisActionViewModel

ActionsBandeauFicheViewPreparer --* ActionsBandeauViewModel AbstractFavorisActionViewPreparer --* FavorisActionViewModel

ActionsBandeauViewModel --*"1..n" AbstractActionViewModel : actions @enduml

Modifier le fichier

Gestion des cookies par K-Sup

La version 7.0 de K-Sup introduit une nouvelle fonctionnalité sur les cookies.

Les cookies, notamment le cookie de session, sont générés par le serveur d'application (tomcat) afin de faire un lien entre un utilisateur et le serveur applicatif.

Les cookies sont ajoutés dans les entêtes de réponse par le serveur d'application via un composant nommé CookieProcessor".
Par défaut, le CookieProcessor de tomcat retourne un cookie sans domaine ni attribut samesite. Le navigateur remplit alors les blancs avec des valeurs par défaut.

  • Le cookie est associé au site courant (et seulement au site courant, sans les sous-domaine)
  • Le coolie est limité au site courant et ses sous-domaine (aka "LAX")

Afin de simplifier la connexion des utilisateurs et de permettre un SSO compatible avec les règles CORS (Cross-Origin Resource Sharing), un CookieProcessor modifié est injecté par le K-Sup au démarrage de la webapp.
Une notion de filtre a été ajoutée afin de n'appliquer ce CookieProcessor spécialisé que sur certains cookies. Par défaut, seul le cookie nommé KSUP-SSO-UUID est pris en charge. Il est possible d'ajouter d'autres cookies en valorisant la propriété cookie.processor.filter avec la liste des cookies concernés sous forme d'une chaine séralisée avec "," comme séparateur (NAME1,NAME2,...)

cookie.processor.filter=KSUP-SSO-UUID

Le nouveau CookieProcessor apporte les fonctionnalités suivantes :

  • L'attribut SameSite est forcé à None sur tous les cookies pris en charge
    L'attribut "SameSite=None" autorise le navigateur à envoyer le cookie vers le domaine du cookie, y compris si l'appel est réalisé depuis un site d'un autre domaine. Depuis le site www.acme.com, si un script réalise un appel XmlHttpRequest vers le site www.acme.fr, le cookie sera envoyé avec la requête. Le SSO K-Sup est basé sur ce fonctionnement.

  • Le drapeau "Secure" est forcé sur tous les cookies pris en charge
    Le drapeau "Secure" doit obligatoirement être positionné dans l'entête de réponse Set-Cookie si l'attribut "SameSite" est valorisé à "None".
    Pour être accepté par le navigateur, le cookie devra donc transiter via un canal sécurisé (HTTPS) ou être interrogé sur le site localhost.

  • Le drapeau "HttpOnly" est forcé sur tous les cookies pris en charge
    Cette fonctionnalité n'est pas obligatoire, mais elle améliore la sécurité en empêchant la lecture en javascript des cookies.

A noter, une particularité des navigateurs permet d'ignorer le drapeau "Secure" des cookie si le domaine courant est "localhost" ou "xxxxx.localhost". Il est donc possible, sous certaines conditions sur les domaines de conserver le fonctionnement standard du CookieProcessor (true + None) dans un environnement local

Modifier le fichier

Les filtres HTTP

Les filtres HTTP permettent d'effectuer des traitements avant (pre-filter) ou après (post-filter) le traitement de la requête HTTP par le code applicatif (processus / controleur, etc... )

Ordre des filtres HTTP

L'ordre des filtres HTTP est géré via l'ajout de l'annotation @Order. (cf la classe FilterDeclarationManager) Lors de l'ajout des filtres, il convient de bien vérifier la fonction de chaque filtre. Par exemple, il ne faut pas insérer votre filtre avant le ContexteFilter si vous utilisez des données du contexte.

Liste des filtres standard apportés par le produit

Le produit apporte en standard un certain nombre de filtres.

  • Filtres sur le répartiteur (dispatcher) REQUEST
ClasseDescriptionOrdre
CharEncodingFilterce filtre a pour fonction de pallier à l'absence d'encodage dans le requete ou la réponse.HIGHEST_PRECEDENCE
MDCFilterce filtre positionne l'identifiant de session dans le contexte MDC+ 10000
HoneyPotFilterce filtre permet de retourner une réponse 200 si un bot est détecté lors de la soumission d'un formulaire+ 20000
HttpsRedirectFilterce filtre gére la demande de bascule HTTP > HTTPS au navigateur si le site courant est définit en HTTPS.+ 30000
SiteAliasResolutionFilterce filtre gére la demande de redirection vers l'URL principal du site si un alias de site est détecté.+ 30000
HttpMarkerFilterce filtre initilise le recueil des informations statistiques de la requête+ 40000
ContexteFilterce filtre positionne le contexte univ à partir des informations de la session de l'utilisateur+ 50000
PDFFilterce filtre gére la génération en PDF d'une page du site.+ 60000
  • Filtres sur le répartiteur (dispatcher) FORWARD
ClasseDescriptionOrdre
ForwardContexteFilterce filtre transmet le contexte en cas de forwardHIGHEST_PRECEDENCE
  • Filtres sur le répartiteur (dispatcher) ERROR
ClasseDescriptionOrdre
HttpsRedirectFilterce filtre gére la demande de bascule HTTP > HTTPS au navigateur si le site courant est définit en HTTPS.+ 30000
SiteAliasResolutionFilterce filtre gére la demande de redirection vers l'URL principal du site si un alias de site est détecté.+ 30000
ContexteFilterce filtre positionne le contexte univ à partir des informations de la session de l'utilisateur+ 50000
  • Filtres spécifiques à la servlet JSP, dispatcher REQUEST + FORWARD + ERROR
ClasseDescriptionOrdre
FrontOfficeFilterce filtre est utilisé lors de la préparation de l'affichage frontHIGHEST_PRECEDENCE + 60000

Ajout d'un filtre HTTP

  1. Créer une classe qui implémente ExtensionFilter
...
public class AcmeFilter implements ExtensionFilter {
    ...
}
  1. Ajouter les annotations Spring
@Component
@CoreContext
@Order(Ordered.HIGHEST_PRECEDENCE + 35000)
@WebFilter(urlPatterns = FILTER_PATTERN_DEFAULT, filterName = "acme-filter")
public class AcmeFilter implements ExtensionFilter {
    ...
}

Modifier le fichier

Envoi d'e-mail

Présentation

L'applicatif K-Sup offre la possibilité d'envoyer des e-mails aux utilisateurs.
Il peut s'agir d'e-mails techniques liés au cycle de vie des fiches ou d'e-mails fonctionnels liès à de la communication (lettres d'information) ou à la gestion du compte utilisateur (activation de compte, réinitialisation de mot de passe, etc...).

Pour réaliser cette opération, le système s'appuie sur un serveur de messagerie externe.

Configuration

PropriétéDescriptionValeurs possiblesValeur par défaut
mail.debugPermet de passer l'envoi de mail en mode debug. Au lieu d'être envoyé, l'e-mail est loggétrue / falsefalse
mail.hostDéfinit le nom d'hote ou ip du serveur de messagerieune chaine de caractèresmtp.xxxx.fr
mail.portDéfinit le port sur le serveur de messagerie (habituellement, 25 ou 587 ou 465)un entier25
mail.userDéfinit le le nom d'utilisateur si le serveur de mail nécessite une authentificationune chaine de caractère
mail.passwordDéfinit le le mot de passe utilisateur si le serveur de mail nécessite une authentificationune chaine de caractère
mail.fromDéfinit l'adresse mail de l'expediteur des mail système. Utiliser de préférence une adresse en no-reply@xxx.frune chaine de caractèrekportal@xxxx.fr
mail.webmasterDéfinit l'adresse mail de contact. Utilisé adresse d'émetteur pour certains e-mails de gestionune chaine de caractèrewebmaster@xxxx.fr
mail.enabletlsPermet d'activer le mode TLS. Le TLS est une évolution plus sécurisé du SSLtrue / falsefalse
mail.enablesslPermet d'activer le mode SSL. Si activé, est prioritaire au TLStrue / falsefalse
mail.senderDéfinit l'emétteur du mail (à défaut, utilise la valeur de mail.fromune chaine de caractère
mail.sleepPermet de préciser un délai entre chaque envoi de mail en msun entier100

Modifier le fichier

Extension

Packaging

Une extension peut-être packagée en différents modules :

  • Un module contenant les mécanismes java (exemple : actualite-core);
  • Un module contenant les écrans de contribution back-office (exemple : actualite-webapp-contrib);
  • Un module contenant les écrans utilisés en front-office (exemple : actualite-webapp-front);
  • Un module pour l'API si l'extension expose des données via l'API K-Sup (exemple : actualite-api).

Chargement et déclaration

L'extension est chargée dans l'application via les mécanismes liés à Spring en créant un contexte dédié. Afin d'isoler les différents projets et extensions, le chargement des contextes doit toujours respecter une relation parent / enfant avec l'extension "Core" qui est l'extension de plus bas niveau. Toutes les extensions ont accès aux fonctionnalités et objets de ce contexte Core. En revanche, le Core ne doit pas référencer de fonctionnalités des extensions et les extensions ne doivent pas se référencer entres elles.

Chaque déclaration d'extension est matérialisée comme étant un bean de type {@link com.kportal.extension.IExtension}, qui est porté par ce contexte fils qui est initialisé dans la classe {@link com.kportal.extension.ExtensionConfigurer}. Ce traitement est responsable de la découverte des différents contextes Spring d'extensions (par convention : /extensions/idextension/ExtensionContext.xml). Pour le module Core, la déclaration est présente dans le fichier CoreContext.xml.

Ajoute de contrôleurs Spring MVC

Les extensions peuvent déclarer leurs propres contrôleurs afin d'exposer des fonctionnalités. Ces contrôleurs sont alors chargés dans le contexte propre de l'extension afin de pouvoir utiliser les fonctionnalités de l'extension (service java par exemple). Afin d'identifier les différents modules dans les URLs, les contrôleurs de chaque extension seront préfixées par un identifiant propre à l'extension (par exemple : www.univ-kosmos.fr/mon-extension/url-controller).

Activation et configuration

L'activation et la configuration des contrôleurs se fait via l'édition d'objet héritant de IExtensionServlet. Il est alors possible d'éditer les propriétés suivantes :

PropriétéDescriptionValeurs possiblesValeur par défaut
servletDispatcherEnabledActivation du Dispatcher ServletBooléen (true, false)false
dispatcherServletBaseUrlPréfixe des URLs de contrôleurs de l'extensionChaîne de caractèresidenfiant de l'extension
servletPriorityPermet de définir un ordre dans le chargement de la servlet de cette extension par rapport aux autres extensionsintegercom.kportal.extension.IExtensionServlet#EXTENSION_SERVLET_PRIORITY
servletNameNom de la servlet utilisée dans la déclaration Spring (pas utile à modifier)Chaîne de caractèresidExtensionDispatcherServlet

Pour chaque extension, il faut également qu'il y ait la configuration d'un objet implémentant org.springframework.web.servlet.HandlerMapping associé. Cela peut être configuré de plusieurs manières :

  • Ajouter l'annotation @EnableWebMvc sur une classe ayant l'annotation @Configuration scannée dans le contexte de l'extension;
  • Une @Configuration scannée dans l'extension peut étendre la classe org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.

Chaque extension peut également étendre son comportement MVC en important dans le contexte des Configurations étendant l'interface org.springframework.web.servlet.config.annotation.WebMvcConfigurer. Il est ainsi possible de configurer Tiles avec l'import de com.kosmos.context.CommonTilesConfig.

@Configuration
@Import(CommonTilesConfig.class)
@EnableWebMvc
public class MonExtensionConfig {
    //
}

Modifier le fichier

Gestion des logs

Logback

Ajout de logger dans l'appender

Modification du niveau de logs

Sentry

Prérequis

Afin d'utiliser sentry, les projets doivent ajouter les dépendances suivantes dans leur pom.xml Les versions sont gérées en dependencyManagement dans le pom parent des projets.

<!-- Fournie le support Logback pour Sentry en utilisant un appender pour envoyer les exceptions dans Sentry -->
<dependency>
    <groupId>io.sentry</groupId>
    <artifactId>sentry-logback</artifactId>
</dependency>

<!-- Nécessaire pour utiliser les conditions dans les appender logback -->
<dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
</dependency>

Paramétrage

Deux paramètres doivent être définis pour utiliser sentry :

  • sentry.dsn : dsn vers lequel les évènements sont envoyés
  • sentry.environment : environement du projet

La présence du sentry.dsn est vérifiée pour activer l'appender.

Les paramètres sentry peuvent être définis

  • via le fichier env.properties présent dans le dossier storage/conf :
sentry.dsn=https://mondsn
sentry.environment=dev-ksup-master
sentry.release=7.0
  • via les JAVA_OPTS :
-Dsentry.dsn=https://mondsn -Dsentry.environment=dev-ksup-master -Dsentry.release=7.0

Liste des paramètres disponibles :

PropriétéValeurDescription
sentry.dsnData Source Name permettant d'envoyer les logs vers le sentry souhaité
sentry.environmentex: dev-ksup-masterNom de l'environnement dans sentry
sentry.releaseex: 7.00.00Version de la release courrante
sentry.shutdownTimeout2000 par défautDurée en ms pour le timeout
sentry.flushTimeoutMillis15000 par défautDurée en ms pour le timeout
sentry.connectionTimeoutMillis5000 par défautDurée en ms pour le timeout
sentry.readTimeoutMillis5000 par défautDurée en ms pour le timeout
sentry.debugtrue ou false (false par défaut)Active le mode debug sur sentry
sentry.maxBreadcrumbs100 par défautNombre d'éléments présents dans le breadcrumb
sentry.sampleRatede 0.0 à 1.0 (1.0 par défaut)Configure le taux d'échantillonnage des événements envoyés La valeur par défaut est 1,0, ce qui signifie que 100 % des événements sont envoyés. S'il est réglé sur 0,1, seuls 10 % des événements d'erreur seront envoyés. Les événements sont choisis au hasard.
sentry.attachThreadstrue ou false (false par défaut)Défini si les threads doivent être attachés aux évènements envoyés à sentry
sentry.attachStacktracetrue ou false (false par défaut)Défini si la stack trace doit être attachée aux évènements envoyés à sentry
sentry.minimumEventLevelERROR par défautNiveau de log minimum à envoyer à sentry
sentry.minimumBreadcrumbLevelERROR par défautNiveau de log minimum à afficher dans le breadcrumb

Appender Sentry

<!-- Appender pour envoyer les logs sur Sentry -->
<if condition='isDefined("sentry.dsn")'>
    <then>
        <appender name="Sentry" class="io.sentry.logback.SentryAppender">
            <minimumEventLevel>${sentry.minimumEventLevel:-ERROR}</minimumEventLevel>
            <minimumBreadcrumbLevel>${sentry.minimumBreadcrumbLevel:-ERROR}</minimumBreadcrumbLevel>
            <options>
                <dsn>${sentry.dsn}</dsn>
                <environment>${sentry.environment:-production}</environment>
                <shutdownTimeout>${sentry.shutdownTimeout:-2000}</shutdownTimeout>
                <flushTimeoutMillis>${sentry.flushTimeoutMillis:-15000}</flushTimeoutMillis>
                <debug>${sentry.debug:-false}</debug>
                <maxBreadcrumbs>${sentry.maxBreadcrumbs:-100}</maxBreadcrumbs>
                <sampleRate>${sentry.sampleRate:-1.0}</sampleRate>
                <attachThreads>${sentry.attachThreads:-false}</attachThreads>
                <attachStacktrace>${sentry.attachStacktrace:-false}</attachStacktrace>
                <serverName>${jvmRoute:-default}</serverName>
                <connectionTimeoutMillis>${sentry.connectionTimeoutMillis:-5000}</connectionTimeoutMillis>
                <readTimeoutMillis>${sentry.readTimeoutMillis:-5000}</readTimeoutMillis>
                <release>${sentry.release:-7.0}</release>
            </options>
        </appender>
    </then>
</if>


<root level="INFO">
    <appender-ref ref="WEBAPP-SIFING-APPENDER"/>
    <if condition='isDefined("sentry.dsn")'>
        <then>
            <appender-ref ref="Sentry" />
        </then>
    </if>
</root>

Modifier le fichier

Gestion des URLs de K-Sup

La fonctionnalité de gestion des URLs permet de disposer pour chaque contenus d'URL propre et spécifique. Cette fonctionnalité permet également d'administrer des URLs courtes.

Fonctionnement

Vue globale

Vue gloable du fonctionnement des URLs

Dans ce schéma on peut voir les différents éléments qui composent la gestion des URLs.

Points d'entrées

Différents acteurs intéragissent avec les URLS (utilisateurs ou acteurs techniques) :

  • Enregistrement d'un contenu : quand un utilisateur sauvegarde une rubrique, une fiche ou un site, les URLs qui sont liées à ce contenu sont impactées
  • Affichage d'un lien : quand on affiche une page qui fait un lien vers un contenu, il faut savoir quelle URL de ce contenu va être affichée
  • Appel d'un lien : à chaque clic sur un lien, on cherche à retrouver le contenu qui lui est associé afin de l'afficher à l'utilisateur
  • Flyway : de manière automatique, au premier démarrage de l'application les URLs sont calculées, mais il est également possible de créer des scripts qui vont demander un recalcul
  • ScanSite : chaque nuit l'application réalise un traitement sur les URLs (Suppression des urls obsolètes).

Messages spring

À chaque enregistrement d'un contenu, un message spring est lancé sur le canal de communication global de l'application. Des écouteurs ont été mis en place afin d'identifier ceux qui intéressent le calcul d'URL.
Ils sont filtrés par des sélecteurs afin d'éviter les recalculs non nécessaires.

Traitements

Les différents éléments seront détaillés plus bas, voici comment on peut les résumer :
SlugTransfomer : S'occupe de transformer le slug saisie par l'utilisateur pour le faire "entrer dans le moule K-sup"
MakeUrlUnique : Se charge de vérifier l'unicité d'une URL et le cas échéant d'ajouter un incrément
UrlStatusProvider : Élément calculant le status d'une URL
ReservedUrlManager : Gestionnaire des URLs techniques ou non réservées par l'application

Modèle Objets

On distingue deux tables dans la base de données pour l'enregistrement des données liées aux URLs.

En complément de ces deux tables, il faut noter la présence d'un champs SLUG dans les différentes tables liées aux différents contenus de l'application (RUBRIQUE, METATAG, ARTICLE ...).

Table URL

Table principale de la gestion des URLs.

ChampDescription
ID_URLIdentifiant de l'URL
FIRST_VISIBLE_SECTIONPremière rubrique visible de l'URL
CODE_SITECode du site
URLURL de l'URL
HASHIdentifiant de l'URL fabriqué en hachant celui-ci
TYPEType de l'URL
STATUSStatut de l'URL
ID_METATAGIdentifiant de la fiche liée à cette URL, peut être null
CREATION_DATEDate de création
UPDATE_DATEDate de mise à jour
ID_TARGET_URLDans le cas d'une URL de redirection, identifiant de l'URL vers lequel cette URL redirige
ID_CANONICAL_URLIdentifiant de l'URL canonique du contenu
SECTIONRubrique du contenu

Table URL_REDIRECT

Table permettant de tracer les appels des URLs de redirection.

ChampDescription
ID_URL_REDIRECTIdentifiant de l'URL de redirection
ID_URLIdentifiant de l'URL qui lui est lié
LAST_USEDDernière date à laquelle l'URL a été utilisée
HITSNombre de fois où l'URL a été appelée

Services et accès à la base de données

Diagramme de classe du service, des beans et des dao

Pour accéder aux données de la base, une couche classique de service et de DAO a été mise en place pour les deux tables citées ci-dessus.
Une particularité a cependant été mise en place concernant la création d'instance d'UrlBean. Il n'est pas possible de créer de telles instances. Pour créer un UrlBean, il faut passer par le UrlBeanBuilder qui réalise des vérifications au moment de la création de l'instance. Cette dernière classe renseigne également de manière automatique certaines données lorsqu'elles sont nécessaires (date de modification, date de création, hash, id, première visible)

ContentController

La classe ContentController est le point d'entrée principal de l'application. Chaque fois qu'une URL de contenu est appelée, c'est ce controller qui répond. Il se charge de récupérer depuis la base de données l'URL appelée et de renvoyer, en fonction du contenu qui lui est associée, la page correspondante.
C'est également ce controller qui se charge de réaliser la redirection dans le cas de l'appel d'une URL au statut redirect.
Enfin, si aucune URL n'est trouvée, ou si son statut ne permet pas un affichage, il se charge de renvoyer une erreur 404.

UrlProvider

L'URLProvider est l'élément qui se charge de fournir une URL quand on cherche à afficher un lien vers un contenu (par exemple dans une liste de fiches, ou un lien dans un menu etc).
Afin de répondre aux différentes règles métier de l'affichage d'une URL, une chaîne de responsabilité a été mise en place. Diagramme de classe du fournisseur d'URL Chaque maillon de la chaîne de responsabilité est un enfant de la classe abstraite UrlProviderProcessor. Chacun se charge de vérifier dans un premier temps s'il est en mesure d'appliquer sa règle métier, si ce n'est pas le cas, il passe la main au maillon suivant. S'il peut y répondre, il applique sa règle et renvoie l'URL.

C'est la classe UrlProviderChainController qui se charge du rôle de chef d'orchestre. Il crée la chaîne de responsabilité dans un ordre précis et identifie les points d'entrées possibles (avec ou sans rubrique forcée) afin de proposer les différents modes de calcul.

Enfin, UrlProvider est le point d'entrée à ce module. Il est d'ailleurs la seule classe visible du package contenant l'ensemble des classes présentes ci-dessus.

Module d'écoute des actions

Grâce au tissage d'aspect, un grand nombre d'actions effectuées sur l'application sont interceptées et transmettent des messages sur un canal de communication globale à l'application. Il est possible de voir l'ensemble des écouteurs présents dans le fichier publish-interceptor.xml.

Des écouteurs sont présents dans l'application, ceux-ci écoutent les messages qui passent sur ce canal principal, et quand un message les intéresse, il lance une action. C'est le cas pour les URLs à travers le fichier url-subscribe.xml.

Cependant, afin de s'assurer que les actions nécessitent un recalcul d'URL, des filtres sont appliqués, sur la base des informations présentes dans le message transmis, les messages qui intéressent le calcul d'URL.
Par exemple la modification du contenu d'une toolbox n'intéresse pas le calcul d'URL, en revanche la modification de son slug implique directement ses URLs.

Ces objets sont les message selector. Il en existe un pour chaque contenu pouvant impacter les URLs :

  • MetatagUrlSelector
  • SectionUrlSelector
  • SiteUrlSelector

Ceux-ci se chargent donc de vérifier si la DSI ou le slug ou les niveaux montant et descendant des sites etc ont été modifiés.

Transformation de slug

De manière historique, tous les caractères ne sont pas autorisés dans les URLs. Certains vont être transformés, d'autres supprimés.
Ci-dessous la table d'association des caractères :

Gamme/caractèreTransformation
a – zaucune
0 – 9aucune
-aucune
A – Za - z
À, Á, Â, à, á, âa
È, É, Ê, Ë, è, é, ê, ëe
Ì, Í, Î, Ï, ì, í, î, ïi
Ò, Ó, Ô, ò, ó, ôo
Ù, Ú, Û, Ü, ù, ú, û, üu
Ç çc
Ñ, ñn
Æ, æae
Œ, œoe
!, ?, ,, ;, ., :, _, , /, , ~-
{, }, ’, ,, [, ], &, ^, ¨, *, €, $, £, #, ', ", `, @, =, +, <, >, |, %, (, )<vide>
Le reste des caractèresencodage en pourcent

Pour réaliser ceci, des objets de transformation de caractère ont été créés.

Diagramme de classe de la transformation de slug

L'interface CharacterTransformation défini comment un objet de transformation doit être écrit. On a donc deux objets de transformation possibles, ceux qui prennent en entrée une fourchette de caractères, et ceux qui prennent un ou plusieurs caractères.
La classe CharacterTransformer instancie les différent éléments de transformations. Enfin SlugTransformer, qui est le point d'entrée de cette fonctionnalité, transforme l'ensemble des caractères d'une chaîne qu'on lui transmet.

Gestion de l'unicité des URLs

Un composant en particulier s'occupe de l'unicité des URLs, il s'agit de MakeUrlUnique. Il prend en paramètre une URL, et se charge de regarder s'il existe déjà une même URL. Si c'est le cas, un incrément est ajouté à la fin de l'URL jusqu'à ce qu'on se retrouve avec une URL qui n'existe pas.
Un incrément max (9999) est déclaré dans la classe afin d'éviter d'itérer sur l'URL à l'infini.

Gestion des URLs réservées de l'application

Au sein de l'application il existe trois types d'URLs réservées :

  • Les URLs réservées par paramétrage, il est possible d'en ajouter via un ListToAddBean sur la liste parametrizedReservedUrlList du bean reservedUrlManager
  • Les URLs techniques réservées, il s'agit de toutes les URLs des différents controllers de l'application ainsi que les URLs de base des différentes extensions
  • Les URLs de site réservées. Ces URLs sont déclarées directement sur la configuration des sites et permettent de bloquer certaines URLs à toute utilisation par une fiche.

Fournisseur de première rubrique visible

La classe FirstVisibleCalculator est appelée directement par l'UrlBeanBuilder dans le but de fournir une première rubrique visible. Elle se charge de remonter l'arbre de la rubrique de l'URL afin de déterminer quelle est la première rubrique parente visible.

Fournisseur de statut d'URL

Cette fonctionnalité permet de fournir un statut pour une URL.

Diagramme de classe du fournisseur de statut

Deux classes sont visibles dans ce package, UrlStatus qui est l'énumération proposant les différents statuts (voir Définition des statuts), et UrlStatusProvider qui est le point d'entrée du calcul de statut.
Dans le schéma ci-dessus on peut voir les interconnexions entre les différents éléments. Quand une des classe n'est pas en mesure de répondre elle va appeler la classe en-dessous elle pour lui demander de fournir un statut à l'URL.

Calcul des URLs

Le calcul des URLs est un traitement coûteux en terme de ressource. L'idée est que ce traitement soit fait au moment de l'enregistrement des contenus afin qu'au moment de sa consultation, aucun traitement de recalcul d'URL ne soit effectué.

Points d'entrées et relais

Le point d'entrée principal du calcul d'URL est la classe CalculateUrlTaskListener. C'est cette classe qui est appelée quand un message (voir module d'écoute des actions) est intercepté et accepté par le module. Elle se charge alors de relayer l'appel vers l'objet de traitement qui correspond.
FullCalculateUrlTask est également un point d'entrée, celui-ci peut être appelé par flyway, mais il est surtout appelé par le job de recalcul d'URL disponible dans le backoffice.

Schéma des relais et opérants

Ce schéma permet de tracer comment les appels sont relayés jusqu'aux principaux services opérants CalculateSectionUrl, CalculateContentUrl et ServiceUrl.

Généralités à propos du calcul

Que ce soit pour le calcul d'URL de rubrique ou de fiche, la même manière de procéder est utilisée. On calcul dans un premier temps la liste des URLs impactées par la modification à l'origine du calcul sans s'intéresser aux URLs déjà présentes en base, puis cette liste est synchronisée (c'est à dire mise en conformité avec les données existantes puis sauvegardée) avec les données de l'application.
Durant ce traitement les URLs à synchroniser sont groupées par fiche afin de conserver une cohérence forte au moment du calcul.

CalculateContentUrl

Cette classe comporte deux traitements, un de suppression (removeUrlForContent) appelé au moment de la suppression effective de la fiche, un de calcul (calculContentUrl) appelé à la modification d'une fiche. Il prend en paramètre le metatag avant et après la modification à l'origine de l'exécution de la méthode.

Ci-dessous le détail du traitement de calcul d'URL :
Détail traitement CalculateContentUrl

Dans la première partie de ce traitement on calcul les urls impactées par la modification en question. Pour ce faire les différentes rubriques de la fiche sont récupérées puis des URLs sont générées pour celles-ci. Des cas particuliers liés aux différentes rubriques sont gérés au passage de ce traitement.
Le traitement de synchronisation est ensuite appelé, voir la section détaillant son fonctionnement.

CalculateSectionUrl

Détail traitement CalculateSectionUrl

Ce traitement fonctionne sensiblement de la même manière que celui vu précédemment. Sa particularité réside dans le fait que sa complexité ne se situe pas au niveau du calcul des URLs du contenu car une rubrique n'a qu'une seule URL visible. Sa complexité se situe dans l'impact qu'il a sur l'ensemble des autres URLs.
En effet, dans la mesure ou le slug de la rubrique modifiée peut apparaître dans un grand nombre d'URL de différents contenus, il faut avoir en tête de notifier tous les contenus impactés par les changements sur la rubrique.
C'est ce qu'on peut voir dans la seconde partie du traitement, la méthode doSectionNiveauSuivant va remonter l'arbre des rubrique pour effectuer le traitement de recalcul sur les rubriques parentes et les contenus qui leur sont liés, et ce de manière récursive.

ServiceUrl

Le ServiceUrl permet de réaliser un trop grand nombre d'actions :

  • Échanges avec la base de données autour de la table URL
  • Calcul d'impact (affichage de popin lors de modification de contenus en backoffice)
  • Synchronisation des URLs
  • etc

Par souci de simplicité (sic), nous détaillerons ici que la partie synchronisation. Le point d'entrée de cette fonctionnalité est la méthode synchronizeUrlList qui s'attend à recevoir les URLs à synchroniser groupées par metatag (il peut y avoir un groupe d'URL sans metatag pour les URLs de rubrique).
L'utilité d'avoir les URLs regroupées est que pour chaque groupe on va pouvoir déterminer en une seule fois quelle est l'URL canonique du groupe d'URL. La synchronisation peut provoquer une modification d'URL canonique pour la fiche liée, dans ce cas, un recalcul est demandé par notification.
Pour chaque URL, quelle soit dans un groupe de metatag ou non, la méthode synchronizeUrl est appelée.

synchronizeUrl

Dans un premier temps, la méthode tente de repérer s'il existe une URL dans la base de données qui pourrait être ré-utilisée pour la nouvelle URL. Cela permet d'éviter d'ajouter un incrément sur des URLs quand cela n'est pas nécessaire et de traiter au passage les URLs qui n'auront plus lieu d'être après le traitement. Si l'URL réutilisée était de type canonique, alors elle est destituée (c'est à dire passée au statut "en ligne") de son statut avant d'être ré-utilisée.
C'est également à ce moment que l'URL est rendue unique en appelant MakeUrlUnique.
Ensuite l'URL est sauvegardée. Si elle est possède un statut canonique, on informe toute ses soeurs (URL pointant sur la même fiche) qu'il y a une nouvelle canonique à référencer. S'il existe des URLs au statut "redirect" pointant sur la même rubrique et, dans le cas d'une URL de type fiche, la même fiche, alors on les informe toutes qu'elles doivent rediriger vers l'URL qui vient d'être créée.

Gestion des anciennes URLs

Afin de gérer les montées de version des applications présentent en prod avant la 6.7, un job de calcul des anciennes URLs a été mis en place, FullCalculateOldUrlTask. Celui-ci se charge de créer des URLs de type redirection vers les nouvelles URLs.
Dans le cas où certaines URLs seraient manquantes, le filtre OldUrlFilter écoute toutes les URLs correspondant au format d'avant la 6.7 et tente de rediriger vers la fiche censée lui correspondre.
Il est important de noter que pour fonctionner, ce job nécessite d'avoir l'ancien format de paramétrage des URLs dans sa configuration :

# Affichage des rubriques dans les urls
site.code_site.reecriture_rubrique_mode = 2
# Niveau à partir duquel la rubrique est précisée dans l'url
site.code_site.reecriture_rubrique_min = 100
# Niveau maximum pris en compte dans l'url Ex : /rub_min/rub_xxx/rub-yyy/rub_max/ma-page.htm
site.code_site.reecriture_rubrique_max = 100

Paramétrage

Propriétés

ParamètreValeurCommentaire
url.purge.maxdaysPar défaut : 365Permet de définir le nombre de jours de rétention des urls de redirection non utilisées

Configuration dans l'application

ParamètreValeurCommentaire
Niveau montant maxEntierÀ définir dans la configuration de site, permet de paramétrer le nombre de rubrique devant apparaître au maximum dans l'url depuis la racine
Niveau descendant maxEntierÀ définir dans la configuration de site, permet de paramétrer le nombre de rubrique devant apparaître au maximum depuis le slug

Modifier le fichier

Propriété dsi.activation

Présentation

La propriété dsi.activation permet d'activer ou désactiver globalement les fonctionnalités DSI dans K-Sup :

  • affichage des écrans/onglets liés à la DSI
  • prise en compte des règles de restriction de contenus et de rubriques
  • synchronisation de groupes DSI, etc.

Configuration

PropriétéValeurs possiblesValeur par défaut
dsi.activation0 ou 11 (dsi activée)

Utilisation

Diffusion/restriction de contenu

Back Office – Saisie/Consultation de fiches

Initialise et affiche l’onglet/champs de diffusion (PUBLIC_VISE_DSI, PUBLIC_VISE_DSI_RESTRICTION, DIFFUSION_MODE_RESTRICTION), et applique les contrôles de ces champs lors de l’enregistrement.

Références :

  • ControleurUniv.preparerPrincipal()
  • ControleurUniv.traiterEnregistrementDiffusion(...)

Back Office – Saisie des rôles/permissions

Ajoute l’onglet Diffusion et les permissions techniques dsi dans l’édition de rôles quand la propriété est active.

Références :

  • SaisieRole.preparerPRINCIPAL()

Back Office – Saisie des rubriques

Ajoute l’onglet Restriction(s) d'accès dans l’édition des rubriques quand la propriété est active.

Références :

  • rubrique_saisie.jsp

Back Office – Saisie des groupes DSI

Active la saisie du champ Type dans l'écran d'édition de groupe DSI.

Références :

  • groupedsi_saisie.jsp

Back Office - Saisie des services externes

Active la saisie des Restrictions dans l'écran d'édition de service externe.

Références :[

  • preferences_service.jsp

Désactivation du contrôle d’accès aux contenus en front office

La désactivation de la propriété dsi.activation permet de court-circuiter la vérification DSI lors de la lecture d'un contenu.

Références :

  • ServiceContent.controlerRestriction()
  • FicheUnivMgr.controlerRestriction()

Désactivation des contraintes des DSI dans la recherche SQL

La propriété dsi.activation alimente la constante ACTIVATION_DSI dans la classe ConstanteSQL.

La désactivation de celle-ci désactive la vérification des contraintes DSI dans les requêtes SQL effectuées par :

  • RequeteurFiches.select()
  • RequeteSQLHelper.getRequeteGenerique()

Autres usages

Front office – FrontOfficeBeanUtil

La propriété dsi.activation alimente l'attribut activationDSI du bean FrontOfficeBean.

Cet attribut est utilisé pour :

  • Affichage du menu intranet sur le template legacy
  • Alimenter l'attribut dsi du bean FrontOfficeBean, l'attribut est à true si la DSI est active et que l'utilisateur est connecté.

L'attribut dsi est utilisé pour :

  • Ajouter une classe connecte dans la liste des classeBody du template legacy
  • Ajouter une classe edition_fiche sur le formulaire de saisie front

Synchronisation automatique des groupes DSI

La désactivationde la propriété dsi.activation désactive la création/synchro automatique des groupes DSI basés sur la structure, même si la synchro structure est activée par ailleurs.

Références :

  • SynchroGroupedsiStructure.synchroniserGroupeDsi()

Modifier le fichier

Gestion des médias

Modèle objet

Les médias sont gérés avec deux objets :

  • Media
    • Table MEDIA
    • Contient toutes les informations décrivant le média :
      • Titre, légende, description, ...
      • Type de ressource : audio, fichier, photo, svg, flash
      • Source : Nom du fichier source
      • Url : Nom du fichier stocké sur disque
  • Ressource
    • Table RESSOURCE
    • Enregistre le lien entre le média et le contenu qui l'utilise
    • La ressource porte les informations suivantes
      • L'idMedia : id du media
      • codeParent : Code calculé permettant de retrouver le contenu utilisant le média
      • etat : Etat de la ressource permettant la gestion de son cycle de vie
      • ordre :

Ajout d'un média

Ajout et utilisation d'un média dans la médiathèque

Lorsqu'un média est ajouté dans la médiathèque, celui-ci devient automatiquement public. Le média sera consultable sans vérification de permission. Le média pourra être utilisé sur toute l'application par les contributeurs. Lorsqu'un média est inséré dans un contenu (toolbox par exemple), un enregistrement sera créé dans la table ressource.

Ajout et utilisation d'un média hors médiathèque

Le média peut être directement ajouté dans un contenu (via la toolbox par exemple). Suivant la configuration de l'environnement (via la propriété mediatheque.secure.xxx) l'accès au média sera contrôlé en fonction des permissions de l'utilisateur (pas d'accès public). Dans tous les cas, le média ne pourra pas être utilisé sur plusieurs contenus.

  • Si la propriété mediatheque.secure.xxxx est égale à 1, alors il n'y a pas de vérification de permission (meilleure performance)
  • Si la propriété mediatheque.secure.xxxx est égale à 2, alors une vérification des permissions est effectuée à chaque appel en fonction des droits de l'utilisateur. Le média porte les restrictions de son contenu l'utilisant (restriction d'une fiche ou restriction d'une rubrique). Lors de l'ajout d'un média, un enregistrement dans la table ressource est effectué afin d'identifier où est utilisé le média.

Remarque sur les Tags medias [id-image] et [id-fichier]

Au cours de la vie du code, le format des médias a évolué. Certains contenus contiennent des anciens format de tag. Aucun traitement de reprise des données n'ayant été effectué, les traitements manipulant des contenus de toolboxes doivent impérativement prendre en compte les formats suivants:

  • [id-image]1234567890[id-image]
  • [id-image]F1234567890[id-image]
  • [id-fichier];1234567890[id-fichier]
  • [id-fichier]1234567890;[id-fichier]
  • [id-fichier]1234567890;INLINE[id-fichier]

La méthode publique ParserRequete.parseRessources tient compte de ces différents format. Elle peut être utilisée pour extraire la liste des médias d'un contenu de toolbox. En fonctionnement standard, cette méthode est appelée lors de la soumission d'une contribution. Cela permet d'jouter dans l'infoBean deux collections pré-initialisées avec les médias détectés dans les toolboxes : contentNewRessources et contentOldRessources.

/!\ Les autres (anciens) tags médias (legende_image, title-image, ..), ne sont pas gérés par cette méthode /!

Optimisation de l'espace de stockage

Le format image/webp

Le nouveau format image/webp permet de reduire considérablement la taille de certaines images sans perte notoire de qualité. Un mécanisme a été mis en place dans K-Sup pour enregistrer automatiquement les images au format webp.

Ce mécanisme est contrôlé grace à la propriété "mediatheque.photo.output.format" qui permet de spécifier un format unique d'enregistrement des medias (image/webp par défaut). Si cette valeur est surchargée à vide par un projet, cela désactive la fonctionnalité. cf mediatheque.photo.output.format, mediatheque.photo.excluded.format, mediatheque.photo.output.quality et mediatheque.photo.output.compression dans le fichier medias-properties.md)

Note sur l'orientation Exif

Certains fichiers peuvent contenir un Exif d'orientation indiquant le sens réel d'affichage d'une image.

Il existe 8 orientations possibles :

  1. 0 degrees: the correct orientation, no adjustment is required.
  2. 0 degrees, mirrored: image has been flipped back-to-front.
  3. 180 degrees: image is upside down.
  4. 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
  5. 90 degrees, mirrored: image has been flipped back-to-front and is on its side.
  6. 90 degrees: image is on its side.
  7. 270 degrees: image has been flipped back-to-front and is on its far side.
  8. 270 degrees, mirrored: image is on its far side.

orientation

Lorsque l'image est enregistrée sans optimisation webp, les informations exif sont conservées.
Cependant, toute réécriture de l'image (rotation, redimensionnement ou crop) entraine la perte des informations exif.
A cet effet, un traitement de redressement est appliqué à l'image avant réécriture en webp ou transformation de l'image.

Modifier le fichier

Paramétrage de la gestion des médias

Le paramétrage de l'extension est réalisé dans le fichier core.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_xxx.properties dans les sources d'un projet
  • env.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
mediatheque.secure.typeMediasIndique si le media est privé ou non (typeMedias à remplacer par les types de médias)0 : Média public et partagé dans la médiathèque
1 : Média public mais non partagé dans la médiathèque
2 : Média privé et non partagé dans la médiathèque (avec vérification de droit)
mediatheque.secure.fichier=2
mediatheque.secure.audio=2
mediatheque.secure.video=2
mediatheque.secure.photo=2
mediatheque.photo.output.formatIndique le type MIME de réécriture des fichiers Image de la médiathèqueimage/png, image/webpimage/webp
mediatheque.photo.excluded.formatIndique les types MIME d'images non concernées par la réécriture, séparés par de ','image/gif, ...image/gif
mediatheque.photo.output.qualityIndique le facteur de compression pour la réécriture des fichier Image de la médiathèqueun nombre en entre 0 et 10.75F
mediatheque.photo.output.compressionIndique le type de compression pour la réécriture des fichier Image de la médiathèqueDépend du format. Pour un webp : Lossy / LossLessLossy
mediatheque.TYPE_RESSOURCE.max_file_sizeDonne la taille limite à ne pas dépasser pour un media de type TYPE_RESSOURCE (photo, audio, video, fichier, ...)123456 ou 100K ou 2Mphoto: 5M
video: 150M
fichier: 10M
audio: 2M

Modifier le fichier

Les processus K-Sup

Point d'entrée

Le point d'entrée pour tous les processus de K-Sup est la servlet SGController. SGContoller effectue une appel à TraitementRequeteHTTP#traiterRequeteHTTP qui renvoie un objet de type ResponseEntity/ModelAndView.

TraitementRequeteHTTP#traiterRequeteHTTP permet de générer le flux retourné par le SGController en fonction du type de flux attendu URL/JSP/XML/HTML/Objet sérialisé.

Affichage d'un flux JSP

Dans le cas d'un flux de type JSP, le viewAdapterSelector pouvant prendre en charge la requête courante est récupéré et son adapter est appelé afin de généré le flux attendu.

L'affichage des écrans de processus back-office sont gérés par le DefaultViewAdapter qui génère le flux de retour à partir de la jsp correspondant au processus et l'écrit directement dans la réponse. Une ResponseEntity.ok() est retournée au controller.

Les Selector

Les Selector sont des beans ordonnés qui étendent AbstractViewAdapterSelector.
Chaque selector prend en charge un contexte, connu à partir de la requête courante.

public boolean accept(final HttpServletRequest request) {
    ...
}

Le selector est directement lié à un ViewAdapter qui est en charge de généré le flux attendu.

public abstract class AbstractViewAdapterSelector<R extends AbstractViewAdapter<?>> implements Selector<HttpServletRequest> {}

Les Adapter

Les Adapter étendent AbstractViewAdapter.
Ils définissent un méthode process en charge de généré le flux de retour pour l'affichage du processus.

public class MonViewAdapter extends AbstractViewAdapter<ModelAndView> {

    @Override
    public ModelAndView process(final DescriptifPageRetour desc, final HttpServletRequest req, final HttpServletResponse res, ProcedureBean bean) {
        ...
    }
}

Modifier le fichier

Paramétrage du HTTPS dans l'usine à sites

Par défaut le HTTPS est activé sur tous les sites créés dans l'usine à site.

Afin de pouvoir lancer des environnements locaux (*.localhost) sans certificat, une propriété permet de surcharger le comportement par défaut.

PropriétéDescriptionValeurs possiblesValeur par défaut
site.secureDéfinit si le https est activé sur l'environnment.true ou false (false uniquement sur les environnement de devtrue

en ajoutant la valeur

site.secure=false 

dans votre env.properties, le HTTPS sera désactivé par défaut.

Modifier le fichier

Insertion de liens dans la toolbox

Style d'affichage des liens

Lors de l'insertion d'un lien dans la toolbox, il est possible de choisir l'apparence du lien. Une liste déroulante "Apparence du lien" présente en bas de la modal d'insertion, permet de choisir l'apparence à appliquer.

Un bean linkStyleManager est défini dans le fichier core-ckeditor.xml pour définir les styles de liens disponibles:

    <bean id="linkStyleManager" class="com.kosmos.content.link.view.LinkStyleManager">
        <property name="styles">
            <map>
                <entry key="style-default" value="Par défaut" />
                <entry key="style-bouton" value="Lien bouton" />
            </map>
        </property>
    </bean>

La clé de chaque entrée correspond à la classe CSS à appliquer au lien. La valeur correspond au libellé affiché dans la liste déroulante.

Ajouter un style de lien

Pour ajouter un style de lien, il suffit de déclarer une nouvelle entrée dans le bean linkStyleManager en définissant la classe CSS à appliquer et le libellé à afficher.

    <bean id="mapToAddlinkStyleManager" class="com.kportal.core.context.MapToAddBean">
        <property name="idExtensionToMerge" value="core"/>
        <property name="idBeanToMerge" value="linkStyleManager"/>
        <property name="mapToMerge" value="styles"/>
        <property name="add">
            <map>
                <entry key="style-bouton-2" value="Lien bouton coloré"/>
            </map>
        </property>
    </bean>

Il faut ensuite déclarer le css correspondant dans les fichiers du projet.

a[data-style="style-bouton-2"] {
    background-color: #f00;
    color: #fff;
    padding: 10px 20px;
    border-radius: 5px;
    text-decoration: none;
}

Modifier le fichier

jsTree avec Multiselect

Dans le cas d'un jsTree multiselect, le chargement initial est fait de manière asynchrone.

En effet, la valeur du champ SELECTED de l'URL peut-être extrêment longue si beaucoup d'éléments sont sélectionnés. Les URLs étant limitées en longueur, il a été nécessaire de changer le chargement dans ce cas précis en le rendant asynchrone pour :

  • les groupes
  • les rubriques
  • les structures

Les autres données représentées en arbre n'étant pas en multiselect.

Mécanisme

Avant le chargement de l'iFrame, deux variables sont ajoutées aux sessionStorage en javascript :

sessionStorage.setItem('jsTreeSelection', $input.val()); // Valeur de la sélection
sessionStorage.setItem('jsTreeInitUrl', $component.data('urlinittree')); // URL de chargement des données
  • jsTreeSelection : contient la liste des éléments sélectionnés dans l'arbre
  • jsTreeInitUrl : contient l'URL du endpoint permettant de récupérer l'initialisation de l'arbre

Coté jsTree, au chargement, si une variable de session jsTreeSelection existe alors, l'URL jsTreeInitUrl est appelée en POST avec en body la valeur de jsTreeSelection. Les variables sont immédiatement supprimées pour éviter de se chevaucher entre deux utilisations de jsTree.

Le résultat permet d'initialiser l'arbre en ouvrant les nœuds nécessaires et en cochant les nœuds sélectionnés.

Un overlay avec un spinner est affiché le temps de ce chargement afin d'indiquer à l'utilisateur de patienter.

Initialisation de l'arbre

Il y a deux méthodes d'initialisation de l'arbre

setState

L'utilisation de set_state du jsTree. Les données injectées sont seulement les états des différents nœuds : sélectionné et/ou ouvert.

setData

L'utilisation de create_node du jsTree. Les données injectées sont complètes, c'est à dire qu'il y a l'état ET les données des nœuds (texte, ID, etc.)

Modifier le fichier

Construction des données JSON-LD

Introduction

Le format JSON-LD est un format de données structurées basé sur le format JSON. Il permet de décrire des données de manière structurée et de les rendre plus facilement interprétables par les machines. Il est notamment utilisé pour décrire des données sémantiques sur le web.

Syntaxe

La syntaxe JSON-LD est basée sur le format JSON. Elle permet de décrire des données sous forme de graphes.

Un document JSON-LD est un objet JSON qui contient un champ @context qui définit le contexte du document. Le contexte permet de définir des abréviations pour les termes utilisés dans le document.

Voici un exemple de document JSON-LD :

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type":"WebSite",
      "url":"http://www.ksup7.localhost:8080/",
      "name":"www",
      "description":"La baseline",
      "inLanguage":"fr"
    }
  ]
}

Construction des données

La classe AbstractViewModel possède un attribut jsonLdModels qui permet de stocker les modèles JSON-LD (JsonLdModel) associés à la vue.

La liste des modèles JSON-LD est alimentée par le view preparer AbstractViewPreparer qui implémente la méthode prepareJsonLdModels.

public List<JsonLdModel> prepareJsonLdModels(final FrontContext frontContext, final V viewModel) {
    
}

Chaque vue possédant des données JSON-LD doit implémenter la méthode prepareJsonLdModels et appelée celle-ci dans la méthode prepare.

Modélisation des données

Pour modéliser les données JSON-LD, une classe AbstractJsonLdModel est fournie. Cette classe abstraite implémente l'interface JsonLdModel et contient les attributs @type et @id communs à la plupart des modèles JSON-LD.

/**
 * Classe abstraite des modèles JsonLd.
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class AbstractJsonLdModel implements JsonLdModel {

    @JsonProperty("@type")
    protected String type;

    @JsonProperty("@id")
    protected String id;

    public String getType() {
        return type;
    }

    public void setType(final String type) {
        this.type = type;
    }

    public String getId() {
        return id;
    }

    public void setId(final String id) {
        this.id = id;
    }
}

Récupération des données

La classe AbstractViewModel possède une méthode getAllJsonLdModels qui permet de récupérer la liste de l'ensemble des modèles JSON-LD associés à la vue finale.
Elle possède également une méthode serializeJsonLdModels qui permet de sérialiser les modèles JSON-LD en une chaîne de caractères JSON, cette méthode va demander à chaque viewModel de sérializer ses données JSON-LD via la méthode serialize.

Le handler de contenu ajoute les données JSON-LD à la vue dans la méthode getModelAndView de la facon suivante :

modelAndView.addObject(JSONLD_MODEL, viewModel.serializeJsonLdModels());

Un view preparer JsonLdViewPreparer est également ajouté pour ajouter les données JSON-LD à la vue finale.
Ce view preparer ajoute une vue de type jsonld qui insère les données JSON-LD dans une balise <script>.

/WEB-INF/jsp/seo/jsonld/jsonLd.jsp :

<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@graph": ${jsonLdModel}
}
</script>

Modifier le fichier

Gestion des libellés

Modifier le fichier

Paramétrage des libellés

Plusieurs propriétés permettent de configurer / adapter les libellés.

Surcharge

La surcharge des propriétés peut se faire dans les fichiers de surcharge du core.

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
table.repertoire.specificEmplacement des fichiers .dat- chemin relatif dans le storage ou dans le classpath (dans le répertoire src/main/resources du projet)/tables_specific

Modifier le fichier

Migration de colonnes texte avec AbstractTextColumnsProcessor


Vue d’ensemble fonctionnelle

AbstractTextColumnsProcessor est une base de migration Flyway (Java) dédiée aux colonnes texte des tables applicatives. Elle permet de:

  • Parcourir automatiquement toutes les tables (sauf exclusions configurées) et détecter les colonnes “texte”.
  • Pour chaque ligne, transformer le contenu des colonnes texte:
    • Si la valeur est un JSON “wrappeur” (string commençant par { et finissant par }), extraire les valeurs texte et leur appliquer une transformation HTML, puis ré-encoder le JSON.
    • Sinon, considérer la valeur comme du HTML et l’envoyer à une transformation HTML spécialisée.
  • Vérifier l’intégrité “fonctionnelle” du contenu après transformation (pas de perte de texte visible) et, en cas d’échec, conserver la valeur d’origine.
  • Mettre à jour en base uniquement les lignes modifiées, avec logs détaillés.

Les classes concrètes définissent:

  • La logique de transformation HTML (processHtmlContent).
  • Le prédicat SQL ciblant uniquement les lignes susceptibles d’être impactées (buildColumnPredicat).

Des exemples existants:

  • V7_3_1_6__migration_grilles: migration des grilles CKEditor -> nouveau layout.
  • V7_3_1_10__migration_accordeons: migration d’anciens accordéons <dl/dt/dd> vers <details>.
  • V7_3_1_7__migration_styles, V7_3_1_8__migration_styles_v2, V7_3_1_9__migration_onglets: mêmes patterns pour d’autres structures.

Architecture et flux d’exécution (technique)

  1. Initialisation Flyway

    • La classe étend BaseJavaMigration et implémente migrate(Context). Flyway l’exécute au moment des migrations (au démarrage).
  2. Sélection des tables à traiter

    • listAllTables(Connection): récupère l’ensemble des tables via les métadonnées JDBC.
    • Exclusions: la propriété text_column_processor.excluded_tables (chargée via PropertyHelper) peut contenir une liste de tables à ignorer, séparées par des virgules.
      • Exemple de configuration (fichier env.properties de l’environnement):
        text_column_processor.excluded_tables=HISTORIQUE_LOGS,TMP_IMPORT
        
  3. Détection des colonnes texte

    • listTextColumns(Connection, String table):
      • Tente un SELECT * FROM {table} LIMIT 1 et lit le ResultSetMetaData.
      • Est considérée comme “texte” une colonne dont le type JDBC est LONGVARCHAR, ou de type VARCHAR d’une taille supérieure à un seuil minimal, ou dont le type contient TEXT.
      • Seuil configurable au niveau de la classe via le champ protégé minimumColumnSize (valeur par défaut: 1024). Une classe fille peut l’ajuster si nécessaire.
      • Exclut systématiquement la colonne identifiant ID_{TABLE}.
  4. Construction des requêtes SQL

    • buildQuerySelect(table, columns): construit le SELECT avec ID_{table} + colonnes texte, et y ajoute une clause WHERE construite par buildColumnPredicat(table, columns) (spécifique à la classe fille).
    • buildQueryUpdate(table, columns): construit un UPDATE avec paramètres, du style UPDATE {table} SET col1=?, col2=?, ... WHERE ID_{table}=?.
    • buildColumnPredicat(Set<String> columns, String needle): utilitaire générique pour produire une clause WHERE (col LIKE '%needle%' OR ...), avec échappement des ' dans needle.
  5. Traitement ligne par ligne

    • Pour chaque ligne du SELECT:
      • On lit l’ID (getIdValue) et chaque colonne texte.
      • processColumn(col, oldVal) décide de la voie:
        • JSON wrappeur ? (isJsonWrapped(value): valeur non vide, trim, commence par { et finit par }): processJsonWrappedContent(col, value)
          • Parse via CodecJSon.decodeStringJSonToClass en Map<String, Object>.
          • Pour chaque valeur de type String, applique processHtmlContent(html).
          • Re-encode le JSON via CodecJSon.encodeObjectToJSonInString.
        • Sinon: processHtmlContent(value) directement.
      • Si la valeur a changé et que l’intégrité est validée, on UPDATE la ligne.
  6. Vérification d’intégrité

    • verifyContentIntegrity(originalValue, processedValue):
      • Convertit HTML -> texte via Jsoup.parse(...).text() de part et d’autre.
      • Normalise (suppression \n, \t, espaces; tri des caractères) puis compare.
      • Si l’intégrité échoue et que applyIntegrityVerification est à true (par défaut), la migration pour cette ligne est annulée (on écrit la valeur d’origine), avec un WARN.
      • Une classe fille peut désactiver ce garde-fou en réglant applyIntegrityVerification=false si elle doit effectuer des transformations modifiant intentionnellement le texte visible.

Points d’attention et bonnes pratiques

  • Prédicats SQL efficaces

    • Évitez un scan complet des tables: construisez un WHERE qui restreint aux contenus suspects (via buildColumnPredicat(columns, "motif")).
    • Double gestion « non échappé / échappé » si vos motifs peuvent se trouver dans du HTML direct ou du HTML sérialisé en JSON string (voir SEARCH_ESCAPED).
  • Intégrité du texte visible

    • Conservez applyIntegrityVerification=true tant que possible. Ne désactivez que si vos transformations changent volontairement le texte (ex. suppression de décorations textuelles devenues obsolètes).

Créer un nouveau script de migration: guide pas-à-pas

  1. Étendre AbstractTextColumnsProcessor

    • Implémenter:
      • protected String processHtmlContent(String htmlValue).
      • protected String buildColumnPredicat(String tableName, Set<String> columns).
  2. Construire des constantes de recherche

    • Exemple:
      private static final String SEARCH = "<div class=\"old-widget\">";
      private static final String SEARCH_ESCAPED = "<div class=\\\\\"old-widget\\\\\">"; // pour JSON string
      
  3. Implémenter buildColumnPredicat

  4. Implémenter processHtmlContent

    • Pattern recommandé:
      @Override
      protected String processHtmlContent(final String value) {
          if (!value.contains("old-widget")) {
              return value; // court-circuit
          }
          final Document doc = Jsoup.parseBodyFragment(value);
          setXmlSettings(doc);
          boolean changed = false;
          // Sélectionner précisément ce que vous transformez
          final Elements widgets = doc.select("div.old-widget");
          for (final Element w : widgets) {
              final Element replacement = transformWidget(w);
              if (replacement != null) {
                  w.replaceWith(replacement);
                  changed = true;
              }
          }
          return changed ? doc.body().html() : value;
      }
      
      private Element transformWidget(final Element old) {
          // Construire le nouveau DOM (balises, classes, attributs) en réutilisant le inner HTML si besoin
          final Element container = new Element("div").addClass("new-widget");
          final Element inner = new Element("div");
          inner.html(old.html());
          container.appendChild(inner);
          return container;
      }
      
  5. Ajuster les garde-fous si nécessaire

    • Si la transformation modifie le texte visible (suppression de libellés), envisagez this.applyIntegrityVerification = false; (dans un bloc d’initialisation ou un constructeur) avec prudence et justification.
    • Vous pouvez aussi abaisser/augmenter minimumColumnSize si votre migration doit couvrir des VARCHAR plus petits/grands.

Modèle minimal de classe de migration

package com.kosmos.core.migration;

import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.kosmos.migration.core.executor.AbstractTextColumnsProcessor;

public class V7_3_1_X__migration_mon_widget extends AbstractTextColumnsProcessor {

    private static final Logger LOG = LoggerFactory.getLogger(V7_3_1_X__migration_mon_widget.class);

    // Motifs pour ciblage SQL (HTML direct vs HTML encodé dans JSON)
    private static final String SEARCH = "<div class=\"old-widget\">";
    private static final String SEARCH_ESCAPED = "<div class=\\\\\"old-widget\\\\\">";

    @Override
    public Integer getChecksum() {
        return 1234567890; // fige le checksum Flyway de cette migration Java
    }

    @Override
    protected String processHtmlContent(final String value) {
        if (!value.contains("old-widget")) {
            return value;
        }
        final Document doc = Jsoup.parseBodyFragment(value);
        setXmlSettings(doc);
        boolean changed = false;
        final Elements olds = doc.select("div.old-widget");
        for (final Element old : olds) {
            final Element neo = transformOldToNew(old);
            if (neo != null) {
                old.replaceWith(neo);
                changed = true;
            }
        }
        return changed ? doc.body().html() : value;
    }

    private Element transformOldToNew(final Element old) {
        final Element container = new Element("div").addClass("new-widget");
        final Element inner = new Element("div");
        inner.html(old.html());
        container.appendChild(inner);
        return container;
    }

    @Override
    protected String buildColumnPredicat(final String tableName, final Set<String> columns) {
        final String p1 = buildColumnPredicat(columns, SEARCH);
        final String p2 = buildColumnPredicat(columns, SEARCH_ESCAPED);
        if (StringUtils.isEmpty(p1) && StringUtils.isEmpty(p2)) return StringUtils.EMPTY;
        if (StringUtils.isEmpty(p1)) return p2;
        if (StringUtils.isEmpty(p2)) return p1;
        return p1 + p2.replace(" WHERE ", " OR ");
    }
}

FAQ / points spécifiques

  • Et si mon contenu JSON n’est pas un objet au niveau racine ?

    • isJsonWrapped ne reconnaît que les strings démarrant/finissant par {/}. Si vous avez du JSON tableau ou un wrapper différent, ajustez isJsonWrapped dans une sous-classe (ou surchargez processColumn).
  • Puis-je désactiver l’intégrité uniquement pour une colonne ?

    • Le garde-fou est global à l’instance. Pour un contrôle plus fin, surchargez processColumn pour décider au cas par cas si l’intégrité doit bloquer la migration et retournez la valeur d’origine si besoin.
  • Et si ID_{TABLE} n’existe pas ?

    • getIdValue tente ID_{TABLE} puis, en fallback, la première colonne du ResultSet. Si aucune n’est accessible, la ligne est ignorée (pas d’UPDATE possible).

Modifier le fichier

Notes sur la prévisualisation dans K-Sup (aperçu)

Au clic sur le bouton "prévisualiser" (ou aperçu en front), le formulaire de saisie est soumis en ajax. L'action est préalablement positionnée à la valeur "ENREGISTRER" et un flag "APERCU" est ajouté au formulaire. Les données soumises sont validées par le processus idoine.

On analyse ensuite le retour Json Le retour est de la forme {"result": "OK", "preview": "https://www.ksup.org/apercu/12345"} ou {"result": KO", "messages": "La date est invalide"}```

  • si le resultat est OK, on ouvre un nouvel onglet pour visualiser l'URL de preview
  • si le resultat est KO, on clippe les message d'erreur dans la zone prévue dans l'écran courant.

Ce fonctionnemnt permet de n'ouvrir l'onglet d'aperçu qu'au dernier moment et la page en cours d'édition reste synchrone avec l'infobean.

Preview sequence

Modifier le fichier

Gestion de la recherche anonyme de K-Sup

Description de la fonctionnalité

La fonctionnalité de recherche anonyme permet d'effectuer une recherche de fiches hors du périmètre de l'utilisateur. L'utilisateur ne doit pas pouvoir éditer la fiche mais doit pouvoir contribuer un lien vers la fiche par exemple à l'aide du tag de toolbox "lien".

Paramétrage

La recherche anonyme s'active pour chaque type de fiche avec la propriete fiche.NOM_OBJET.recherche_anonyme, les valeurs possibles sont les suivantes :

  • recherche anonyme=0 pas de recherche anonyme autorisée
  • recherche anonyme=1 recherche anonyme autorisée

Activation de la recherche anonyme

Le déclenchement de la recherche anonyme est piloté par le service OutscopedSearchService.

Ce service est initialisé avec une liste de beans qui permettent d'activer ou non la recherche anonyme en fonction d'un paramètre de contexte.

Chaque bean d'activation implémente l'interface OutscopedSearchActivator.

Dès qu'un des activators retourne "true", la recherche anonyme est activée pour le contexte.

Le service vérifie ensuite que la recherche anonyme est possible pour le type de fiche considéré. (par vérification d'une propriété fiche.OBJET.recherche_anonyme valorisée à 1)

Le contexte est porté par le composant lui même. Par exemple, un selecteur de fiche pour un lien vers une fiche sera associé à un UUID de contexte. Un selecteur multifiche sera associé à un autre UUID de contexte. Chaque composant de recherche de fiche (ou écran de recherche de fiche) possède son propre contexte.

La classe com.univ.utils.actiongenerator.ContextZoneAction resense les différents contextes connus.

La recherche anonyme intervient dans les différents datagrids des fiches :

graph
style E fill:#a02020,stroke:#333,stroke-width:1px
erA0(FicheUnivDatagrid) --> B(#traiterRechercheDepuisRequete)
A1(FicheToolboxDatagrid) --> B(#traiterRechercheDepuisRequete)
B --> C(#traiterRequeteAvecControlePerimetre)
C --> D(#initialiserControleBo)
D --> E(OutscopedSearchService#isOutscopedSearchActive)
graph
style F fill:#a02020,stroke:#333,stroke-width:1px
A0(MultiFicheDatagrid) --> B(#traiterRechercheDepuisRequete)
A1(MultiFicheToolboxDatagrid) --> B(#traiterRechercheDepuisRequete)
A2(SaisieFiche  #ECRAN_LOGIQUE#=RECHERCHE) --> C(RechercheMultificheHelper#rechercherParmisToutesLesFiches)
B --> C
C --> D0(RechercheMultificheHelper#rechercheMetaParUrlFiche)
D0 --> E(#verifierDroitVisibiliteFiche)
E --> F
C --> D1(RechercheMultificheHelper#rechercherParCriteresMultiples)
D1 --> F(OutscopedSearchService#isOutscopedSearchActive) 

Tests

Fonctionnalités à tester sur la recherche anonyme :

  • Insertion d'une fiche en page d'accueil de rubrique
  • Rattachement manuel d'une fiche à une rubrique (kmonoselect)
  • Rattachement d'une fiche à une liste manuelle
  • Lien interne vers une fiche dans une toolbox
  • Rattachement manuel d'une fiche à une fiche (kmonoselect)
  • Rattachement manuel de fiches à une fiche (kmultiselect)
  • Selecteur de fiche (critère) dans un écran de recherche de fiche (taglink)

Modifier le fichier

Gestion du robots.txt

Le produit expose un controller permettant de générer dynamiquement le fichier robots.txt.

Ce fichier est initialisé à l'aide du fichier webapp-contrib/src/main/resources/default-robots.txt
Le contenu de ce fichier peut être surchargé en projet par environnement depuis le storage.

Il suffit de créer un fichier robots.txt avec le contenu souhaité dans le dossier conf du storage

Par ailleurs, chaque extension (y compris le projet) peut ajouter un contenu spécifique dans le robots.txt généré.
Il suffit pour cela que l'extension expose un fichier robots-xxx.txt à la racine des ressources (src/main/resources) où xxx est l'identifiant de votre extension (ou du projet).

L'ordre de construction du robots.txt généré est le suivant:

  • Le fichier default-robots.txt du produit ou le fichier conf/robots.txt du storage en cas de surcharge
  • Les fichiers robots-xxx.txt des extensions
  • Les références aux fichiers sitemap.

Modifier le fichier

Paramétrage de la saisie

Plusieurs propriétés permettent de configurer / adapter la saisie des fiches ou de certains contenus.

Fiche

Il est possible de spécifier de manière globale ou objet par objet

  • si un champ est saisissable
  • si un champ est modifiable
  • si un champ est facultatif ou obligatoire
  • la taille maximmum des champs texte

(cf UnivFmt.java méthode getModeSaisieZone et getMaximumCharLength)

Seuls les champs communs RESUME, THEMATIQUE et PHOTO bénéficient de cette fonctionnalité.

Les propriétés INVISIBLE, MODIFIABLE et FACULTATIF ont une porté au niveau de la source d'import. Dans ce cas, la propriété est de la forme

saisie.{{NOM_DU_CHAMP}}.{{SOURCE_IMPORT}}.INVISIBLE=0

ou pour un type de fiche donné, de la forme

saisie.{{OBJET}}.{{NOM_DU_CHAMP}}.{{SOURCE_IMPORT}}.INVISIBLE=0

Si le paramètrage s'applique quelle que soit la source, la propriété est de la forme

saisie.{{NOM_DU_CHAMP}}.INVISIBLE=0

ou pour un type de fiche donné, de la forme

saisie.{{OBJET}}.{{NOM_DU_CHAMP}}.INVISIBLE=0

Remarque sur le comportement :

  • Si un champ renseigné est rendu invisible, il n'est pas effacé au ré-enregistrement de la fiche (et pourrait continuer de s'afficher en front)
  • Si un champ passe obligatoire, il faudra le renseigner si on modifie une fiche dans laquelle il est n'est pas renseigné.
  • Si on passe obligatoire un champ non modifiable, il ne sera pas possible de le saisir pour les fiches dnas lesquelles il n'est pas renseigné.
  • Si on ne saisit pas un champ facultatif non modifiable à la création, on ne pourra pas le renseigner en modification

Surcharge

La surcharge des propriétés par défaut peut se faire dans les fichiers de surcharge du core. La surcharge des propriétés par objet peut se faire dans les fichiers de surcharge des extensions.

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
saisie.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].INVISIBLEMasque le champ en saisie0 (visible) ou 1 (invisible)0
saisie.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].MODIFIABLEN'autorise la saisie du champ qu'à la création0 (non modifiable) ou 1 (modifiable)1
saisie.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].FACULTATIFIndique le caractère optionnel du champ0 (obligatoire) ou 1 (facultatif)1
saisie.{{NOM_DU_CHAMP}}.TAILLEMAXIndique le nombre maximum de caractères autorisés du champentier positifaucune
saisie.THEMATIQUE.INVISIBLEActive la saisie du champs THEMATIQUE par défaut0 (visible) ou 1 (invisible)0
saisie.THEMATIQUE.MODIFIABLEAutorise la modification du champ THEMATIQUE par défaut0 (non modifiable) ou 1 (modifiable)1
saisie.THEMATIQUE.FACULTATIFRend facultative la saisie du champ THEMATIQUE par défaut0 (obligatoire) ou 1 (facultatif)1
saisie.RESUME.INVISIBLEActive la saisie du champs RESUME par défaut0 (visible) ou 1 (invisible)0
saisie.RESUME.MODIFIABLEAutorise la modification du champ RESUME par défaut0 (non modifiable) ou 1 (modifiable)1
saisie.RESUME.FACULTATIFRend facultative la saisie du champ RESUME par défaut0 (obligatoire) ou 1 (facultatif)1
saisie.RESUME.TAILLEMAXLimite la saisie du champ RESUME par défautentier positif2048
saisie.PHOTO.INVISIBLEActive la saisie du champ PHOTO par défaut0 (visible) ou 1 (invisible)0
saisie.PHOTO.MODIFIABLEAutorise la modification du champ PHOTO par défaut0 (non modifiable) ou 1 (modifiable)1
saisie.PHOTO.FACULTATIFRend facultative la saisie du champ PHOTO par défaut0 (obligatoire) ou 1 (facultatif)1

Les propriétés peuvent être adaptées par objet pour surcharger les valeurs par défaut

PropriétéDescriptionValeurs possibles
saisie.{{OBJET}}.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].INVISIBLEMasque le champ en saisie pour un objet spécifique0 (visible) ou 1 (invisible)
saisie.{{OBJET}}.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].MODIFIABLEN'autorise la saisie du champ qu'à la création pour un objet spécifique0 (non modifiable) ou 1 (modifiable)
saisie.{{OBJET}}.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].FACULTATIFIndique le caractère optionnel du champ pour un objet spécifique0 (obligatoire) ou 1 (facultatif)
saisie.{{OBJET}}.{{NOM_DU_CHAMP}}.TAILLEMAXIndique le nombre maximum de caractères autorisés du champ pour un objet spécifiqueentier positif

Modifier le fichier

Création d'un script automatisé

Déclaration XML

L'ajout d'un script automatisé nécessite au minimum la définition de deux beans dans le context Spring.

Déclaration d'un bean de type JobModule qui prend plusieurs paramètres :

Nom du paramètreDescriptionValeur possibleValeur par défaut
jobDetailsListe des beans de type JobDetailFactoryBean
typeType de scriptVoir IModule.TYPE_*TYPE_NON_PARAMETRABLE_NON_AFFICHABLE
libelleLibellé du script
descriptionDescription du script
permissionsListe des permissions nécessaires pour le script
etatEtat par défaut du scriptVoir IModule.ETAT_*ETAT_ACTIF

Exemple de définition d'un JobModule :

<bean id="monScriptJobModule" class="com.kportal.scheduling.module.JobModuleImpl">
    <property name="jobDetails">
        <list>
            <ref bean="monScriptJob"/>
        </list>
    </property>
    <property name="type" ref="TYPE_PARAMETRABLE"/>
    <property name="libelle" value="MON_SCRIPT.JOB_LIBELLE"/>
    <property name="permissions">
        <list>
            <bean class="com.kportal.core.autorisation.Permission">
                <property name="code" value="monScriprJob"/>
                <property name="type" value="SCRIPT"/>
                <property name="actions">
                    <list>
                        <bean class="com.kportal.core.autorisation.ActionPermission">
                            <property name="code">
                                <util:constant static-field="com.kportal.scheduling.util.ScriptsAutomatisesUtil.ACTION_LANCEMENT"/>
                            </property>
                        </bean>
                    </list>
                </property>
                <property name="libelle" value="MON_SCRIPT.JOB_LIBELLE"/>
            </bean>
        </list>
    </property>
</bean>

Déclaration du bean de job qui étend JobDetailFactoryBean qui prend plusieurs paramètres :

Nom du paramètreDescriptionValeur possibleValeur par défaut
jobClassLa classe du jobEx : com.kosmos.search.batch.job.IndexerJob
durabilityIndique si le job doit rester disponible dans le job storetrue ou falsefalse
descriptionDescription du job

Exemple de définition d'un JobDetailFactoryBean :

<bean id="monScriptJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="description" value="Description du job"/>
    <property name="jobClass" value="com.kosmos.toto.batch.job.MonScriptJob"/>
    <property name="durability" value="true"/>
</bean>

Création de la classe du job

La classe d'exécution du job doit étendre LogReportJob et implémenter Runnable. Le traitement du job est effectué dans la méthode run().

Exemple de classe d'exécution du job :

public class MonScriptJob extends LogReportJob implements Runnable {
 
    /**
     * Logger.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(MonScriptJob.class);
 
    /**
     * Constructeur vide.
     */
    public MonScriptJob() {
        super();
    }
 
    /**
     * {@inheritDoc}
     */
    @Override
    public void run() {
        // Implémentation de l'exécution du job
    }
 
    /**
     * {@inheritDoc}
     */
    @Override
    public void perform() {
        run();
    }
}

Utilisation de paramètres pour l'exécution du job

Il est possible de saisir des paramètres lors du lancement d'un script automatique.

Liste de paramètres

Déclaration des paramètres

Les paramètres du job sont passés sous forme de Map au bean de type JobDetailFactoryBean via la propriété jobDataAsMap.

Exemple pour le job déclaré précédément :

<bean id="monScriptJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="description" value="Description du job"/>
    <property name="jobClass" value="com.kosmos.toto.batch.job.MonScriptJob"/>
    <property name="durability" value="true"/>
     <property name="jobDataAsMap">
        <map>
            <entry key="parametres">
                <value>param1;param2</value>
            </entry>
        </map>
    </property>
</bean>

Le label du champ de saisie du paramètre est récupéré à partir d'une clé de message portant le nom du paramètre () Il est possible de définir 3 types de paramètre de job différents :

  1. Champ de saisie Si le paramètre du job ne contient qu'une clé et une valeur vide, un champ de saisie de type text sera affiché à l'utilisateur afin de saisir la valeur du paramètre.
  2. Valeur unique non modifiable Si le paramètre du job ne contient qu'une seule valeur non vide, un champ de saisie non modifiable sera présent avec la valeur renseignée.
  3. Liste de valeurs Si le paramètre contient une liste de valeurs, une liste déroulante sera affichée avec la liste des valeurs.

Pour chaque paramètre saisissable une entrée est ajoutée dans la map avec pour clé le nom du paramètre et pour value la liste des valeurs possibles.

Définition de la liste des valeurs

La liste des valeurs pour un paramètre peut être définie de 2 manières : Avec une Map En passant une Map<String, String> en valeur de votre paramètre, la liste déroulante sera alimentée avec pour chaque entrée de la map, la clé en tant que valeur et la value en tant que libellé.

Avec une liste de String Il est possible d'alimenter la liste de valeurs d'un paramètre à partir d'une liste de String, chaque valeur étant séparée par un point virgule :

<map>
	<entry key="parametres">
		<value>param1;param2;param3</value>
	</entry>
</map>

Lors de l'utilisation d'une liste de String, la liste déroulante sera alimentée avec pour valeurs les différentes chaine de caractères séparées par un point virgule et pour libellés une clé de message définie dans les fichiers I18n de l'application.

Les traductions des paramètres sont saisies dans le fichier I18N de l'extension avec la règle de nommage suivante BO.NOM_EXTENSION.JOB_PARAM.NOM_PARAMETRE.

Exemple pour les paramètres déclarés ci-dessus :

# Clé de message pour le label du champ
BO.CORE.JOB_PARAM.PARAMETRES=Paramètre du job

# Clés de message pour les différentes valeurs sélectionnables
BO.CORE.JOB_PARAM.PARAM1=Paramètre 1
BO.CORE.JOB_PARAM.PARAM2=Paramètre 2
BO.CORE.JOB_PARAM.PARAM3=Paramètre 3

Récupération des paramètres saisis

Les paramètres saisis lors du lancement du script peuvent être récupérés via le JobContext, celui-ci est disponible dans les méthodes init() et execute() de LogReportJob.

Exemple de récupération des paramètres pour le job monScriptJob :

public class MonScriptJob extends LogReportJob implements Runnable {
 
    ...

    private String parametre;

    ...


    @Override
    public void init(final JobExecutionContext context) {
        // Récupération du paranètre saisi lors du lancement du script
        // Celui-ci peut ensuite être utilisé dans la méthode run()
        parametre = context.getMergedJobDataMap().getString("parametres");
    }

    ...
}

Modifier le fichier

Gestion du SSO interne de K-Sup

La fonctionnalité de gestion du SSO permet d'authentifier automatiquement un utilisateur sur différents sites K-Sup avec des domaines différents.

Description de la fonctionnalité

La fonctionnalité de SSO s'active dans le back-office, dans la gestion des sites.
Il suffit de cocher la case "Site SSO" dans l'onglet "informations générales".

Case à cocher SSO

Le processus est le suivant :

  • L'utilisateur s'authentifie sur le site courant.
    • À la fin de l'autentification, un fichier XML avec les informations de la session est créé dans le dossier "session" du storage.
    • Un rebond est effectué sur une URL d'enregistrement de la connexion (connectionRegistry)
    • Une fois la connexion enregistrée, l'utilisateur est envoyé vers la page initialement demandée.
  • L'utilisateur navigue sur un site d'un autre domaine (site K-Sup)
    • Si le site est SSO et que l'utilisateur n'est pas authentifié sur le site courant, un check est effectué sur le site principal
    • Comme la connexion de l'utilisateur a été enregistrée dans le registre, le check va retrouver la connexion précédente et générer un jeton signé à usage unique pour l'autoconnexion.
    • Une popin s'ouvre en bas de la page et propose à l'utilisateur d'être connecté automatiquement sur le site courant.
  • Si l'utilisateur accepte, l'utilisateur est connecté via le jeton d'authentification.
    • Un rebond est ensuite effectué sur le site principal pour enregistrer la connexion sur le nouveau site.
    • L'utilisateur est ensuite redirigé vers la page qu'il consultait initialement.

Les fichiers session sont de la forme session_950c4fff-a3c6-4ffa-92db-6c5a4a4338da.xml avec le contenu suivant

<?xml version="1.0" encoding="utf-8"?>
<SESSION>
    <ID>950c4fff-a3c6-4ffa-92db-6c5a4a4338da</ID>
    <CODE>john.doe</CODE>
    <CODE_LDAP/>
    <CIVILITE>0000</CIVILITE>
    <NOM>Doe</NOM>
    <PRENOM>John</PRENOM>
    <EMAIL>john.doe@kosmos.fr</EMAIL>
    <STRUCTURE/>
    <PROFIL/>
    <GROUPES/>
</SESSION>

Les fichiers jeton JWT sont de la forme 0oT7FMGxuf sans contenu La session TOMCAT sera initialisée par les données présentes dans le fichier XML (alimentation réalisée dans le filter ContexteFilter). L'utilisateur sera alors reconnu comme authentifié et identifié.

@startuml
autonumber
skinparam style strictuml

Actor "Bob" as B
Participant Navigateur as N
Participant "K-Sup" as K

activate B
B -> N: Se connecte sur site A
activate N
N -> K: Appele l'URL de login
activate K
K -> N: Génère la page de login
deactivate K
N -> B: Affiche la page
deactivate N
B -> N: Saisit son identifiant et le mot de passe
activate N
N -> K: Soumet le formulaire d'identification
activate K
K -> K: Authentifie l'utilisateur
K -> K: Détermine la page d'atterissage
K -> N: Retourne un formulaire auto-submit sur le site principal
deactivate K
N -> K: Soumet le formulaire
activate K
K -> K: Enregistre La connexion \nde l'utilisateur sur le site A \ndans une connectionRegistry
K -> N: Retourne une redirection vers la page d'atterrisage
deactivate K
N -> K: Demande la page d'atterrissage
activate K
K -> N: Retourne la page demandée
deactivate K
N -> B: Affiche la page
deactivate N
...L'utilisateur navigue sur le site...
B -> N: Clique sur un lien vers le site B
activate N
N -> K: Appelle l'URL demandée
activate K
K -> N: Génère la page demandée <font color="red"><b>"Anonyme"</b></font>
deactivate K
N -> B: Affiche la page
N -> K: Interroge la connectionRegistry sur le site principal
activate K
K -> N: Retourne une jeton d'auto-connexion 
deactivate K
N -> B: Affiche la popin d'auto-connexion
B -> N: Clique sur le lien d'auto-connexion
N -> K: Soumet le formulaire d'auto-connexion
activate K
K -> K: Valide le jeton 
K -> K: Authentifie l'utilisateur sur le site B
K -> N: Retourne un formulaire auto-submit sur le site principal
deactivate K
N -> K: Soumet le formulaire
activate K
K -> K: Enregistre La connexion \nde l'utilisateur sur le site B\ndans une connectionRegistry
K -> N: Retourne une redirection vers la page initiale
deactivate K
N -> K: Demande la page initiale
activate K
K -> K: Génère la page\ncontextualisée <font color="green"><b>à bob</b></font>
K -> N: 200: retoune la page générée <font color="green"><b>pour bob</b></font>
deactivate K
N -> B: Affiche la page
deactivate N
@enduml

Service "RegistryCleaner"

Les connexions sont enregistrées par défaut pendant 8 heures à partir de la dernière connexion.
Une tache récurrente a pour fonction de purger les connexions expirées. Cette tâche démarre par défaut au bout de 60 seconde et s'exécute par défaut toutes les 60 secondes.
La session SSO est supprimée uniquement si elle ne contient plus de session HTTP.

Les propiétés suivantes permettent de personnaliser le fonctionnement de la purge:

  • connection.registry-cleaner.delay : fixe le délais avant le lancement de la première purge.
  • connection.registry-cleaner.period : fixe la période entre deux purges
  • connection.registry.expiration : fixe la durée de vie (TTL) de la session SSO

Cette purge n'impacte que la fonctionnalité de SSO. : un utilisateur connecté au BO ne sera pas déconnecté tant que sa session est vivante.

Modifier le fichier

Gestion du SLO interne de K-Sup

La fonctionnalité de gestion du SLO permet de déconnecter un utilisateur sur les différents sites K-Sup SSO auxquels il s'est connecté.

Description de la fonctionnalité

La déconnection est toujours effectuée sur un endpoint du site principal

Le processus est le suivant :

  • L'utilisateur clique sur le bouton "Déconnecter" du site courant
  • Il est redirigé vers le endpoint de déconnexion du site principal qui contient le registre des connexions
  • Les différentes sessions liées à la connexion SSO sont invalidées sur les différents membres du cluster.
    • En cas de connexion depuis une source externe , une fois les différents sites K-Sup déconnectés, l'utilisateur est renvoyé sur l'URL de déconnexion de la source externe
    • Une fois la déconnexion terminée, l'utilisateur est redirigé vers la page d'atterrissage

Calcul de la page d'atterrissage

  • En déconnexion FO, l'utilisateur est redirigé par défaut sur la page d'accueil du site courant
  • En déconnexion BO, l'utilisateur est redirigé par défaut sur la page /adminsite/ du site courant
  • Si la propriété source.redirection_front est activée (ou sinon la propriété default.redirection_front), l'utilisateur sera redirigé vers l'accueil front en cas de déconnexion depuis le BO
  • Si le propriété source.fo.logout.callback est valorisée (ou sinon la propriété default.logout.fo.callback), l'utilisateur sera redirigé vers la page indiquée par la propriété pour une déconnexion FO
  • Si le propriété source.bo.logout.callback est valorisée (ou sinon la propriété default.logout.bo.callback), l'utilisateur sera redirigé vers la page indiquée par la propriété pour une déconnexion BO
@startuml
autonumber
skinparam style strictuml

Actor "Bob" as B
Participant Navigateur as N
Participant "K-Sup" as K
Participant "Source Auth." as S

activate B
B -> N: Se déconnecte depuis le site A
activate N
N -> K: Appele l'URL de logout
activate K
K -> K: Invalidation des sessions locales
K -> K: Invalidation des sessions cluster
K -> N: Génération de l'URL de déconnexion de la source
deactivate K
N -> S: deconnexion de la source
activate S
S -> N: redirection vers la page d'atterrissage
deactivate S
N -> K: génération de la page demandé
activate K
K -> N: page générée
deactivate K
N -> B: Affichage de la page d'atterrissage
deactivate N
@enduml

Modifier le fichier

Sonde de vie

Un endpoint permet d'obtenir l'état de santé (basique) de l'application. Par défaut, le endpoint écoute l'URL /health et retourne un code 200 (sans contenu pour l'instant) si l'application est démarrée.

PropriétéDescriptionValeurs possiblesValeur par défaut
healthController.mappingpath du endpoint de la sondepath d'URL/health

Modifier le fichier

Tags K-Sup

Définition d'un tag

Un tag doit être de type DefaultPluginTag ou doit l'étendre.

Lors de la définition du tag, les propriétés suivantes peuvent être définies :

  • identifiantTag : identifiant du tag
  • extracteur : bean de type ExtracteurTag qui défini la facon dont le contenu du tag est extrait du texte parsé
  • interpreteurs : liste de beans de type AbstractInterpreteurTag qui définissent comment sera interprété le tag selon un contexte donné
  • baliseOuvrante : chaine de caractères définissant le début du tag
  • baliseFermante : chaine de caractères définissant la fin du tag
  • order : ordre d'interprétation du tag (1000 par défaut)
<bean id="tagMailto" class="com.kportal.extension.module.plugin.toolbox.DefaultPluginTag">
    <property name="identifiantTag" value="[mailto]"/>
    <property name="extracteur">
        <bean class="com.kportal.tag.extracteur.impl.ExtracteurHtmlEnDehorsDuTag">
            <property name="identifiantTag" value="[mailto]"/>
        </bean>
    </property>
    <property name="interpreteurs">
        <bean class="com.kportal.tag.interpreteur.impl.InterpreteurMailto">
            <property name="contextesTag">
                <list>
                    <util:constant static-field="com.kportal.tag.util.ContexteTagUtil.DEFAUT"/>
                </list>
            </property>
        </bean>
    </property>
    <property name="baliseOuvrante" value="&lt;a"/>
    <property name="baliseFermante" value="&lt;/a&gt;"/>
</bean>

Extracteur

L'extracteur permet de récupérer au sein d'un texte, le contenu du tag ainsi que les balises ouvrante/fermante.
La classe de l'extracteur doit implémenter ExtracteurTag et définir une méthode getContenuTagPresentDansTexte qui retourne le contenu du tag dont les balises sont passées en paramètre.

public class DefautExtracteurTag implements ExtracteurTag {

    @Override
    public String getContenuTagPresentDansTexte(String texte, String baliseOuvrante, String baliseFermante) {
        String contenuTag = StringUtils.substringBetween(texte, baliseOuvrante, baliseFermante);
        if (contenuTag != null) {
            contenuTag = baliseOuvrante + contenuTag + baliseFermante;
        }
        return contenuTag;
    }
}

Interpreteurs

Les interpréteurs du tag ont deux fonctions :

  • parser le tag et retourner le résultat
  • calculer les références vers les fiches/médias

Un tag peut avoir N interpréteurs, ceux-ci sont déclarés lors de la définition du tag ou peuvent être ajoutés via un ListToAddBean.

Chaque interpréteur défini la liste des contextes qu'il prend en charge.

  <property name="interpreteurs">
    <bean class="com.kportal.tag.interpreteur.impl.InterpreteurMailto">
        <property name="contextesTag">
            <list>
                <util:constant static-field="com.kportal.tag.util.ContexteTagUtil.DEFAUT"/>
            </list>
        </property>
    </bean>
</property>

Ordonancement des tags

Dans certains cas, il est nécessaire d'ordonnancer l'interprétation des tags : par construction, il est possible de générer des tags imbriqués

C'est le cas notamment du tag "centre d'intêret" qui peut-être utilsé comme paramètre du tag liste de fiche. Afin d'obtenir une interprétation correcte des tags, le tag "centre d'intêret" doit être interprété avant le tag "liste de fiches"

Cet ordonnancement est controlé par l'attribut "order" lors de la déclaration du tags dans le fichier de contexte, par exemple:

    ...
        <!--TAGS CENTRES D'INTERET -->
<bean id="tagCentresinteret" class="com.kportal.extension.module.plugin.toolbox.DefaultPluginTag">
    <property name="identifiantTag" value="[centresinteret]"/>
    <property name="extracteur" ref="defautExtracteurTag" />
    <property name="order" value="#{TAG_DEFAULT_ORDER-100}" />
    <property name="interpreteur">
        <bean class="com.kportal.tag.interpreteur.impl.InterpreteurCentresinteret"/>
        ...

Par défaut, tous les tags ont un ordre positionné à "1000". 4 tags ont été priorisés spécifiquement pour être interprétés prioritairement :

  1. Le tag [centresinteret], ordre=900
  2. Le tag [media;video;], ordre=950
  3. Le tag [media;audio;], ordre=950
  4. Le tag [media;pdfviewer;], ordre=950

Modifier le fichier

Limitation de la taille des fichiers (upload)

Le mécanisme de limitation de la taille des fichiers téléchargés a été amélioré afin de donner plus de possibilité de contrôle en fonction du contexte.

  • La propriété fichiergw.maxsize est conservée.
  • Un nouveau niveau de contrôle permet d'ajouter le code d'une extension K-Sup afin de spécifier une taille propre aux fichiers uplodés dans les processus de l'extension.
    Par exemple, la propriété fichiergw.uas.maxsize permet de limiter la taille des fichiers téléchargés dans les templates de site de l'usine à site.

De la même manière, on pourrait limiter la taille des fichiers ajouté aux formulaires en ajoutant la propriété fichiergw.formulaire.maxsize dans le env.properties.

PropriétéDescriptionValeurs possiblesValeur par défaut
fichiergw.maxsizeDéfinit la taille limite des fichiers uploadés par défaut.un nombre (en Ko)50000
fichiergw.uas.maxsizeDéfinit la taille limite des fichiers uploadés dans l'usine à sites.un nombre (en Ko)5000
fichiergw.<<EXT>>.maxsizeDéfinit la taille limite des fichiers uploadés dans l'extension K-Sup <<EXT>>.un nombre (en Ko)sans objet

Note: Les medias téléchargés dans la médiathèque (Processus SAISIE_MEDIA) sont gérés par un mécanisme propre à la médiathèque de contrôle basé sur le type de la ressource.

Modifier le fichier

Configuration des toolbox

Documentation fonctionnelle

La configuration d'une toolbox peut être définie à plusieurs niveaux :

  • avec l'attribut configuration du composant toolbox
  • en fonction du processus, sous forme de clé {nom_processus}, par exemple SAISIE_PAGELIBRE=/adminsite/ckeditor/configurations/styleParagraphe.json.
  • en concaténant le nom du processus et le nom du champ, sous forme de clé {nom_processus}.{nom_champ}. Par exemple SAISIE_PAGELIBRE.COMPLEMENTS=/adminsite/ckeditor/configurations/styleParagraphe.json.
  • en fonction du nom du champs, sous forme de clé {champ}, par exemple COMPLEMENTS=/adminsite/ckeditor/configurations/styleParagraphe.json. C'est utilisé notamment pour les champs communs à plusieurs fiches, comme les encadrés.

C'est la configuration la plus précise qui sera utilisée. Pour la page libre, on a la configuration suivante dans pagelibre_ckeditor.properties :

CKEDITOR_DEFAULT_PATH=/adminsite/ckeditor/configurations
SAISIE_PAGELIBRE=${CKEDITOR_DEFAULT_PATH}/styleParagraphe.json
SAISIE_PAGELIBRE.COMPLEMENTS=${CKEDITOR_DEFAULT_PATH}/defaultConfig.json

Cette configuration signifie que toutes les toolbox de la fiche page libre ont la configuration styleParagraphe.json, sauf la toolbox du champ COMPLEMENTS.

Si aucune configuration n'est définie, c'est la configuration par défaut définie dans ckeditor.properties qui sera utilisée :

DEFAULT=${CKEDITOR_DEFAULT_PATH}/defaultConfig.json

Pour ajouter une toolbox dans une jsp, on utilise le composant toolbox. Exemple du champ "Informations complémentaires" de la fiche page libre :

<components:toolbox extension="pagelibre" fieldName="COMPLEMENTS" label="BO_PAGELIBRE_COMPLEMENTS" min="0" max="16000" editOption='<%= FormateurJSP.SAISIE_FACULTATIF %>'/>

Surcharge d’une configuration dans un projet

Le chargement des fichiers *.properties servant à surcharger la configuration des toolbox suit un mécanisme particulier. Il nécessite l’utilisation d’un bean implémentant CkeditorConfigurer, permettant notamment de définir le format du nom des fichiers à rechercher. Pour effectuer une surcharge, il faut d’abord connaître la clé de la configuration à modifier. Exemple : pour surcharger CONTENU_ENCADRE dans l’extension article, il faut indiquer cette clé dans un fichier properties adapté.

  • Si la configuration est spécifique à une fiche, on utilise le format suivant : nomextension_ckeditor-override.properties → visible uniquement par l’extension concernée.

  • Si la configuration est commune à toutes les fiches on utilise le format suivant : application_nomextension_ckeditor.properties → visible par le core.

Dans tous les cas, la configuration définie dans le projet prend le pas sur celle du produit (extension ou koreparent).


Tutoriels

Surcharge d’une configuration spécifique à l’extension article : description

Dans le cadre d'un projet, on souhaite modifier la toolbox du champ Description (élément spécifique à Article, défini dans son saisie.jsp).

Objectif : retirer l’élément Accordéon.

  1. Création du fichier : src/main/resources/article_ckeditor-override.properties → il sera pris en compte uniquement par l’extension Article.

  2. Clé concernée (d’après CkeditorConfigurerUtils) : CORPS.

  3. Contenu du fichier :

    SAISIE_ARTICLE.CORPS=/adminsite/ckeditor/configurations/styleParagrapheSpe.json
    SAISIE_ARTICLE_FRONT.CORPS=/adminsite/ckeditor/configurations/styleParagrapheSpe.json
    
  4. Le style par défaut est styleParagraphe. On crée donc une variante styleParagrapheSpe.json, sans Accordéon, dans le dossier src/main/webapp/adminsite/ckeditor/configurations.

Cette configuration spécifique sera utilisée en priorité grâce à la surcharge réalisée.


Surcharge d’une configuration commune : contenu encadré

Toujours pour un projet, on souhaite cette fois retirer l’élément Onglet de la toolbox contenu encadré, qui n’est pas spécifique à Article (défini dans fiche_bas.jsp de Koreparent).

  1. Création du fichier : src/main/resources/application_article_ckeditor.properties → visible par le core.

  2. Clé concernée : CONTENU_ENCADRE.

  3. Contenu du fichier :

    SAISIE_ARTICLE.CONTENU_ENCADRE=/adminsite/ckeditor/configurations/styleParagrapheSpe2.json
    SAISIE_ARTICLE_FRONT.CONTENU_ENCADRE=/adminsite/ckeditor/configurations/styleParagrapheSpe2.json
    
  4. Le style par défaut étant styleParagraphe, on crée une version adaptée styleParagrapheSpe2.json (sans Onglet) dans le même répertoire de configuration que les styles existants de Koreparent.

Cette configuration spécifique sera utilisée en priorité grâce à la surcharge réalisée.

Modifier le fichier

Tiles

Présentation

Tiles est utilisé principalement en BO pour insérer des fragments de JSP. Il est également utilisé dans les front legacy (pour search par exemple)

La classe java com.kosmos.context.CommonTilesConfig porte la configuration de tiles.

Les fichiers de configuration sont scannés aux emplacements suivants

  • /WEB-INF/**/tiles*.xml
  • /**/extensions/**/WEB-INF/**/tiles*.xml
  • /**/legacy/tiles*.xml
  • /WEB-INF/**/*-tiles.xml

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
tiles.configuration.refresh.activationActive le controle du rechargement de la configurationtrue : rechargement activé
false : rechargement desactivé
false

Modifier le fichier

Template « Site web »

Objectif

Le template « Site web » est un layout générique fourni par koreparent pour construire une page d’accueil en une colonne et 15 lignes, simple à personnaliser côté projet.

Définition

  • Déclaration du layout et des slots: koreparent/layout/src/main/resources/core-layout.xml
  • Bean du layout: siteWeb (com.kosmos.layout.grid.impl.DragNDropGrid)
  • Liste des slots: bean slotsTemplateSiteWeb
  • Par défaut, chaque slot accepte les types ToolboxCardBean et SearchCardBean

Structure et slots

  • Le template expose 15 slots verticaux numérotés de 0 à 14 (soit 15 positions).
  • Chaque slot est un bean DragNDropSlot nommé siteWeb-slotX.
  • Pour chaque slot, la liste des types de cards autorisées est fournie par un bean CardClassList nommé allowedCardTypesTemplateSiteWeb-slotX.

Extrait simplifié de core-layout.xml:

<!-- Liste par défaut des cards autorisées pour le template -->
<bean id="defaultAllowedCardTypesTemplateSiteWeb" class="java.util.ArrayList" abstract="true">
    <constructor-arg type="java.util.Collection">
        <list>
            <value>com.kosmos.layout.card.bean.ToolboxCardBean</value>
            <value>com.kosmos.layout.card.bean.SearchCardBean</value>
        </list>
    </constructor-arg>
</bean>

        <!-- Slot 0 (même principe pour 0..14) -->
<bean id="allowedCardTypesTemplateSiteWeb-slot0" class="java.util.ArrayList" parent="defaultAllowedCardTypesTemplateSiteWeb"/>

<bean id="siteWeb-slot0" class="com.kosmos.layout.slot.impl.DragNDropSlot">
<property name="allowedCardTypes" ref="allowedCardTypesTemplateSiteWeb-slot0"/>
<property name="key" value="ba204506-dd15-4ee0-b79b-191260da31b1"/>
<property name="row" value="0"/>
<property name="column" value="0"/>
<property name="colSpan" value="1"/>
<property name="rowSpan" value="1"/>
</bean>

Règles de personnalisation des cards autorisées

  • La liste par défaut des types de cards autorisées pour le template est defaultAllowedCardTypesTemplateSiteWeb, c'est une définition abstraite donc non instanciée, elle n'est ainsi pas extensible via le listToAddBean.
  • Chaque instance de liste pour un slot (allowedCardTypesTemplateSiteWeb-slot0-slot14) est initialisée depuis cette liste abstraite.

Il est possible de :

  • surcharger la liste abstraite pour impacter tous les slots du template
  • ajouter/retirer des types uniquement pour un slot donné

Cas d’usage et exemples

  1. Modifier globalement les types autorisés (tous les slots) en surchargeant defaultAllowedCardTypesTemplateSiteWeb
<!-- Dans le ApplicationContext.xml du projet -->
<bean id="defaultAllowedCardTypesTemplateSiteWeb" class="java.util.ArrayList" abstract="true">
    <constructor-arg type="java.util.Collection">
        <list>
            <!-- Conservez celles du socle et ajoutez les vôtres -->
            <value>com.kosmos.layout.card.bean.ToolboxCardBean</value>
            <value>com.kosmos.layout.card.bean.SearchCardBean</value>
            <value>com.kosmos.actualite.card.ActualiteCardBean</value>
        </list>
    </constructor-arg>
</bean>

Effet : tous les slots (0..14) accepteront ToolboxCardBean, SearchCardBean et ActualiteCardBean.

  1. Ajouter une card uniquement sur un slot précis en faisant un listToAddBean
<!-- Ajoute une card seulement sur le slot 7 -->
<bean id="addDemarcheCardBeanToTemplateSiteWeb-slot1" class="com.kportal.core.context.ListToAddBean">
    <property name="idBeanToMerge" value="allowedCardTypesTemplateSiteWeb-slot1"/>
    <property name="add">
        <list value-type="java.lang.Class">
            <value>com.univ.objetspartages.card.DemarcheCardBean</value>
        </list>
    </property>
</bean>

Effet : seul siteWeb-slot1 acceptera DemarcheCardBean en supplément des autres types définis.

Modifier le fichier

Module de recherche avec Opensearch

Ce module gère l'ensemble du mécanisme permettant de mettre en place le moteur de recherche sur le produit K-Sup.

Le moteur de recherche est basé sur l'utilisation du serveur Opensearch dans la version 2.4.0

Dans ce module vous trouverez :

  • La configuration de la communication avec le serveur de données (Opensearch).
  • Le mécanisme d'indexation des données.
  • Les méthodes pour constituer les requêtes et les exécuter.

Les notes suivantes décrivent le fonctionnement général intégré à K-Sup. Vous trouverez aussi des informations pour surcharger certaines parties du comportement.

Infrastucture et intégration d'Opensearch

Fonctionnement

Fonctionnement général

Le moteur Opensearch est un serveur RESTful nativement scalable. Ce moteur expose des API REST pour échanger avec lui. K-Sup va interagir avec ce moteur en utilisant les différentes API REST exposées.

Opensearch utilise une base de données NoSQL orientée document. Les objets manipulés sont donc des documents.

API Java

Opensearch propose de plus des librairies Java permettant de manipuler facilement les objets échangés :

  • Pour la constitution des requêtes de recherche : query, agrégation, highlight
  • Pour l'utilisation des outils exposés : percolateur, santé du serveur ...
  • Pour la manipulation des objets retournés : réponse, highlight ...

Deux librairies sont publiées :

  • Java Low Level REST Client : permettant la communication via HTTP avec le serveur
  • Java High Level REST Client : surcouche de la librairie précédente, mais exposant les APIs métier.

Nativement, K-Sup utilise un moteur de recherche mono-serveur (configuré avec un seul shard), mais il est possible de modifier ce comportement pour communiquer avec un cluster Opensearch par exemple ( ou pour ajouter des shards ou des réplicas).

Afin de fonctionner, le serveur Opensearch nécessite l'installation de plugins :

Environnement de production

L'installation d'un serveur Opensearch est décrite dans la documentation se trouvant sur le site de la documentation K-Sup : Serveur Opensearch.

Attention cette documentation n'est pas à jour car pointe sur un environnement 2.4.6 (alors que la présente version est en 7.9).

Environnement de développement

Afin de pouvoir utiliser la recherche sur un poste de développement, il faut avoir a disposition un serveur Opensearch déployé.

Si vous avez besoin de déployer un serveur sur votre poste, vous pouvez utiliser le conteneur docker suivant : ksup-docker-opensearch. Vous trouverez sur le repository la documentation d'utilisation de ce conteneur.

Déclaration du serveur sur un poste K-Sup

Dans les fichiers de properties de votre projet, il faut déclarer le serveur Opensearch cible. Pour se faire, il faut créer un fichier env_opensearch.properties dans le répertoire storage de votre application et déclarer à minima la propriété suivante search.nodes pour indiquer le hostname du serveur Opensearch ainsi que son port :

search.nodes=localhost:32787

Modification du comportement K-Sup

Propriétés exposées

CléValeur possibleValeur DéfautCommentaire
cluster.nameStringcoreNom du cluster
nameString${cluster.name}.node.defaultNom du noeud
index.number_of_shardsint1Nombre de shard de l'index par défaut
index.number_of_replicasint0Nombre de replicas de l'index par défaut
http.enabledbooleantrueActivation du HTTP par défaut
client.transport.sniffbooleantrueHeartbeat du client
discovery.zen.ping.multicast.enabledbooleantrueHeartbeat du client

Indexation

Fonctionnement

Les documents présents dans Opensearch sont sérialisés au format json par K-Sup et sont ensuite poussés via l'API du serveur Opensearch. Seules les fiches sont aujourd'hui sérialisées au travers de l'objet FicheIndexDocument. Cet objet permet lors de la sérialisation, de dénormaliser le modèle objet des fiches afin de stocker en plus des données propre de la fiche :

  • Les différentes rubriques sur lesquelles la fiche est visible : rubrique de rattachement et multi-publication.
  • Les plugins liés à la fiche
  • Les ressources attachées : médias issus de la médiathèque ou non.
  • Différentes informations techniques
    • Code de la fiche.
    • ID du metatag lié à la fiche.
    • Type de fiche.
    • Date de publication de la fiche.
    • Si la fiche est visible en front offic (en ligne, dans un site actif ...).
    • Les contrôles d'accès : Champ utilisé pour effectuer avec une regexp la vérification des droits sur la fiche.
  • Liste des champs utilisés pour les agrégations transverses entre chaque fiche (thématique).

L'indexation des fiches est effectuée de différentes manières :

  • au fil de l'eau lors de l'enregistrement d'un contenu.
    • si un contenu modifié est référencé dans d'autres contenus, ces contenus parents seront mis à jour afin d'avoir les données en phase. Ces traitements sont réalisés via des jobs SpringBatch. Les contenus écoutés de cette manière sont :
      • Les fiches enfant : fiche structure par exemple.
      • Les libellés.
      • Les utilisateurs.
      • Les rubriques.
    • Les traitements d'indexation au fil de l'eau sont réalisés via un bus de message déclenché lors d'évènements métiers (enregistrement). Ces messages sont gérés via le framework Spring Integration
  • de manière globale lors de l'exécution du script automatisé indexerJobModule Indexation complète des contenus.

Afin de sérialiser les objets et leurs attributs avec le bon type (Texte, date, booléen ...), Opensearch utilise des templates. Il faut donc décrire comment chaque champ doit être indexé.

Opensearch met à disposition 2 types de templates :

  • index_template : template utilisé pour décrire le mapping d'un template
  • component_template : fragment de template mutualisé entre différents index_template.

Dans K-Sup il y a autant d'index que de langues (et donc autant de index_template). Les templates sont chargés à la création de chaque index.

Description des templates

Les templates décrivant l'indexation des données K-Sup sont placés dans le répertoire /resources/com/kosmos/search/mapping. Le répertoire index_template contient les fichiers décrivant les indexes et le répertoire component_template contient les fragments.

Description des fichier index_template

Les fichiers index_template décrivent les analyzers utilisés pour les différentes langues. Il y a un ainsi fichier par langue. Ces templates vont décrire les analyzer utilisés pour chaque langue :

  • Tokenizer utilisés
  • Filtres spécifiques à la langue : stemming, stop words ...

Les analyzer de chaque langue sont toujours écrits de la même manière :

  • un analyzer nommé analyzer_core_xx décrivant l'analyze par défaut des données stockées.
  • un analyzer nommé analyzer_core_raw_xx décrivant l'analyze des données avec peu de traitement.
    • Les tokens seront ensuite stockés avec un chemin préfixé par .raw*.
  • un analyzer nommé analyzer_core_raw_stemmer_xx l'analyze des données sans stemming.
  • un analyzer nommé analyzer_core_ngram_xx décrivant l'analyze des données avec l'utilisation du filtre edgengram.
    • Les tokens seront ensuite stockés avec un chemin préfixé par .ngram.

Plusieurs mapping sont ensuite décrits dans ce fichier

Champ fulltext

Afin de limiter le nombre de champs sur lesquels est effectuée la recherche un champ nommé fullText est créé en plus des champs de la fiche. Ce champ va contenir toutes valeurs de tous les champs candidats à la recherche fullText. Dans l'index, nous aurons donc l'ensemble des champs et ce champ. Il sera possible de requêter ainsi de manière indifférente sur ce champ ou sur un champ individuel (pour ajouter un boost particulier sur un champ par exemple).

Mapping dynamique

Un mapping dynamique est aussi présent pour détailler comment sérializer les champs de type String de l'application.

Ce mapping dynamique doit décrire :

  • le champ par défaut utilisant l'analyzer analyzer_core_xx
    • Ce champ doit aussi contenir l'instruction pour copier les données dans le champ full_text.
  • un champ raw utilisant l'analyzer analyzer_core_xx
  • un champ ngram utilisant l'analyzer analyzer_core_ngram_xx Afin d'effectuer les highlights avec l'algorithme (plus performant), il est nécessaire de mettre l'attribut "term_vector": "with_positions_offsets" pour chaque analyzer.

Description des fichiers component_template

Ces fichiers contiennent les fragments mutualisés entre les différentes langues. Ils décrivent notamment le mapping de certains champs comme ceux que l'on souhaite exclure du mapping par défaut :

  • code de fiche
  • date ...

Indexation par batch

Lors de certaines modifications des batchs sont déclenchés afin de mettre à jour les données présentes dans les indexes Opensearch. Ces batches réalisés avec Spring-Batch sont déclarés dans le fichier search-batch-job.xml. Pour chaque élément modifié une requête est effectuée pour récupérer l'ensemble des éléments liés à modifier. Ces requêtes sont déclarées en utilisant un template mustache. Les items lus sont ensuite modifiés et de nouveau sérialisés dans le serveur Opensearch. Exemple de requête :

<bean id="searchFicheMetatagItemReader" parent="searchAbstractItemReader" scope="step">
		<property name="template">
			<bean class="org.opensearch.script.mustache.SearchTemplateRequest">
				<property name="script">
					<value>
						{
							"query":{
								"query_string" : {
									"fields": [
										"*.fiche_id_metatag"
									],
									"query": "{{idFicheMetatag}}"
								}
							}
						}
					</value>
				</property>
				<property name="scriptType" value="INLINE"/>
				<property name="scriptParams">
					<map>
						<entry key="idFicheMetatag" value="#{jobParameters['job.id_fiche_metatag']}" />
					</map>
				</property>
			</bean>
		</property>
	</bean>

Indexation des dépendances

Lors de l'indexation d'un contenu, les dépendances du contenu seront aussi sérialisées (libellé, fiche, rubrique, utilisateur). Ces serialisations sont déclenchées par l'ajout d'annotation sur les attributs des beans décrivant les objets (héritant de AbstractFicheBean)

Sérialisation des utilisateurs

La sérialisation est activée en ajoutant l'annotation @User sur l'attribut.

@User
protected String codeValidation = StringUtils.EMPTY;

Une fois sérialisé, un utilisateur est de la forme suivante :

"codeValidation": {
    "user_birthday": null,
    "user_ldap_code": "",
    "user_mail": "admin@xxx.fr",
    "user_first_name": "Administrateur",
    "user_code": "admin",
    "user_id": 1,
    "user_name": "K-Sup",
"user_gender": null
},

Sérialisation des rubriques

La sérialisation est activée en ajoutant l'annotation @Rubrique sur l'attribut.

@Rubrique
protected String codeRubrique = StringUtils.EMPTY;

Une fois sérialisé, un utilisateur est de la forme suivante :

"codeRubrique": [
    {
      "rubrique_category": "0000",
      "rubrique_value": "Fonctionnalités éditoriales",
      "rubrique_code": "1404583540039",
      "rubrique_id": 16
    }
],

Sérialisation des libellés

La sérialisation est activée en ajoutant l'annotation @Label sur l'attribut.

@Label(type = "04")
protected String thematique = null;

Cette anotation prend 3 paramètres :

  • type : Définit le type de Libelle (issu de la table LIBELLE).
  • strategy : Stratégie de mapping permettant d'associer le code de libellé avec une valeur. Il y a deux valeurs possibles :
    • ServiceLabelStrategy (stratégie par défaut) : stratégie utilisée pour les libellés stockés en base de données.
    • DatLabelStrategy : stratégie utilisée pour les libellés stockés dans les fichiers .dat. Cette propriété est obligatoire si on utilise la stratégie DatLabelStrategy.
  • contexte : Contexte d'extension (extension dans laquelle se trouve le libellé)

Une fois sérialisé, un libellé est de la forme suivante :

"thematique": [
    {
        "label_value": "Thématique 1",
        "label_id": 1000035,
    	"label_code": "THEM"
    },
    {
        "label_value": "Administratif",
        "label_id": 1000010,
    	"label_code": "01"
    },
    {
    	"label_value": "Sports-Loisirs",
      	"label_id": 1000014,
    	"label_code": "05"
	}
],

Sérialisation des fiches

La sérialisation est activée en ajoutant l'annotation @Fiche sur l'attribut.

@Fiche(contexte = "ofin", nomObjet = "annuaireksup")
private String codeResponsable = null;

Cette annotation prend 2 paramètres :

  • nomObjet : nom de l'objet cible du rattachement.
  • contexte : contexte de l'extension dans laquelle se trouve l'objet cible. Afin de ne pas sérialiser l'ensemble de la base de données, la profondeur de sérialisation est paramétrable et est limitée par défaut à 2 niveaux de profondeur. Pour les données écartées, leurs identifiants sont sérialisés (code, langue, état).

La fiche structure d'une fiche est sérialisée. Mais la fiche structure parente de la fiche structure ne l'est pas. Seules ses identifiants sont sérialisée.

Indexation des toolboxes

Afin de sérialiser les toolboxes, il faut que l'annotation @GetterAnnotation soit placé sur le getter de l'attribut souhaité.

@GetterAnnotation(isToolBox = true)
protected String getResume() {
    return this.resume;
}

Cette annotation indique que le contenu indexé provient d'une toolbox, il va donc interpréter les tags de toolbox et le code HTML au lieu de les mettre en brut. Lors de la serialisation, les balises HTML sont filtrées et ne sont donc pas présentes dans le document Opensearch. La sérialisation est effectuée par la classe ToolboxSerializer.

Indexation des plugins

Les plugins sont indexés dans le champ plugins de la fiche. Les données sont sérialisées sont à intégrer dans un bean héritant de PluginIndexableBean généré par la méthode generatePluginIndex du Plugin (héritant de IPluginFiche). L'implémentation par défaut de DefaultPluginFiche return null. Il faut donc implémenter un traitement spécifique pour chaque plugin en surchargeant la méthode generatePluginBean. Le bean PluginBean indexé peut utiliser les annotations de sérialisation (@Label, @User, ...) afin de sérialiser des objets liés comme pour les fiches.

Indexation des medias

L'indexation des médias est réalisé lors de la sérialisation des fiches. Tous les médias sont sérialisés dans le champ resources de la fiche.

Ajouter un champ additionnel à l'indexation d'une fiche

Il est possible d'indexer des données additionnelles lors de l'indexation d'une fiche en déclarant un bean de type AdditionalSearchField. Lors de l'indexation d'une fiche, l'ensemble des beans de type AdditionalSearchField sont récupérés et ajoutés dans un objet additionalProperties.[TYPE_FICHE] dans l'index de la fiche.

"additionalProperties": {
    "formation": {
      "libelleAffichable": "Master Sciences du Médicament"
    }
}

Pour créer une donnée additionnelle, il faut créer une classe qui étende AdditionalSearchField. La classe doit surcharger la méthode serialize pour définir le contenu du champ. Exemple AdditionalFieldLibelleAffichable :

public class AdditionalFieldLibelleAffichable extends AdditionalSearchField {

    private static final long serialVersionUID = -7342698426096788996L;

    @Override
    public void serialize(final AbstractFicheBean abstractFicheBean, final JsonGenerator gen, final SerializerProvider serializers) throws IOException {
        final ServiceFiche serviceFiche = ServiceManager.getServiceForBean((abstractFicheBean.getClass()));
        if (serviceFiche != null) {
            gen.writeStringField(getFieldName(), serviceFiche.getLibelleAffichable(abstractFicheBean));
        }
    }
}

Puis ajouter la déclaration du bean dans le contexte du projet en déclarant le nom que portera le champ dans l'index :

<bean id="additionalFieldLibelleAffichable" class="com.kosmos.search.index.mapper.AdditionalFieldLibelleAffichable">
    <property name="fieldName" value="libelleAffichable"/>
</bean>

Modification du comportement K-Sup

Propriétés exposées

CléValeur possibleValeur DéfautCommentaire
search.inclusion_objetChaîne de caractères : Code d'objet séparés par des , (exemple : CODE_OBJET1,CODE_OBJET2)videListe des codes objets a indexer (exécutée avant search.exclusion_objet). Si vide alors tous les objets sont pris en compte.
search.exclusion_objetChaîne de caractères : Code d'objet séparés par des ,videListe des objets à exclure de l'indéxation
search.restriction_etatChaîne de caractères : Code des états séparés par des ,0005,0006noms des états à exclure au moment de l'indexation
search.read.scroll.timeoutint10Durée du scroll Opensearch
search.read.scroll.timeout.unitTimeUnitMINUTESUnité de la durée du scroll Opensearch
search.batch.indexation.commit-intervalint50commit-interval pour le job de réindexation complet
search.batch.indexation.throttle-limitint4nombre maximum d'exécution en parallèle pour le job de réindexation complet (nombre de thread)
search.batch.indexation.skip-limitint10000nombre maximum d'éléments ignorés pour le job de réindexation complet
batch.indexation.pagesizeint10nombre de documents récupéré par le reader pour le job de réindexation complet
search.batch.indexation.fiche.metatag.commit-intervalint10commit-interval pour le job de réindexation suite à la modification d'une fiche
search.batch.indexation.fiche.metatag.skip-limitint10000nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'une fiche
search.batch.indexation.user.commit-intervalint10commit-interval pour le job de réindexation suite à la modification d'un utilisateur
search.batch.indexation.user.skip-limitint10000nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'un utilisateur
search.batch.indexation.rubrique.commit-intervalint10commit-interval pour le job de réindexation suite à la modification d'une rubrique
search.batch.indexation.rubrique.skip-limitint10000nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'une rubrique
search.batch.indexation.label.commit-intervalint10commit-interval pour le job de réindexation suite à la modification d'un label
search.batch.indexation.label.skip-limitint10000nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'un label
search.batch.indexation.media.commit-intervalint10commit-interval pour le job de réindexation suite à la modification d'un média
search.batch.indexation.media.skip-limitint10000nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'un média

Ajout de template

Ajout d'une langue

Si votre projet nécessite le besoin d'ajouter une langue, il suffit d'ajouter un fichier nommé template_xx.json dans le répertoire /resources/com/kosmos/search/mapping/index_template de votre application.

Il faudra ensuite décrire l'ensemble des éléments nécessaires à la description des mappings.

Pour cela il faut s'inspirer des templates existant : template_fr.json.

Ajout d'un template spécifique

Si votre projet a besoin de décrire un template spécifique pour un objet que vous avez à sérialiser, il faut ajouter un fichier dans le répertoire : /resources/com/kosmos/search/mapping/component_template de votre application.

Ajout de serializer spécifique

Lors de la sérialisation des objets, il est possible de déclarer un serializer spécifique afin d'effectuer un traitement particulier. Il faut alors l'enregistrer dans le fichier de contexte de l'extension : Création du serializer :

public class CustomIndexSerializer extends StdSerializer<CustomIndexableBean> {
 
    public CustomIndexSerializer() {
        super(CustomIndexableBean.class);
    }
 
    /**
     * Sérialisation  des données.
     */
    @Override
    public void serialize(final CustomIndexableBean customBean, final JsonGenerator jgen, final SerializerProvider serializers) throws IOException, JsonProcessingException {
        jgen.writeStartObject();
        //TODO serialize specific data
        jgen.writeEndObject();
    }
 
}

Déclaration du serializer dans le fichier de contexte de l'extension (ExtensionContext.xml) :

<bean id="customModule" class="com.fasterxml.jackson.databind.module.SimpleModule">
    <constructor-arg type="java.lang.String" value="customModule"/>
    <property name="serializers">
        <bean id="agendaSimpleSerializer" class="com.fasterxml.jackson.databind.module.SimpleSerializers">
            <constructor-arg>
                <list>
                    <bean id="customStdSerialize" class="com.kosmos.agenda.index.CustomIndexSerializer"/>
                </list>
            </constructor-arg>
        </bean>
    </property>
</bean>
 
<!-- Ajout du module de serialisation aux serializers du core -->
<bean id="coreSearchMapper" class="com.kportal.core.context.MergedModulesJacksonBean">
    <property name="idBeanToMerge" value="coreSearchMapper"/>
    <property name="idExtensionToMerge" value="core"/>
    <property name="modules">
        <list>
            <ref bean="customModule"/>
        </list>
    </property>
</bean>

Indexation d'une fiche spécifique

Pour qu'une fiche soit indexable, il faut :

  • que l'objet FicheUniv représentant la fiche soit indexable. Il faut vérifier que l'attribut isIndexable ne soit pas à false @FicheAnnotation(isIndexable = false) (il est à true par défaut).
  • ajouter les différentes annotations (@Label, @User ...) sur les attributs du bean héritant de AbstractFicheBean afin d'ajouter la sérialisation des objets liés.

Indexation des plugins

Pour indexer les plugins, il faut :

  • créer un objet héritant IndexablePluginBean détaillant les champs à sérialiser.
  • implémenter la méthode generatePluginIndex dans la classe héritant de IPluginFiche du plugin. Cette méthode alimente le bean IndexablePluginBean.

Recherche

Documentation du controllerSearchServlet

Introduction

La SearchServlet dans le package com.kosmos.search.query.servlet est un contrôleur Spring qui gère les requêtes de recherche. Elle exécute la recherche et interroge les différents AbstractSearchHandler pour déterminer lequel peut prendre en charge la construction de la vue.

Fonctionnement

Méthode doGet

Cette méthode est appelée pour traiter les requêtes GET.

  1. Génération des options de recherche :
  • Les options de recherche sont générées à partir des paramètres de la requête en utilisant searchOptionFeeder.
  1. Appel du service de recherche :
  • Le service de recherche (FulltextSearchFactory) est appelé pour obtenir un ServiceSearcher approprié.
  • Si un ServiceSearcher est trouvé, il exécute la recherche et obtient un SearchResultBean.
  1. Initialisation du contexte de recherche :
  • La méthode initContext initialise un SearchContext avec les informations de la requête, les résultats de la recherche, et d'autres métadonnées.
  1. Gestion des résultats de recherche :
  • Si des résultats de recherche sont obtenus, le servlet parcourt les AbstractSearchHandler disponibles pour trouver celui qui peut gérer la requête et la réponse.
  • Le AbstractSearchHandler approprié est utilisé pour construire et retourner un ModelAndView.
  1. Gestion des erreurs :
  • Si aucun ServiceSearcher ou résultat de recherche n'est trouvé, une exception est levée.

Modifier le fichier

Documentation du Système de Chargement des Templates OpenSearch

Introduction

Cette documentation explique en détail le fonctionnement du chargement des templates OpenSearch, où ils sont stockés et dans quel ordre ils sont chargés.

Architecture du Système de Chargement

Types de Templates

Le système gère deux types principaux de templates OpenSearch :

  1. Component Templates (templates de composants)

    • Emplacement : com/kosmos/search/mapping/component_template/**/*.json
    • Fragments réutilisables de configuration de mapping
    • Chargés automatiquement depuis le classpath
  2. Index Templates (templates d'index)

    • Définis via des beans Spring IndexTemplateReference
    • Templates complets pour la création d'index
    • Peuvent référencer des component templates

Processus de Chargement

Le chargement des templates est orchestré par la classe TemplateLoader, qui est un composant Spring activé au démarrage de l'application. Le processus se déroule en deux phases distinctes :

  1. Chargement des Component Templates

    • Exécuté en premier
    • Scanne le classpath pour trouver tous les fichiers JSON dans le répertoire des component templates
    • Charge chaque template trouvé dans OpenSearch
  2. Chargement des Index Templates

    • Exécuté après les component templates
    • Récupère tous les beans Spring de type IndexTemplateReference
    • Charge chaque template référencé dans OpenSearch

Mécanisme de Découverte des Templates

Découverte des Component Templates

Les component templates sont découverts par un scan du classpath, en utilisant le pattern suivant :

classpath*:com/kosmos/search/mapping/component_template/**/*.json

Ce pattern permet de trouver tous les fichiers JSON dans le répertoire des component templates, y compris ceux qui se trouvent dans des JAR dépendants.

Le processus de découverte utilise une chaîne de responsabilité avec deux lecteurs :

  1. ClasspathFileListReader : Pour lire les fichiers depuis les JARs
    • Bootstrap ClassLoader
      • Classes de la JVM (rt.jar, java.base, etc.)
    • System ClassLoader / Common ClassLoader (Tomcat level)
      • Fichiers JAR dans ${CATALINA_HOME}/lib
  2. FileSystemFileListReader : Pour lire les fichiers depuis le système de fichiers local
    • Webapp ClassLoader (ParallelWebappClassLoader)
      • Fichiers WEB-INF/classes/
      • Fichiers WEB-INF/lib/*.jar

Découverte des Index Templates

Les index templates sont découverts par l'inspection du contexte Spring, en recherchant tous les beans de type IndexTemplateReference. Chaque bean définit :

  • Un nom de template
  • Un chemin vers le fichier JSON du template
  • Une priorité
  • Des patterns d'index
  • Optionnellement, un chemin vers des component templates à utiliser

Ordre de Chargement et Mécanisme de Surcharge

Ordre de Chargement des Fichiers

L'ordre de chargement des fichiers dans le classpath est déterminé par plusieurs facteurs :

  1. Ordre de Découverte

    • Les fichiers sont découverts dans l'ordre du classpath Java
    • Les JARs sont scannés dans l'ordre défini par le classloader
    • Les fichiers du système de fichiers local sont généralement traités en dernier
  2. Déduplication

    • Les templates avec le même nom sont dédupliqués
    • Le premier template trouvé est conservé

Mécanisme de Surcharge

Lorsqu'un projet inclut koreparent en tant que dépendance JAR, il peut surcharger les templates existants de deux façons :

  1. Surcharge par Nom

    • En plaçant un fichier avec le même nom au même chemin relatif
    • Exemple : pour surcharger com/kosmos/search/mapping/component_template/content/template_fiche.json du JAR koreparent, placer un fichier avec le même nom au même chemin dans le projet
  2. Surcharge par Priorité (pour les Index Templates)

    • En définissant un bean IndexTemplateReference avec une priorité plus élevée
    • Les templates avec une priorité plus élevée sont appliqués en priorité par OpenSearch

Diagramme du Processus de Chargement

Diagramme du processus de chargement des templates

Ordre de Priorité des Templates

Component Templates

Pour les component templates, l'ordre de priorité est déterminé par l'ordre de chargement :

  1. Les templates définis dans le projet ont priorité sur ceux définis dans les JARs dépendants
  2. En cas de conflit de noms, le dernier template chargé (selon l'ordre du classpath) écrase les précédents

Index Templates

Pour les index templates, l'ordre de priorité est explicitement défini par le paramètre priority :

  1. Plus la valeur de priorité est élevée, plus le template est prioritaire
  2. En cas d'égalité de priorité, le dernier template chargé a la priorité

Exemple de Surcharge de Templates

Surcharge d'un Component Template

Pour surcharger un component template existant dans koreparent :

  1. Identifiez le template à surcharger, par exemple :

    com/kosmos/search/mapping/component_template/content/template_fiche.json
    
  2. Créez un fichier avec le même nom au même chemin dans votre projet :

    src/main/resources/com/kosmos/search/mapping/component_template/content/template_fiche.json
    
  3. Le template de votre projet sera chargé avant celui de koreparent et sera conservé.

Ajout d'un Nouveau Component Template

Pour ajouter un nouveau component template :

  1. Créez un fichier JSON dans le répertoire approprié :

    src/main/resources/com/kosmos/search/mapping/component_template/custom/mon_template.json
    
  2. Le template sera automatiquement découvert et chargé.

Surcharge d'un Index Template

Pour surcharger un index template existant :

  1. Créez un bean IndexTemplateReference avec une priorité plus élevée :

    @Bean
    public IndexTemplateReference monTemplateIndex() {
        return new IndexTemplateReference(
            "nom_template",
            "chemin/vers/mon_template.json",
            300, // Priorité plus élevée que le template original
            null,
            "pattern_index"
        );
    }
    
  2. Le template avec la priorité la plus élevée sera appliqué par OpenSearch.

Génération des templates dynamiques OpenSearch

Fonctionnement

Au démarrage de l'application, le système scanne tous les beans qui implémentent l'interface TemplateField et génère un template JSON contenant tous les champs déclarés. Ce template est injecté dans OpenSearch sous le nom "template_fiche_dynamique".

Comment déclarer un nouveau champ de template

Vous pouvez déclarer un bean XML dans votre fichier de configuration Spring qui utilise la classe MultiPathKeywordTemplateField ou MultiPathDateTemplateField et spécifier une liste de chemins à mapper. Exemple :

<bean id="monKeywordTemplate" class="com.kosmos.search.mapping.fields.MultiPathKeywordTemplateField">
    <property name="paths">
        <list>
            <value>*.diffusionPublicVise</value>
            <value>*.diffusionPublicViseRestriction</value>
            <value>*.etatObjet</value>
            <value>*.id</value>
        </list>
    </property>
</bean>

<bean id="coreDateTemplate" class="com.kosmos.search.mapping.fields.MultiPathDateTemplateField">
    <property name="match" value="date*" />
</bean>

Chaque extension peut déclarer son propre bean avec sa liste de champs à mapper. Les différents beans sont lus au démarrage pour générer le template. Un nom unique est généré automatiquement pour chaque chemin dans la liste, en supprimant les "." et en convertissant les segments en CamelCase (par exemple, "DiffusionPublicViseTemplate" pour le chemin ".diffusionPublicVise" ou "ActualiteTypeEvenementLabelCodeTemplate" pour le chemin ".actualite.typeEvenement..label_code").

Le mapping peut être effectué sur une liste de chemins paths, ou sur une regex correspondant au nom du champ match.

Surcharger un template dynamique

Pour surcharger un template dynamique il faut effectuer un AttributeToOverrideBean pour redéfinir les paths ou le match du bean de template.

Exemple :

<bean id="coreKeywordTemplateOverride" class="com.kportal.core.context.AttributeToOverrideBean">
   <property name="idExtensionToMerge" value="core"/>
   <property name="idBeanToMerge" value="coreKeywordTemplate"/>
   <property name="attributes">
      <map>
         <entry key="paths">
            <list>
               <value>*toto</value>
            </list>
         </entry>
      </map>
   </property>
</bean>

Classes de base disponibles

  • KeywordTemplateField : Classe de base abstraite pour créer des champs de type keyword
  • DateTemplateField : Classe de base abstraite pour créer des champs de type date
  • MultiPathKeywordTemplateField : Implémentation concrète de KeywordTemplateField pour définir plusieurs chemins via XML
  • MultiPathDateTemplateField : Implémentation concrète de DateTemplateField pour définir plusieurs chemins via XML

Créer une nouvelle classe de base pour un nouveau type de champ

Si vous avez besoin d'un nouveau type de champ, créez d'abord une classe abstraite qui étend AbstractTemplateField et définit les propriétés communes à ce type de champ. Ensuite, créez des implémentations concrètes qui étendent cette nouvelle classe de base.

// Classe de base pour un nouveau type de champ
public abstract class NewFieldTypeTemplateField extends AbstractTemplateField {
    @Override
    public String getFieldType() {
        return "new_field_type";
    }
}

Exemple de template généré

Le template généré aura une structure similaire à celle-ci :

{
  "template": {
    "mappings": {
      "dynamic_templates": [
        {
          "dateTemplate": {
            "match_mapping_type": "long",
            "match": "date*",
            "mapping": {
              "type": "date",
              "format": "strict_date_optional_time||epoch_millis"
            }
          }
        },
        {
          "idTemplate": {
            "match_mapping_type": "string",
            "path_match": "*.id",
            "mapping": {
              "type": "keyword"
            }
          }
        },
        {
          "DiffusionPublicViseTemplate": {
            "match_mapping_type": "string",
            "path_match": "*.diffusionPublicVise",
            "mapping": {
              "type": "keyword"
            }
          }
        },
        {
          "DiffusionPublicViseRestrictionTemplate": {
            "match_mapping_type": "string",
            "path_match": "*.diffusionPublicViseRestriction",
            "mapping": {
              "type": "keyword"
            }
          }
        }
      ]
    }
  }
}

Création du template

Le template est créé au démarrage de l'application via le ComponentTemplateLoader. Voici le processus :

  1. Le DynamicTemplateGenerator génère le template JSON en scannant tous les beans TemplateField
  2. Le ComponentTemplateLoader vérifie si le template existe déjà sur OpenSearch
  3. S'il existe, le loader compare la version existante avec la nouvelle version générée
  4. Si les versions sont différentes ou si le template n'existe pas, le nouveau template est créé/mis à jour sur OpenSearch

Le template est stocké comme un component template nommé "template_fiche_dynamique".

Bonnes Pratiques

  1. Nommage Unique

    • Utilisez des préfixes spécifiques à votre projet pour éviter les collisions de noms
    • Exemple : projet_template_fiche.json au lieu de template_fiche.json
  2. Priorités Cohérentes

    • Utilisez des plages de priorités cohérentes :
      • 0-100 : Templates de base (koreparent)
      • 101-200 : Templates de modules
      • 201+ : Templates spécifiques aux projets
  3. Documentation

    • Documentez les templates que vous ajoutez ou surchargez
    • Indiquez clairement les modifications apportées aux templates existants
  4. Tests

    • Testez vos templates avant déploiement
    • Vérifiez que les surcharges fonctionnent comme prévu

Conclusion

Le système de chargement des templates OpenSearch dans koreparent offre une grande flexibilité pour personnaliser et étendre les fonctionnalités d'indexation. En comprenant l'ordre de chargement et les mécanismes de surcharge, les projets peuvent adapter les templates à leurs besoins spécifiques tout en bénéficiant des fonctionnalités de base fournies par koreparent.

La clé pour une utilisation efficace de ce système est de comprendre que :

  1. Les component templates sont chargés avant les index templates
  2. Les fichiers sont chargés selon l'ordre du classpath Java
  3. Les templates avec le même nom sont dédupliqués
  4. Les index templates peuvent définir des priorités explicites pour contrôler l'ordre d'application

Modifier le fichier

Script de migration flyway du module de recherche

Certaines modifications dans la structure des fiches ou dans la définition des templates nécessitent des actions au niveau du moteur de recherche. A cet effet, le module Search met à disposition deux outils dédiés.

L'AbstractSearchEngineIndexing

Cet outil permet de planifier un job de reindexation totale ou partielle (par type de fiche). Pour utiliser cet outil, il suffit de créer une classe qui étend la classe abstraite et d'implémenter la méthode migrate.

Implémentation pour une indexation complète :

public class V1_2_3_45__reindexation_complete extends AbstractSearchEngineIndexing {

    private static final Integer CHECKSUM = 1234567890;

    @Override
    public Integer getChecksum() {
        return CHECKSUM;
    }

    @Override
    public void migrate(final Context context) throws Exception {
        // On indique qu'il s'agit d'une indexation complète
        scheduleFullIndexation();
        super.migrate(context);
    }
}

Implémentation pour une indexation partielle :

public class V1_2_3_45__reindexation_partielle extends AbstractSearchEngineIndexing {

    private static final Integer CHECKSUM = 1234567890;

    @Override
    public Integer getChecksum() {
        return CHECKSUM;
    }

    @Override
    public void migrate(final Context context) throws Exception {
        // On indique via une collection les codes objet à indexer.
        schedulePartialIndexation("0016", "0002");
        super.migrate(context);
    }
}
  • Si plusieurs ré-indexations complètes sont demandées, une seule sera réellement exécutée.
  • Si plusieurs ré-indexations partielles sont demandées, les types de fiches concernés seront fusionnés et le traitement lancé sur les types de fiches concernés.
  • Si une ré-indexation totale est demandée, les ré-indexations partielles sont ignorées (car inutiles du fait de la ré-indexation totale).

L'AbstractSearchEngineManagement

Cet outil permet d'exposer le client REST du moteur de recherche. Il peut être utilisé par exemple pour supprimer un index devenu inutile.

public class V1_2_3_45__suppression_index extends AbstractSearchEngineManagement {

    private static final Integer CHECKSUM = 1234567890;

    @Override
    public Integer getChecksum() {
        return CHECKSUM;
    }
    
    @Override
    public void migrate(final RestHighLevelClient restClient) {
        final DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("nom_index_à_supprimer");
        restClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
    }
}

Modifier le fichier

Module de statistiques

Présentation

Le module statistiques se base des technologies existantes du core. Lorsque un indicateur est collecté, un message spring-integration est diffusé sur la file channel.core.out.
Une nouvelle file nommée "channel.in.statistics" duplique les messages channel.core.out via un bridge.
Les messages sont routés vers un service activator (le logger) afin d'être matérialisés sur disque ou dans une table de la base de données.

Format de sortie des indicateurs

Le format de sortie est décrit dans cette page

Le module met à disposition le service StatisticsService qui permet de générer des entêtes standardisés. La méthode initMessageHeaders permet d'initiliser de manière standardisée les entêtes. La méthode retourne une map d'entêtes nécessaires aux indicateurs

    ...
final Map<String, Object> headers=statisticsService.initMessageHeaders("NOM_DU_MARQUEUR");
        ...

L'envoi de l'indicateur est réalisé via le service ServiceCorePublisher

    ...
private ServiceCorePublisher publisher;
        ...
        publisher.publish(payload,TARGET_MARKER_XXXXX,null,null,headers);
        ...

La valeur TARGET_MARKER_XXXXX est une chaine de caractère permettant d'aiguiller le message vers un service activator adapté.

Selection d'un endpoint

Les différents message spring integration sont envoyés vers un nouveau channel channel.in.statistics via un bridge.
Un routeur permet de répartir les message vers deux endpoints fournis par le produit.

  1. Le StatisticsLogger qui permet d'écrire dans un fichier de log (type logback). Pour inscrire un indicateur dans le fichier de log, il suffit de valoriser l'entête "target" du message avec la constante StatisticsService.TARGET_MARKER_LOGGER
  ...
      publisher.publish(payload,StatisticsService.TARGET_MARKER_LOGGER,null,null,headers);
      ...
  1. Le StatisticEventsService qui permet d'écrire des indicateurs dans une table de donnée (table STATISTIC_EVENTS).
    Cette table a vocation à cumuler les indicateurs d'action avant de procéder à une aggrégation (comptage) et à leur extraction.
    Pour inscrire un indicateur dans la table STATISTIC_EVENTS, il suffit de valoriser l'entête "target" du message avec la constante StatisticsService.TARGET_MARKER_EVENT
  ...
      publisher.publish(payload,StatisticsService.TARGET_MARKER_EVENT,null,null,headers);
      ...

Modifier le fichier

Format de sortie des indicateurs

Les indicateurs écrit dans le fichier respectent le format suivant

LOGGER.info("{};{};{};{};{};{}", marker, timestamp, client, server, profile, payload);
  • marker est le nom de l'indicateur.
    Le fichier concentre les différents indicateurs collectés.
  • timestamp est la date de la collecte à un des formats ISO-8601
  • client est le code du client.
    Le code est extrait de l'identifiant du bean extension depuis le fichier de contexte du projet.
    À défaut, le système utilise la propriété statistics.client.id (cf core-statistics.properties)
  • server est la jvmRoute configurée.
  • profile est une valeur calculée à partir des droits de l'utilisateur connecté.
    Un ensemble ordonné de bean étendant "AbstractProfileDecider" permette de catégoriser fonctionnellement les utilisateurs en fonction de la présence de certaines permissions.
  • payload est la carge utile de l'indicateur.
    La charge utile dépend de chaque indicateur.
    Elle est composée d'une chaine de type "clé=valeur" séparée par le caractère "&".
    Par exemple, METHOD=GET&URL=http%3A%2F%2Fkdev70.localhost%2F&TIME=3223&SQLCOUNT=258&SQLTIME=181&SQLMAX=8

Modifier le fichier

Calcul d'un profil fonctionnel

Le calcul du profil fonctionnel est réalisé par une liste ordonnée de convertisseur (? extends AbstractProfileDecider).
Le module met à disposition un ensemble de ProfileDecider.

  • anonymousProfileDecider : ce bean détecte les utilisateurs non connectés (AutorisationBean null dans le contexte).
  • webmasterProfileDecider : ce bean détecte les utilisateurs avec la permission "Webmaster".
  • siteManagerProfileDecider : ce bean détecte les utilisateurs avec la permission "Edition de rubrique (C ou M ou S)".
  • contentManagerProfileDecider : ce bean détecte les utilisateurs la permission de création ou traduction et la modification et la suppression sur au moins une fiche hors fiches des extensions formation.
  • contributorProfileDecider : ce bean détecte les utilisateurs avec des permissions.
  • defaultProfileDecider : ce bean détecte les utilisateurs connectés, mais sans permission.

Tous ces beans sont ordonnés via l'utilisation de la propriété "order".
Les extensions peuvent insérer dans la liste leur(s) propre(s) bean(s) de décision.
C'est par exemple le cas des extensions ofin ou offreformation qui ajoutent le bean suivant:

  • programManagerProfileDecider : ce bean détecte les utilisateurs avec la permissions de modification sur la fiche Formation. Le bean est inséré entre les beans contentManagerProfileDecider et contributorProfileDecider.

Pour ajouter un nouveau bean, il suffit

  • soit d'instancier le bean PermissionProfileDecider dans votre fichier de contexte en valorisant les propriétés requiredPermissions et/ou excludedPermissions et order selon vos besoins
  • soit de créer un nouvean bean étendant AbstractProfileDecider en implémentant la méthode String apply(AutorisationBean).
    La méthode apply prend en paramètre l'AutorisationBean de l'utilisateur courant et retourne une chaine de caractère correspondant au profil fonctionnel souhaité.

Modifier le fichier

Intégration projet du module Statistiques

Activation du module

Par défaut, le module "Statistiques" est désactivé.

Avant d'activer le module, il est nécessaire d'obtenir l'accord du client.

Activation programmatique

Afin d'activer les statistiques de manière programmatique, il est nécessaire de le faire au premier démarrage de l'application.

L'activation s'effectue en ajoutant les 3 beans suivant dans le fichier ExtensionContext.xml de votre projet :

Exemple : monProjetExtensionContext.xml

    <bean id="activationTemplateSiteComposant" class="com.kportal.core.context.AttributeToOverrideBean">
        <property name="idExtensionToMerge" value="core"/>
        <property name="idBeanToMerge" value="composantStatistics"/>
        <property name="attributes">
            <map>
                <entry key="etat">
                    <util:constant static-field="com.kportal.extension.module.IModule.ETAT_ACTIF"/>
                </entry>
            </map>
        </property>
    </bean>
    <bean id="activationStatisticsJobModule" class="com.kportal.core.context.AttributeToOverrideBean">
        <property name="idExtensionToMerge" value="core"/>
        <property name="idBeanToMerge" value="statisticJobModule"/>
        <property name="attributes">
            <map>
                <entry key="etat">
                    <util:constant static-field="com.kportal.extension.module.IModule.ETAT_ACTIF"/>
                </entry>
            </map>
        </property>
    </bean>
    <bean id="activationStatisticsTrigger" class="com.kportal.core.context.AttributeToOverrideBean">
        <property name="idExtensionToMerge" value="core"/>
        <property name="idBeanToMerge" value="statisticsTrigger"/>
        <property name="attributes">
            <map>
                <entry key="etat">
                    <util:constant static-field="com.kportal.extension.module.IModule.ETAT_ACTIF"/>
                </entry>
            </map>
        </property>
    </bean>

Activation manuelle

Il est possible d'activer manuellement le module dans l'interface de K-Sup. Mais il est conseillé de réaliser l'activation de manière programmatique.

Dans le backoffice, l'activation du module nécessite les actions suivantes :

  • Depuis le gestionnaire d'extensions, déplier la section "Socle CMS" Gestionnaire d'extension
  • Activer les modules "Statistique" et "Script automatisé de génération des statistiques" Module statistiques
  • Depuis l'écran de gestion des traitement planifiés, activer le traitement planifié "Batch d'extraction des statistiques" Traitement planifié

A partir de la version 7.0, l'applicatif va déterminer lui-même le client en scannant l'ExtensionContext du projet. A défaut de bean Extension dans le projet, c'est la valeur de la propriété statistics.client.id qui est utilisée.

En 6.7,vous devez valoriser la propriété statistics.client.id dans le fichier application_projet.properties de votre projet

Ajout d'un endpoint

L'ajout d'un nouveau endpoint, nécessite la déclaration d'un nouveau channel et d'un bridge depuis channel.in.core
Dans le cadre des statistiques, il est peu probable d'avoir besoin d'un nouveau endpoint. Le cas échéant, voir avec l'équipe produit.

Ajout d'un profil fonctionnel

Le fonctionnement du calcul du profil est décrit dans cette page

Pour ajouter un nouveau profil fonctionnel , il suffit

  • Soit d'instancier le bean PermissionProfileDecider dans votre fichier de contexte en valorisant les propriétés * requiredPermissions* et/ou excludedPermissions et order selon vos besoins
    Par exemple, pour ajouter un profil fonctionnel regroupant les utilisateurs ayant les droits sur la médiathèque,
      ...
      <bean id="mediaManagerProfileDecider" class="com.kosmos.statistics.util.decider.profile.PermissionProfileDecider">
          <property name="profile" value="MEDIAMANAGER"/>
          <property name="order" value="xxxxxx"/>
          <property name="requiredPermissions" value="#{'TECH/pho/C'.split(',')}"/>
      </bean>
      ...
    
    A l'aide de l'attribut order, vous pouvez insérer votre bean dans la liste des deciders à l'endroit adapté à vos besoins.
  • Soit de créer un nouvean bean étendant AbstractProfileDecider en implémentant la méthode String apply(AutorisationBean).
    La méthode apply prend en paramètre l'AutorisationBean de l'utilisateur courant et retourne une chaine de caractère correspondant au profil fonctionnel souhaité.
    ...
    public class XxxxxxManagerProfileDecider extends AbstractProfileDecider {
    
        private static final String XXXXX_MANAGER = "XXXXXMANAGER";
    
        @Override
        public String apply(final AutorisationBean arg) {
            if (arg != null && arg.isXxxxxxManager()) {
                LOGGER.debug("L'utilisateur actuel est .....");
                return XXXXX_MANAGER;
            }
            LOGGER.trace("L'utilisateur actuel n'est pas ....");
            return null;
        }
    }
    ...
    
    Il suffit ensuite de déclarer le bean dans votre fichier de contexte
      ...
      <bean id="siteManagerProfileDecider" class="com.acme.statistics.util.decider.XxxxxxManagerProfileDecider">
          <property name="order" value="42000"/>
      </bean>
      ...
    
    A l'aide de l'attribut order, vous pouvez insérer votre bean dans la liste des deciders à l'endroit adapté à vos besoins.

Ajout d'un indicateur d'action

L'ajout d'un indicateur d'action se fera de manière préférentielle par de l'AOP sur une ou plusieurs méthodes d'un service (ou assimilé) voire d'un DAO dans le cas de certains objets historiques K-Sup.
Selon les cas, on utilisera un aspect de type Before ou Around.

Ajout d'un indicateur HTTP d'accès à une URL

L'ajout d'un indicateur d'action nécessite la simple déclaration d'un filter. Par exemple, pour filtrer tous les appels HTTP au processus MON_PROCESSUS de l'extension MON_EXTENSION, déclarer le bean suivant et l'injecter dans les filtres d'inclusion du marqueur HTTP

    ...
    <bean id="monprocessusHttpMarker" class="com.kosmos.statistics.marker.http.requestfilter.UrlMatchesFilter">
        <property name="uri" value="${SGcontroller.mapping:/servlet/com.jsbsoft.jtf.core.SG}"/>
        <property name="parameters">
            <map>
                <entry key-ref="EXT" value="MON_EXTENSION"/>
                <entry key-ref="PROC" value="MON_PROCESSUS"/>
            </map>
        </property>
    </bean>

    <bean class="com.kportal.core.context.ListToAddBean">
        <property name="idExtensionToMerge" ref="EXTENSION_CORE"/>
        <property name="idBeanToMerge" value="httpMarkerProcessor"/>
        <property name="listToMerge" value="includes"/>
        <property name="add">
            <list>
                  <ref bean="monprocessusHttpMarker"/>
            </list>
        </property>
    </bean>
    ...

Ajout d'un indicateur d'action sur une création / mise à jour d'un bean de type PersitanceBean

Pour les PersitanceBean, le produit met à disposition un aspect générique, ContentUpdateAspect, permettant d'exécuter un code applicatif suite à une interception de méthode. Cet aspect applique une liste de processeurs (cf interface MarkerProcessor) Le bean applique une liste de MarkerProcessor (La liste des processeurs est initialisée par découverte). Chaque MarkerProcessor à la responsabilité de vérifier qu'il s'applique ou non via la méthode accept

    ...
@Override
    public boolean accept(final PersitanceBean before, final PersitanceBean after) {
        return isConditionRemplie && isClassAccepted(before.getClass());
        }
        ...

La seconde méthode permet d'éxécuter un code applicatif

    ...
@Override
    public void process(final PersitanceBean before, PersitanceBean after){
        ...
        Code applicatif permettant de générer l'indicateur
        ...
        }
        ...

Ajout d'un indicateur d'action sur un objet autre qu'un PersitanceBean

  • Déclarer un intercepteur sur la ou les méthodes.
    • La méthode doit être publique et non statique
    • La méthode doit être appelée depuis l'extérieur de la classe — l'AOP ne fonctionne pas sur les appels internes
    • La classe doit être dans le même contextel'AOP ne fonctionne pas sur une classe d'un jar externe
    • La classe doit être un bean du contexte Spring
    • La méthode interceptée doit être dans la classe écoutée, sinon, écouter la classe parente
    • Ne pas réaliser d'interception de type * sur une classe ou un package, mais cibler précisément les méthodes
    ...
    <aop:config proxy-target-class="true">
        <aop:aspect id="serviceXxxxxAspect" ref="xxxxxAspect">
            <aop:pointcut id="xxxxxStatisticsInterceptor" expression="execution(* com.acme.service.ServiceXxxxx.save(..))" />
            <aop:around pointcut-ref="xxxxxStatisticsInterceptor" method="maMethode"/>
        </aop:aspect>
    </aop:config>
    ...
    <bean id="xxxxxAspect" class="com.acme.aspect.XxxxxAspect" parent="abstractStatisticsAspect"/>
    ...
    
  • Créer un nouvel aspect dédié à votre indicateur.
    La classe doit étendre AbstractStatisticsAspect afin de bénéficier du service ServiceCorePublisher de publication de message (publisher) et du service ServiceStatistique.
    ...
    public class XxxxxAspect extends AbstractStatisticsAspect {
    
        public void maMethode(ProceedingJoinPoint call) throws Throwable {
            call.proceed();
            try {
                final Object arg0 = call.getArgs()[0];
                ...
                construction de la payload et des headers
                ...
                publisher.publish(payload, ..., null, null, headers);
            } catch (final Exception e) {
                LOG.error("Impossible de générer le marqueur statistiques", e);
            }
        }
       ...
    }
    

Ajout d'un indicateur de volume (batch)

Packages

Voici la syntaxe des packages a utiliser pour la mise en place des indicateurs sur votre projet :

package com.kosmos.[EXTENSION].statistics.marker.processor;
package com.kosmos.[EXTENSION].statistics.marker.module;
package com.kosmos.[EXTENSION].statistics.marker.volume.counter;

Ajout d'un indicateur de volume sur un type de fiche en ligne

Vous n'avez rien à faire : le produit va détecter les objets partagés et générer les compteurs de fiches en ligne.

Ajout d'un compteur spécifique

Pour ajouter un compteur de volume, il faut créer une classe implémentant com.kosmos.statistics.marker.volume.counter.Counter et déclarer le bean dans le contexte.

Exemple avec le compteur de "document en mode téléchargement" :

package com.kosmos.ged.statistics.marker.volume.counter;

import com.kosmos.ged.context.GedConfig;
import com.kosmos.statistics.marker.volume.counter.Counter;
import com.kosmos.statistics.util.Indicator;
import com.kosmos.statistics.marker.volume.counter.CounterOnlineContent;
import com.univ.objetspartages.bean.DocumentBean;
import com.univ.objetspartages.dao.impl.DocumentDAO;
import com.univ.objetspartages.om.EtatFiche;

import java.util.List;

/**
 * Coumpteur du nombre de fiches Document uniquement en téléchargement.
 */
public class CounterDocumentTelechargement implements Counter {

    private static final String CONTENT = "DocumentTelechargement";

    private DocumentDAO documentDAO;

    @Override
    public Indicator count() {
        Indicator result = new Indicator();
        result.setComponent(GedConfig.ID_EXTENSION);
        result.setMarker(CounterOnlineContent.MARKER);
        final List<DocumentBean> documents = documentDAO.select(" JOIN METATAG m on m.META_ID_FICHE = T1.ID_DOCUMENT and m.META_CODE_OBJET = '0017' and ETAT_OBJET = '" + EtatFiche.EN_LIGNE + "' and META_DOCUMENT_FICHIERGW = '1'");
        result.addToPayload(Indicator.PAYLOAD_CONTENT_TYPE, CONTENT);
        result.addToPayload(Indicator.PAYLOAD_COUNT, documents.size());
        return result;
    }

    public void setDocumentDAO(final DocumentDAO documentDAO) {
        this.documentDAO = documentDAO;
    }
}

<bean id="statisticsDocumentTelechargementCounter" class="com.kosmos.ged.statistics.marker.volume.counter.CounterDocumentTelechargement">
    <property name="documentDAO" ref="documentDAO"/>
</bean>

Modifier le fichier

Insertion du tableau de bord et gestion des jetons d'accès

Intégration du tableau de bord dans K-Sup

Le tableau de bord est basé sur Apache Superset. Superset est hébergé par Kosmos. Pour pouvoir visualiser le tableau de bord, nous avons utilisé le package NPM @superset-ui/embedded-sdk. Il se charge de gérer lui-même l'intégration du tableau de bord dans la page, via une iframe. Il permet également de renouveler automatiquement le jeton d'accès qui permet de visualiser le tableau de bord.

Lorsqu'un utilisateur accède à la page de visualisation des statistiques, du JavaScript se lance pour faire appel à une JSP renvoyant seulement le token d'accès en JSON. Ce token d'accès est soit généré en appelant l'API de Superset, soit donné directement dans les propriétés de l'application. Le tableau de bord est ensuite affichée dans l'iframe grâce au package qui se charge de lui fournir les données dont elle a besoin.

Configuration nécessaire pour le tableau de bord

Afin de pouvoir voir le tableau de bord, il faut renseigner la propriété dashboard.guestToken (jeton généré par Kosmos, voir ci-dessous). Par défaut, le profil Webmaster a la permission de voir le tableau de bord. Pour les autres profils, il faut leur accorder l'accès (permission à donner dans la gestion des rôles).

Gestion de l'accès au tableau de bord

Afin de visualiser le tableau de bord, il faut soit :

  • Générer un jeton d'accès (guest_token) grâce à un utilisateur Superset qui a les droits nécessaires
  • Donner un jeton valide au package embedded-sdk

Remarque : Nous utilisons un seul tableau de bord et un seul serveur Apache Superset pour tous les clients. Ils accèdent au même tableau de bord, mais avec des restrictions d'accès différentes (ils ne voient que les données de leur établissement). Or, seul un utilisateur peut être désigné pour générer les jetons d'accès dans Superset. On ne peut donc pas créer un utilisateur par client et restreindre l'accès aux données directement depuis Superset. K-Sup étant open source, on ne peut pas non plus définir les restrictions d'accès dans le projet, car elles seraient modifiables et les établissements pourraient changer le code pour accéder à d'autres données (toutes les données ou celles d'autres établissements). Le choix fait pour l'instant est que Kosmos génère un jeton d'accès avec des droits qui limitent aux données d'un seul établissement. Il y a ainsi un token à générer par client.

Les properties mentionnées sont dans le fichier src/main/resources/core-statistics.properties.

Avec un jeton d'accès entré manuellement

Si on choisit de saisir manuellement un jeton, il aura été généré au préalable par Kosmos et aura une durée de vie longue (plusieurs mois, voire plusieurs années). Il est renseigné par la propriété dashboard.guestToken.

Les jetons prennent en compte des restrictions d'accès aux données (pour que chaque établissement ne voie que ses données).

Le diagramme suivant détaille ensuite le déroulement de l'affichage du tableau de bord si un jeton est entré manuellement. Le participant "Embed Superset (client)" désigne le package NPM embedded-sdk utilisé automatiquement par le client.

Diagramme de séquence illustrant les appels au package et à Superset si un token d'accès est directement renseigné dans les propriétés de K-Sup

Les tokens sont générés via un script interne à Kosmos dont voici le diagramme de séquence.

Diagramme de séquence illustrant la génération d'un token d'accès avec droits limités par un script interne à Kosmos

Voici la liste des variables les propriétés qui leur correspondent ainsi qu'une valeur d'exemple :

Nom de la variable dans le diagrammeDescriptionPropriété correspondanteValeur d'exemple
idDashboardIdentifiant du tableau de bord qu'on souhaite visualiserdashboard.id4adbc771-5b67-48d7-a656-8447cd4db8f0
RLSRestrictions d'accès aux données du tableau de bordorganisation='Kosmos'
hostUrlURL de l'hébergeur d'Apache Supersetdashboard.provider.hostnamehttps://ksup.analytics.skolengo.com
apiLoginPathPath de l'API d'Apache Superset, qui renvoie un jeton d'accès et un refresh tokendashboard.provider.api.login"/api/v1/security/login"
apiGuestTokenPathPath de l'API d'Apache Superset pour obtenir le guest_token (jeton permettant de visualiser le tableau de bord)dashboard.provider.api.guest_token"/api/v1/security/guest_token"
usernameNom d'utilisateur pour le profil ayant le droit de générer un guest tokendashboard.provider.username
passwordMot de passe de l'utilisateur pour le profil ayant le droit de générer un guest tokendashboard.provider.password
providerProvider du tableau de borddashboard.provider.providerdb
refreshVrai si on souhaite obtenir un refresh token lors de la connexion à l'API, faux sinondashboard.provider.refreshtrue
TYPE_DASHBOARDType de ressource demandée à Superset, variable statique de com/kosmos/statistics/api/superset/model/RessourceForGuestToken.javaTYPE_DASHBOARDdashboard

Avec un jeton d'accès généré puis renouvelé

Pour pouvoir générer un jeton d'accès, il faut disposer du nom d'utilisateur et mot de passe du compte qui a les droits pour générer ce jeton. On peut les renseigner via les propriétés dashboard.provider.username et dashboard.provider.password.

Le package intégrant le tableau de bord se chargera ensuite de faire les appels nécessaires à Superset pour obtenir le jeton. Il sera renouvelé toutes les 5 minutes et peut intégrer des restrictions d'accès aux données (pour que chaque établissement n'ait accès qu'à ses données).

On doit tout d'abord se connecter à l'API de Superset en précisant l'utilisateur, le provider (db ou ldap) ainsi qu'un booléen indiquant si on souhaite obtenir un token de rafraîchissement ou non. Une fois connecté à l'API, on peut demander le guest_token, en précisant le token d'accès (utilisé pour se connecter à l'API), le nom d'utilisateur, le type de ressource à laquelle on souhaite accéder, l'identifiant du tableau de bord auquel on souhaite accéder puis des restrictions d'accès aux données (en choisissant un établissement par exemple).

Le diagramme suivant explique les appels faits par le processus /src/main/java/com/kosmos/statistics/processus/VisualiserStatistiques.java si un utilisateur générant un token est renseigné.

Diagramme de séquence illustrant les appels faits au package et à Superset pour obtenir le jeton d'accès permettant de visualiser le tableau de bord

Les variables utilisées sont les mêmes que celles désignées dans le tableau ci-dessus à l'exception de tokenUrl. Cette variable désigne l'URL de la JSP qui contient le guestToken. En effet, les appels montrés sont déclenchés par un fetch d'une page contenant seulement un guest token JSON, effectué par le JavaScript de ka JSP de visualisation des statistiques.

Modifier le fichier

Paramétrage du module statistique

Surcharge

La surcharge des propriétés peut se faire dans les fichiers de surcharge du socle. En créant un fichier :

  • application_core.properties dans les sources d'un projet
  • env.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
statistics.client.idValeur par défaut du code client (fallback)Chaîne de caractèresDEFAULT
statistics.http.duration.thresholdTemps maximum d'une requête HTTP en ms (une requête ne doit pas dépasser n millisecondes)Entier positif ou -1 si pas de seuil3000
statistics.performance.exclude.uri.start.withPattern d'URL à exclure de l'indicateur de performance (début de l'URL)Début d'un path d'URL/static/,/jsp/styles/,/jsp/images/,/jsp/scripts/
statistics.performance.exclude.uri.end.withPattern d'URL à exclure de l'indicateur de performance (fin de l'URL)Fin d'un path d'URL.js,.css.map,.css,.ttf,.woff,.json,.png
statistics.dirChemin de génération des fichiers de statistiquesPath sur le disque de l'application${storage.dir}/statistics
statistics.batch.filenameNom du fichier généré par le batch d'export des statistiquesNom de fichierstatistics-volume.jsonl
job_params.blacklistListe des paramètres de job à exclure de la payloadListe de chaînes de caractèresfireTime,nextFireTime,previousFireTime,noResumeAfterComplete
messaging.statistics.subscribe.taskexecutor.sizeTaille du pool d'exécution du job des statistiquesEntier positif5
messaging.statistics.subscribe.taskexecutor.maxsizeTaille maximum du pool d'exécution du job des statistiquesEntier positif5
scheduler.statistics.cronExpressionExpression cron pour l'exécution automatisée du script d'export des statistiquesExpression cron0 5 0 1 * ?
statistics.performance.maxhistoryDurée de conservation (en mois) des logs de performancesEntier positif6
dashboard.ui.hideTitleVrai si on veut cacher le titre du tableau de bord, faux sinontrue ou falsetrue
dashboard.ui.hideChartControlsVrai si on veut cacher les contrôles du tableau de bord (rafraîchir les données, exporter le graphique, mettre en plein écran) faux sinontrue ou falsefalse
dashboard.ui.filters.expandedVrai si on veut que le volet des filtres (à gauche de l'écran) soit déployé lors du chargement, faux sinon (il reste déployable dans tous les cas)true ou falsefalse
dashboard.provider.hostnameURL de l'hébergeur d'Apache SupersetChaîne de caractèreshttps://ksup.analytics.skolengo.com
dashboard.provider.api.loginPath de l'API d'Apache Superset, qui renvoie un jeton d'accès et un refresh tokenChaîne de caractères/api/v1/security/login
dashboard.provider.api.guest_tokenPath de l'API d'Apache Superset pour obtenir le guest_token (jeton permettant de visualiser le tableau de bord)Chaîne de caractères/api/v1/security/guest_token/
dashboard.provider.usernameNom d'utilisateur pour le profil ayant le droit de générer un guest token (si le guest token n'est pas précisé)Chaîne de caractères
dashboard.provider.passwordMot de passe de l'utilisateur pour le profil ayant le droit de générer un guest token (si le guest token n'est pas précusé)Chaîne de caractères
dashboard.provider.providerProvider du tableau de bordChaîne de caractèresdb
dashboard.provider.refreshVrai si on souhaite obtenir un refresh token lors de la connexion à l'API, faux sinontrue ou falsetrue
dashboard.idIdentifiant du tableau de bord qu'on souhaite visualiserChaîne de caractèresFIXME : pas encore disponible
dashboard.guestTokenJeton d'accès au tableau de bord (si nécessaire, on peut autrement le récupérer automatiquement en précisant dashboard.provider.username et dashboard.provider.password, le jeton peut donner un accès à des données filtrées)Chaîne de caractèresFIXME : dépend des organisations

Modifier le fichier

Affichage du front office de K-Sup

Cette section décrit la construction des vues du front office de K-Sup.
Cet affichage se base sur la bibliothèque de composants K-Sup.

Modifier le fichier

Construction des vues en front office

Théorie

La construction des vues repose sur le modèle MVC.
Une vue est définie par trois éléments :

  • un fichier jsp
  • un preparer
  • un modèle

Le preparer doit récupérer les données nécessaires à la construction du modèle, il met en forme ces données telles qu'elles doivent apparaitre à l'écran et les injecte dans le modèle.
Le modèle est ensuite poussé dans la jsp via le controller.

MVC

Une vue peut correspondre à un organisme, une molécule ou un atome. La page finale affichée à l'écran correspond à un ensemble de vue qui sont combinées et imbriquées.

Dans la pratique

Une vue est définie par un ViewPreparer qui étend AbstractViewPreparer, celui-ci déclare les propriétés suivantes :

  • view : chemin de la jsp
  • type : type de la vue
  • order : ordre de priorité d'utilisation de la vue
  • includedSiteTemplate : liste des codes de template de site pouvant utiliser la vue
  • excludedSiteTemplate : liste des codes de template de site ne pouvant pas utiliser la vue
  • expectedViewTypes : liste des types de vue qui compose la vue

Le preparer définit le modèle qu'il utilise :

public class MyViewPreparer<V extends MyViewModel> extends AbstractViewPreparer<V> {
    ...
}

Le preparer doit posséder les méthodes suivantes :

accept : défini le contexte pour lequel la vue répond. La méthode prend en paramètre un FrontContext et retourne un boolean.

public boolean accept(FrontContext context) {
    return true;
}

prepare : construit le modèle associé à la vue. La méthode prend en paramètre un FrontContext et la liste des viewPreparers qui composent la vue.

public V prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
    final V viewModel = super.prepare(frontContext, preparers);
    ...
}

Le modèle

Le modèle porte l'ensemble des données affichées dans la vue, le viewPreparer est en charge d'effectuer la transformation des données afin de les injecter dans le modèle. Le modèle défini une méthode getId() qui retourne l'id utilisé pour l'insertion du modèle dans la jsp.

ArticleViewModel :

public class ArticleViewModel extends AbstractContentViewModel {

    @Override
    public String getId() {
        return "articleViewModel";
    }
}

article.jsp :

<jsp:include page="${articleViewModel.view}"/>

Le modèle possède également une liste de viewModels qui correspondent aux types de vue qui composent la vue.

protected Map<String, IViewModel> viewModels = new HashMap<>();

Il est possible de récupérer la grappe complète de viewModels à plat via la méthode getAllViewModels().

Un point d'extension permet d'effectuer un traitement à postériori de la préparation.
Une fois les différents preparers appelés, on récupère la totalité des bean actifs ordonnés de type ViewModelPostProcessor. On applique alors chaque processor accepé sur les viewModels.

Gestion des inclusions/exclusions des vues dans les templates de site

Par défaut, une vue est incluse dans tous les templates de site existants.

Il est possible de définir une liste de templates ne pouvant pas utiliser la vue via la propriété excludedSiteTemplate. Si un ou plusieurs codes de template sont présents dans les excludedSiteTemplate, la vue va être utilisée par tous les templates SAUF ceux présent dans les excludedSiteTemplate.

Le paramètre includedSiteTemplate est restrictif et absolu. Cela veut dire que si une vue définit un ou plusieurs codes dans la liste des includedSiteTemplate, seules ces templates pourront utiliser la vue. Cela veut également dire que si un code de template est présent dans la liste des includedSiteTemplate et également présent dans la liste des excludedSiteTemplate, l'exclusion n'est pas prise en compte.

Exemple :


<bean id="myViewPreparer1" >
    <property name="includedSiteTemplate">
        <list>
        </list>
    </property>
    <property name="excludedSiteTemplate">
        <list>
        </list>
    </property>
</bean>

<bean id="myViewPreparer2" >
    <property name="includedSiteTemplate">
        <list>
            <value>template2</value>
        </list>
    </property>
    <!-- exclus dans aucun templates de site -->
    <property name="excludedSiteTemplate">
        <list>
        </list>
    </property>
</bean>

<bean id="myViewPreparer3" >
    <property name="includedSiteTemplate">
        <list>
            <value>template1</value>
        </list>
    </property>
    <property name="excludedSiteTemplate">
        <list>
            <value>template1</value>
            <value>template3</value>
        </list>
    </property>
</bean>

<bean id="myViewPreparer4" >
    <!-- inclus par défaut dans tous les templates de site -->
    <property name="includedSiteTemplate">
        <list></list>
    </property>
    <!-- exclus dans aucun templates de site -->
    <property name="excludedSiteTemplate">
        <list>
            <value>template1</value>
            <value>template2</value>
        </list>
    </property>
</bean>

template1template2template3
myViewPreparer1
myViewPreparer2
myViewPreparer3
myViewPreparer4

Récupération des vues pour un template de site

Au démarrage de l'application, le SiteTemplateBeanManager créer une Map contenant la liste des vues regroupées par type pour chaque template de site :

public class SiteTemplateBeanManager extends AbstractBeanManager {

    // Map<codeTemplate, Map<typeVue, List<viewPreparer>>>
    private Map<String, Map<String, List<IViewPreparer>>> viewPreparersByTemplatesSiteAndTypes;
    ...
}

Il est possible de récupérer la liste des viewPreparers pour un template de site comme suit :

final Map<String, List<IViewPreparer>> viewPreparers = SiteTemplateBeanManager.getInstance().getViewPreparersByTemplateSiteCode(context.getInfosSite().getCodeTemplate());

Affichage de la vue

Le controller

L'affichage d'une vue est géré par un controller. Les controllers en charge d'afficher une vue possèdent une liste de handlers typés selon la vue à afficher (fiche, rubrique, search...). La liste de handlers est interrogée afin de récupérer le handler pouvant prendre en charge le contexte courant, puis demande au handler de retourner le ModelAndView correspondant à la vue.

Exemple :

public ModelAndView handleContent(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
    final ContentContext context = initContext(request);
    final Map<String, AbstractContentHandler> handlerList = ApplicationContextManager.getAllActivatedBeansOfType(AbstractContentHandler.class);
    final AbstractContentHandler handler = handlerList.values().stream()
        .filter(h -> h.supports(request, response, context))
        .findFirst()
        .orElse(null);
    return handler.getModelAndView(request, response, context);
}

Les handlers

Les handlers sont en charge de retourner le ModelAndView final au controller.

Un handler doit implémenter com.kosmos.controllers.handlers.ModelAndViewHandler.

public class MyHandler implements ModelAndViewHandler {
    ...
}

Le handler doit posséder une méthode supports qui défini pour quel contexte il répond. Les handlers sont ordonnés afin de pouvoir prioriser plusieurs handlers supportant le même contexte.
Exemple :

@Override
public boolean supports(final HttpServletRequest request, final HttpServletResponse response, final FrontContext frontContext) {
    final ContentContext contentContext = ContentContext.of(frontContext);
    return contentContext.getUrlBean() != null && contentContext.getUrlBean().getIdMetatag() != null;
}

Le handler doit également posséder une méthode getModelAndView en charge de construire le ModelAndView à retourner au controller.
Cette méthode va faire appel ViewModelHelper.prepareViewModel afin de préparer la grappe de viewPreparers qui composent le template que l'on souhaite afficher.

@Override
public ModelAndView getModelAndView(HttpServletRequest request, HttpServletResponse response, final FrontContext frontContext) throws ErreurApplicative {
    final ContentContext contentContext = populateContext(frontContext);
    final AbstractViewModel viewModel = ViewModelHelper.prepareViewModel(DefaultTemplateViewModel.TEMPLATE, contentContext, request, response);
    final ModelAndView modelAndView = new ModelAndView();
    ViewModelHelper.setView(viewModel, modelAndView);
    modelAndView.addAllObjects(viewModel.getAllViewModels());
    return modelAndView;
}

Les templates

Les templates sont des vues globales qui composent la page finale affichée à l'écran, elles sont de type template.
Les templates sont des vues particulières qui représentent le squelette de la page, elles sont utilisées pour regrouper et imbriquer les vues.

<bean id="defaultTemplateViewPreparer" class="com.kosmos.template.DefaultTemplateViewPreparer">
    <property name="type" value="template" />
    <property name="view" value="/WEB-INF/jsp/main.jsp" />
    <property name="expectedViewTypes">
        <list>
            <value>head</value>
            <value>banner</value>
            <value>skiplinks</value>
            <value>header</value>
            <value>breadcrumbs</value>
            <value>body</value>
            <value>footer</value>
            <value>autologin</value>
        </list>
    </property>
</bean>

Modifier le fichier

Fonctionnement de la grille KSup

KSup implémente une structure de page générique, comprenant différentes zones qui peuvent être agencées entre elles de différentes façon selon les pages visitées et les supports sur lesquels elles sont consultées. Ces zones sont repérées par les classes suivantes :

  • .pre-content : contient les éléments sous l'en-tête qui précèdent le contenu principal de la page
  • .page-main-content : le cœur de la page avec son contenu
  • .page-sidebar : une barre de navigation généralement placée sur le côté
  • .page-boxes : des contenus annexes, présentés sous forme d' « encadrés »

À ces classes s'ajoutent des classes qui vient se positionner sur le body :

  • .is-homepage : indique que la page visitée est une page d'accueil, le point d'entrée du site Web (à différencier de la fiche Accueil, qui présente un contenu riche dont l'agencement est personnalisé en contribution)
  • .is-insidepage : qualifie toutes les autres pages du site Web

Variables CSS et personnalisation

L'ensemble de la page est agencé à l'aide d'une grille CSS. Un certain nombre de calculs (détaillés ci-après) "dégrossissent" le travail d'agencement des différentes zones décrites ci-avant. L'idée ici est de répondre à 2 besoins :

  • N'avoir, idéalement, à modifier que des variables pour personnaliser l'agencement des contenus sur un site Web
  • Éviter, autant que faire se peut, de "surcharger" des JSP critiques de KSup

Voici les variables en question, déclarées sur body :

VariableDescriptionValeur par défaut
--page-layout-columns-countNombre de colonnes du layout de base12 *
--page-layout-offsetPremière colonne du layout de base2 **
--page-layout-use-same-row-content0: empiler les zones, 1: permettre d'avoir les asides sur la même ligne que le contenu0
--page-layout-sidebar-columns-countNombre de colonnes de la sidebar de navigation12
--page-layout-boxes-columns-countNombre de colonnes de la sidebar des encadrés12
--page-layout-main-content-columns-countNombre de colonnes du contenu principal12
--page-layout-main-content-offsetPosition de la première colonne du contenu principal2
--page-layout-pre-content-columns-countNombre de colonnes du contenu principal et des sidebars12
--page-layout-pre-content-offsetPosition de la première colonne du contenu principal et des sidebars2

* Le nombre de colonnes de base devrait se baser sur le nombre de colonnes configuré dans le projet (variable SCSS).

Le tableau ci-dessus décrit les valeurs par défaut définies pour ces variables. L'implémentation des sites KSup suivant le principe du mobile-first, on peut donc considérer que ces valeurs par défaut vont s'appliquer notamment sur des petits écrans. L'implémentation de la grille réalisée par défaut vient ensuite changer les règles au-delà du point de rupture $screen-md (48rem, 768px par défaut).

Les calculs réalisés sur les écrans plus larges sont les suivants :

  1. Une "nouvelle" variable est introduite : --_page-layout-aside-columns-count (le _ au début désigne une variable "privée" dans le sens où elle permet simplement de simplifier les calculs suivants). Cette variable calcule le nombre de colonnes occupées par la sidebar et les encadrés, s'ils sont sur la même ligne que le contenu principal. Le calcul est le suivant :

    aside-columns-count = use-same-row-content * (sidebar-columns-count + boxes-columns-count)
    

    Ici, la variable --page-layout-use-same-row-content prend tout son sens : si on ne veut pas afficher les contenus sur la même ligne (ils seront donc les uns sous les autres), la valeur de 0 viendra donc annuler la multiplication. Si c'est 1, on additionne donc le nombre de colonnes occupées par la sidebar et par la zone d'encadrés.

  2. Le nombre de colonne du contenu principal central (--page-layout-main-content-columns-count) est recalculé :

    main-content-columns-count = columns-count - aside-columns-count
    

    Il s'agit du nombre total de colonnes de la grille moins le nombre de colonnes occupées par la sidebar et les encadrés.

  3. L'indice de la première colonne où doit se placer le contenu principal (--page-layout-main-content-offset) est recalculé :

    main-content-offset = offset - sidebar-columns-count
    

    On prend l'indice de la première colonne de l'intégralité du contenu* et on y ajoute le nombre de colonnes occupées par la sidebar.

  4. Le nombre de colonnes du "pré-contenu" est recalculé. Il correspond en réalité au nombre de colonnes occupées par le contenu principal, de la sidebar et la zone d'encadrés (qui n'est pas forcément égal au nombre total de colonnes de la grille).

    pre-content-columns-count = main-content-columns-count + aside-columns-count
    
  5. Enfin, on recalcule l'indice de la première colonne du "pré-contenu" sur le même principe que le point №2.

    pre-content-offset = columns-count - pre-content-columns-count + offset
    

** L'intégralité du contenu ne commence par nécessairement à la 1e colonne. Dans le cas de KSup, la 1e colonne de la grille CSS (ainsi que la dernière) contient la marge variable sur le côté, permettant de centrer le contenu du site dans la fenêtre. L'intégralité du contenu démarre donc réellement à la 2e colonne.

Une fois tous ces calculs réalisés, il ne reste plus qu'à modifier la position des éléments en fonction des différents contextes. Prenons le cas d'une page intérieure ("inside page") sur un écran large (> 768px) avec des encadrés.

Les règles CSS qui vont s'appliquer sont les suivantes (d'après ce qui est définit dans les feuilles de styles de KSup) dans sa version simplifiée :

@media screen and (min-width: 48rem) {
    body {
        --_page-layout-aside-columns-count: calc(
            var(--page-layout-use-same-row-content)
            * (var(--page-layout-sidebar-columns-count)
            + var(--page-layout-boxes-columns-count))
        );

        --page-layout-main-content-columns-count: calc(
            var(--page-layout-columns-count)
            - var(--_page-layout-aside-columns-count)
        );

        --page-layout-main-content-offset: calc(
            var(--page-layout-offset)
            + var(--page-layout-sidebar-columns-count)
        );

        --page-layout-pre-content-columns-count: calc(
            var(--page-layout-main-content-columns-count)
            + var(--_page-layout-aside-columns-count)
        );

        --page-layout-pre-content-offset: calc(
            var(--page-layout-columns-count)
            - var(--page-layout-pre-content-columns-count)
            + var(--page-layout-offset)
        );
    }

    .is-insidepage {
        --page-layout-sidebar-columns-count: 0;
    }


    .is-insidepage:has(.page-sidebar, .page-boxes) {
        --page-layout-columns-count: 12;
        --page-layout-offset: 2;
        --page-layout-use-same-row-content: 1;
    }

    .is-insidepage:has(.page-boxes) {
        --page-layout-boxes-columns-count: 3;
    }
}

Notez ici que les calculs de positionnement dans la grille sont effectifs dès lors que les éléments sont présents dans la page. Cela, même si lesdits éléments sont vides !

Si on détaille les différentes règles :

  • On a d'abord le body qui définit les règles de calcul avec les variables qui seront déclarées après.
  • On peut voir que le nombre de colonnes occupées par la sidebar vaut 0 par défaut. Il n'y a pas de sidebar, donc cette valeur restera à 0.
  • On indique que le nombre de colonnes de l'intégralité du contenu est à 12. Cette variable est redéfinie ici, car dans KSup, le nombre de colonnes du contenu principal d'une page intérieure, sans sidebar et sans zone d'encadrés, est de 10.
  • Avec 12 colonnes de contenu, on replace la première colonne du contenu à 2 (au lieu de 3) par défaut.
  • On précise que l'on veut mettre tous les contenus sur la même ligne.
  • Enfin, on indique que la zone d'encadrés occupe 3 colonnes à elle seule.

Dans la feuille de styles de KSup, les différentes zones sont placées, par défaut, de cette façon, en réutilisant les variables décrites précédemment :

.pre-content {
    grid-column: var(--page-layout-pre-content-offset) / span var(--page-layout-pre-content-columns-count);
}

.page-main-content {
    grid-column: var(--page-layout-main-content-offset) / span var(--page-layout-main-content-columns-count);
}

.page-sidebar {
    grid-column: content / span var(--page-layout-sidebar-columns-count);
}

.page-boxes {
    grid-column: span var(--page-layout-boxes-columns-count) / content;
}

Le mot-clé span indique que la valeur utilisée désigne un nombre de colonnes et non un indice de référence. Le mot-clé content désigne la colonne occupée par l'intégralité du contenu par défaut (dans les faits, ça indique donc l'indice 2 pour l'indice de début, et l'indice 11 pour l'indice de fin de la colonne) ; cela découle directement du fonctionnement des zones nommées dans les grilles CSS.

Dispositions définies par défaut dans KSup en fonction des contextes et des contenus

En vue mobile (< 768px), on applique les règles suivantes :

  • La sidebar n'est pas affichée
  • Les contenus se placent les uns sous les autres et occupent toute la largeur (12 colonnes)
  • Dans l'ordre, on affiche le "pré-contenu", le contenu principal puis les encadrés

À l'heure actuelle, il n'y pas de règles qui régissent les écrans moyens (tablettes). Les règles d'affichage en vue "desktop" (≥ 768px) sont plus élaborées et sont décrites ci-dessous.

Type de ficheEmplacementPré-contenuSidebarEncadrésContenu principalMême ligne ? ****
Accueil ***Page d'accueil12 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 12 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 12 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 12 (@2‑14)✅ 12 (@2‑14)12 (@2‑14)Non
Accueil ***Page intérieure12 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 12 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 12 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 12 (@2‑14)✅ 12 (@2‑14)12 (@2‑14)Non
AutrePage d'accueil12 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 3 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 3 (@2‑14)12 (@2‑14)Non
""12 (@2‑14)✅ 3 (@2‑14)✅ 3 (@2‑14)12 (@2‑14)Non
AutrePage intérieure10 (@3‑13)10 (@3‑13)Oui
""12 (@2‑14)✅ 3 (@11‑14)9 (@2‑11)Oui
""12 (@2‑14)✅ 3 (@2‑5)9 (@5‑14)Oui
""12 (@2‑14)✅ 3 (@2‑5)✅ 3 (@11‑14)6 (@5‑11)Oui

Les numéros indiquées dans le tableau suivent ce modèle :

L (@S‑E)
  • L désigne le nombre de colonnes occupées par la zone (L pour Length)
  • S désigne l'indice de la colonne du début de la zone (S pour Start)
  • E désigne l'indice de la colonne de fin de la zone (E pour End)

Les symboles présents dans le tableau désignent :

  • ✅ : La zone est présente dans la page
  • ❌ : La zone est absente de la page

*** La taille du "contenu principal" de la fiche accueil (ne pas confondre avec "page d'accueil") peut, en réalité, dépasser sur les zones latérales (marges). Le nombre indiqué dans la colonne "contenu principal" indique simplement où se place la majorité du contenu de la fiche accueil.

**** La colonne "Même ligne ?" désigne si les zones "sidebar", "contenu principal" et "encadrés" doivent se trouver sur la même ligne ou non. Le "pré-contenu" se trouve toujours avant tout le reste.

Sources

Le tableau ci-dessus se base sur l'intégration qui est faite dans le fichier dédié.

Documents de spécification :

Deux commentaires viennent également appuyer ce fonctionnement (mais viennent potentiellement en contradiction avec ce qui est implémenté actuellement) :

Modifier le fichier

Affichage d'une fiche

Le controller

L'affichage d'une fiche à pour point d'entrée ContentController, celui-ci initialise un contexte de type ContentContexte et filtre sa liste d'AbstractContentHandler afin de récupérer le handler prenant en charge le contexte courant.

Le handler

Les handlers utilisés pour l'affichage des fiches doivent hérités de AbstractContentHandler, le handler par défaut est ContentHandler.

public class ContentHandler extends AbstractContentHandler {
  ...
}

La méthode supports du handler vérifie la présence d'un id métatag dans l'UrlBean du contexte.

    @Override
public boolean supports(final HttpServletRequest request, final HttpServletResponse response, final FrontContext frontContext) {
    final ContentContext contentContext = ContentContext.of(frontContext);
    return contentContext.getUrlBean() != null && contentContext.getUrlBean().getIdMetatag() != null;
}

Le contexte

Le contexte de type ContentContext est initialisé par le ContentController à partir du contexte de type FrontContext.

private ContentContext initContext(final HttpServletRequest request) {
    final ContexteUniv ctx = ContexteUtil.getContexteUniv();
    final String uri = getURI(request.getRequestURI());
    final InfosSite infosSite = serviceInfosSite.getActiveSiteByHost(request.getServerName());
    final UrlBean urlBean = serviceUrl.getByUrlAndSite(uri, infosSite);
    final ContentContext context = ContentContext.of(new DefaultContext());
    context.setCtx(ctx);
    context.setInfosSite(infosSite);
    context.setUrlBean(urlBean);
    return context;
}

Il est ensuite alimenté par le ContentHandler pour récupérer la fiche courante, le titre et le metatag de la fiche.

private ContentContext populateContext(final FrontContext context) throws ErreurApplicative {
    final ContentContext contentContext = ContentContext.of(context);
    final AbstractFicheBean ficheBean = new ContentKsupFetcher(contentContext.getUrlBean()).getAbstractFicheBean();
    contentContext.getCtx().setFicheCourante(ReferentielObjets.initFiche(ficheBean));
    contentContext.getCtx().setLocale(LangueUtil.getLocale(ficheBean.getLangue()));
    contentContext.setContent(ficheBean);
    final MetatagBean metatag = serviceMetatag.getById(contentContext.getUrlBean().getIdMetatag());
    contentContext.setTitle(metatag.getMetaLibelleFiche());
    contentContext.setMetatag(metatag);
    return contentContext;
}

La vue

Des templates par défaut sont définis dans le produit, ils sont utilisés pour l'affichage des fiches.
Ils incluent les vues de type head, header, body, footer etc...

Il est possible de définir un template de fiche spécifique pour un type de contenu.
Pour cela, il faut créer un viewPreparer qui hérite de DefaultTemplateViewPreparer et surcharger la méthode supports pour que celui-ci prenne en charge le type de contenu souhaité.

Dans la plupart des cas, l'affichage spécifique d'une fiche ne concerne que le corps de la page (type body).
Un viewPreparer de type body est apporté par défaut dans le produit, il inclut le contenu de la fiche (type content) ainsi que les encadrés (type boxes).

Chaque fiche déclare un viewPreparer de type content qui inclut les données de la fiche dans le modèle.

<bean id="articleContentViewPreparer" class="com.kosmos.article.ArticleContentViewPreparer" parent="contentViewPreparer">
    <property name="imagePreparer" ref="imageViewPreparer"/>
    <property name="type" value="content"/>
    <property name="view" value="/extensions/article/WEB-INF/jsp/article-content.jsp"/>
</bean>

Le viewPreparer étend AbstractContentViewPreparer, il surcharge la méthode accept pour définir quel type de fiche il prend en charge.

@Override
public boolean accept(final FrontContext context) {
    return ContentContext.of(context).getContent() instanceof ArticleBean;
}

Puis alimente le modèle à partir des données de la fiche via la méthode prepare.

Modifier le fichier

Tutoriel : Mise en place d'une personnalisation projet d'un affichage d'une fiche

K-Sup est conçu pour servir de socle commun à différents projets clients, chacun ayant ses propres besoins fonctionnels. Pour éviter toute duplication ou modification du produit, des mécanismes de surcharge permettent aux projets de personnaliser ou d’enrichir le socle sans le modifier.

Plusieurs approches sont possibles pour adapter l’affichage ou le comportement.

Pendant ce tutoriel, nous allons prendre l'exemple de l'extension Pagelibre, dont un projet va souhaiter personnaliser différents aspects.

1. Mécanismes de surcharge dans K-Sup

Le socle K-Sup repose sur une architecture modulaire avec des extensions et des contextes Spring isolés. Il offre plusieurs mécanismes de surcharge permettant aux projets de personnaliser leur comportement ou leur interface :

  • Surcharge de la configuration Spring
  • Surcharge de beans, notamment les ViewPreparer (ou le modèle) et les CardViewBuilder
  • Surcharge des vues JSP Ces mécanismes permettent aux projets d’intervenir uniquement sur les points nécessaires sans impacter les autres extensions ou le cœur du produit.

2. Surcharge de la configuration Spring

2.1 Description de la fonctionnalité

Chaque extension dispose de son propre contexte Spring, isolé. Les projets peuvent surcharger les beans en déclarant leur propre configuration XML, sans impacter le produit ou les autres extensions.

Ce mécanisme permet de modifier la valeur d’un ou plusieurs attributs d’un bean, ou de le redéfinir totalement.

2.2 Architecture générale

Prenons l’exemple du bean pagelibreContentViewPreparer :

<bean id="pagelibreContentViewPreparer" class="com.kosmos.pagelibre.PagelibreContentViewPreparer" parent="contentViewPreparer">
    <property name="type" value="content"/>
    <property name="view" value="/extensions/pagelibre/WEB-INF/jsp/pagelibre-content.jsp"/>
    <property name="expectedViewTypes">
        <value>contentBox</value>
    </property>
</bean>
  • type : permet à un autre viewPreparer, comme un preparer de template, de demander à ce que soit préparé un certain type de bean.

  • view : correspond au chemin vers la JSP du modèle à afficher.

  • expectedViewTypes : liste les types de vues qui seront préparés via AbstractViewPreparer.prepareExpectedViews(). Ce mécanisme permet notamment à un template d’utiliser des sous-vues (header, body, content…) sans connaître leur implémentation exacte. Il existe également d'autres paramètres communs:

  • includedSiteTemplate : indique la liste des template ou ce preparer doit être utilisé

  • excludedSiteTemplate : indique la liste des template ou ce preparer NE doit Pêtre utilisé

  • order : priorise le bean lors de la recherche de beans.

2.3 Procédure

Pour surcharger ce bean, il suffit de créer un fichier XML dans le dossier resources/spring du projet.

Ce fichier doit être explicitement importé par le fichier de contexte de l’extension concernée. Par exemple, dans l’extension Pagelibre :

<!-- Ouverture à la surcharge projet -->
<import resource="classpath*:spring/pagelibre-override.xml"/>

On place alors dans resources/spring/pagelibre-override.xml une redéfinition partielle ou totale du bean concerné, selon les besoins du projet.

3. Cas d’usage : surcharge d’un ViewPreparer

La surcharge de configuration Spring est souvent utilisée pour injecter des beans spécifiques dans le projet. Un cas fréquent est celui des ViewPreparer, introduits dans le cadre de la refonte des vues dynamiques.

3.1 Fonctionnement général des ViewPreparer

Chaque ViewPreparer fournit un modèle (ViewModel) associé à une vue, utilisable dans une JSP.

Lorsqu’un contrôleur est appelé, il demande une vue adaptée à son contexte. Le système parcourt les ViewPreparer déclarés en tant que beans Spring et sélectionne le premier qui accepte de préparer la vue (via la méthode accept()).

3.2 Surcharge via Spring : injection du nouveau bean

Un projet peut vouloir injecter son propre ViewPreparer, plus prioritaire que celui du socle. Il suffit pour cela :

  1. De créer un bean dans un fichier -override.xml
  2. De le rendre prioritaire via la propriété order
  3. D’utiliser un parent pour hériter de la configuration existante

Exemple : Ajout de la date en haut des pages libres

<!-- Pagelibre avec affichage de la date de mise à jour -->
<bean id="datedPagelibreContentViewPreparer" class="com.kosmos.pagelibre.DatedPagelibreContentViewPreparer" parent="pagelibreContentViewPreparer">
    <property name="order" ref="ORDRE_CRITIQUE"/>
    <property name="view" value="/WEB-INF/jsp/pagelibre/pagelibre-content.jsp"/>
</bean>

Définir un order plus prioritaire que celui du bean par défaut est indispensable pour que ce nouveau bean soit sélectionné.

Bonne pratique : utiliser la constante Spring suivante pour avoir la priorité la plus haute possible :

<util:constant id="ORDRE_CRITIQUE" static-field="org.springframework.core.Ordered.HIGHEST_PRECEDENCE"/>

3.3 Surcharge via Java : redéfinition du comportement prepare()

Dans la classe Java associée au nouveau bean, on peut redéfinir le comportement métier, tout en conservant celui du bean parent.

Exemple Java

public class DatedPagelibreContentViewPreparer extends PagelibreContentViewPreparer<PagelibreContentViewModel> {

    @Override
    public PagelibreContentViewModel prepare(final FrontContext frontContext, final Map<String, List<IViewPreparer>> preparers) {
        final PagelibreContentViewModel pagelibreContentViewModel = super.prepare(frontContext, preparers);
        final ContentContext contentContext = ContentContext.of(frontContext);
        final PagelibreBean bean = contentContext.getContent();
        pagelibreContentViewModel.setPublicationDate(PublicationDateProcessor.process(
            contentContext.getMetatag().getMetaDateMiseEnLigne(),
            bean.getDateModification()
        ));
        return pagelibreContentViewModel;
    }
}

Ce mécanisme permet de personnaliser complètement la donnée injectée dans la JSP sans perturber les autres composants.

3.4 Surcharge du ViewModel (optionnel)

Il est également possible d’utiliser un ViewModel différent pour y injecter des attributs supplémentaires spécifiques au projet. Il faut alors :

  • Définir un nouveau modèle Java
  • Adapter la JSP en conséquence (voir section suivante)

4. Surcharge de JSP

4.1 Description de la fonctionnalité

L’interface peut être adaptée en redéfinissant les fragments JSP utilisés. Deux approches principales sont possibles :

  • Surcharge par redéfinition à l'identique
  • Surcharge par modification du chemin dans le bean

4.2 Surcharge par redéfinition du fichier à l'identique

Il s’agit de redéfinir un fichier JSP à l’identique dans le projet.

Le fichier du projet, s’il est trouvé à un chemin identique, sera utilisé à la place de celui du socle.

Exemple : surcharge de la JSP Pagelibre

Le bean pagelibreContentViewPreparer utilise la JSP suivante :

/WEB-INF/jsp/pagelibre/pagelibre-content.jsp

Pour la surcharger, il faut créer dans le projet le fichier suivant :

src/main/webapp/WEB-INF/jsp/pagelibre/pagelibre-content.jsp

Le moteur de rendu utilisera automatiquement la version projet s’il la trouve à cet emplacement.

4.3 Surcharge par changement de chemin de vue

Une autre solution consiste à ne pas surcharger par overlay, mais à déclarer un nouveau chemin de JSP dans un bean surchargé. Cela permet d’avoir un fichier au nom différent, plus clair, et de coexister avec la version produit sans la masquer.

Exemple dans le bean projet :

<property name="view" value="/WEB-INF/jsp/pagelibre/pagelibre-content-projet.jsp"/>

Cette méthode est particulièrement utile pour éviter toute ambiguïté ou écrasement involontaire.

Attention, il n'est parfois pas nécessaire de surcharger une vue entièrement pour modifier l'affichage. Si l'élément à modifier est défini par une propriété (comme il est courant pour les messages), il suffit de surcharger cette propriété via un fichier properties.

Modifier le fichier

Principes d'intégration des vues

Déclaration d'une nouvelle vue

  1. Créer la jsp de la vue
  2. Créer un viewModel hériant de AbstractViewModel qui contient les données de la vue
public class MyViewModel extends AbstractViewModel {
    ...
}
  1. Créer un viewPreparer qui hérite de AbstractViewPreparer
    • Implémenter la méthode accept, pour définir le contexte d'utilisation de la vue
    • Implémenter la methode prepare, pour injecter les données dans le modèle
import com.kosmos.context.front.FrontContext;

public class MyViewPreparer<V extends MyViewModel> extends AbstractViewPreparer<V> {

   @Override
   public boolean accept(FrontContext context) {
        ...
   }

   @Override
   public V prepare(FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
      final V viewModel = super.prepare(context, preparers);
        ...
      return viewModel;
   }
}
}
  1. Déclarer le bean du viewPreparer en spécifiant
    • Le type de vue
    • Le chemin vers la jsp de la vue
    • Son ordre
    • Les types des sous-vues composant la vue
    • Les templates de site à inclure/exclure
<bean id="myViewPreparer" class="com.kosmos.preparer.MyViewPreparer">
    <property name="type" value="myViewType" />
    <property name="view" value="/WEB-INF/jsp/myView.jsp" />
    <property name="order" value="1000" />
    <property name="expectedViewTypes">
        <list>
            ...
        </list>
    </property>
    <property name="includedSiteTemplate">
        <list>
            ...
        </list>
    </property>
    <property name="excludedSiteTemplate">
        <list>
            ...
        </list>
    </property>
</bean>

Retrait d'un code de template site dans les listes includedSiteTemplate/excludedSiteTemplate d'une vue produit

  1. Effectuer un AttributeToOverrideBean pour modifier les listes includedSiteTemplate/excludedSiteTemplate
<bean id="myArticleViewPreparer" class="com.kportal.core.context.AttributeToOverrideBean">
    <property name="idExtensionToMerge" value="article"/>
    <property name="idBeanToMerge" value="articleViewPreparer"/>
    <property name="attributes">
        <map>
            <entry key="includedSiteTemplate">
                <list>
                    ...
                </list>
            </entry>
            <entry key="excludedSiteTemplate">
                <list>
                    ...
                </list>
            </entry>
        </map>
    </property>
</bean>

Ajout d'un code de template site dans les listes includedSiteTemplate/excludedSiteTemplate d'une vue

  1. Effectuer un ListToAddBean pour ajouter les codes désirés dans les listes includedSiteTemplate/excludedSiteTemplate
 <bean id="myArticleViewPreparer" class="com.kportal.core.context.ListToAddBean">
   <property name="idBeanToMerge" value="articleViewPreparer"/>
   <property name="idBeanToMerge" value="articleViewPreparer"/>
   <property name="listToMerge" value="includedSiteTemplate"/>
   <property name="add">
      <list>
         <value>template1</value>
      </list>
   </property>
</bean>

Modification de l'ordre de priorité d'une vue

  1. Effectuer un AttributeToOverrideBean pour modifier l'attribut order sur le viewPreparer
<bean id="myArticleViewPreparer" class="com.kportal.core.context.AttributeToOverrideBean">
    <property name="idExtensionToMerge" value="article"/>
    <property name="idBeanToMerge" value="articleViewPreparer"/>
    <property name="attributes">
        <map>
            <entry key="order">
                <value>2000</value>
            </entry>
        </map>
    </property>
</bean>

Surcharge d'une vue pour modifier son affichage

Création d'une jsp sur le projet avec le même chemin et nom que la jsp à surcharger.

OU

Création d'une nouvelle jsp et modification du chemin de la vue dans le viewPreparer via AttributeToOverrideBean.

Surcharge d'une vue pour modifier le contenu du modèle

  1. Création d'un nouveau viewPreparer qui étend le viewPreparer de la vue à surcharger
  2. Override de la méthode prepare pour changer la construction du modèle
  3. Déclaration du viewPreparer avec un order prioritaire au viewPreparer surchargé

Surcharge d'une vue pour modifier la structure du modèle

La modification de la structure du modèle implique la modification de tous les éléments de la vue (jsp, preparer, model). Cela implique donc de créer une nouvelle vue possédant la même méthode accept que la vue à surcharger et de lui assigner un order plus prioritaire.

Modifier le fichier

Intégration d'un nouveau controller affichant une vue

Récupération du handler

L'ajout d'un nouveau controller nécessite de récupérer le handler du type de page à afficher.

protected ModelAndView doGet(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
    final Frontcontext context = initContext(request, result);
    final Map<String, MyHandler> handlerMap = ApplicationContextManager.getAllActivatedBeansOfType(MyHandler.class);
    final MyHandler handler = handlerMap.values().stream()
        .filter(h -> h.supports(request, response, context))
        .findFirst()
        .orElseThrow(() -> new IllegalStateException("Aucun handler trouvé"));
    return handler.getModelAndView(request, response, frontContext);
}

Création du handler

Le handler est une classe qui implémente l'interface ModelAndViewHandler.
Les handlers sont ordonnés afin de pouvoir déterminer lequel doit être utilisé en priorité.
Ils définissent une méthode supports qui permet de déterminer si le handler peut gérer la requête courante.
La méthode getModelAndView permet de récupérer le modèle et la vue à afficher.

public class MyHandler implements ModelAndViewHandler {

    private int order;

    @Override
    public boolean supports(final HttpServletRequest request, final HttpServletResponse response, final FrontContext frontContext) {
        final MyContext context = MyContext.of(frontContext);
        return myContext.canHandle();
    }

    @Override
    public ModelAndView getModelAndView(HttpServletRequest request, HttpServletResponse response, final FrontContext frontContext) {
        final MyContext context = MyContext.of(frontContext);
        final AbstractViewModel viewModel = ViewModelHelper.prepareViewModel("myTemplateType", contentContext, request, response);
        final ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName(viewModel.getView());
        modelAndView.addAllObjects(viewModel.getAllViewModels());
        return modelAndView;
    }

    @Override
    public int getOrder() {
        return order;
    }

    public void setOrder(final int order) {
        this.order = order;
    }

}

Création de la vue

La création de la vue repose sur le même principe que pour les autres vues.
Un viewModel est créé et un viewPreparer est associé à ce viewModel (cf Principe d'intégration des vues).

Modifier le fichier

Intégration des encadrés

4 types d'encadrés sont disponibles dans K-Sup :

Type d'encadréDescriptionCode template
encadré de contenuencadré saisissable dans l'onglet "Encadrés" de l'interface d'administrationcontentBox
encadré de rubriqueencadré saisissable dans l'onglet "Encadré(s)" de l'interface de saisie de rubriquesectionBox
encadré de génériqueencadré saisissable à partir du menu "Editorial > Encadré générique"genericBox

Chaque type d'encadré possède son modèle et son préparateur de vue.

Le modèle par défaut contient une liste de com.kosmos.box.Box, chaque Box contient un titre, et un contenu.

L'affichage par défaut de la zone d'encadré est déterminé par la présence de contenu préparé. Pour calculer la présence d'un contenu, le BoxesViewPreparer va itérer sur les preparedViews (les vues préparées via par le expectedViewTypes) et vérifier si la vue à un contenu. Pour cela, il faut que chaque vue du expectedViewTypes implémente l'interface BoxViewModel et la méthode hasContent().

Modifier le fichier

Les plugins

Intégration d'un plugin

L'intégration d'un plugin suit les mêmes principes d'intégration que pour les autres éléments :

  • Création de la jsp de vue
  • Déclaration d'un ViewModel
  • Déclaration d'un ViewPreparer

Le ViewPreparer doit étendre AbstractPluginViewPreparer et déclarer quatre propriétés :

  • dataKey : clé poussé dans le contexte pour stocker les informations du plugin
  • idBean : permet de vérifier la présence du plugin sur la fiche courante
  • idExtensionPoint : permet d'identifier de manière unique la vue du plugin
  • pointNames : défini la liste des zones dans lequel le plugin va s'afficher.

Exemple :


<bean id="ficheLinkViewPreparer" class="com.kosmos.preparer.FicheLinkViewPreparer">
    <property name="type" value="fichelinkPlugin"/>
    <property name="view" value="/WEB-INF/jsp/ficheLinkPlugin.jsp"/>
    <property name="idBean" value="fichelinkPluginFiche"/>
    <property name="dataKey" value="fichelinkPluginFiche#fichelink"/>
    <property name="idExtensionPoint" value="fichelinkPluginFicheAddOn"/>
    <property name="pointNames">
        <list>
            <value>default-plugins</value>
        </list>
    </property>
</bean>

Cela permet de bénéficier de la méthode accept du AbstractPluginViewPreparer qui vérifie si le plugin est présent dans le contexte courant et s'il est disponible pour la fiche courante et permet également au plugin d'être récupéré par le PluginsViewPreparer (voir section suivante),

Inclusion de tous les plugins

Il est possible de faire une inclusion de tous les plugins disponibles pour la fiche courante au sein de la page. Le ViewPreparer dans lequel les plugins seront injectés doi ajoutés dans sa liste de expectedViewTypes le type plugins.


<property name="expectedViewTypes">
    <value>plugins</value>
</property>

L'ajout du type plugins dans les expectedViewTypes va effectuer la préparation du PluginsViewPreparer.
Le PluginsViewPreparer est en charge de récupérer la liste des tous les ViewPreparer qui étendent AbstractPluginViewPreparer et appeler la méthode prepare de chaque ViewPreparer; les ViewModel résultants sont poussés dans la map reliant chaque zones d'affichages aux plugins qui y sont abonnés, dans PluginsViewModel.
L'affichage des plugins est géré par le pluginAddOn.

Modifier le fichier

Intégration d'une carte de layout

L'intégration d'une carte de layout utilise le fonctionnement standard des viewPreparers.

Définition d'un viewPreparer de carte

Le mécanisme s'appuie sur un viewModel qui étend AbstractCardViewModel

public class AcmeCardViewModel extends AbstractCardViewModel {
    private String uuid;
    private String subtitle;
    
    ...
}

et d'un preparer qui étend AbstractCardViewPreparer.

public class AcmeCardViewPreparer<V extends AcmeCardViewModel> extends AbstractCardViewPreparer<V> {

    protected ServiceAcme serviceAcme;

    ...

    @Override
    public boolean accept(final FrontContext context) {
        return super.accept(context) && CardContext.of(context).getCard() instanceof AcmeCardBean && ...;
    }

    @Override
    public V prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
        final V viewModel = super.prepare(context, preparers);
        final AcmeCardBean card = CardContext.of(context).getCard();
        ...
}

Déclaration d'un viewPreparer de carte

Création d'un viewPreparer qui à pour parent abstractCardViewPreparer et déclare comme propriétés :

  • type : card
  • view : La jsp correspondant à la carte
<bean id="acmeCardViewPreparer" parent="abstractCardViewPreparer">
    <property name="type" value="card"/>
    <property name="view" value="/WEB-INF/jsp/layout/acme.jsp"/>
    <property name="serviceAcme" value="serviceAcme"/>
    ...
</bean>

Modifier le fichier

Intégration d'une fiche

Les fiches de K-Sup sont servies par défaut par le ContentHandler, celui-ci va récupérer les viewPreparer de type template afin de retrouver la liste des templates de fiche disponibles pour le template de site courant.

Le template va représenter la structure globale de la page, celui-ci défini les types de vue présents à la racine de la vue principale (exemple : head, header, ..., body, ..., footer).

Création d'un template de fiche

Par défaut, le template de fiche est défini globalement pour l'ensemble des fiches (via le template du site), il est possible de définir un template de fiche spécifique pour un type de contenu.

  1. Création d'un viewPreparer qui hérite de AbstractContentViewPreparer
public class ArticleTemplateViewPreparer extends AbstractContentViewPreparer<ArticleTemplateViewModel> {
    
    @Override
    public boolean accept(ContentContext context) {
        return context.getContent() instanceof ArticleBean;
    }

    @Override
    public ArticleTemplateViewModel prepare(FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
        return super.prepare(context, preparers);
    }
}
  1. Création du viewModel, l'id du viewModel des fiches est par défaut mainViewModel (défini dans MainViewModel).
public class ArticleTemplateViewModel extends AbstractContentViewModel {

}
  1. Définition du bean viewPreparer avec le type template
<bean id="articleTemplateViewPreparer" class="com.kosmos.preparer.ArticleTemaplateViewPreparer">
    <property name="order" value="-100" />
    <property name="type" value="template" />
    <property name="view" value="/WEB-INF/jsp/article.jsp" />
    <property name="expectedViewTypes">
        <list>
            <value>head</value>
            <value>header</value>
            <value>body</value>
            <value>footer</value>
            <value>autologin</value>
        </list>
    </property>
</bean>
  1. Création de la jsp principale qui effectue l'insertion des sous-vues
<%@ page trimDirectiveWhitespaces="true" errorPage="/WEB-INF/jsp/error/exception.jsp" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%--@elvariable id="headViewModel" type="com.kosmos.core.model.fragments.HeadViewModel"--%>
<%--@elvariable id="headerViewModel" type="com.kosmos.core.header.HeaderViewModel"--%>
<%--@elvariable id="bodyViewModel" type="com.kosmos.body.BodyViewModel"--%>
<%--@elvariable id="footerViewModel" type="com.kosmos.footer.FooterViewModel"--%>

<!DOCTYPE html>
<html>
<c:if test="${not empty headViewModel.view}">
    <jsp:include page="${headViewModel.view}"/>
</c:if>
<body class="k-body">
<c:if test="${not empty headerViewModel.view}">
    <jsp:include page="${headerViewModel.view}"/>
</c:if>
<jsp:include page="${bodyViewModel.view}"/>
<c:if test="${not empty footerViewModel.view}">
    <jsp:include page="${footerViewModel.view}"/>
</c:if>
</body>
</html>

Création d'un contenu de fiche

  1. Création d'un viewPreparer qui hérite de AbstractContentViewPreparer
public class ArticleContentViewPreparer<V extends ArticleContentViewModel> extends AbstractContentViewPreparer<V> {

    @Override
    public boolean accept(final FrontContext context) {
        return ContentContext.of(context).getContent() instanceof ArticleBean;
    }

    @Override
    public V prepare(final FrontContext frontContext, final Map<String, List<IViewPreparer>> preparers) {
        final V viewModel = super.prepare(frontContext, preparers);
        final ContentContext contentContext = ContentContext.of(frontContext);
        final ArticleBean bean = contentContext.getContent();
        viewModel.setPublicationDate(PublicationDateProcessor.process(contentContext.getMetatag().getMetaDateMiseEnLigne(), bean.getDateModification()));
        viewModel.setDate(prepareDate(bean));
        ...
        return viewModel;
    }
}
  1. Création du viewModel (l'id du viewModel des fiches est par défaut mainViewModel (défini dans MainViewModel))
public class ArticleContentViewModel extends AbstractContentViewModel {

    private String corps;

    private String resume;

    private String dateCreation;

    private String dateUpdate;

    private String sousTitre = StringUtils.EMPTY;

    private Set<String> thematiques = new HashSet<>();
    
    ...
}
  1. Définition du bean viewPreparer avec le type content
<bean id="articleContentViewPreparer" class="com.kosmos.preparer.ArticleContentViewPreparer">
    <property name="type" value="content"/>
    <property name="view" value="/WEB-INF/jsp/article-content.jsp"/>
</bean>
  1. Création de la jsp d'affichage du contenu
<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="kuik" uri="kuik" %>
<%@ taglib prefix="kore" uri="kore" %>

<%--@elvariable id="mainViewModel" type="com.kosmos.article.ArticleContentViewModel"--%>
<%--@elvariable id="contentBoxViewModel" type="com.kosmos.content.box.ContentBoxViewModel"--%>

<div class="l-col-9°md l-col-12">
    <article class="grid">
        <c:if test="${not empty mainViewModel.topics}">
            <ul class="list list--inlined l-col-12">
                <c:forEach items="${mainViewModel.topics}" var="thematique">
                    <li class="list__item"><kuik:cartouche size="xl"><c:out value="${thematique}"/></kuik:cartouche></li>
                </c:forEach>
            </ul>
...
  1. Répéter les étapes 1 à 4 pour créer les sous-vues qui composeront la page

Modifier le fichier

Intégration d'un nouveau style de liste

Description

Les styles de liste permettent de mettre en forme le résultat d'une requête de liste de fiches au niveau des toolbox notamment.

<span class="klist">[traitement;requete;objet=actualite#TRI_DATE=DATE_ASC#STYLE=0002#CODE_RUBRIQUE=1733170925027#NOMBRE=3#DATE_PATTERN_FORMAT=dd/MM/yyyy]</span>

Création de style de liste

La création d'un nouveau style nécessite les opérations suivantes :

Déclaration du style de liste

Les différents styles de liste doivent être déclarés dans un fichier dat adapté au contenu.
Pour cela, créer un fichier /resources/tables/<<EXTENSION>>/<<OBJET>>_style_affichage.dat avec les différents styles à intégrer.
Par exemple, /resources/tables/annuairesup/ANNUAIREKSUP_style_affichage.dat pour créer des style d'affichage pour l'objet Annuaire de l'extension Annuairesup.

Titre + Dates + Résumé                                              0001
Titre + Dates + Lieux                                               0002
Titre + Dates                                                       0003

Création d'un bean d'affichage

Le produit met à disposition le bean KsupMedia qui est directement utilisable par le tag media de la librairie ksup-components.

<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="kuik" uri="kuik" %>

<%--@elvariable id="contentListItemViewModel" type="com.kosmos.content.list.AbstractContentListItemViewModel"--%>

<kuik:media media="${contentListItemViewModel.content}" orientation="vertical"/>

Il est possible de typer plus fortement le contenant en étendant cette classe.

public class ActualiteMediaCard extends KsupMedia {}

Les données préparées seront stockées dans ce bean.

Création du viewModel d'élément de liste

Afin de mutualiser le code, un viewModel container encapsule le bean de données.

public class ActualiteContentListItemViewModel<T extends ActualiteMediaCard> extends AbstractContentListItemViewModel<T> {}

Création d'un builder commun aux différents style de liste

Ce builder ksupMediaBuilder mutualise les éléments communs:

public class KsupMediaBuilder<T extends KsupMediaBuilder<T, C, B>, C extends KsupMedia, B extends AbstractFicheBean> {
    ...
}

Ce builder construit les éléments communs a toute les fiches

  • Titre (Inconditionnel)
  • Lien vers le contenu (Inconditionnel)
  • Thématiques (Conditionnel)
  • Résumé (Conditionnel)
  • Image (Conditionnel)

exemple

    ...
    final MediaCard data = new KsupMediaBuilder<>(context, thumbnailViewPreparer, serviceActualite)
        .withTopics()
        .withSummary()
        .build
    ...

Etendre le builder a tous les styles de fiches

public class ActualiteMediaCardBuilder<C extends ActualiteMediaCard> extends KsupMediaBuilder<ActualiteMediaCardBuilder<C>, C, ActualiteBean> {
    ...
}

Ce builder étend KsupMediaBuilder et ajoute

  • Sous-titre (Conditionnel)
  • Date (Conditionnel)

exemple

    ...
    final ActualiteMediaCard Actualitedata = new ActualiteMediaCardBuilder<>(context, thumbnailViewPreparer, serviceActualite)
        .withTopics()
        .withDate()
        .withSubtitle()
        .build
    ...

Création du preparer abstrait d'élément de liste

Ce preparer a pour fonction de mutualiser le code commun à tous vos styles de liste:


public abstract class AbstractActualiteContentListItemViewPreparer<V extends ActualiteContentListItemViewModel<ActualiteMediaCard>> extends AbstractContentListItemViewPreparer<V> {
    
    protected ServiceActualite serviceActualite;

    ...
    @Override
    public boolean accept(final FrontContext context) {
        return ContentContext.of(context).getContent() instanceof ActulaliteBean
               && this.supportedStyle().equals(context.get(ContentListTagProcessor.STYLE, String.class));
    }

    public void setServiceActualite(final ServiceActualite serviceActualite) {
        this.serviceActualite = serviceActualite;
    }
}

Création des preparers de liste et d'éléments de liste

Ensuite,

  • dériver la classe abstraite avec chaque style de liste,
public class Style1ActualiteContentListItemViewPreparer<V extends ActualiteContentListItemViewModel<ActualiteMediaCard>> extends AbstractActualiteContentListItemViewPreparer<V> {
    ...
}
  • définir une condition pour prise en charge du preparer
    @Override
    protected String supportedStyle() {
        return "0001";
        }
  • surcharger la méthode prepare
    @Override
    public V prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparer) {
        ...
        }

La méthode prepare est structurée de la manière suivante:

  • la génération du viewModel via le super.prepare(..) et construction de la MediaCard via builder
    @Override
    public V prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparer) {
        final V viewModel = super.prepare(context, preparer);
        final ActualiteMediaCard actualiteMediaCard = new ActualiteMediaCardBuilder<>(context, thumbnailViewPreparer, serviceActualite)
            .withTopics()
            .withSummary()
            .build();
            viewModel.setContent(actualiteMediaCard);
            return viewModel;
    }

Déclaration des beans de style dans le contexte de l'extension

La dernière étape consiste à déclarer les beans dans le fichier de contexte de l'extension /resources/spring/<<EXTENSION>>-override.xml

    <bean id="abstractActualiteContentListItemViewPreparer" class="com.kosmos.actualite.AbstractActualiteContentListItemViewPreparer" abstract="true" parent="abstractContentListItemViewPreparer">
        <property name="serviceActualite" ref="actualiteService"/>
        <property name="order" value="-10000"/>
    </bean>

    <bean id="style1ActualiteContentListItemViewPreparer" class="com.kosmos.actualite.Style1ActualiteContentListItemViewPreparer" parent="abstractActualiteContentListItemViewPreparer"/>
    
    <bean id="style2ActualiteContentListItemViewPreparer" class="com.kosmos.actualite.Style2ActualiteContentListItemViewPreparer" parent="abstractActualiteContentListItemViewPreparer"/>

Activation des styles de liste

Les styles de liste ne sont pas disponibles par défaut sur tous les types de contenus.
Pour permettre à l'utilisateur de sélectionner un style de liste lors de la contribution d'un tag liste, la propriété fiche.<<OBJET>>.style_affichage doit être valorisée à :

  • 1 (style obligatoire)
  • 2 (style optionnel)

Ajouter la clé dans le fichier application_<<EXTENSION>>.properties de votre projet ou env_<<EXTENSION>>.properties de votre storage selon le besoin.

fiche.DOCUMENT.style_affichage=2

How to

Je veux un affichage spécifique de mon élément de liste

Par défaut les éléments de liste utilisent la configuration produit


<bean id="abstractContentListItemViewPreparer" class="com.kosmos.content.list.AbstractContentListItemViewPreparer" abstract="true">
    <property name="type" ref="TYPE_CONTENT_LIST_ITEM"/>
    <property name="view" value="/WEB-INF/jsp/toolbox/content-list-item.jsp"/>
</bean>

Tout bean dont le parent direct ou indirect est abstractContentListItemViewPreparer et qui ne surcharge pas la vue utilisera la jsp content-list-item.jsp du produit.

<%@ page trimDirectiveWhitespaces="true" %>
...

<%--@elvariable id="contentListItemViewModel" type="com.kosmos.content.list.AbstractContentListItemViewModel"--%>

<li class="l-col-12 l-col-6°sm l-col-4°md">
    <kuik:media media="${contentListItemViewModel.content}" orientation="vertical">
        ...
    </kuik:media>
</li>

Pour avoir un visuel spécifique, ajoutez la propriété view dans la définition de votre bean (abstract ou styleX selon vos besoins)

    <bean id="abstractActualiteContentListItemViewPreparer" class="com.kosmos.actualite.AbstractActualiteContentListItemViewPreparer" abstract="true" parent="abstractContentListItemViewPreparer">
        <property name="serviceActualite" ref="actualiteService"/>
        <property name="order" value="-10000"/>
    </bean>

    <bean id="style1ActualiteContentListItemViewPreparer" class="com.kosmos.actualite.Style1ActualiteContentListItemViewPreparer" parent="abstractActualiteContentListItemViewPreparer">
        <property name="view" value="/jsp/actualite/style1.jsp"/>
    </bean>
    
    <bean id="style2ActualiteContentListItemViewPreparer" class="com.kosmos.actualite.Style2ActualiteContentListItemViewPreparer" parent="abstractActualiteContentListItemViewPreparer"/>
    
    ...

et créez la jsp avec votre affichage spécifique. L'élément de liste est disponible dans le scope "request" sous le nom contentListItemViewModel

    ...
    <span>${contentListItemViewModel.content.title}</span>
    ...

Je veux un affichage spécifique de ma liste

C'est le même principe qu'au dessus, mais avec le bean abstractContentListViewPreparer

Je veux un style de liste même si le style n'est pas défini

Par défaut, s'il n'y a pas de style défini, le produit affiche simplement le libellé de la fiche dans une liste à puce (<ul> + <li> natif html).

Il est cependant possible de forcer un affichage différent.
Pour cela, il faut définir un preparer qui étend AbstractContentListViewPreparer et un qui étend AbstractContentListItemViewPreparer. Ces beans vont prendre l'ascendant par rapport aux preparers par défaut.

    <bean id="acmeContentListViewPreparer" class="com.kosmos.acme.AcmeContentListViewPreparer"  parent="abstractContentListViewPreparer">
    <property name="view" value="/jsp/acme/content-list.jsp"/>
        <property name="order" value="-10000"/>
    </bean>

    <bean id="acmeContentListItemViewPreparer" class="com.kosmos.acme.AcmeContentListItemViewPreparer" parent="abstractContentListItemViewPreparer">
        <property name="view" value="/jsp/acme/content-list-item.jsp"/>
        <property name="order" value="-10000"/>
    </bean>

Mon élément de liste n'est pas une fiche ou le contenu n'étend pas KsupMedia

L'effacement de type est ouvert (pas d'extends) : vous pouvez faire une liste de n'importe quel bean.
Par exemple, regarder le modèle de vue com.kosmos.toolbox.DefaultContentListItemViewModel et son preparer com.kosmos.content.list.DefaultContentListItemViewPreparer dans le module webapp-front de koreparent.

Modifier le fichier

Tutoriel : Mise en place d'une personnalisation projet de la structure de la page

Construction de la structure d'une page

Les pages refronte sont construites à partir de différents ViewModel. La structure de la page est définie par le modèle de vue "template". Le template possède en sous-vue les principaux bloc de la page, qui ont eux-mêmes des sous-vues.

image Structure par défaut des pages K-Sup V7

Ce système d'emboitement de vue permet de personnaliser une partie de la structure de la page sans impacter le reste, par exemple personnaliser le header. On peut toutefois vouloir modifier la structure globale de la page.

Pour rappel, on parle ici de template au sens de template de page, pas de template de site. Cf documentation sur les templates de site. On peut créer un nouveau template de site sans nouveau template de page, en déclarant de nouveaux viewPreparer pour les zones à personnaliser.

Architecture générale

Le template des pages suit le pattern MVC de Spring et s'intègre dans l'architecture K-Sup avec les composants suivants :

  • ViewPreparer de template : hérite de DefaultTemplateViewPreparer
  • Bean de déclaration du template : implémente le viewPreparer, liste les sous-vues
  • Jsp du template : la jsp d'affichage

Il n'y a généralement pas besoin de personnaliser le modèle de vue, le template n'affiche pas de données. Les données sont portées par les sous-vues.

Procédure

Le tutoriel présenté ci-dessous reprend les principes de personnalisation de la structure. Il n'est pas toujours obligatoire de modifier tous les éléments listés.

Déclarer le bean du prépareur de vue

En fonction de la personnalisation voulue, il ne sera pas utile de personnaliser tous les éléments :

  • Si la condition d'utilisation du template est sur un template de site, il n'est pas utile de créer un nouveau préparer de vue. Il faut déclarer un bean de classe DefaultTemplateViewPreparer et indiquer le code du template dans la propriété includedSiteTemplate du bean.
  • Si la personnalisation du template consiste à ne pas utiliser certain type de sous-vue, il n'y a pas besoin de personnaliser la jsp

Exemple de bean à déclarer :


    <util:constant id="codeTemplateVotreTemplate" static-field="fr.votreprojet.utils.VotreprojetUtils.CODE_TEMPLATE_VOTRETEMPLATE"/>

    <bean id="votreTemplateViewPreparer" class="fr.votreprojet.template.view.VotreTemplateViewPreparer">
        <property name="type" value="template" />
        <property name="view" value="/WEB-INF/jsp/votreProjet/main.jsp" />
        <property name="expectedViewTypes">
            <list>
                <value>head</value>
                <value>header</value>
                <value>body</value>
                <value>footer</value>
            </list>
        </property>
        <property name="includedSiteTemplate">
            <list>
                <ref bean="codeTemplateVotreTemplate"/>
            </list>
        </property>
    </bean>

Créer le préparateur de vue du template

La classe fr.votreprojet.template.view.VotreTemplateViewPreparer permet de spécifier les conditions d'utilisation du template et de préparer les données qui vont être affichées

/**
 * Préparateur de la vue de [Description].
 */
public class VotreTemplateViewPreparer extends DefaultTemplateViewPreparer {

    /**
     * Indique si le préparateur de contenu est habilité à traiter une requête en fonction du contexte.
     * @param frontContext Le contexte.
     * @return true si le préparateur est habilité, false sinon.
     */
    @Override
    public boolean accept(final FrontContext frontContext) {
        return // vos conditions;
    }
}

Créer la jsp d'affichage

Créer la jsp à l'emplacement indiqué dans le bean :

<%@ page trimDirectiveWhitespaces="true" errorPage="/WEB-INF/jsp/error/exception.jsp" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%--@elvariable id="headViewModel" type="com.kosmos.core.model.fragments.HeadViewModel"--%>
<%--@elvariable id="headerViewModel" type="com.kosmos.core.header.HeaderViewModel"--%>
<%--@elvariable id="bodyViewModel" type="com.kosmos.body.BodyViewModel"--%>
<%--@elvariable id="footerViewModel" type="com.kosmos.footer.FooterViewModel"--%>

<!DOCTYPE html>
<html lang="${headViewModel.lang}">
    <c:if test="${not empty headViewModel.view}">
        <jsp:include page="${headViewModel.view}"/>
    </c:if>
    <body class="k-body body l-expanded-body">
        <c:if test="${not empty headerViewModel.view}">
            <jsp:include page="${headerViewModel.view}"/>
        </c:if>
        <jsp:include page="/WEB-INF/jsp/technical/pre-content.jsp" />
        <jsp:include page="${bodyViewModel.view}"/>
        <c:if test="${not empty footerViewModel.view}">
            <jsp:include page="${footerViewModel.view}"/>
        </c:if>
        <jsp:include page="/WEB-INF/jsp/technical/footer.jsp" />
    </body>
</html>

Modifier le fichier

Personnalisation d'une partie de la page

Description de la fonctionnalité

Quelle que soit la partie à personnaliser, il faut se rapprocher au maximum du fonctionnement produit et ne personnaliser qu'en cas d'absolu nécessité. Chaque partie de la page a un type. Il est possible de définir plusieurs affichages par type et de conditionner l'affichage au template de site, ou sur d'autres critères. Ici, on prend pour exemple la personnalisation du header par l'ajout d'une sous-vue.

Architecture générale

Le header suit le pattern MVC de Spring et s'intègre dans l'architecture K-Sup avec les composants suivants :

  • ViewPreparer : hérite de AbstractViewPreparer
  • ViewModel : hérite de AbstractViewModel
  • Bean du ViewPreparer : déclaration du bean du ViewPreparer personnalisé
  • Bean du Header : ajoute le ViewPreparer personnalisé
  • Jsp du header : ajoute le ViewModel personnalisé
  • Jsp personnalisée : l'affichage qu'on souhaite ajouter au header

Procédure

Créer le modèle de vue

Créer le modèle de vue contenant les données à afficher :

package fr.votreprojet.votrepackage;

/**
 * Modèle de vue [description]
 */
public class ExempleViewModel extends AbstractViewModel {
 
    private AttributExemple attributExemple;
 
    @Override
    public String getId() {
        return "exempleViewModelId";
    }
 
    public AttributExemple getAttributExemple() {
        return attributExemple;
    }
 
    public void setAttributExemple(final AttributExemple attributExemple) {
        this.attributExemple = attributExemple;
    }
}

Créer le préparer de vue

Créer le préparer de vue, indiquer les conditions d'utilisation dans la méthode ExempleViewPreparer#accept et préparer les données à afficher dans la méthode ExempleViewPreparer#prepare.

package fr.votreprojet.votrepackage;

/**
 * Préparateur de la vue [description]
 */
public class ExempleViewPreparer extends AbstractViewPreparer<ExempleViewModel> {

    /**
     * Indique si le préparateur de contenu est habilité à traiter une requête en fonction du contexte.
     * @param frontContext Le contexte.
     * @return true si le préparateur est habilité, false sinon.
     */
    @Override
    public boolean accept(final FrontContext context) {
        return ...; // Les conditions d'affichage, si besoin.
    }

    /**
     * Prépare le modèle de la vue en fonction du contexte.
     * @param frontContext Le contexte d'affichage
     * @param preparers les preparers de la page courante
     * @return Le modele T qui étend {@link AbstractViewModel}.
     */
    @Override
    public ExempleViewModel prepare(final FrontContext frontContext, final Map<String, List<IViewPreparer>> preparers) {
        final ExempleViewModel viewModel = super.prepare(frontContext, preparers);
        viewModel.setAttributExemple(attributExemple);
        return viewModel;
    }
}

Déclarer les beans

Déclaration du bean du ViewPreparer du nouvel élément et déclaration du header projet pour y ajouter le nouveau type dans la liste expectedViewTypes :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- ViewPreparer d'exemple -->
    <bean id="exempleViewPreparer" class="fr.votreprojet.votrepackage.ExempleHeaderViewPreparer">
        <property name="type" value="exemple"/>
        <property name="view" value="/WEB-INF/jsp/votreChemin/exemple.jsp"/>
    </bean>

    <bean id="votreHeader" class="com.kosmos.core.header.HeaderViewPreparer">
        <property name="type" value="header"/>
        <property name="view" value="/WEB-INF/jsp/votreChemin/headerVotreProjet.jsp"/>
        <property name="order">
            <util:constant static-field="org.springframework.core.Ordered.HIGHEST_PRECEDENCE"/>
        </property>
        <property name="attributes">
            <map>
                <entry key="expectedViewTypes">
                    <list>
                        <value>mainMenu</value>
                        <value>quickaccess</value>
                        <value>langMenu</value>
                        <value>panel-search-bar</value>
                        <value>exemple</value>
                    </list>
                </entry>
            </map>
        </property>
    </bean>
</beans>

Le paramétrage est commun à tous les ViewPreparer, cf documentation Construction des vues en front office. Pour rappel, on peut spécifier les templates sur lesquels les préparers de vue sont utilisées avec les paramètres suivants :

  • includedSiteTemplate : liste des template sur lesquels le préparer de vue est accepté
  • excludedSiteTemplate : liste des templates sur lesquels le préparer de vue est exclu

Attention, on ajoute un type de ViewModel dans la liste des view du header, pas directement un viewPreparer précis. Ce mécanisme permet de déclarer plusieurs ViewPreparer du même type, utilisés dans des cas différents. Par exemple, on pourrait avoir un seul header pour plusieurs template, mais vouloir spécifier notre ViewPreparer de type "exemple". C'est la méthode #accept des ViewPreparer qui permettra de déterminer quel ViewPreparer afficher. Si deux ViewPreparer sont acceptés, il est possible d'indiquer une priorité vie l'attribut order dans le bean.

Création de la jsp affichant les nouvelles données dans /WEB-INF/jsp/votreChemin/exemple.jsp :

...
<%@ page trimDirectiveWhitespaces="true" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:if test="${not empty exempleViewModelId.attributExemple}">
    <c:out value="${exempleViewModelId.attributExemple}"/>
</c:if>

...

Ajouter l'appel de la nouvelle vue dans /WEB-INF/jsp/votreChemin/headerVotreProjet.jsp :

...
<%--@elvariable id="exempleViewModelId" type="fr.votreprojet.votrepackage.ExempleViewModel"--%>
<c:if test="${not empty exempleViewModelId}">
    <jsp:include page="${exempleViewModelId.view}"/>
</c:if>
...

Modifier le fichier

Tutoriel : Créer un accueil de rubrique en refronte

Vue d'ensemble

Un accueil de rubrique permet définir un écran à afficher lorsque l'utilisateur se rend sur la rubrique. Il peut être de différents types (moteur de recherche, fiche, liste automatique...). La documentation utilisateur est disponible ici : https://docs.ksup.org/fr/version-6-7/utiliser/rubriques # TODO : URL à éditer

Architecture générale

L'accueil de rubrique suit le pattern MVC de Spring et s'intègre dans l'architecture K-Sup avec les composants suivants :

  1. Classe d'accueil de rubrique : Hérite de DefaultPageAccueilRubrique
  2. Bean de page d'accueil : Implémente BeanPageAccueil
  3. View Model : Modèle de vue pour le front
  4. View Preparer : Prépare les données pour la vue
  5. Configuration : Configuration Spring

Étapes de création

Le tutoriel ci-dessous nous permettra de créer un accueil de rubrique qui affiche un titre et une description.

Créer le Bean de page d'accueil

Cette classe représente les données relatives à la page d'accueil, dans fr.votreprojet.votremodule.rubrique. La classe doit implémenter BeanPageAccueil.

package fr.votreprojet.votremodule.rubrique;

import java.io.Serial;

import org.apache.commons.lang3.StringUtils;

import com.kportal.extension.module.plugin.rubrique.BeanPageAccueil;

/**
 * Bean de la page d'accueil pour [Description de votre module].
 */
public class VotreModuleBeanPageAccueil implements BeanPageAccueil {

    /**
     * serialVersionUID.
     */
    @Serial
    private static final long serialVersionUID = -5646188278620975957L;
    
    /**
     * Le titre à afficher.
     */
    private String titre;

    /**
     * La description à afficher.
     */
    private String description;
    
    @Override
    public String getUrlRubrique(final String codeRubrique, final String langue, final boolean ampersands) {
        return StringUtils.EMPTY; // Indispensable actuellement pour le front legacy
    }

    @Override
    public String getUrlModification(final String codeRubrique, final String langue, final boolean ampersands) {
        return StringUtils.EMPTY; // Indispensable actuellement pour le front legacy
    }

    @Override
    public String getLibelleAffichable() {
        return StringUtils.EMPTY; // Indispensable actuellement pour le front legacy
    }

    public String getTitre() {
        return titre;
    }

    public void setTitre(final String titre) {
        this.titre = titre;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(final String description) {
        this.description = description;
    }
}
  • Notes :
    • Les méthodes getUrlRubrique, getUrlModification et getLibelleAffichble étaient utilisées pour le front legacy. Il est encore nécessaire de les implémenter jusqu'à ce que le ticket CORE-7775 soit résolu.

Créer la classe d'accueil de rubrique

Il faut ensuite créer la classe fr.votreprojet.votremodule.rubrique.VotreModuleAccueilRubrique représentant l'accueil de rubrique (enregistrement et affichage en BO). Elle doit hériter de DefaultPageAccueilRubrique. Elle contient les méthodes preparerPRINCIPAL et traiterPRINCIPAL relatives à l'enregistrement et l'affichage de l'accueil de rubrique en BO :

package fr.votreprojet.votremodule.rubrique;

import java.io.Serial;
import java.util.Map;

import com.kportal.extension.module.plugin.rubrique.DefaultPageAccueilRubrique;

/**
 * Accueil de rubrique permettant d'afficher [Description de votre fonctionnalité].
 */
public class VotreModuleAccueilRubrique extends DefaultPageAccueilRubrique<VotreModuleBeanPageAccueil> {

    /**
     * Le type d'accueil de rubrique.
     */
    public static final String TYPE_ACCUEIL_RUBRIQUE = "8010";

    /**
     * serialVersionUID.
     */
    @Serial
    private static final long serialVersionUID = 2744706230254106056L;

    @Override
    public void preparerPRINCIPAL(final Map<String, Object> infoBean, final VotreModuleBeanPageAccueil beanPageAccueil) {
        // Logique de préparation des données si nécessaire
        infoBean.put("TITRE", beanPageAccueil.getTitre());
        infoBean.put("DESCRIPTION", beanPageAccueil.getDescription());
    }

    @Override
    public void traiterPRINCIPAL(final Map<String, Object> infoBean, final VotreModuleBeanPageAccueil beanPageAccueil) {
        // Logique de traitement des données si nécessaire
        beanPageAccueil.setTitre((String) infoBean.get("TITRE"));
        beanPageAccueil.setDescription((String) infoBean.get("DESCRIPTION"));
    }
}
  • La constante TYPE_ACCUEIL_RUBRIQUE représente le code de l'accueil de rubrique. Il doit être unique
  • Dans la méthode preparerPRINCIPAL, on affecte à l'infobean les données qui seront affichées en back-office.
  • Dans la méthode traiterPRINCIPAL, on récupère les données de l'infobean pour pouvoir ensuite les enregistrer en base.

Créer le modèle de vue

Il faut ensuite créer un modèle de vue, permettant d'afficher les données. Dans notre cas, nous allons afficher un titre et une description contribués en back-office au sein d'une page. Dans fr.votreprojet.votremodule.view.model, on vient créer une classe VotreModuleViewModel.

package fr.votreprojet.votremodule.view.model;

import com.kosmos.body.BodyViewModel;

/**
 * Modèle de vue pour l'affichage de [Description].
 */
public class VotreModuleViewModel extends BodyViewModel {

    /**
     * Le titre.
     */
    private String titre;

    /**
     * La description.
     */
    private String description;

    /**
     * Constructeur.
     */
    public VotreModuleViewModel() {
        super();
    }

    // Getters et setters
    public String getTitre() {
        return titre;
    }

    public void setTitre(final String titre) {
        this.titre = titre;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(final String description) {
        this.description = description;
    }
}
  • Notes :
    • Dans cet exemple, nous souhaitons afficher des données au sein d'une page sur un site K-Sup. Nous reprenons donc l'entête et le pied de page définis par défaut pour le site. Afin de ne surcharger que le contenu relatif au corps de la page, nous étendons BodyViewModel. (cf. Principe d'intégration)).

Créer le préparateur de vue

La classe fr.votreprojet.votremodule.view.preparer.VotreModuleViewPreparer permet de préparer les données qui vont être affichées.

package fr.votreprojet.votremodule.view.preparer;

import java.util.List;
import java.util.Map;

import com.kosmos.context.front.FrontContext;
import com.kosmos.context.front.SectionContext;
import com.kosmos.preparer.IViewPreparer;
import com.kosmos.section.home.AbstractSectionHomeViewPreparer;

/**
 * Préparateur de la vue de [Description].
 */
public class VotreModuleViewPreparer extends AbstractSectionHomeViewPreparer<VotreModuleViewModel> {

    @Override
    public boolean accept(final FrontContext context) {
        return super.accept(context) && SectionContext.of(context).getBeanPageAccueil() instanceof VotreModuleBeanPageAccueil;
    }

    @Override
    public VotreModuleViewModel prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
        final VotreModuleViewModel viewModel = super.prepare(context, preparers);
        final SectionContext sectionContext = SectionContext.of(context);
        // Préparer les données du viewModel
        final VotreModuleBeanPageAccueil beanPageAccueil = (VotreModuleBeanPageAccueil) sectionContext.getBeanPageAccueil();
        viewModel.setTitre(beanPageAccueil.getTitre());
        viewModel.setDescription(beanPageAccueil.getDescription());
        return viewModel;
    }
}
  • *Notes :
    • La classe étend de AbstractSectionHomeViewPreparer puisqu'on veut afficher des données au sein d'une page K-Sup. Pour d'autres affichages, d'autres préparateurs de vue abstraits sont disponibles.
    • Méthode accept : cette méthode permet de savoir si le préparateur de vue peut préparer les données pour le bean de page d'accueil courant ;
    • Méthode prepare : cette méthode va préparer les données à afficher. Puisqu'on est au sein d'une page K-Sup, on récupère le contexte de la section (SectionContext).

Fichiers de propriétés pour l'internationalisation

On crée les fichiers relatifs aux traductions dans src/main/resources. Par exemple, pour les messages en français, on crée le fichier Application_votremodule_fr_FR.properties avec :

# Votre Module
## Accueil de rubrique
RUBRIQUE.PAGE_ACCUEIL_VOTRE_MODULE=Votre premier accueil de rubrique

VOTRE_PROJET.VOTRE_MODULE.RUBRIQUE.PAGE_ACCUEIL.BO.TITRE=Titre
VOTRE_PROJET.VOTRE_MODULE.RUBRIQUE.PAGE_ACCUEIL.BO.DESCRIPTION=Description

Configuration XML Spring

Il faut ensuite configurer les beans pour votre accueil de rubrique dans le fichier src/main/resources/spring/votreprojet-votremodule.xml Pour les paramètres du préparateur de vue, vous pouvez vous référer à la documentation Principes d'intégration des vues

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <util:constant id="ORDRE_CRITIQUE" static-field="org.springframework.core.Ordered.HIGHEST_PRECEDENCE"/>

    <!-- Préparateur de vue pour votre module -->
    <bean id="votreModuleViewPreparer" class="fr.votreprojet.votremodule.view.preparer.VotreModuleViewPreparer" parent="abstractSectionHomeViewPreparer">
        <property name="order" ref="ORDRE_CRITIQUE"/>
        <property name="view" value="/templates/votretemplate/template/votremodule/index.jsp"/>
        <property name="includedSiteTemplate">
            <list>
                <value>...</value>
            </list>
        </property>
    </bean>

    <!-- Accueil de rubrique pour votre module -->
    <bean id="votreModuleAccueilRubrique" class="fr.votreprojet.votremodule.rubrique.VotreModuleAccueilRubrique">
        <property name="typeRubrique">
            <util:constant static-field="fr.votreprojet.votremodule.rubrique.VotreModuleAccueilRubrique.TYPE_ACCUEIL_RUBRIQUE"/>
        </property>
        <property name="libelle" value="RUBRIQUE.PAGE_ACCUEIL_VOTRE_MODULE"/>
        <property name="interne" value="true"/>
        <property name="pathSaisieBo" value="/WEB-INF/jsp/votremodule/bo/saisie.jsp"/>
    </bean>
</beans>
  • Notes :
    • Déclaration du préparateur de vue votreModuleViewPreparer :
      • order : L'ordre de priorité du préparateur de vue. Pour des préparateurs de vue liés au projet, on utilisera la priorité Spring HIGHEST_PRECEDENCE ;
      • view : Le chemin vers la jsp de la vue ;
      • includedSiteTemplate / excludedSiteTemplate : Les templates de site à inclure/exclure ;
      • type : Le type de vue (ici, il est déclaré dans le bean abstractSectionHomeViewPreparer) ;
    • Déclaration de l'accueil de rubrique votreModuleAccueilRubrique :
      • typeRubrique : Le code de votre nouvel accueil de rubrique. Il doit être unique ;
      • libelle : Le libellé qui apparaîtra dans la liste déroulante "Type d'écran" (Gestion des sites > Rubriques > Ma Rubrique > Informations générales > Page d'accueil > Type d'écran) ;
      • interne : Indique si la redirection vers la page d'accueil sera faite de manière invisible pour l'utilisateur (via un forward de la requête) ou non (via un redirect de la requête). Dans le cas d'une redirection "interne", l'URL du navigateur reste celle de la rubrique. Cela permet de masquer l'URL "technique".
      • pathSaisieBo (optionnel) : Une JSP qui sera affichée pour la saisie en BO.

Affichage des données

Back-office

Afin de permettre au contributeur de renseigner un titre et une description, nous avons déclaré dans notre bean votreModuleAccueilRubrique une JSP permettant la saisie back-office. Elle est placée dans src/main/webapp/WEB-INF/jsp/votremodule/bo/saisie.jsp :

<jsp:useBean id="infoBean" class="com.jsbsoft.jtf.core.InfoBean" scope="request"/>
<jsp:useBean id="univFmt" class="com.univ.utils.UnivFmt" scope="page"/>
<jsp:useBean id="fmt" class="com.jsbsoft.jtf.core.FormateurJSP" scope="page"/>
<% 
    univFmt.insererChampSaisie(fmt, out, infoBean, "TITRE", FormateurJSP.SAISIE_OBLIGATOIRE, FormateurJSP.FORMAT_TEXTE,0, 255, module.getMessage("VOTRE_PROJET.VOTRE_MODULE.RUBRIQUE.PAGE_ACCUEIL.BO.TITRE")); 
    univFmt.insererChampSaisie(fmt, out, infoBean, "DESCRIPTION", FormateurJSP.SAISIE_FACULTATIVE, FormateurJSP.FORMAT_TEXTE,0, 255, module.getMessage("VOTRE_PROJET.VOTRE_MODULE.RUBRIQUE.PAGE_ACCUEIL.BO.DESCRIPTION"));
%>

  • Dans la JSP de saisie back-office, il faut ajouter deux champs de saisie, l'un pour le titre (qui sera obligatoire) et l'autre pour la description, qui est facultative.

Front-office

Pour afficher nos données, il est nécessaire de créer la JSP déclarée dans le bean votreModuleViewPreparer, à l'emplacement suivant : src/main/webapp/templates/votretemplate/template/votremodule/index.jsp.

<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="kuik" uri="kuik" %>

<%--@elvariable id="bodyViewModel" type="fr.votreprojet.votremodule.view.model.VotreModuleViewModel"--%>
<%--@elvariable id="mainViewModel" type="com.kosmos.template.MainViewModel"--%>

<c:if test="${not empty mainViewModel.bodyType}">
    <c:set var="bodyTypeClass" value="main--${mainViewModel.bodyType}"/>
</c:if>

<main class="main ${bodyTypeClass} l-grid l-expanded-body__expander" data-layout="page">
    <div class="l-content">
        <kuik:heading title="${bodyViewModel.titre}" level="1" size="md" class="visually-hidden" />

        <div class="box box--light box--expand">
            <c:if test="${not empty bodyViewModel.description}">
                <p><c:out value="${bodyViewModel.description}"/></p>
            </c:if>
        </div>
    </div>
</main>
  • L'instruction trimDirectiveWhitespaces permet d'éviter les sauts de lignes indésirables. Si sa présence n'est pas obligatoire, elle reste fortement conseillée.
  • Pour afficher le titre de l'accueil de rubrique, nous utilisons la librairie kuik. Cette librairie est présente par défaut avec le produit. Sa documentation est disponible ici.
  • Pour connaître le type de body sur lequel nous nous trouvons, nous pouvons récupérer la propriété bodyType. La classe contenue dans cette propriété est utilisée pour l'intégration.

Configuration Spring

Si votre accueil de rubrique fait appel à un contrôleur (par exemple, chargement de données via l'appel à une URL dans un datagrid), il faut penser à ajouter l'annotation @ComponentScan(basePackages = {"fr.votreprojet"}) dans la classe de configuration.

package fr.votreprojet.context;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(basePackages = {"fr.votreprojet"})
@EnableWebMvc
public class VotreProjetConfig {
    // Configuration beans si nécessaire
}

Modifier le fichier

Utilisation des points d'extensions des vues en front office (frontAddOn)

Un frontAddOn est un point d'extension présent dans une jsp. Il permet d'injecter un affichage sans avoir à impacter la jsp mère.

Par exemple: l'extension panier injecte un bouton d'ajout dans le panier sur une fiche de K-Sup qui ne connaît pas panier.

Une JSP peut contenir plusieurs frontAddOn pour proposer des points d'extensions à différents endroits de la vue. Un frontAddOn est décrit par un identifiant permettant de le cibler (pointName).

Utilisation des frontAddOn

Un FrontAddon est toujours contextuel à un contexte. Le contexte est le viewModel courant. Cela permet de gérer des FrontAddon au niveau des item de liste.

Injecter une vue spécifique dans un frontAddOn

En utilisant un frontAddOn, il est possible d'injecter une vue dans une autre JSP, sans impacter cette dernière.

Identifier un frontAddOn

Un frontAddOn possède un paramètre obligatoire et peut avoir des paramètres optionnels.

Paramètre obligatoireDescriptionFormat de donnéeExemple de valeur
pointNameIdentifiant permettant de cibler le point d'extension.Chaine de caractèreapplicationcontent-tools
contextLe contexte dans lequel recherche l'addon pour le pointNameIViewModel${applicationViewModel}

Un frontAddOn peut mettre à dispositions un ou plusieurs paramètres, qui seront alors utilisables par les vues injectées.

Exemple: La JSP ficheApplication propose un point d'extension, tout en mettant à disposition son modèle :

<kore:frontAddOn pointName="applicationcontent-tools" context="${applicationViewModel}"/>

Injecter une vue

La vue injectée est un viewModel standard de refronte. C'est donc le résultat d'un viewPreparer.

<bean id="acmeAddon1" class="com.kosmos.ViewPreparer">
    <property name="type" value="addon1"/>
    <property name="view" value="/extensions/acme/WEB-INF/jsp/addon1.jsp"/>
    <property name="pointNames">
        <list>
            <value>emplacement13</value>
            <value>emplacement42</value>
        </list>
    </property>
</bean>

Pour injecter une vue dans un frontAddon, il suffit abonner son preparer aux addons de la vue soit directement dans la déclaration, soit à l'aide d'un ListeToAddBean.

<bean id="acmeViewPreparer" class="com.kosmos.AcmeViewPreparer">
    <property name="type" value="acmeType"/>
    <property name="view" value="/extensions/acme/WEB-INF/jsp/acme.jsp"/>
    <property name="expectedViewTypes">
        <list>
            <value>expected1</value>
            <value>...</value> 
         </list>
    </property>
    <property name="addons">
        <list>
            <value>addon1</value>
            <value>...</value> 
        </list>
    </property>
</bean>

Ajouter un addOn dans une jsp

Le paramètre pointName est l'identifiant du point d'extension.

Pour proposer un point d'extension dans une JSP, il faut utiliser le tag frontAddOn:

    ...
    <div class="app-header__links">
        <kore:frontAddOn pointName="emplacement13" context="${mainViewModel}"/>
        ...
    </div>
    ...

Modifier le fichier

Liens d'évitement

Description

Les liens d’évitement permettent aux utilisateurs naviguant au clavier, ou équipés de lecteurs d'écran, d’accéder plus rapidement à des zones précises de la page.

Mise en oeuvre

Le produit fournit 3 preparers pour générer les liens d'évitements vers le header de recherche, vers le contenu principal et vers le footer.

Le lien d'évitement consiste simplement en un lien de type "a href" vers une ancre de la la page.
Le fonctionnement impose donc que les composants ciblés possèdent un identifiant unique.
Par exemple, 'header-search-trigger' pour le header de recherche,

...
<kuik:button status="neutral" icon="true" popovertarget="panel-search" id="header-search-trigger">
    <kuik:icon source="icon://ui/search" size="md"/>
...

'content' pour le contenu principal,

...
<main id="content" class="main ${bodyTypeClass} l-grid l-expanded-body__expander u-rg-sm">
    <div>
...

et 'footer' pour le footer,

...
<footer id="footer" class="footer" role="contentinfo">
    <div class="footer__body">
...

En cas de surcharge de JSP, il est donc important de bien positionner les identifiants si nécessaire.

Chaque composant à la responsabilité d'appeler son preparateur de lien d'évitement.
Cela garantit que le lien d'évitement vers le contenu ne sera généré que si le contenu est généré.
Par exemple, le lien d'évitement vers le bandeau de recherche est un item de l'"expectedViewTypes" du composant panelSearchBarViewPreparer:

    <bean id="panelSearchBarViewPreparer" class="com.kosmos.search.PanelSearchBarViewPreparer">
        <property name="type" value="panel-search-bar" />
        <property name="view" value="/WEB-INF/jsp/search/panel-search-bar.jsp" />
        <property name="expectedViewTypes">
            <value>skiplink-header-search</value>
        </property>
    </bean>

Modifier le fichier

Entêtes HTTP du document principal

Lors de l'affichage d'une page, des entêtes Http sont automatiquement ajoutés à la réponse lors de l'appel de la méthode utilitaire ViewModelHelper.prepareViewModel.

Le mécanisme se base sur une liste de préparateurs qui s'appliquent selon le contexte d'affichage. Une fois le modèle préparé, le système va itérer sur les beans de type HttpHeaderPreparer et appliquer tous ceux qui acceptent le contexte.

Entêtes produit

Le produit ajoute 3 entêtes sur tous les documents affichés.

  • Un entête Referrer-Policy
  • Un entête X-Content-Type-Options
  • Un entête Content-Security-Policy

Propriétés

Les valeurs écrites dans les entêtes peuvent êtres surchagées en projet si besoin

PropriétéDescriptionValeurs possiblesValeur par défaut
header.front.referrer-policyValeur du header Referrer-PolicyChaîne de caractères (cf MDN)no-referrer
header.front.x-content-type-optionsValeur du header X-Content-Type-OptionsChaîne de caractères (cf MDN)nosniff
header.front.content-security-policyValeur du header Content-Security-PolicyChaîne de caractères (cf MDN)default-src 'self' 'unsafe-inline'; img-src https://* data:

Mise en oeuvre d'un nouvel entête

Une classe abstraite permet de stocker le nom du header et la valeur à ajouter et propose une implémentation simple permettant d'ajouter le header dans la réponse.

Pour déclarer un header à ajouter inconditionnellement sur le document principal, il suffit simplement de déclarer un bean de type DefaultHttpHeaderPreparer.

...
<bean id="<<ID_DU_BEAN>>" class="com.kosmos.http.header.DefaultHttpHeaderPreparer">
    <property name="name" value="<<NOM_DE_L_ENTETE>>"/>
    <property name="value" value="<<VALEUR_DE_L_ENTETE>>"/>
</bean>
        ...

Ce header sera alors automatiquement ajouté dans la réponse.

Parfois, la valeur du header dépend du contexte ou ne doit pas être systématiquement ajoutée. Dans ce cas, il faut

  • Créer une classe qui étend AbstractHttpHeaderPreparer et implémenter la méthode accept et/ou prepare selon le besoin.
...
public class MonHeaderSpecifiqueHttpHeaderPreparer extends AbstractHttpHeaderPreparer {

    @Override
    public boolean accept(FrontContext context) {
        return conditionEstRemplie(context);
    }

    @Override
    public void prepare(final FrontContext context, final HttpServletResponse response) {
        response.setHeader(name, calculerValeurHeader(context));
    }
    
    ...
}
  • Déclarer le bean dans le fichier de contexte du projet.
...
<bean id="<<ID_DU_BEAN>>" class="com.kosmos.xxx.yyy.MonHeaderSpecifiqueHttpHeaderPreparer">
    <property name="name" value="<<NOM_DE_L_ENTETE>>"/>
    <property name="monService" ref="monService"/>
    ...
</bean>
...

Modifier le fichier

Architecture générale du panel des préférences

Le panel des préférences est un composant de l'interface utilisateur permettant aux utilisateurs de personnaliser divers aspects de l'application. Ce panel est organisé sous forme d'onglets, chaque onglet représentant une catégorie différente de paramètres.

Le panel des préférences est ajouté dans la page par le préparer SettingsViewPreparer lié à la JSP settings/settings.jsp Le panel est un receptacle sans contenu qui sera peuplé à chaque ouverture du panel et vidé à chaque fermeture.

Affichage du panel des préférences

Le panel des préférences est de type popover. Il est déclenché par un bouton contenant un attribut popovertarget="settings-panel"

Pour sélectionner un onglet particulier à l'ouverture, il faut positionner un attribut data-url-params. Par exemple l'attribut data-url-params="tab=user-settings-profile" permet de pré-sélectionner l'onglet user-settings-profile à l'ouverture.

Le panel écoute les évènements toggle pour gérer le chargement de son contenu et son nettoyage.

A l'ouverture du panel un appel fetch est réalisé sur le controller UserSettingsController. Ce controller a pour fonction d'appeler le preparer UserSettingsViewPreparer. Ce préparer est configuré pour préparer un ensemble d'expectedViews

    <bean id="userSettingsViewPreparer" class="com.kosmos.user.settings.UserSettingsViewPreparer">
        <property name="type" value="#{T(com.kosmos.user.settings.UserSettingsViewPreparer).TYPE}"/>
        <property name="view" value="/WEB-INF/jsp/settings/settings-panel.jsp"/>
        <property name="expectedViewTypes">
            <list>
                <value>#{T(com.kosmos.user.settings.profile.ProfileSettingsTabViewPreparer).TYPE}</value>
                <value>onglet2TabSettings</value>
                <value>onglet3TabSettings</value>
            </list>
        </property>
    </bean>

Rendu du panel

Le fichier settings-panel.jsp est responsable du rendu de base du panel des préférences.

<kuik:tabs id="settings-tabs" size="md">
    <c:forEach var="settingsTab" items="${userSettingsViewModel.tabs}">
        <kore:include viewModel="${settingsTab}"/>
    </c:forEach>
</kuik:tabs>

Gestion des onglets côté client

Le fichier user-settings.js contient la fonction handleTabChange() qui attache des écouteurs d'événements aux boutons d'onglets pour gérer les changements d'onglets.

Par défaut, si un attribut data-url est disponible sur l'onglet cliqué, l'url est appeléer et le contenu (html) est inséré dans l'élément ciblé par l'attribut data-body de l'onglet cliqué.

Un groupe de ressource nommé settings dans le bundle.json permet d'ajouter des fichiers javascript nécessaires au fonctionnement des onglets. Par exemple,

    ...
    "settings": {
      "files": [
        "META-INF/resources/static/js/user-settings-profile.js"
      ],
      "type": "module"
    }
    ...

Ajout d'un nouvel onglet

Pour ajouter un nouvel onglet au panel des préférences, suivez ces étapes:

Étape 1: Créer un nouveau ViewModel pour l'onglet

Créez une classe qui étend UserSettingsTabViewModel:

package com.kosmos.user.settings.mononglet;

import com.kosmos.user.settings.UserSettingsTabViewModel;

public class MonNouvelOngletViewModel extends UserSettingsTabViewModel {
    // Ajoutez des propriétés spécifiques à votre onglet si nécessaire
}

Étape 2: Créer un nouveau Controller pour l'onglet

Créez un contrôleur qui gérera les requêtes spécifiques à votre onglet:

package com.kosmos.user.settings.mononglet;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

import com.kosmos.user.settings.AbstractSettingsController;

@Controller
public class MonNouvelOngletController extends AbstractSettingsController {

    public static final String URL_MON_ONGLET = "mon_nouvel_onglet.mapping";

    /**
     * Point d'entrée pour l'affichage de l'onglet.
     */
    @GetMapping(value = "${" + URL_MON_ONGLET + "}")
    public ModelAndView getMonNouvelOnglet() {
        // Logique spécifique
        
        final SettingsContext context = initContext();
        return prepareView(context, MonNouvelOngletViewPreparer.TYPE, request, response);
    }
}

Étape 3: Créer un ViewPreparer pour l'onglet

Créez un préparateur qui définira l'ordre, l'icône et d'autres propriétés de l'onglet:

package com.kosmos.user.settings.mononglet;

import java.util.List;
import java.util.Map;

import com.kosmos.context.front.FrontContext;
import com.kosmos.preparer.IViewPreparer;
import com.kosmos.user.settings.SettingsContext;
import com.kosmos.user.settings.UserSettingsTabViewModel;
import com.kosmos.user.settings.UserSettingsTabViewPreparer;

public class MonNouvelOngletTabViewPreparer<V extends UserSettingsTabViewModel> extends UserSettingsTabViewPreparer<V> {

    public static final String TYPE = "mon-nouvel-onglet-settings-tab";
    protected static final String TAB_IDENTIFIER = "user-settings-mon-onglet";

    @Override
    public V prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
        final V viewModel = super.prepare(context, preparers);
        viewModel.setUrl(MonNouvelOngletController.buildUrl());
        final SettingsContext settingsContext = SettingsContext.of(context);
        if (settingsContext.getSelectedTab() != null) {
            viewModel.setActive(TAB_IDENTIFIER.equals(settingsContext.getSelectedTab()));
        }
        return viewModel;
    }
}

Étape 4: Créer le JSP pour l'onglet

Créez un fichier JSP pour le contenu de votre onglet, par exemple mon-nouvel-onglet.jsp dans le répertoire settings:

<%@ page trimDirectiveWhitespaces="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="kuik" uri="kuik" %>

<div class="mon-nouvel-onglet-container">
    <!-- Contenu de l'onglet -->
    <h2>Mon nouvel onglet</h2>
    <p>Contenu de mon nouvel onglet...</p>
</div>

Étape 5: Configurer l'onglet dans le fichier de configuration Spring

Ajoutez la configuration de votre onglet dans un fichier XML de configuration Spring:

<bean id="monNouvelOngletTabViewPreparer" class="com.kosmos.user.settings.mononglet.MonNouvelOngletTabViewPreparer">
    <property name="type" value="#{T(com.kosmos.user.settings.mononglet.MonNouvelOngletTabViewPreparer).TYPE}"/>
    <property name="view" value="/WEB-INF/jsp/settings/mononglet/tab.jsp"/>
    <property name="tabIcon" value="${user-setting.tab.mononglet.icon:icon://ui/user}"/>
    <property name="displayOrder" value="42"/>
</bean>

<bean id="monNouvelOngletViewPreparer" class="com.kosmos.user.settings.profile.MonNouvelOngletViewPreparer">
    <property name="type" value="#{T(com.kosmos.user.settings.profile.MonNouvelOngletViewPreparer).TYPE}"/>
    <property name="view" value="/WEB-INF/jsp/settings/mononglet/tab-body.jsp"/>
    <!-- Ajoutez d'autres propriétés si nécessaire -->
</bean>

<bean class="com.kportal.core.context.ListToAddBean">
    <property name="idBeanToMerge" value="userSettingsViewPreparer"/>
    <property name="listToMerge" value="expectedViewTypes"/>
    <property name="add">
        <list>
            <value>#{T(com.kosmos.user.settings.mononglet.MonNouvelOngletTabViewPreparer).TYPE}</value> <!-- Ajoutez votre nouvelle entrée ici -->
        </list>
    </property>
</bean>

Paramètres importants

  • displayOrder : L'ordre d'affichage dans les onglets (les valeurs plus petites s'affichent en premier)

Étape 6: Déclarer l'URL du contrôleur

Ajoutez la propriété d'URL dans votre fichier de propriétés de l'application:

mon_nouvel_onglet.mapping=/settings/mon-nouvel-onglet

Modifier le fichier

Configuration d'un domaine statique pour les ressources

Il est possible de configurer un domaine statique pour les ressources statiques servies. Ce mécanisme permet de partager les ressources statiques entre les différents sites K-Sup (partage de cache navigateur et éventuellement apache)

Mise en peuvre

Configuration K-Sup

  • Valoriser la propriété "static.resource.domain" dans le env.properties avec le nom de domaine statique (par exemple https://static.acme.com)

Ce nom de domaine est injecté dans le contexte de la requête via le tag ksup:staticDomain (cf META-INF/resources/WEB-INF/jsp/head.jsp)

...
<head>
    <c:if test="${not empty headViewModel.staticDomain}">
        <resources:staticDomain domain="${headViewModel.staticDomain}"/>
    </c:if>
    <title><c:out value="${headViewModel.title}" /></title>
    ...

Tous les scripts ajoutés via les tag resources:link, resources:script ou resources:getScripts seront automatiquement prexifés avec le nom du domaine statique.

Ce mécanismse fonctionne en collaboration avec un filtre CORS (cf src/main/java/com/kosmos/http/filter/StaticDomainCORSFilter.java) qui ajoute un header Access-Control-Allow-Origin et permet au navigateur d'autoriser le chargement de la ressource.

Note: Ce mécanisme ne fonctionne pas avec les fichiers SVG (le SVG est un format xml et le navigateur interdit le chargement d'un xml en dehors du domaine courant, y compris sur les sous-domaines.

Configuration du frontal web

Un site virtuel doit être mis en place sur le frontal web afin de répondre aux requêtes sur le domaine statique. Ce site virtuel va ensuite récrire le header Host avec le domaine du site principal et formwarder la requete vers le K-Sup.

Un cache peut également être mis en oeuvre afin de décharger le tomcat.

Par exemple, pour un serveur apache,

   <VirtualHost *:*>
      ServerName static.acme.com
      ProxyPass / "http://tomcat:8080/"
      RequestHeader set Host "www.acme.com"
      ProxyPreserveHost On
      
      <IfModule mod_cache.c>
         CacheQuickHandler on
         CacheHeader on
         CacheIgnoreNoLastMod On
         CacheDefaultExpire 7200
         CacheIgnoreCacheControl On
         <IfModule mod_cache_disk.c>
            CacheRoot "/var/cache/apache2/mod_cache_disk"
            CacheEnable disk  "/"
            CacheDirLevels 3
            CacheDirLength 3
            CacheMaxFileSize 2000000
            CacheIgnoreHeaders Set-Cookie Cookie
         </IfModule>
      </IfModule>
   </VirtualHost>

Modifier le fichier

Surcharge de tags

Mise en œuvre

  1. Activer le profil maven override-tags
  2. Surcharger le fichier .tag dans src/main/webapp/WEB-INF/tags

Maven

Le profile override-tags permet de surcharger les tags de kuik et de koreparent/webapp-front en simulant un overlay des jar concernés dans la webapp du projet.

Cette simulation consiste à copier les fichiers dans le target au build.

Surcharge du fichier tag

Par exemple, pour surcharger le tag button.tag de la bibliothèque kuik, il faut créer le fichier src/main/webapp/WEB-INF/tags/kuik/button/button.tag.

Modifier le fichier

Gestion des Liens en Front

Table des matières

  1. Vue d'ensemble
  2. Architecture du système de liens
  3. LinkRule - Règles de traitement des liens
  4. Types de liens prédéfinis
  5. Affichage en front-office
  6. Surcharge et personnalisation côté projet
  7. Configuration

Vue d'ensemble

Le système de gestion des liens dans KSUP permet de :

  • Préparer et formater automatiquement les liens selon leur type (externe, interne, etc.)
  • Appliquer des règles métier sur les attributs des liens (target, rel, class, title)
  • Surcharger les comportements par défaut au niveau du projet
  • Gérer les liens multilingues (hreflang)

Architecture du système de liens

Composants principaux

LinkBuilderFactory
    ↓ utilise
LinkRule (abstract)
    ↓ implémente
[ExternalLinkRule, NotSameSiteLinkRule, HrefLangLinkRule, ...]
    ↓ applique sur
ILink (interface)

Fichiers clés

  • Core : core/koreparent/webapp-front/src/main/java/com/kosmos/link/

    • LinkBuilderFactory.java : Fabrique de liens avec injection des règles
    • LinkContext.java : Contexte de calcul contenant les données
    • LinkUtils.java : Utilitaires (détection liens externes, etc.)
    • rules/LinkRule.java : Classe abstraite de base
    • rules/ExternalLinkRule.java : Règle pour liens externes
    • rules/NotSameSiteLinkRule.java : Règle pour liens vers autres sites
    • rules/HrefLangLinkRule.java : Règle pour attributs hreflang
  • Configuration : core/koreparent/webapp-front/src/main/resources/core-front-link.xml


LinkRule - Règles de traitement des liens

Classe abstraite LinkRule

package com.kosmos.link.rules;

import org.springframework.core.Ordered;
import com.kosmos.link.LinkContext;
import fr.kosmos.web.kore.attributes.interfaces.ILink;

public abstract class LinkRule implements Ordered {

    protected String type;
    protected LinkUtils linkUtils;
    private int order;

    /**
     * Vérifie si la règle est applicable pour le contexte donné.
     * @param context le contexte de calcul du lien
     * @return true si la règle est applicable, false sinon
     */
    public boolean accept(final LinkContext context) {
        return !context.getOrElse(type, Boolean.class, Boolean.FALSE);
    }

    /**
     * Applique la règle au lien donné et indique dans le contexte
     * que la règle a été appliquée.
     * @param context le contexte de calcul du lien
     * @param link le lien à calculer
     */
    public void apply(final LinkContext context, final ILink link) {
        context.setValue(type, Boolean.TRUE);
    }

    // getters/setters...
}

Méthodes clés

MéthodeDescriptionUsage
accept(LinkContext)Détermine si la règle s'applique au contexteOverride pour ajouter des conditions
apply(LinkContext, ILink)Applique les modifications au lienOverride pour modifier les attributs du lien
getOrder()Ordre d'exécution (implements Ordered)Définit la priorité d'application

Types de liens prédéfinis

1. ExternalLinkRule

Fichier : com.kosmos.link.rules.ExternalLinkRule

Détection : Liens externes (domaine différent du site courant)

Attributs appliqués :

  • target : _blank (configurable)
  • title : Message i18n FO.LINK.EXTERNAL_SITE.TITLE
  • rel : noopener noreferrer (configurable)
  • class : Classes CSS personnalisables

Code :

public class ExternalLinkRule extends LinkRule {

    protected String target;
    protected String title;
    protected String relation;
    protected String classes;

    public ExternalLinkRule() {
        this.type = "externalLinkRule";
    }

    @Override
    public boolean accept(final LinkContext context) {
        return super.accept(context)
            && linkUtils.isExternal(context.getUrl());
    }

    @Override
    public void apply(final LinkContext context, final ILink link) {
        super.apply(context, link);
        link.setTarget(target);
        link.setTitle(MessageHelper.getCoreMessage(title));
        link.setRelation(relation);
        link.setClasses(classes);
    }

    // setters...
}

2. NotSameSiteLinkRule

Fichier : com.kosmos.link.rules.NotSameSiteLinkRule

Détection : Liens vers d'autres sites de la même plateforme

Attributs appliqués :

  • target : _blank (configurable)
  • title : Message i18n FO.LINK.NOT_SAME_SITE.TITLE
  • class : Classes CSS personnalisables

3. HrefLangLinkRule

Fichier : com.kosmos.link.rules.HrefLangLinkRule

Détection : Liens multilingues

Attributs appliqués :

  • hreflang : Code langue du lien cible

Variantes :

  • RelativeUrlHrefLangLinkRule : Pour URLs relatives
  • AbsoluteUrlHrefLangLinkRule : Pour URLs absolues

Affichage en front-office

L'affichage de lien en front-office est géré par la composant kuik:link.

Exemple d'utilisation du composant :

<kuik:link link="${userItem.link}" class="menu-list__link">
    <c:if test="${not empty userItem.icon}">
        <kuik:icon-content icon="${userItem.icon}" class="icon-content--centered" iconTitle="">
            <c:out value="${userItem.title}"/>
        </kuik:icon-content>
    </c:if>
</kuik:link>

Le composant prend en paramètre un objet fr.kosmos.web.kore.attributes.interfaces.ILink et peut contenir un body.

Voir la documentation du composant link.


Surcharge et personnalisation côté projet

Surcharge d'une règle existante

Exemple : Personnalisation de ExternalLinkRule pour le menu QuickAccess

Création d'un preparer spacifique pour le menu dans le projet pour indiquer dans le context que l'on prépare le menu QuickAccess :

package com.kosmos;

import java.util.Map;

import com.kosmos.context.front.FrontContext;
import com.kosmos.menu.quickaccess.QuickAccessViewModel;
import com.kosmos.menu.quickaccess.QuickAccessViewPreparer;
import com.univ.objetspartages.bean.RubriqueBean;

import fr.kosmos.web.kore.attributes.interfaces.QuickAccessMenu;

public class DevQuickAccessViewPreparer extends QuickAccessViewPreparer {

  @Override
  public QuickAccessViewModel prepare(final FrontContext context, final Map preparers) {
    context.setValue("menu", "quickaccess");
    return super.prepare(context, preparers);
  }

  @Override
  protected QuickAccessMenu populateQuickAccessMenu(final RubriqueBean section, final FrontContext context) {
    context.setValue("menuItem", section);
    return super.populateQuickAccessMenu(section, context);
  }
}

Création de la règle de calcul du lien externe qui étend ExternalLinkRule pour les quickAccess :

package com.kosmos;

import com.kosmos.link.LinkContext;
import com.kosmos.link.rules.ExternalLinkRule;
import com.univ.objetspartages.bean.RubriqueBean;

/**
 * Règle personnalisée pour les liens externes du menu "Quick Access"
 * avec un item spécifique "yolo".
 */
public class QuickAccessExternalLinkRule extends ExternalLinkRule {

    @Override
    public boolean accept(final LinkContext context) {
        // Applique la règle parent ET les conditions spécifiques
        return super.accept(context)
            && context.get("menu", String.class).equals("quickaccess")
            && context.get("menuItem", RubriqueBean.class).getIntitule().equals("yolo");
    }

    @Override
    public void apply(final LinkContext context, final ILink link) {
        super.apply(context, link);

        // Personnalisations supplémentaires
        link.setClasses(link.getClasses() + " quick-access-external");
    }
}

Déclaration de la règle :

<bean id="quickAccessExternalLinkRule"
      class="com.kosmos.QuickAccessExternalLinkRule"
      parent="linkRule">
    <property name="target" value="_blank" />
    <property name="title" value="CUSTOM.LINK.QUICK_ACCESS.TITLE" />
    <property name="relation" value="noopener noreferrer" />
    <property name="classes" value="external-link quick-access" />
    <property name="order" value="-1000" />
</bean>

Méthode 2 : Création d'une nouvelle règle

Exemple : Règle pour liens PDF

Création d'un nouvelle classe de règle qui étend LinkRule :

package com.kosmos;

import com.kosmos.link.LinkContext;
import com.kosmos.link.rules.LinkRule;
import fr.kosmos.web.kore.attributes.interfaces.ILink;
import org.apache.commons.lang3.StringUtils;

/**
 * Règle pour les liens vers fichiers PDF.
 * Ajoute des attributs spécifiques : icône, tracking, download.
 */
public class PdfLinkRule extends LinkRule {

    private String pdfIcon;
    private boolean forceDownload;

    public PdfLinkRule() {
        this.type = "pdfLinkRule";
    }

    @Override
    public boolean accept(final LinkContext context) {
        return super.accept(context)
            && context.getUrl() != null
            && context.getUrl().toLowerCase().endsWith(".pdf");
    }

    @Override
    public void apply(final LinkContext context, final ILink link) {
        super.apply(context, link);

        // Ajout d'une classe CSS spécifique
        String currentClasses = link.getClasses();
        link.setClasses(StringUtils.isNotBlank(currentClasses) ? currentClasses + " pdf-link" : "pdf-link");


        // Title personnalisé
        String title = link.getTitle();
        link.setTitle(StringUtils.isNotBlank(title) ? title + " (PDF)" : "Document PDF");
    }

    private String extractFileName(String url) {
        return url.substring(url.lastIndexOf('/') + 1);
    }
}

Déclaration de la règle :

<bean id="pdfLinkRule"
      class="com.kosmos.PdfLinkRule"
      parent="linkRule">
    <property name="order" value="-1000" />
</bean>

Configuration

Configuration Spring

Fichier : core-front-link.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Factory principale -->
    <bean id="linkBuilderFactory" class="com.kosmos.link.LinkBuilderFactory">
        <property name="rules">
            <bean class="com.kosmos.factory.CoreBeanListFactoryBean">
                <property name="beanType" value="com.kosmos.link.rules.LinkRule"/>
            </bean>
        </property>
    </bean>

    <!-- Utilitaires -->
    <bean id="linkUtils" class="com.kosmos.link.LinkUtils">
        <property name="serviceInfosSite" ref="serviceInfosSiteProcessus" />
    </bean>

    <!-- Bean parent abstrait -->
    <bean id="linkRule" class="com.kosmos.link.rules.LinkRule" abstract="true">
        <property name="linkUtils" ref="linkUtils" />
    </bean>

    <!-- Règles prédéfinies -->
    <bean id="notSameSiteLinkRule"
          class="com.kosmos.link.rules.NotSameSiteLinkRule"
          parent="linkRule">
        <property name="target" value="${link.not_same_site.target}" />
        <property name="title" value="FO.LINK.NOT_SAME_SITE.TITLE" />
        <property name="classes" value="${link.not_same_site.classes}" />
    </bean>

    <bean id="externalLinkRule"
          class="com.kosmos.link.rules.ExternalLinkRule"
          parent="linkRule">
        <property name="target" value="${link.external_site.target}" />
        <property name="title" value="FO.LINK.EXTERNAL_SITE.TITLE" />
        <property name="relation" value="${link.external_site.relation}" />
        <property name="classes" value="${link.external_site.classes}" />
    </bean>

    <bean id="relativeUrlHrefLangLinkRule"
          class="com.kosmos.link.rules.RelativeUrlHrefLangLinkRule"
          parent="hrefLangLinkRule" />

    <bean id="absoluteUrlHrefLangLinkRule"
          class="com.kosmos.link.rules.AbsoluteUrlHrefLangLinkRule"
          parent="hrefLangLinkRule" />
</beans>

Configuration properties

Fichier : application_core.properties

# Liens externes
link.external_site.target=_blank
link.external_site.relation=noopener noreferrer
link.external_site.classes=external-link

# Liens vers autres sites
link.not_same_site.target=_blank
link.not_same_site.classes=same-platform-link

Messages i18n

Fichier : Message_fr_FR.properties

FO.LINK.EXTERNAL_SITE.TITLE=Lien externe - Nouvelle fenêtre
FO.LINK.NOT_SAME_SITE.TITLE=Lien vers un autre site - Nouvelle fenêtre
LINK_TYPE.EXTERNAL.LABEL=URL du lien externe
LINK_TYPE.EXTERNAL.TIP=Saisissez l'URL complète (ex: https://example.com)
LINK_STYLE.GENERAL.LINK.LABEL=Style du lien
LINK_TYPE.GENERAL.LINK.LABEL=Texte du lien

Ordre d'application des règles

Les règles sont appliquées selon leur propriété order (Ordered interface) :

Plus le order est bas, plus la règle est prioritaire.


Bonnes pratiques

  1. Toujours appeler super.accept() et super.apply() dans les surcharges
  2. Utiliser des noms de type uniques pour éviter les conflits
  3. Définir un ordre approprié selon la spécificité de la règle

Modifier le fichier

Objectif

Cette documentation explique, pas à pas, comment obtenir et utiliser une classe héritant de AbstractControllerUrlBuilder pour construire des URLs d’endpoints de vos controllers (Spring MVC) dans une extension Kosmos.


Rappels des classes en jeu

  • AbstractControllerUrlBuilder (base commune):
    • Résout la base d’URL du DispatcherServlet de l’extension via le bean ControllerUrlResolver.
    • Expose 3 surcharges de buildUrl(...) pour composer l’URL finale à partir d’un chemin relatif, de variables de chemin et/ou de paramètres de requête.
  • CoreControllerUrlBuilder: implémentation prête à l’emploi pour le “core” (contexte par défaut).
  • DemarcheControllerUrlBuilder: implémentation pour l’extension Démarche.
  • ControllerUrlResolver: préfixe vos chemins relatifs par l’URL de base du DispatcherServlet de l’extension. Peut renvoyer une chaîne vide si la servlet dédiée est désactivée.

Emplacements utiles:

  • koreparent/webapp-front/src/main/java/com/kosmos/common/AbstractControllerUrlBuilder.java
  • koreparent/webapp-front/src/main/java/com/kosmos/common/CoreControllerUrlBuilder.java
  • demarche/demarche-webapp-front/src/main/java/com/kosmos/demarche/DemarcheControllerUrlBuilder.java
  • koreparent/core/src/main/java/com/kosmos/controllers/ControllerUrlResolver.java

Quand utiliser un ControllerUrlBuilder ?

Utilisez un UrlBuilder dès que vous devez créer un lien vers un endpoint de controller d’une extension. Il garantit le bon préfixe (base URL de la DispatcherServlet de l’extension) et gère proprement:

  • les segments variables ({id}, {slug}, …),
  • les paramètres de requête (?page=1&size=20),
  • les cas où l’extension est désactivée (URL vide).

Obtenir une instance (Core et Démarche)

  • Core: Récupérer (ApplicationContextManager.getBean()) / injecter le bean coreControllerUrlBuilder dans vos beans.

  • Démarche: Récupérer (ApplicationContextManager.getBean()) / injecter le bean demarcheControllerUrlBuilder dans vos beans.


Construire des URLs: les 3 surcharges de buildUrl

AbstractControllerUrlBuilder expose:

  1. buildUrl(String path)
String url = urls.buildUrl("/users");
// -> "/<base-dispatcher>/users" (préfixée par l’extension)
  1. buildUrl(String path, Map<String, String> variables)
  • Pour les chemins contenant des variables {...}.
Map<String, String> vars = Map.of("userId", "42");
String url = urls.buildUrl("/users/{userId}", vars);
// -> "/<base-dispatcher>/users/42"
  1. buildUrl(String path, MultiValueMap<String, String> queryParams)
  • Pour ajouter des paramètres de requête.
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

MultiValueMap<String, String> qp = new LinkedMultiValueMap<>();
qp.add("page", "1");
qp.add("size", "20");
String url = urls.buildUrl("/users", qp);
// -> "/<base-dispatcher>/users?page=1&size=20"
  1. Complet: buildUrl(String path, MultiValueMap<String,String> queryParams, Map<String,String> variables)
  • Combine variables de chemin et query params.
MultiValueMap<String, String> qp = new LinkedMultiValueMap<>();
qp.add("include", "roles");

Map<String, String> vars = Map.of("userId", "42");
String url = urls.buildUrl("/users/{userId}", qp, vars);
// -> "/<base-dispatcher>/users/42?include=roles"

Sous le capot, la méthode:

UriComponentsBuilder.fromPath(path)
    .queryParams(queryParams)
    .buildAndExpand(variables)
    .toString();

assure l’expansion des variables et l’encodage correct.


Créer votre propre UrlBuilder pour une autre extension

Si votre extension « X » n’a pas encore son builder, créez une classe finale qui hérite de AbstractControllerUrlBuilder et passez l’ID d’extension à super(...).

package com.kosmos.x;

import com.kosmos.common.AbstractControllerUrlBuilder;

public final class XControllerUrlBuilder extends AbstractControllerUrlBuilder {
    public XControllerUrlBuilder() {
        super("x-extension-id"); // l’ID de contexte/extension tel que configuré
    }
}

Bonnes pratiques:

  • Faites référencer l’ID via une constante de config de l’extension (ex.: XConfig.ID_EXTENSION) plutôt qu’une chaîne en dur.
  • Gardez la classe final (utilitaire simple, non destinée à l’héritage).

Ensuite, usage identique:

XControllerUrlBuilder urls = new XControllerUrlBuilder();
String url = urls.buildUrl("/features/{featureId}", Map.of("featureId", "abc"));

Gestion des erreurs et cas limites

  • Bean manquant: si le bean ControllerUrlResolver n’est pas trouvé pour l’extension, une IllegalArgumentException est levée par AbstractControllerUrlBuilder.getUrlResolver(...).
  • Extension désactivée: ControllerUrlResolver peut renvoyer "" (vide). Tenez-en compte si vous affichez l’URL telle quelle dans une vue.
  • Variables non fournies: si path contient {var} mais que la clé manque dans variables, UriComponentsBuilder.buildAndExpand(...) lèvera une IllegalArgumentException.
  • Encodage: UriComponentsBuilder gère l’encodage des query params et des segments de chemin lors de la transformation en chaîne.

Modifier le fichier

Documentation : Comment ajouter une entrée de menu dans le menu profil

Introduction

Cette documentation explique comment ajouter une nouvelle entrée dans le menu profil utilisateur géré par la classe ProfileMenuViewPreparer. Le menu profil est un composant qui affiche les options liées au compte utilisateur, comme la gestion des profils, l'accès à la page d'accueil, les paramètres et la déconnexion.

Architecture du menu profil

Le menu profil est basé sur une architecture de préparateurs de vue (ViewPreparer). La classe principale ProfileMenuViewPreparer gère le conteneur du menu, tandis que des classes spécifiques gèrent chaque entrée du menu.

Le système utilise des expectedViews pour chaque entrée du menu.

Étapes pour ajouter une nouvelle entrée au menu profil

1. Créer une classe pour la nouvelle entrée de menu

Créez une nouvelle classe qui étendra une classe de base appropriée pour les éléments de menu profil. Vous pouvez vous inspirer des classes existantes comme HomeItemMenuViewPreparer, SettingsItemMenuViewPreparer, etc.

package com.kosmos.menu.profile;

public class NouvelleEntreeMenuViewPreparer extends AbstractViewPreparer {
    protected String icon;

    protected int displayOrder;

    @Override
    public boolean accept(final FrontContext context) {
        return super.accept(final FrontContext context) && ....;
    }

    @Override
    public V prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
        final V viewModel = super.prepare(context, preparers);
        final QuickAccessMenu menuItem = new DefaultQuickAccessMenu();
        menuItem.setView(getView());
        menuItem.setTitle(MessageHelper.getCoreMessage("XXXXXXXXX"));
        menuItem.setIcon(icon);
        viewModel.setItem(menuItem);
        viewModel.setOrder(displayOrder);
        return viewModel;
    }

    public void setIcon(final String icon) {
        this.icon = icon;
    }

    public void setDisplayOrder(final int displayOrder) {
        this.displayOrder = displayOrder;
    }
}

2. Définir le bean dans la configuration Spring

Ajoutez un bean pour votre nouvelle entrée de menu dans le fichier de configuration Spring (généralement le même fichier où profileMenuViewPreparer est défini) :

<bean id="nouvelleEntreeItemMenuViewPreparer" class="com.kosmos.menu.profile.NouvelleEntreeMenuViewPreparer">
    <property name="type" value="userMenu-nouvelleEntree" />
    <property name="view" value="/WEB-INF/jsp/menu/nouvelleEntree.jsp" />
    <property name="icon" value="${FRONT_MENU_PROFILE_ICON_NOUVELLE_ENTREE:icon://ui/default-icon}"/>
    <property name="displayOrder" value="12"/>
    <!-- Ajoutez d'autres propriétés si nécessaire -->
</bean>

Paramètres importants

  • displayOrder : L'ordre d'affichage dans le menu (les valeurs plus petites s'affichent en premier)

3. Mettre à jour la liste des types de vues attendus

Modifiez le bean profileMenuViewPreparer pour ajouter votre nouveau type à la liste expectedViewTypes :

<bean id="profileMenuViewPreparer" class="com.kosmos.menu.profile.ProfileMenuViewPreparer">
    <property name="type" value="userMenu" />
    <property name="view" value="/WEB-INF/jsp/menu/profileMenu.jsp" />
    <property name="icon" value="${FRONT_MENU_PROFILE_ICON_USER}"/>
    <property name="expectedViewTypes">
        <list>
            <value>userMenu-profiles</value>
            <value>userMenu-home</value>
            <value>userMenu-settings</value>
            <value>userMenu-logout</value>
            <value>userMenu-nouvelleEntree</value> <!-- Ajoutez votre nouvelle entrée ici -->
        </list>
    </property>
</bean>

La liste peut également être modifiée via un bean ListToAddBean de la forme

    <bean class="com.kportal.core.context.ListToAddBean">
        <property name="idBeanToMerge" value="profileMenuViewPreparer"/>
        <property name="listToMerge" value="expectedViewTypes"/>
        <property name="add">
            <list>
                <value>userMenu-nouvelleEntree</value> <!-- Ajoutez votre nouvelle entrée ici -->
            </list>
        </property>
    </bean>

4. Créer la vue JSP associée

Créez le fichier JSP qui sera utilisé pour rendre votre entrée de menu :

<%-- Fichier: /WEB-INF/jsp/menu/nouvelleEntree.jsp --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<li class="menu-item">
    <a href="${targetUrl}" class="menu-link">
        <c:if test="${not empty icon}">
            <span class="menu-icon">
                <img src="${icon}" alt=""/>
            </span>
        </c:if>
        <spring:message code="menu.nouvelle.entree" />
    </a>
</li>

5. Ajouter les ressources de traduction

Ajoutez les clés de traduction dans vos fichiers de ressources pour supporter le multilinguisme :

# messages_fr.properties
menu.nouvelle.entree=Nouvelle Entrée

# messages_en.properties 
menu.nouvelle.entree=New Entry

Modifier le fichier

Intégration d'un template de site

Création d'un nouveau template

  1. Déclaration d'un bean de type com.kosmos.usinesite.template.bean.impl.SiteTemplateImpl dans le contexte projet en spécifiant le code, le titre et la description.
    Exemple:
<bean id="template1" class="com.kosmos.usinesite.template.bean.impl.SiteTemplateImpl">
    <property name="code" value="TEMPLATE1"/>
    <property name="title" value="Template de site 1"/>
    <property name="description" value="Le template de site 1"/>
</bean>

Ajout d'une propriété complémentaire dans un template

  1. Déclaration des beans de propriété de type com.kosmos.usinesite.template.property.bean.TemplateSiteProperty dans le contexte projet Exemple:
<bean id="couleurFond" class="com.kosmos.usinesite.template.property.bean.impl.TemplateSitePropertyColor">
    <property name="code" value="couleur_fond"/>
    <property name="libelle" value="Couleur de fond"/>
    <property name="description" value="Modifier la couleur de fond de votre site"/>
    <property name="obligatoire" value="false"/>
</bean>
  1. Ajout de la propriété dans la liste des propriétés du template propertyList
    Exemple:
<bean id="template1" class="com.kosmos.usinesite.template.bean.impl.SiteTemplateImpl">
    <property name="code" value="TEMPLATE1"/>
    <property name="title" value="Template de site 1"/>
    <property name="description" value="Le template de site 1"/>
    <property name="propertyList">
        <list>
            <ref bean="couleurFond" />
        </list>
    </property>
</bean>

Modifier le fichier

Layout global

Encadrés (sauf encadrés de fiches)

          | Page d'accueil | Page intérieure |
          |----------------|-----------------|

Fiche accueil | En bas | En bas | Fiche autre | En bas | À droite |

Encadrés de fiche (1)

          | Page d'accueil | Page intérieure |
          |----------------|-----------------|

Fiche accueil | En bas | En bas | Fiche autre | En bas | En bas |

(1) placés avant les autres encadrés car dans le contenu de la page

Plugins (zone par défaut) (2)

          | Page d'accueil | Page intérieure |
          |----------------|-----------------|

Fiche accueil | En bas | En bas | Fiche autre | En bas | En bas |

(2) placés après les encadrés

Sidenav

          | Page d'accueil | Page intérieure              |
          |----------------|------------------------------|

Fiche accueil | NON | NON | Fiche autre | NON | À gauche (desktop seulement) |

Modifier le fichier

Gestion des templates de sites

Le template de site représente la coquille vide qui sert à aggréger les différents préparateurs de vue de qui le compose.

Définition d'un nouveau template

Un template possède les propriétés suivantes :

  • code : Le code permettant d'identifier le template
  • title : L'intitulé du template
  • description : La description du template
  • propertyList : Liste de propriétés complémentaires de type TemplateSiteProperty (couleur, logo, baseline...)
  • active : Indique si le template est actif ou non (true par défaut)

Le template doit être de type SiteTemplateImpl ou doit l'étendre.

Exemple :

<bean id="template1" class="com.kosmos.usinesite.template.bean.impl.SiteTemplateImpl">
    <property name="code" value="TEMPLATE1"/>
    <property name="title" value="Template de site 1"/>
    <property name="description" value="Le template de site 1"/>
    <property name="propertyList">
        <list>
            <ref bean="couleurFond" />
        </list>
    </property>
</bean>

<bean id="couleurFond" class="com.kosmos.usinesite.template.property.bean.impl.TemplateSitePropertyColor">
    <property name="code" value="couleur_fond"/>
    <property name="libelle" value="Couleur de fond"/>
    <property name="description" value="Modifier la couleur de fond de votre site"/>
    <property name="obligatoire" value="false"/>
</bean>

Modifier le fichier

Documentation : Ajouter un nouveau style de liste dans les résultats de recherche

Introduction

Cette documentation explique comment ajouter un nouveau style de liste dans les résultats de recherche en utilisant la classe SearchResultsViewPreparer. Ce mécanisme permet de personnaliser l'affichage des résultats de recherche selon différents types de contenu ou de contextes.

Architecture du système

Le système de recherche utilise le mécanisme de style resolver pour déterminer comment afficher chaque résultat. Voici les composants clés :

  1. SearchResultsViewPreparer : Classe principale qui prépare les résultats de recherche
  2. ListItemStyleResolver : Interface définissant les resolvers de style
  3. AbstractSearchResultListItemStyleResolver : Classe abstraite pour faciliter l'implémentation des resolvers

Fonctionnement général

La classe SearchResultsViewPreparer utilise la méthode createSubcontext pour créer un sous-contexte pour chaque résultat de recherche. Dans cette méthode, le style à utiliser est déterminé en interrogeant tous les ListItemStyleResolver disponibles dans l'application.

private FrontContext createSubcontext(final FrontContext context, final SearchResultDoc doc, AbstractFicheBean fiche) {
    // ...
    final String style = getStyleResolvers().stream()
        .filter(resolver -> resolver.accept(subcontext))
        .findFirst()
        .map(resolver -> resolver.resolve(subcontext))
        .orElse(null);
    subcontext.setValue(ContentListTagProcessor.STYLE, style);
    // ...
}

Comment ajouter un nouveau style de liste

Étape 1 : Créer une classe implémentant ListItemStyleResolver

Vous pouvez étendre AbstractSearchResultListItemStyleResolver pour simplifier l'implémentation :

public class MonNouveauStyleResolver extends AbstractSearchResultListItemStyleResolver {
    
    @Override
    protected String getCodeObjet() {
        return "MON_CODE_OBJET"; // Code de l'objet concerné
    }
    
    @Override
    public String resolve(FrontContext context) {
        return "mon-style"; // Nom du style à utiliser
    }
}

Étape 2 : Déclarer le bean dans le contexte Spring

Créez un bean Spring pour votre resolver :

<bean id="monNouveauStyleResolver" class="com.monpackage.MonNouveauStyleResolver" />

Étape 3 : Créer le template correspondant au style

Créez le template qui sera utilisé pour afficher les résultats avec ce style.

Exemple complet

Voici un exemple complet pour ajouter un style de liste pour les résultats de recherche d'événements :

1. Création du resolver de style

package com.kosmos.evenement.search;

import com.kosmos.context.front.FrontContext;
import com.kosmos.search.results.AbstractSearchResultListItemStyleResolver;

/**
 * Resolver de style pour les résultats de recherche d'événements.
 */
public class EvenementSearchResultListItemStyleResolver extends AbstractSearchResultListItemStyleResolver {
    
    @Override
    protected String getCodeObjet() {
        return "EVENEMENT"; // Code de l'objet événement
    }
    
    @Override
    public String resolve(FrontContext context) {
        return "evenement-card"; // Style à utiliser
    }
}

2. Déclaration du bean

<bean id="evenementSearchResultListItemStyleResolver" class="com.kosmos.evenement.search.EvenementSearchResultListItemStyleResolver" />

3. Création du préparateur de vue pour ce type de résultat

package com.kosmos.evenement.search;

import java.util.List;
import java.util.Map;

import com.kosmos.content.list.CommonContentListItemViewModel;
import com.kosmos.content.list.CommonContentListItemViewPreparer;
import com.kosmos.context.ContentContext;
import com.kosmos.context.front.FrontContext;
import com.kosmos.preparer.IViewPreparer;
import com.kosmos.search.SearchContext;
import com.kosmos.tags.KsupMedia;
import com.ksup.objetspartages.bean.EvenementBean;

/**
 * Préparateur pour les résultats de recherche d'événements.
 */
public class EvenementSearchResultListItemViewPreparer<V extends CommonContentListItemViewModel<KsupMedia>> 
        extends CommonContentListItemViewPreparer<V> {

    @Override
    public boolean accept(final FrontContext context) {
        return super.accept(context) && 
               context instanceof SearchContext && 
               ContentContext.of(context).getContent() instanceof EvenementBean;
    }

    @Override
    public V prepare(final FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
        final V viewModel = super.prepare(context, preparers);
        final ContentContext contentContext = ContentContext.of(context);
        final EvenementBean evenement = (EvenementBean) contentContext.getContent();
        
        // Personnalisation du viewModel pour les événements
        viewModel.setTitle(evenement.getTitre());
        // Ajout d'autres informations spécifiques aux événements
        
        return viewModel;
    }
}

4. Déclaration du préparateur

<bean id="evenementSearchResultListItemViewPreparer" class="com.kosmos.evenement.search.EvenementSearchResultListItemViewPreparer">
    <!-- Définissez ici les dépendances nécessaires -->
</bean>

Fonctionnement détaillé

  1. Lorsqu'un utilisateur effectue une recherche, SearchResultsViewPreparer est appelé pour préparer les résultats.
  2. Pour chaque résultat, un sous-contexte est créé dans createSubcontext.
  3. La méthode getStyleResolvers() récupère tous les beans de type ListItemStyleResolver déclarés dans le contexte Spring.
  4. Le système parcourt ces resolvers pour trouver le premier qui accepte le contexte (méthode accept).
  5. Le resolver sélectionné détermine le style à utiliser (méthode resolve).
  6. Le style est stocké dans le sous-contexte avec la clé ContentListTagProcessor.STYLE.
  7. Ce style est ensuite utilisé pour sélectionner le template approprié pour afficher le résultat.

Cas particuliers et bonnes pratiques

Resolver avec conditions complexes

Si vous avez besoin de conditions plus complexes pour déterminer le style, vous pouvez surcharger la méthode accept :

@Override
public boolean accept(final FrontContext context) {
    if (!(context instanceof SearchContext)) {
        return false;
    }
    
    ContentContext contentContext = ContentContext.of(context);
    if (!(contentContext.getContent() instanceof MonTypeBean)) {
        return false;
    }
    
    // Conditions supplémentaires
    MonTypeBean bean = (MonTypeBean) contentContext.getContent();
    return bean.isSpecial() && bean.getCategory().equals("importante");
}

Styles dynamiques selon le contenu

Vous pouvez également déterminer le style dynamiquement selon le contenu :

@Override
public String resolve(FrontContext context) {
    ContentContext contentContext = ContentContext.of(context);
    MonTypeBean bean = (MonTypeBean) contentContext.getContent();
    
    if (bean.isHighlighted()) {
        return "mon-style-highlight";
    } else if (bean.isNew()) {
        return "mon-style-new";
    }
    
    return "mon-style-default";
}

Conclusion

L'ajout d'un nouveau style de liste dans les résultats de recherche se fait en trois étapes principales :

  1. Créer un resolver de style qui implémente ListItemStyleResolver
  2. Déclarer ce resolver comme bean Spring
  3. Créer les templates correspondants au style

Cette architecture permet une grande flexibilité dans l'affichage des résultats de recherche, en adaptant la présentation selon le type de contenu ou d'autres critères.

Modifier le fichier

Ajouter un style d'affichage pour les listes de sous-rubriques

Introduction

Cette documentation explique comment ajouter un nouveau style d'affichage pour les listes de sous-rubriques.

Étapes pour ajouter un nouveau style

1. Créer un fichier JSP pour le nouveau style

Créez un nouveau fichier JSP pour l'item de liste.

Exemple pour un style à 3 colonnes, créer le fichier result-3-columns.jsp dans le dossier /src/main/resources/META-INF/resources/WEB-INF/jsp/search/.

2. Ajouter les clés de traduction

Ajoutez les clés de traduction pour le nouveau style dans les fichiers de messages :

  • Dans /src/main/resources/Core_front_fr_FR.properties (ou équivalent surchargé) :
    SECTION.HOMEPAGE.SUBSECTIONS_STYLE.3-COLUMNS=3 colonnes
    

3. Configurer le bean Spring pour le nouveau style

Modifiez le fichier /src/main/resources/core-front-section-home.xml pour ajouter un nouveau bean pour votre style (ou dans le fichier de contexte projet):

<bean id="3ColumnsSectionListItemViewPreparer" class="com.kosmos.section.home.subsections.SectionListItemViewPreparer">
    <property name="type" value="section" />
    <property name="order" value="-10000" />
    <property name="view" value="/WEB-INF/jsp/search/result-3-columns.jsp" />
    <property name="supportedStyle" value="3-COLUMNS"/>
    <property name="supportedStyleLabel" value="SECTION.HOMEPAGE.SUBSECTIONS_STYLE.3-COLUMNS"/>
</bean>

4. Ajouter le nouveau bean à la liste des préparateurs

Dans le même fichier XML, ajoutez une référence à votre nouveau bean dans la liste subsectionItemPreparers (étendez la liste à l'aide d'un ListToAddBean en projet):

<util:list id="subsectionItemPreparers">
    <ref bean="defaultSectionListItemViewPreparer"/>
    <ref bean="2ColumnsSectionListItemViewPreparer"/>
    <ref bean="3ColumnsSectionListItemViewPreparer"/>
</util:list>

Personnalisation avancée

Si vous avez besoin d'une logique spécifique pour votre style, vous pouvez créer une sous-classe de SectionListItemViewPreparer.

Puis référencez cette classe dans votre bean Spring au lieu de la classe générique SectionListItemViewPreparer dans le bean Spring.

Modifier le fichier

Extension

K-Sup fonctionne avec l'utilisation d'extensions afin d'isoler les différentes fonctionnalités. Un projet exst alors construit en assemblant les différentes extensions disponibles.

Modifier le fichier

Documentation technique de la fiche article


Code objet : 0015 Table : ARTICLE


Modifier le fichier

Paramétrage de l'extension Article

Le paramétrage de l'extension est réalisé dans le fichier article.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_article.properties dans les sources d'un projet
  • env_article.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeurs possiblesValeur par défault
fiche.ARTICLE.sitemapActivation de la présente des fiches article dans le fichier sitemapbooléen K-Sup : 0 (non affichées) / 1 (affichées)1
fiche.ARTICLE.recherche_avanceeActivation de la recherche avancéebooléen K-Sup : 0 (désactivée) / 1 (activée)1
fiche.ARTICLE.style_affichageActivation des styles d'affichage dans la toolboxbooléen K-Sup : 0 (désactivée) / 1 (activée)1
export_rss.article.classDéfinition d'un export spécifique pour les fiches articleString -> Classe java de l'exportcom.univ.rss.ExportRssArticle
search.article.index.boostBoost de la fiche dans ElasticSearchnumérique25

Modifier le fichier

Documentation technique de la fiche page libre


Code objet : 0016 Table : PAGELIBRE


Modifier le fichier

Paramétrage de l'extension Page Libre

Le paramétrage de l'extension est réalisé dans le fichier pagelibre.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_pagelibre.properties dans les sources d'un projet
  • env_pagelibre.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeur par défault
fiche.PAGELIBRE.sitemapDéfinit que les fiches page libre seront affichées dans le fichier sitemap1
fiche.PAGELIBRE.recherche_avanceeActivation de la recherche avancée1
search.pagelibre.boostBoost de la fiche dans ElasticSearch10

Modifier le fichier

Documentation Technique : Carte "Dernières Actualités"

Vue d'ensemble de l'architecture

La fonctionnalité de carte "Dernières Actualités" est implémentée selon un modèle MVC (Modèle-Vue-Contrôleur) avec les composants clés suivants :

  1. Modèle : ActualiteCardBean - Stocke les données de configuration de la carte
  2. Modèle de Vue : ActualiteCardViewModel - Prépare les données pour l'affichage dans la vue
  3. Constructeur de Vue : ActualiteCardViewBuilder - Transforme le modèle en modèle de vue
  4. Vues : Fichiers JSP pour le rendu front-office et back-office
  5. Logique Côté Client : Fichiers JavaScript pour le chargement dynamique du contenu et les interactions

Détails des composants

Modèle de Données

ActualiteCardBean

  • Package : com.kosmos.actualite.card
  • Étend : CardBean
  • Objectif : Stocke les données de configuration de la carte d'actualités
  • Propriétés Principales :
    • title : Titre de la carte
    • subtitle : Sous-titre de la carte
    • types : Types d'actualités sélectionnés à afficher
    • newsPerTab : Nombre d'actualités par onglet
    • displayStyle : Style d'affichage
    • displayContext : Contexte de filtrage des actualités
    • linkLabel : Libellé du lien "voir toutes les actualités"
  • Chemins des Vues :
    • Front-office : /extensions/actualite/WEB-INF/jsp/actualite/card/fo/actualiteCardView.jsp
    • Back-office : /extensions/actualite/WEB-INF/jsp/actualite/card/bo/actualiteCardView.jsp
    • Fragment d'édition : /extensions/actualite/WEB-INF/jsp/actualite/card/bo/actualiteCardEdit.jsp

Modèle de Vue

ActualiteCardViewModel

  • Package : com.kosmos.actualite.card
  • Étend : CardViewModel<ActualiteCardBean>
  • Objectif : Prépare les données pour l'affichage dans la vue
  • Propriétés Principales :
    • title : Titre de la carte
    • subtitle : Sous-titre de la carte
    • newsTypes : Liste des types d'actualités avec leurs libellés et URLs de recherche
    • newsPerTab : Nombre d'actualités par onglet
    • searchAllNewsUrl : URL pour rechercher toutes les actualités
    • searchAllNewsResultsUrl : URL pour afficher tous les résultats de recherche
    • linkLabel : Libellé du lien "voir toutes les actualités"
  • Classe Interne : NewsTypeViewModel - Représente un type d'actualité avec valeur, libellé et URL de recherche

Constructeur de Vue

ActualiteCardViewBuilder

  • Package : com.kosmos.actualite.card
  • Étend : AbstractViewModelBuilder<ActualiteCardBean, ActualiteCardViewModel>
  • Objectif : Transforme le modèle en modèle de vue
  • Méthodes Principales :
    • getViewModel : Crée un modèle de vue à partir du bean de carte
    • getNewsTypes : Récupère et filtre les types d'actualités à afficher
    • filterTypesFromSearch : Filtre les types d'actualités selon les résultats de recherche
    • buildSearchUrl : Construit les URLs de recherche pour les types d'actualités
    • getTypeLabel : Récupère les libellés des types d'actualités

Filtre de Recherche

DateEvenementFilterBuilder

  • Package : com.kosmos.actualite.search
  • Implémente : SearchQueryBuilder<SearchFilterRangeConfiguration>
  • Objectif : Construit les requêtes de recherche pour filtrer les actualités par date
  • Constantes Principales :
    • DATE_FIN : Nom du champ pour la date de fin d'événement
    • DATE_DEBUT : Nom du champ pour la date de début d'événement
  • Méthode Principale :
    • build : Crée une requête qui filtre les événements en cours et à venir

Vues

Vue Front-office (actualiteCardView.jsp)

  • Emplacement : /extensions/actualite/WEB-INF/jsp/actualite/card/fo/actualiteCardView.jsp
  • Objectif : Affiche la carte d'actualités pour les utilisateurs finaux
  • Fonctionnalités Principales :
    • Affiche le titre et le sous-titre
    • Crée des onglets pour les différents types d'actualités
    • Configure les conteneurs pour le chargement dynamique
    • Ajoute un lien pour voir toutes les actualités

Vue Back-office (actualiteCardView.jsp)

  • Emplacement : /extensions/actualite/WEB-INF/jsp/actualite/card/bo/actualiteCardView.jsp
  • Objectif : Affiche un aperçu de la carte dans le back-office
  • Fonctionnalités Principales :
    • Affiche le nom de la carte
    • Indique si la carte est active ou inactive

Vue d'édition Back-office (actualiteCardEdit.jsp)

  • Emplacement : /extensions/actualite/WEB-INF/jsp/actualite/card/bo/actualiteCardEdit.jsp
  • Objectif : Fournit un formulaire pour configurer la carte
  • Fonctionnalités Principales :
    • Champs de saisie pour titre, sous-titre et libellé du lien
    • Sélection multiple pour les types d'actualités
    • Champ numérique pour le nombre d'actualités par onglet
    • Sélecteur de style d'affichage
    • Sélecteur de contexte d'affichage

Logique Côté Client

Gestionnaire de Carte (actualite_cardsHandlers.js)

  • Emplacement : /js/actualite_cardsHandlers.js
  • Objectif : Gère la validation et les mises à jour de la vue dans le back-office
  • Fonctions Principales :
    • check : Valide si la carte est correctement configurée
    • updateView : Met à jour la vue de la carte dans le back-office

Chargeur de Contenu Dynamique (lastNewsCard.js)

  • Emplacement : /static/js/lastNewsCard.js
  • Objectif : Charge dynamiquement le contenu dans le front-office
  • Fonctions Principales :
    • loadAndDisplayFetchResult : Récupère et affiche le contenu avec un indicateur de chargement
    • handleTabClick : Configure les écouteurs d'événements pour les clics sur les onglets
    • initAllNewsTabs : Initialise l'onglet "Toutes les actualités" au chargement de la page

Flux de Données

  1. Configuration : Les administrateurs configurent la carte via le formulaire d'édition back-office
  2. Création du Modèle : Le système crée un ActualiteCardBean avec les données de configuration
  3. Construction du Modèle de Vue : ActualiteCardViewBuilder transforme le bean en ActualiteCardViewModel
    • Récupère et filtre les types d'actualités
    • Construit les URLs de recherche pour chaque type d'actualités
  4. Rendu : Le JSP front-office affiche la structure initiale de la carte avec les onglets
  5. Chargement Dynamique :
    • Au chargement de la page, lastNewsCard.js charge le contenu de l'onglet "Toutes les actualités"
    • Quand un utilisateur clique sur un onglet, le script charge le contenu pour ce type d'actualité spécifique
    • Le contenu est récupéré depuis les URLs de recherche fournies dans le modèle de vue

Paramétrage de l'extension Article

Le paramétrage de la carte est réalisé dans le fichier actualite.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_actualite.properties dans les sources d'un projet
  • env_actualite.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeurs possiblesValeur par défault
lastNewsCard.allowed.stylesListe des styles dans le sélecteur de style de la carte "Dernières actualités"
(cf actualite-webapp-front/src/main/resources/tables/actualite/actualite_style_affichage.dat)
chaine sérialisée0001,0013
lastNewsCard.default.styleStyle d'affichage à utiliser par défautchaine0001

Localisation

La fonctionnalité supporte la localisation via des fichiers de propriétés :

  • actualite-contrib_fr_FR.properties : Chaînes en français
  • actualite-contrib_en.properties : Chaînes en anglais

Les chaînes de localisation principales incluent le nom de la carte, les libellés des champs et les messages utilisateur.

Points d'Intégration

  • Search : Utilise le système de recherche pour récupérer et filtrer les actualités
  • Layout : S'intègre au système de mise en page comme composant de carte
  • Système de Libellés : Utilise le système de libellés pour les types d'actualités

Modifier le fichier

Module AnnuaireSup

Ce fichier liste les spécificités de la fiche AnnuaireSup.

Modèle de vue

Le modèle de vue de la fiche annuaire AnnuaireContentViewModel possède un sous-modèle de vue AnnuaireRestrictedContentViewModel contenant les champs de la liste rouge. Lorsqu'une fiche est sur liste rouge (listeRouge = 1), ces champs ne sont pas affichés.

Vignette avatar par défaut

Le modèle de vue de la fiche annuaire AbstractAnnuaireContentListItemViewPreparer possède des sous modèles de style de vue Style*AnnuaireContentListItemViewPreparer contenant une vignette. Si l'utilisateur ne charge pas de photo lors de la création d'une fiche, une vignette par défaut sera affichés. Le chemin de cette vignette par défaut est stockée dans les propriétés de annuairesup-front via le paramètre style.annuaire.avatar.path = /static/images/avatar-default.png, si l'on supprime le chemin celle-ci ne sera pas affichée. (cf Propriétés )

Modifier le fichier

Paramétrage de l'extension Annuaire

Le paramétrage de l'extension est réalisé dans le fichier annuaire.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_annuairesup.properties dans les sources d'un projet
  • annuairesup-override.properties dans les sources d'un projet
  • env_annuairesup.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeurs possiblesValeur par défault
style.annuaire.avatar.pathchemin de la vignette par défaut dans les styles de listeun chemin absolu vers une image/static/images/avatar-default.png

Modifier le fichier

Documentation technique de la fiche application


Code objet : 0030 Table : APPLICATION


Libellés

TypeCode
(Application) Catégorie(s )APP1
(Application) Type(s)APP2

Modifier le fichier

Paramétrage de l'extension Application

Le paramétrage de l'extension est réalisé dans le fichier application.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_override.properties dans les sources d'un projet.
  • env_application.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeurs possiblesValeur par défault
fiche.APPLICATION.style_affichage=1Activation des styles d'affichage dans la toolboxbooléen K-Sup : 0 (désactivée) / 1 (activée)1

Modifier le fichier

Documentation technique de l'extension message

Modifier le fichier

Paramétrage de l'extension Message

Le paramétrage de l'extension est réalisé dans le fichier message.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_message.properties dans les sources d'un projet
  • env_message.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeur par défault

Modifier le fichier

Documentation technique de l'extension Newsletter

Modifier le fichier

Paramétrage de l'extension Newsletter

Le paramétrage de l'extension est réalisé dans le fichier newsletter.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • newsletter-override.properties dans les sources d'un projet
  • env_newsletter.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeurs possiblesValeur par défault
newsletter.styleStyles CSS utilisés pour la newsletterun fichier, une liste de fichiers (séparés par des point-virgules) ou un dossier (dans ce 2e cas, tous les styles du dossier seront inclus)/jsp/styles/extension-newsletter.css
newsletter.fichiers_joints.poids_maxTaille maximale autorisée pour les fichiers joints (en Ko)entier positif (Ko)1024
newsletter.liste_archivesActive le listing des newsletters archivées (non personnalisées et non purgées)0 = désactivé, 1 = activé1
newsletter.templateChemin du template JSP utilisé pour l’aperçu d’une newsletterchemin vers une JSP du projet (classpath/webapp)/WEB-INF/jsp/fo/newsletter.jsp
mailing.templateChemin du template JSP utilisé pour l’aperçu d’un mailingchemin vers une JSP du projet (classpath/webapp)/WEB-INF/jsp/fo/mailing.jsp
newsletter.purge.delaiDélai de purge des envois personnalisés (en jours)entier positif (jours)15
newsletter.dsi.restrictionRestriction de l’arbre des groupes DSI au périmètre de l’utilisateur lors de la diffusion des envois0 = désactivé, 1 = activé1

Modifier le fichier

Panier

Ajout de l'extension au projet

Dans votre pom.xml ajouter les instructions ci-dessous

Définition de la version

Indiquer la version de panier souhaitée

<properties>
    <version.panier>7.0</version.panier>
</properties>

Déclaration des dépendances

<dependency>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>panier-webapp-contrib</artifactId>
    <version>${panier.version}</version>
</dependency>
<dependency>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>panier-webapp-legacy</artifactId>
    <version>${panier.version}</version>
</dependency>

Modifier le fichier

Paramétrage de l'extension Panier

Le paramétrage de l'extension est réalisé dans le fichier panier.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_panier.properties dans les sources d'un projet
  • env_panier.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

Dans l'extension Panier,les propriétés s'appliquent que pour un type de panier donné.

Propriété spécifiqueDescriptionParamètreFormat de donnéeValeur par défaut
tailleDéfinit la taille maximum du panier.panier.TYPE_PANIER.tailleNombre entier. Valeur "-1" si la taille du panier est infinie.-1
actifDéfinit si le panier est actif. Cela permet de désactiver dans le projet un panier activé par défaut dans le produit.panier.TYPE_PANIER.actifBooléen qui est soit false (le panier est désactivé) ou true (le panier est activé).true
modaleDéfinit si le panier utilise les modales de renommage et de confirmation de suppression d'un item.panier.TYPE_PANIER.modaleBooléen qui est soit false (le panier n'utilise pas les modales) ou true (le panier utilise les modales).false
drag-n-dropDéfinit si le drag'n drop est autorisé sur la vue liste d'un panier pour réordonnancer les items présents.panier.TYPE_PANIER.drag-n-dropBooléen qui est soit false (le panier n'utilise pas le drag'n drop) ou true (le panier utilise le drag'n drop).false
comportementDéfinit le mode de persistance d'un panier dans l'application. Un mode BDD permettra l'enregistrement en base de données du panier tandis qu'un mode SESSION enregistrera le panier dans la session utilisateur (supprimée lors de la fermeture du navigateur).panier.TYPE_PANIER.comportementChaîne de caractère qui est soit BDD ou SESSION.BDD (favoris)/SESSION (boutique)
quantifiableDéfinit si le panier est quantifiable. Cela signifie qu'on peut ajouter plusieurs fois le même item dans un panier.panier.TYPE_PANIER.quantifiableBooléen qui est soit false (le panier n'est pas quantifiable) ou true (le panier est quantifiable).false (favoris)/true (boutique)
typeFicheAutoriseDéfinit la liste des objets pouvant etre ajoutés au panier.panier.TYPE_PANIER.type-fiche-autoriseListe de codes fiche séparées par des virgules sans espaces.
typeFicheExcluDéfinit la liste des objets étant par défaut exclu du panier.panier.TYPE_PANIER.type-fiche-excluListe de codes ficheséparées par des virgules sans espaces.

Un certain nombre de propriétés sont déjà définies pour des types de paniers existant. Actuellement l'application définit deux paniers :

  • Un panier de type FAVORIS non quantifiable, de taille infinie et qui se persiste en base de données.
  • Un panier de type BOUTIQUE quantifiable, de taille infinie et qui se persiste en session.

Par défaut, le drag'n drop et les modales sont désactivées pour tous les paniers. Tous les paniers définis dans les produits sont actifs par défaut.

Suggestion de configuration

Voici une suggestion de la configuration des propriétés vues ci-dessus pour les besoins du projet :

[source,properties]

panier.BOUTIQUE.actif=0 panier.FAVORIS.modale=1 panier.FAVORIS.drag-n-drop=1

Creation d'un nouveau type de Panier

Pour pouvoir utiliser un nouveau panier, il est nécessaire de déclarer son ban ainsi que les beans des services de fichePanier et Panier. Voici une suggestion de déclaration pour un panier Favoris :

[source, xml]

<bean id="favorisPanier" class="com.kosmos.panier.bean.PanierDefinitionBean">
    <property name="type" value="FAVORIS"/>
    <property name="taille" value="${panier.FAVORIS.taille:-1}"/>
    <property name="actif" value="${panier.FAVORIS.actif:true}"/>
    <property name="typeFicheAutorise" value="${panier.FAVORIS.type-fiche-autorise:}"/>
    <property name="typeFicheExclu" value="${panier.FAVORIS.type-fiche-exclu:0030}"/>
    <property name="comportement" value="${panier.FAVORIS.comportement:BDD}"/>
    <property name="quantifiable" value="${panier.FAVORIS.quantifiable:false}"/>
    <property name="dragAndDrop" value="${panier.FAVORIS.drag-n-drop:true}"/>
    <property name="modale" value="${panier.FAVORIS.modale:false}"/>
</bean>

<bean id="serviceFichePanierFavoris" class="com.kosmos.panier.service.impl.ServiceFichePanierBDDImpl">
    <property name="panierDefinitionBean" ref="favorisPanier"/>
    <property name="dao" ref="fichePanierDao"/>
</bean>

<bean id="servicePanierFavoris" class="com.kosmos.panier.service.impl.ServicePanierBDDImpl">
    <property name="dao" ref="panierDao"/>
    <property name="panierDefinitionBean" ref="favorisPanier"/>
    <property name="serviceFichePanier" ref="serviceFichePanierFavoris"/>
    <property name="serviceMetatag" ref="serviceMetatag"/>
</bean>

Les propriétés du panier Favoris seront surchargeable en projet.

Modifier le fichier

Définir et utiliser un panier de fiches

Création d’un nouveau type de panier

Pour créer un nouveau type de panier, vous devez déclarer le bean correspondant, ainsi que les beans des services fichePanier et panier, dans un fichier XML situé à l’emplacement suivant : classpath*:com/kosmos/panier/context/panier-definition-name.xml, où name correspond au nom de votre type de panier.

Voici un exemple de déclaration pour un panier de type "Favoris" :

    <bean id="favorisPanier" class="com.kosmos.panier.bean.PanierDefinitionBean">
        <property name="type" value="FAVORIS"/>
        <property name="taille" value="${panier.FAVORIS.taille:-1}"/>
        <property name="actif" value="${panier.FAVORIS.actif:true}"/>
        <property name="typeFicheAutorise" value="${panier.FAVORIS.type-fiche-autorise:0015}"/>
        <property name="comportement" value="${panier.FAVORIS.comportement:BDD}"/>
        <property name="quantifiable" value="${panier.FAVORIS.quantifiable:false}"/>
        <property name="dragAndDrop" value="${panier.FAVORIS.drag-n-drop:true}"/>
        <property name="modale" value="${panier.FAVORIS.modale:false}"/>
    </bean>
    
    <bean id="serviceFichePanierFavoris" class="com.kosmos.panier.service.impl.ServiceFichePanierBDDImpl">
        <property name="panierDefinitionBean" ref="favorisPanier"/>
        <property name="dao" ref="fichePanierDao"/>
    </bean>
    
    <bean id="servicePanierFavoris" class="com.kosmos.panier.service.impl.ServicePanierBDDImpl">
        <property name="dao" ref="panierDao"/>
        <property name="panierDefinitionBean" ref="favorisPanier"/>
        <property name="serviceFichePanier" ref="serviceFichePanierFavoris"/>
        <property name="serviceMetatag" ref="serviceMetatag"/>
    </bean>

Les propriétés du panier Favoris seront surchargeable en projet.

Informations supplémentaires :

Création d’un bouton d’ajout au panier pour une fiche

Choix de l'intégration du bouton {#integration}

Un bouton d’ajout au panier peut être intégré à différents endroits et sous différentes formes. Il convient donc de déterminer en premier lieu où ce bouton doit apparaître.

  • Si le bouton doit être affiché dans une vue qui n’a pas connaissance de l’extension panier (comme c’est le cas par exemple sur les fiches articles), il faudra probablement recourir à un frontAddOn. → Voir la documentation sur les frontAddOn

  • Si le bouton doit apparaître aux côtés d’autres actions (comme les notifications, l’export PDF, etc.), et que ces actions peuvent être désactivées ou réorganisées, vous pouvez l’intégrer au sein d’un bandeau d’actions. → Voir la documentation sur les bandeauActions

Mise en place du bouton

Quel que soit l'intégration choisi pour le bouton, sa préparation reste identique. La classe AbstractCartButtonViewPreparer permet de préparer les éléments du bouton, tels que le libellé pour l’ajout ou la suppression, l’icône, etc. La première étape consiste à étendre cette classe et à surcharger la méthode getTypePanier.

Voici un exemple pour un panier de type "Favoris" :

public class FavoriteCartButtonViewPreparer<V extends ContentCartButtonViewModel> extends AbstractCartButtonViewPreparer<V> {

    private static final String TYPE_PANIER = "FAVORIS";

    @Override
    public String getTypePanier(){
        return TYPE_PANIER;
    }
}

La définition du bean :

<bean id="favoriteCartButtonViewPreparer" class="com.kosmos.panier.FavoriteCartButtonViewPreparer">
    <property name="type" value="#{T(com.kosmos.panier.actionsbandeau.preparer.FavorisActionFicheViewPreparer).FAVORITE_ACTION}" />
    <property name="view" value="/extensions/panier/WEB-INF/jsp/buttonContentPanier.jsp"/>
</bean>

Ce bouton est de type favorite-action. Par conséquent, tout preparer chargé de générer ce contenu de bouton de panier doit inclure ce type dans sa liste de expectedViewTypes.

Il est donc nécessaire de créer un preparer dont le rôle est de préparer le cartButtonViewPreparer. Dans le cas des favoris, on pourrait par exemple définir une classe FavorisActionFicheViewPreparer.

Cette classe doit étendre AbstractContentCartActionViewPreparer, et utiliser comme modèle ContentCartActionViewModel. Il faudra alors surcharger la méthode prepareCardButton.

Ce preparer va être chargé de préparer un favorite-action, ici lefavoriteCartButtonViewPreparer.

Voici un exemple de résultat pour un panier de type "Favoris" :

public class FavorisActionFicheViewPreparer<V extends ContentCartActionViewModel> extends AbstractContentCartActionViewPreparer<V> {

    public static final String FAVORITE_ACTION = "favorite-action"; // C'est le type que prépare notre favoriteCartButtonViewPreparer

    @Override
    public boolean accept(final FrontContext frontContext) {//Conditions dans laquelle notre bouton s'affichera
        return frontContext.isLogged() && super.accept(frontContext);
    }

    @Override
    public ContentCartButtonViewModel prepareCardButton(final FrontContext context, final Map<String, List<IViewPreparer>> preparers) {
        return preparers.get(FAVORITE_ACTION)
            .stream()
            .filter(preparer -> preparer.accept(context))
            .findFirst()
            .map(preparer -> preparer.prepare(context, Collections.emptyMap()))
            .map(ContentCartButtonViewModel.class::cast)
            .orElse(null);
    }
}

Proposition de déclaration du bean :

<bean id="favorisActionFicheViewPreparer" class="com.kosmos.panier.actionsbandeau.preparer.FavorisActionFicheViewPreparer">
    <property name="view" value="/extensions/panier/WEB-INF/jsp/buttonContentPanier.jsp" />
    <property name="actionActive" value="${front.actionbandeaufiche.favoris.active:true}" />
    <property name="actionPosition" value="${front.actionbandeaufiche.favoris.order:3}" />
    <property name="includedContents" value="${panier.FAVORIS.included.contents}"/>
    <property name="excludedContents" value="${panier.FAVORIS.excluded.contents}"/>
</bean>

Une fois le bouton préparé, il suffit de l’inclure dans la vue souhaitée.

Inclusion du bouton dans la vue ciblée

L’inclusion du bouton de panier peut se faire de différentes façons, selon son contexte d’affichage : via un frontAddOn, dans un bandeau d’action, au sein d’une fiche, d’une card, ou encore à travers un preparer de frontAddOns.

Inclusion dans une fiche ou une card

Lorsque le bouton est affiché dans une fiche ou une card sans contrainte particulière de contexte (c’est-à-dire sans nécessité de passer par un frontAddOn pour une inclusion anonyme), il suffit d’ajouter un attribut et une méthode dans le preparer ou la card associé(e) à la vue souhaitant inclure ce bouton.

Par exemple, pour un panier de type Favoris :

protected FavorisActionFicheViewPreparer<ContentCartActionViewModel> ficheViewPreparer;
        
//Préparation d'un contexte personnalisé permettant d'etre utilisé depuis un preparer ou builder
private ContentCartActionViewModel buildActionFavoris(final FichePanierDto fiche) {
  final ContentContext context = prepareContext(fiche);
  final SiteTemplate template = SiteTemplateBeanManager.getInstance().getSiteTemplateByCode(context.getCtx().getInfosSite().getCodeTemplate());
  final Map<String, List<IViewPreparer>> preparers = SiteTemplateBeanManager.getInstance().getViewPreparersByTemplateSiteCode(template.getCode());
  return ficheViewPreparer.prepare(context, preparers);
}

/**
 * Prépare un context permettant de construire un cart button.
 * @param fiche la fiche à partir duquel préparer le cart sera créée
 * @return le ContentContext préparé
 */
private ContentContext prepareContext(final FichePanierDto fiche) {
    final ContexteUniv ctx = ContexteUtil.getContexteUniv();
    ctx.setUrlPageCourante(fiche.getFicheViewModel().getUrl());
    final ContentContext context = ContentContext.of(new DefaultContext());
    context.setCtx(ctx);
    context.setTitle(fiche.getTitre());
    final MetatagBean metatagBean = new MetatagBean();
    metatagBean.setId(fiche.getIdMeta());
    context.setMetatag(metatagBean);
    return context;
}

Inclusion via un frontAddOn

Lorsque la vue qui doit afficher le bouton ne doit pas dépendre directement de l’extension panier, ou si la zone d’affichage du bouton est susceptible de varier, il peut être préférable d’utiliser un frontAddOn. Voir la section choix integration.

Dans ce cas, il est possible de surcharger la méthode prepare dans votre FavorisActionFicheViewPreparer afin d’ajouter, via le frontAddOn, l' extensionPoint correspondant à la vue du bouton préparé.

  @Override
  public V prepare(final FrontContext frontContext, final Map<String, List<IViewPreparer>> preparers) {
      final V model = super.prepare(frontContext, preparers);
      FrontAddOnManager.getInstance().addAddOnElement(pointNameButtonPanier, model.getView());
      return model;
  }

Il est évidemment essentiel de déclarer le pointName dans la JSP où vous souhaitez permettre l'inclusion du bouton de panier.

Inclusion dans un bandeau d’action

Lorsque le bouton de panier s’insère dans un ensemble d’actions (par exemple aux côtés de boutons de notification, "J’aime", export PDF…), et que ces actions peuvent être réorganisées ou désactivées par un contributeur, il convient d’utiliser un bandeau d’action.
Pour la création et la configuration d’un bandeau, reportez-vous à la documentation dédiée. Voir la section choix integration.

Afin d’inclure votre bouton dans un bandeau d’action, commencez par identifier le type d’actions que ce bandeau est conçu pour préparer.

<bean id="defaultActionsBandeauFicheViewPreparer" class="com.kosmos.actionsbandeau.bandeau.ActionsBandeauFicheViewPreparer">
    <property name="type" value="bandeau-action-fiche" />
    <property name="view" value="/WEB-INF/jsp/actionsbandeau/actionsBandeau.jsp" />
    <property name="expectedViewTypes">
        <list>
            <value>action-fiche</value>
        </list>
    </property>
</bean>

Dans cet exemple, le bandeau prépare des actions de type action-fiche. Il faut donc modifier notre FavorisActionFicheViewPreparer afin d’y déclarer le type d’action attendu par le bandeau dans lequel le bouton doit apparaître. Cela permettra au bandeau d’invoquer automatiquement ce preparer.

Par exemple, pour un panier de type Favoris :

<bean id="favorisActionFicheViewPreparer" class="com.kosmos.panier.actionsbandeau.preparer.FavorisActionFicheViewPreparer">
    <property name="type" value="action-fiche" />
    . . .
</bean>

Modifier le fichier

Paramétrage de l'extension Usine à sites

Le paramétrage de l'extension est réalisé dans les fichiers

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_uas.properties dans les sources d'un projet
  • application_uas-override.properties dans les sources du psk
  • env_uas.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)
  • application_uas-contrib.properties dans les sources d'un projet
  • env_uas-contrib.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés uas.properties (et surcharges)

PropriétéDescriptionValeurs possiblesValeur par défaut
uas.fonctionnaliteAvanceeDéfinit si les fonctionnalités avancées sont activées.0 (non) ou 1 (oui)0

Propriétés uas-contrib.properties (et surcharges)

PropriétéDescriptionValeurs possiblesValeur par défaut

Modifier le fichier

Documentation Technique : paramètrage eprivacy

POM

<properties>
    <eprivacy.version>7.x</eprivacy.version>
</properties>

<dependency>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>eprivacy</artifactId>
    <version>${eprivacy.version}</version>
    <type>war</type>
</dependency>

<dependency>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>eprivacy</artifactId>
    <version>${eprivacy.version}</version>
    <type>jar</type>
    <classifier>classes</classifier>
</dependency>

Paramètres

PropriétéDescriptionValeurs possiblesValeur par défaut
eprivacy.services.listListe des services présent sur le projetanalytics_cookies,facebook,twitter,instagram,linkedin,youtube
eprivacy.services.list.hiddenListe des services à masqueressentials_cookies
eprivacy.configuration.cacheDurée du cache sur le fichier configuration.jsPT1H
eprivacy.configuration.privacypolicyChemin vers les mentions légales/mentions-legales
eprivacy.configuration.privacypolicy.text{privacyPolicy}
eprivacy.configuration.fr.privacypolicy.url/mentions-legales
eprivacy.configuration.en.privacypolicy.url/legal-notices
eprivacy.configuration.cookieexpiresafterday365
eprivacy.configuration.mustconsentfalse
eprivacy.configuration.mustnoticefalse
eprivacy.configuration.debugfalse
eprivacy.configuration.declineLinkfalse

Paramètres d'un service

PropriétéDescriptionValeurs possiblesValeur par défaut
eprivacy.configuration.app.{SERVICE_ID}.categoryIdentifiant de la catégorie (CATEGORY_ID)
eprivacy.configuration.app.{SERVICE_ID}.optOutCe service est-il en opt-out ?true ou falsefalse
eprivacy.configuration.app.{SERVICE_ID}.defaultCe service est-il actif par défaut ?true ou falsefalse
eprivacy.configuration.app.{SERVICE_ID}.cookiesListe de cookies qui seront supprimés à la désactivation du service par l'utilisateurcookies séparés par des virgules

Messages

Catégories de services

Clé ({CATEGORY_ID} à remplacer)Exemple
EPRIVACY.CONFIGURATION.CATEGORIE.{CATEGORY_ID}.TITLECookies de réseaux sociaux
EPRIVACY.CONFIGURATION.CATEGORIE.{CATEGORY_ID}.DETAILSDétails...
EPRIVACY.CONFIGURATION.CATEGORIE.{CATEGORY_ID}.DESCRIPTIONLes réseaux sociaux peuvent déposer des cookies afin de permettre de visualiser des contenus sur les sites...

Services

Clé ({SERVICE_ID} à remplacer)Exemple
EPRIVACY.CONFIGURATION.APP.{SERVICE_ID}.TITLEYoutube
EPRIVACY.CONFIGURATION.APP.{SERVICE_ID}.DESCRIPTIONConsultez la politique de confidentialité de Youtube

Bean de transformation HTML

    <!-- Dailymotion -->
    <bean id="dailymotionCmpAdapter" class="com.kosmos.eprivacy.cmp.IFrameCmpAdapter">
        <constructor-arg name="service" value="dailymotion"/>
        <constructor-arg name="pattern" value="dailymotion.com/embed/video/"/>
    </bean>

Gestion du cache

Un cache est mise en place pour la configuration des choix des services par sites : ConsentConfigurationService.getPromotedByCodeSite.

Modifier le fichier

Dépôts à récupérer

Installation

Pré-requis

  • MySQL/MariaDB (Vous pouvez utiliser la commande docker run -p 3306:3306 --name ksup-installation-db mariadb:10.2)
  • Tomcat 8.5+
  • Pretix (docker-compose)

Installer et configurer Pretix

Utiliser le fichier docker-compose.yml fourni (cd dossier docker de l'extension).

Il faut également récupérer et adapter le fichier pretix.cfg sur le dépôt https://github.com/chaostreff-flensburg/pretix-docker_compose pour le placer dans ./docker/pretix/conf.

  • Se connecter avec le compte admin@localhost:admin.
  • Se mettre en mode admin. Dans les confs utilisateurs, mettre la langue française.
  • Toujours en mode admin, se créer un Organisateur.
  • Se rendre dans Organisateurs et le sélectionner.
  • Dans Réglages > Général > Localization, cocher Français uniquement et sauvegarder.
  • puis aller dans la vue Équipes, sélectionner l'équipe administrateur
  • Ajouter un token et le copier (il est visible dans le message de confirmation d'ajout en vert) dans env_inscription.properties (voir en dessous).
api.pretix.server.base-url=http://localhost:81
api.pretix.server.api.base-url=http://api.localhost:81
api.pretix.token=<Le token généré>
api.pretix.organizer=<le nom court de l'organisateur>
# L'adresse doit également être déclarée dans le docker-compose, dans la section  "extra_hosts"
api.pretix.webhook.base-url=<Adresse du site K-SUP local> 
api.pretix.server.widget-script-url=http://localhost:81/widget/v1.fr.js
api.pretix.server.widget-css-url=http://localhost:81/orgnizer/widget/v1.css

Allez plus loin

La documentation fonctionnelle dans confluence

Modifier le fichier

Pretix

Configuration préalable pour une intégration de l'extension Inscription.

Configuration de l'application

Configuration système de l'aplication Pretix. Certaines opérations peuvent être réalisées dans l'interface, mais il est plus facile de positionner les paramétrages par fichier.

Configuration de l'application dans le fichier pretix.cfg utilisé.

Le paramétrage ci-dessous est à adapter en fonction de l'instance associée (base de données,...) Il faut au préalable créer un schéma de base pretix. Par défaut, l'utilisation de la même base de données relationnelle MySQL / MariaDB est préconisée.

Seule la langue fr est activée dans la configuration ci-dessous, cela évite de le réaliser dans le cadre de l'initialisation de l'organisateur.

[pretix]
instance_name=Inscription
url=https://inscription.universite.fr
currency=EUR
datadir=/var/pretix/data
password_reset=off
csp_additional_header=frame-ancestors https://*.universite.fr; style-src 'self' https://*.universite.fr
trust_x_forwarded_for=on
trust_x_forwarded_proto=on
[locale]
default=fr
timezone=Europe/Paris
[languages]
enabled=fr
; Très intéressant : on peut positionner des traductions dans un répertoire spécifique (pour du custom, sans obligation de référencer un fork)
; Ex : /path/to/my/translations/pt_BR/LC_MESSAGES/django.po
;path=/path/to/my/translations
[database]
; For MySQL, replace with "mysql"
backend=mysql
name=pretix
user=pretix
; For MySQL, enter the user password. For PostgreSQL on the same host,
; we don't need one because we can use peer authentification if our
; PostgreSQL user matches our unix user.
password=pretixdatabasepassword
; For MySQL, use local socket, e.g. /var/run/mysqld/mysqld.sock
; For a remote host, supply an IP address
; For local postgres authentication, you can leave it empty
host=127.0.0.1
[mail]
; See config file documentation for more options
from=inscription@univ-nantes.fr
host=mailhog.univ-nantes.prive
port=1025
[redis]
location=redis://127.0.0.1/0
sessions=true
[celery]
backend=redis://127.0.0.1/1
broker=redis://127.0.0.1/2 

L'ensemble des paramètres et les différentes façons de les charger sont disponibles sur la documentation

Après démarrage de Pretix, on veillera à changer le mot de passe par défaut de l'instance (admin@localhost / admin), dans les paramètres du compte.

Dans l'écran de configuration du compte, choisir le fuseau horaire "Europe/Paris" par défaut.

Mise en conformité avec la licence AGPLv3

Cette étape est obligatoire si l'application Pretix utilisée est modifiée pour des besoins spécifiques. S'il s'agit de la version standard, cette validation est seulement recommandée.

Conformité avec une instance de Pretix non modifiée

Sélectionner les éléments suivants

  • I only use pretix to organize events which are executed by my own company or its affiliated companies, or to sell products sold by my own company.
  • This installation of pretix is running without any custom modifications or extensions (except for installed plugins).
  • I want to use pretix under the terms of the AGPLv3 license without restriction on the scope of usage and therefore without making use of any additional permission.
  • This installation of pretix has installed plugins which are available freely under a non-copyleft license (Apache License, MIT License, BSD license, …).
  • This installation of pretix has installed plugins which are available freely under a license with strong copyleft (GPL, AGPL, …).

Conformité pour une instance de Pretix dont le code a été modifié

Sélectionner les éléments suivants

  • I only use pretix to organize events which are executed by my own company or its affiliated companies, or to sell products sold by my own company.
  • This installation of pretix includes changes or extensions made to the source code.
  • I want to use pretix under the terms of the AGPLv3 license without restriction on the scope of usage and therefore without making use of any additional permission.
  • This installation of pretix has installed plugins which are available freely under a non-copyleft license (Apache License, MIT License, BSD license, …).
  • This installation of pretix has installed plugins which are available freely under a license with strong copyleft (GPL, AGPL, …).

Footer : Kosmos Education

Link : https://www.kosmos-education.com

Source code instructions

pretix (AGPLv3 with additional terms): https://github.com/pretix/pretix
Kosmos Education https://git.kosmos.fr/projects/KFO/repos/pretix

Configuration de l'organisateur

Par défaut, aucun organisateur n'est créé dans l'application Pretix. Il faut en créer un et générer un token pour utiliser l'extension.

Précisions sur l'organisateur

  • L'application inscription n'utilise qu'un seul organisateur pour gérer l'ensemble des évènements. La gestion des droits est entièrement portée par l'extension Inscription.
  • L'organisateur initialisé apparaît dans l'URL d'un évènement (URL de gestion de la commande). Il est donc important de configurer l'organisateur (et surtout le slug associé) en tenant compte des URL générées.

Création de l'organisateur

  • Aller dans l'écran Organisateurs > Créer un nouvel organisateur
  • Choisir un nom d'organisateur, par exemple, le nom de l'université (Exemple : Universite de Nantes)
  • Choisir une forme courte, qui permette d'éviter de faire doublon avec le nom de domaine (Exemple : events).
  • Sauvegarder

Configurer le token de gestion des évènements

  • Aller dans l'écran Equipes > Administrateurs (une équipe existe déjà par défaut, on peut la réutiliser)
  • Dans la partie Token API, saisir le nom du token (Exemple : K-Sup), et cliquer sur ajouter.

Reporter la configuration du token dans le fichier d'environnement env_inscription.properties

Par exemple :

api.pretix.server.base-url=https://inscription.universite.fr
api.pretix.token=5adaz2defsdqs4ddfzer4
api.pretix.organizer=events

Une fois cette configuration réalisée, il est possible de démarrer l'application utilisant cette extension, et de procéder à la gestion des évènements.

Notes

L'extension inscription n'est pas compatible avec les métadonnées obligatoires sur les évènements. Il faudra donc veiller à ne pas configurer de métadonnées de ce type dans la configuration de l'application Pretix.

Modifier le fichier

Tâches d'integrations

Configuration

Le didacticiel suivant vous guide dans la configuration K-Sup pour Pretix.
La liste complète des propriétés est décrite dans cette documentation.

Activation du plugin PAYBOX

Créez un fichier inscriptionExtensionContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
</beans>

Ajouter les beans suivants :

    ...
    <util:constant static-field="fr.kosmos.web.extensions.inscription.service.ServicePretixPlugin.PAYBOX_PAYMENT" id="PAYBOX"/>

    <bean class="com.kportal.core.context.ListToAddBean">
        <property name="idExtensionToMerge" value="inscription"/>
        <property name="idBeanToMerge" value="servicePretixPlugin"/>
        <property name="listToMerge" value="plugins"/>
        <property name="add">
            <list>
                <value>#{PAYBOX}</value>
            </list>
        </property>
    </bean>
    ...

Ajouter un nouveau formulaire

cf cette documentation

Modifier le fichier

Paramétrage de l'extension Inscription

Le paramétrage de l'extension est réalisé dans le fichier inscription.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_inscription.properties dans les sources d'un projet
  • env_inscription.properties dans le storage d'un environnement

Configuration de base

| Clé | Description | Valeurs possibles | Valeur par défaut | | --------|-----------------------------------------------------------------------|--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | api.pretix.organizer | Code (slug) de l’organisateur | texte libre, type slug d'url | pas de valeur par défaut | | api.pretix.server.base-url | URL publique du serveur pretix | | pas de valeur par défaut | | api.pretix.server.api.base-url | URL interne du serveur pretix | | ${api.pretix.server.base-url} | | api.pretix.token | Token d’accès de l’API | texte alphanumérique généré par pretix | pas de valeur par défaut | | api.pretix.webhook.action-types | Liste des actions déclenchant un appel webhook de pretix vers K-Sup | valeurs en fonction de la version de pretix | pretix.event.order.placed,pretix.event.order.placed.require_approval,
pretix.event.order.paid,pretix.event.order.canceled,pretix.event.order.reactivated,
pretix.event.order.expired,pretix.event.order.modified,pretix.event.order.contact.changed,
pretix.event.order.changed.*,pretix.event.order.refund.created.externally,pretix.event.order.approved,
pretix.event.order.denied,pretix.event.checkin,pretix.event.checkin.reverted,pretix.event.added,pretix.event.changed,
pretix.event.deleted,pretix.subevent.added,pretix.subevent.changed,pretix.subevent.deleted | | api.pretix.webhook.base-url | URL de K-Sup pour le webhook pretix | | l’URL du site principal | | api.pretix.webhook.enabled | Active les webhook | true / false | true | | pretix-webhook.mapping | le chemin de l'URL du webhook K-Sup | texte | /inscription/pretix/webhook |

Configuration des valeurs par défaut de l'évènement

CléDescriptionValeurs possiblesValeur par défaut
evenement.courriel.contact.defautCourriel utilisé pour l’initialisation de l’écran de saisie d’un évènementemailpas de valeur par défaut
evenement.courriel.expediteur.defautCourriel utilisé pour l’initialisation de l’écran de saisie d’un évènementemail type no-reply@acme.compas de valeur par défaut
evenement.modeleformulaire.defautCode du formulaire par défaut pour l’inscriptioncode d'un formulaire disponibleidentite
evenement.type.widget.defautType d’affichage (pretix) par défaut des évènement dans le widgetlist / (week) / calendarcalendar
event.setting.default.max_items_per_orderNombre maximun de place par inscription, tous produits confondusnumérique100
event.setting.default.cancel_allow_user_untilDélai d’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementRELDATE/x/date_from|date_to|presale_startRELDATE/7/-/date_from/
event.setting.default.cancel_allow_userAutorise l’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementtrue / falsetrue
event.setting.default.change_allow_user_addonsAutorise la modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementtrue / falsetrue
event.setting.default.change_allow_user_untilDélai de modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementRELDATE/x/date_from|date_to|presale_startRELDATE/7/-/date_from/
event.setting.default.localesListe des langues supportéescode languefr
event.setting.default.localeLangue par défaut dans les langues supportéescode languefr
event.setting.default.regionRegion par défaut (utilisé pour les dates notamment)code paysFR
inscription.limite.categorie.defautValeur par défaut utilisée lors de l’activation d’une restriction par catégorienombre positif strictement2

Configuration des valeurs par défaut de l'évènement - champs imposés par l'API

CléDescriptionValeurs possiblesValeur par défaut
event.setting.change_allow_user_priceValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Supany / gt / gte/ eqany
event.setting.payment_term_accept_lateValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Suptrue / falsefalse
event.setting.cancel_allow_user_paid_refund_as_giftcardValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Supon / offoff
event.setting.redirect_to_checkout_directlyValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Suptrue / falsetrue

Configuration de l'anonymisation d'une inscription

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- |------------------------| -------- | | anonymous.attendee.address.street | Valeur utilisée pour anonymiser une ligne d’adresse | texte libre | ${anonymous.default} | | anonymous.attendee.email | Valeur utilisée pour anonymiser un email | texte libre | ${anonymous.default} | | anonymous.attendee.name_parts.family_name | Valeur utilisée pour anonymiser le nom de famille | texte libre | ${anonymous.default} | | anonymous.attendee.name_parts.given_name | Valeur utilisée pour anonymiser le prénom | texte libre | ${anonymous.default} | | anonymous.attendee.phone | Valeur utilisée pour anonymiser le téléphone | texte libre | ${anonymous.default} | | anonymous.default | Valeur par défaut pour l’anonymisation des données | texte libre | ******** |

Configuration de l'export des inscrits via API

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- |----------------------------------------------------------------------------------------------------------------------------------| -------- | | api.pretix.event.exporter.identifier | Identifiant de l’export à récupérer via l’API | Dépend de l'installation pretix (cf page "Export" du menu "Réservations" d'un évènement dans pretix | orderlist | | api.pretix.event.exporter.format | Format de l’export pretix via l’API | Dépend de l'installation pretix et de l'identifier précédent (cf page "Export" du menu "Réservations" d'un évènement dans pretix | positions:semicolon |

Configuration de l'export des inscrits via K-Sup

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- |----------------------------------------------------------------| -------- | | inscription.export.delimiter | Délimiteur utilisé pour l’export CSV | tout caractère de séparation sauf le caractère d'échappement | ; | | inscription.export.quote-mode | Mode d’échappement des champs | ALL / ALL_NON_NULL / MINIMAL / NON_NUMERIX / NONE | MINIMAL (cf org.apache.commons.csv.QuoteMode) | | inscription.export.quote | Caractère utilisé pour l’échappement des champs | tout caractère d'échappement, sauf le caractère de séparation | " | | inscription.export.trim | Active le nettoyage (trim) des valeurs exportées | true / false | true |

Configuration du batch

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- |---------------|----------------------------| -------- | | import.event.max_error_count | Nombre d’erreur maximum dans l’import avant arrêt du batch | entier strictement positif | 100 |

Configuration du widget

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- | -------- | -------- | | api.pretix.server.widget-css-url | URL de la CSS pretix pour le widget | dépends de l'installation pretix | ${api.pretix.server.base-url}/${api.pretix.organizer}/widget/v1.css | | api.pretix.server.widget-script-url | URL du script Javascript pretix pour le widget | dépends de l'installation pretix | ${api.pretix.server.base-url}/widget/v1.fr.js | | api.pretix.server.widget.skip_ssl_check | Désactive la vérification SSL sur le widget | true / false | false | | api.pretix.server.widget.use-custom-css | Active l’utilisation des CSS custom pretix pour le widget | true / false | true | | api.pretix.server.widget.use-custom-iframe-css | Active l’utilisation des CSS custom pretix pour le widget liste | true / false | true | | api.pretix.server.widget.use-pretix-css | Active l’utilisation des CSS pretix pour le widget | true / false | true | | inscription.widget.css.cache | Durée du cache des CSS du widget | cf durations | PT1H | | url.inscription.widget.css.mapping | URL du endpoint qui fournit la CSS utilisée dans le widget (CSS utilisant des couleurs UAS) | toute URL K-Sup non réservée | /inscription/widget/widget.css | | url.inscription.widget.iframe.css.mapping | URL du endpoint qui fournit la CSS utilisée dans l’iframe (CSS utilisant des couleurs UAS) | toute URL K-Sup non réservée | /inscription/widget/iframe.css |

Modifier le fichier

Dépôts à récupérer

Installation

Pré-requis

  • MySQL/MariaDB (Vous pouvez utiliser la commande docker run -p 3306:3306 --name ksup-installation-db mariadb:10.2)
  • Tomcat 8.5+
  • Pretix (docker-compose)

Installer et configurer Pretix

Utiliser le fichier docker-compose.yml fourni (cd dossier docker de l'extension).

Il faut également récupérer et adapter le fichier pretix.cfg sur le dépôt https://github.com/chaostreff-flensburg/pretix-docker_compose pour le placer dans ./docker/pretix/conf.

  • Se connecter avec le compte admin@localhost:admin.
  • Se mettre en mode admin. Dans les confs utilisateurs, mettre la langue française.
  • Toujours en mode admin, se créer un Organisateur.
  • Se rendre dans Organisateurs et le sélectionner.
  • Dans Réglages > Général > Localization, cocher Français uniquement et sauvegarder.
  • puis aller dans la vue Équipes, sélectionner l'équipe administrateur
  • Ajouter un token et le copier (il est visible dans le message de confirmation d'ajout en vert) dans env_inscription.properties (voir en dessous).
api.pretix.server.base-url=http://localhost:81
api.pretix.server.api.base-url=http://api.localhost:81
api.pretix.token=<Le token généré>
api.pretix.organizer=<le nom court de l'organisateur>
# L'adresse doit également être déclarée dans le docker-compose, dans la section  "extra_hosts"
api.pretix.webhook.base-url=<Adresse du site K-SUP local> 
api.pretix.server.widget-script-url=http://localhost:81/widget/v1.fr.js
api.pretix.server.widget-css-url=http://localhost:81/orgnizer/widget/v1.css

Allez plus loin

La documentation fonctionnelle dans confluence

Modifier le fichier

Pretix

Configuration préalable pour une intégration de l'extension Inscription.

Configuration de l'application

Configuration système de l'aplication Pretix. Certaines opérations peuvent être réalisées dans l'interface, mais il est plus facile de positionner les paramétrages par fichier.

Configuration de l'application dans le fichier pretix.cfg utilisé.

Le paramétrage ci-dessous est à adapter en fonction de l'instance associée (base de données,...) Il faut au préalable créer un schéma de base pretix. Par défaut, l'utilisation de la même base de données relationnelle MySQL / MariaDB est préconisée.

Seule la langue fr est activée dans la configuration ci-dessous, cela évite de le réaliser dans le cadre de l'initialisation de l'organisateur.

[pretix]
instance_name=Inscription
url=https://inscription.universite.fr
currency=EUR
datadir=/var/pretix/data
password_reset=off
csp_additional_header=frame-ancestors https://*.universite.fr; style-src 'self' https://*.universite.fr
trust_x_forwarded_for=on
trust_x_forwarded_proto=on
[locale]
default=fr
timezone=Europe/Paris
[languages]
enabled=fr
; Très intéressant : on peut positionner des traductions dans un répertoire spécifique (pour du custom, sans obligation de référencer un fork)
; Ex : /path/to/my/translations/pt_BR/LC_MESSAGES/django.po
;path=/path/to/my/translations
[database]
; For MySQL, replace with "mysql"
backend=mysql
name=pretix
user=pretix
; For MySQL, enter the user password. For PostgreSQL on the same host,
; we don't need one because we can use peer authentification if our
; PostgreSQL user matches our unix user.
password=pretixdatabasepassword
; For MySQL, use local socket, e.g. /var/run/mysqld/mysqld.sock
; For a remote host, supply an IP address
; For local postgres authentication, you can leave it empty
host=127.0.0.1
[mail]
; See config file documentation for more options
from=inscription@univ-nantes.fr
host=mailhog.univ-nantes.prive
port=1025
[redis]
location=redis://127.0.0.1/0
sessions=true
[celery]
backend=redis://127.0.0.1/1
broker=redis://127.0.0.1/2 

L'ensemble des paramètres et les différentes façons de les charger sont disponibles sur la documentation

Après démarrage de Pretix, on veillera à changer le mot de passe par défaut de l'instance (admin@localhost / admin), dans les paramètres du compte.

Dans l'écran de configuration du compte, choisir le fuseau horaire "Europe/Paris" par défaut.

Mise en conformité avec la licence AGPLv3

Cette étape est obligatoire si l'application Pretix utilisée est modifiée pour des besoins spécifiques. S'il s'agit de la version standard, cette validation est seulement recommandée.

Conformité avec une instance de Pretix non modifiée

Sélectionner les éléments suivants

  • I only use pretix to organize events which are executed by my own company or its affiliated companies, or to sell products sold by my own company.
  • This installation of pretix is running without any custom modifications or extensions (except for installed plugins).
  • I want to use pretix under the terms of the AGPLv3 license without restriction on the scope of usage and therefore without making use of any additional permission.
  • This installation of pretix has installed plugins which are available freely under a non-copyleft license (Apache License, MIT License, BSD license, …).
  • This installation of pretix has installed plugins which are available freely under a license with strong copyleft (GPL, AGPL, …).

Conformité pour une instance de Pretix dont le code a été modifié

Sélectionner les éléments suivants

  • I only use pretix to organize events which are executed by my own company or its affiliated companies, or to sell products sold by my own company.
  • This installation of pretix includes changes or extensions made to the source code.
  • I want to use pretix under the terms of the AGPLv3 license without restriction on the scope of usage and therefore without making use of any additional permission.
  • This installation of pretix has installed plugins which are available freely under a non-copyleft license (Apache License, MIT License, BSD license, …).
  • This installation of pretix has installed plugins which are available freely under a license with strong copyleft (GPL, AGPL, …).

Footer : Kosmos Education

Link : https://www.kosmos-education.com

Source code instructions

pretix (AGPLv3 with additional terms): https://github.com/pretix/pretix
Kosmos Education https://git.kosmos.fr/projects/KFO/repos/pretix

Configuration de l'organisateur

Par défaut, aucun organisateur n'est créé dans l'application Pretix. Il faut en créer un et générer un token pour utiliser l'extension.

Précisions sur l'organisateur

  • L'application inscription n'utilise qu'un seul organisateur pour gérer l'ensemble des évènements. La gestion des droits est entièrement portée par l'extension Inscription.
  • L'organisateur initialisé apparaît dans l'URL d'un évènement (URL de gestion de la commande). Il est donc important de configurer l'organisateur (et surtout le slug associé) en tenant compte des URL générées.

Création de l'organisateur

  • Aller dans l'écran Organisateurs > Créer un nouvel organisateur
  • Choisir un nom d'organisateur, par exemple, le nom de l'université (Exemple : Universite de Nantes)
  • Choisir une forme courte, qui permette d'éviter de faire doublon avec le nom de domaine (Exemple : events).
  • Sauvegarder

Configurer le token de gestion des évènements

  • Aller dans l'écran Equipes > Administrateurs (une équipe existe déjà par défaut, on peut la réutiliser)
  • Dans la partie Token API, saisir le nom du token (Exemple : K-Sup), et cliquer sur ajouter.

Reporter la configuration du token dans le fichier d'environnement env_inscription.properties

Par exemple :

api.pretix.server.base-url=https://inscription.universite.fr
api.pretix.token=5adaz2defsdqs4ddfzer4
api.pretix.organizer=events

Une fois cette configuration réalisée, il est possible de démarrer l'application utilisant cette extension, et de procéder à la gestion des évènements.

Notes

L'extension inscription n'est pas compatible avec les métadonnées obligatoires sur les évènements. Il faudra donc veiller à ne pas configurer de métadonnées de ce type dans la configuration de l'application Pretix.

Modifier le fichier

Tâches d'integrations

Configuration

Le didacticiel suivant vous guide dans la configuration K-Sup pour Pretix.
La liste complète des propriétés est décrite dans cette documentation.

Activation du plugin PAYBOX

Créez un fichier inscriptionExtensionContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
</beans>

Ajouter les beans suivants :

    ...
    <util:constant static-field="fr.kosmos.web.extensions.inscription.service.ServicePretixPlugin.PAYBOX_PAYMENT" id="PAYBOX"/>

    <bean class="com.kportal.core.context.ListToAddBean">
        <property name="idExtensionToMerge" value="inscription"/>
        <property name="idBeanToMerge" value="servicePretixPlugin"/>
        <property name="listToMerge" value="plugins"/>
        <property name="add">
            <list>
                <value>#{PAYBOX}</value>
            </list>
        </property>
    </bean>
    ...

Ajouter un nouveau formulaire

cf cette documentation

Modifier le fichier

Paramétrage de l'extension Inscription

Le paramétrage de l'extension est réalisé dans le fichier inscription.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_inscription.properties dans les sources d'un projet
  • env_inscription.properties dans le storage d'un environnement

Configuration de base

| Clé | Description | Valeurs possibles | Valeur par défaut | | --------|-----------------------------------------------------------------------|--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | api.pretix.organizer | Code (slug) de l’organisateur | texte libre, type slug d'url | pas de valeur par défaut | | api.pretix.server.base-url | URL publique du serveur pretix | | pas de valeur par défaut | | api.pretix.server.api.base-url | URL interne du serveur pretix | | ${api.pretix.server.base-url} | | api.pretix.token | Token d’accès de l’API | texte alphanumérique généré par pretix | pas de valeur par défaut | | api.pretix.webhook.action-types | Liste des actions déclenchant un appel webhook de pretix vers K-Sup | valeurs en fonction de la version de pretix | pretix.event.order.placed,pretix.event.order.placed.require_approval,
pretix.event.order.paid,pretix.event.order.canceled,pretix.event.order.reactivated,
pretix.event.order.expired,pretix.event.order.modified,pretix.event.order.contact.changed,
pretix.event.order.changed.*,pretix.event.order.refund.created.externally,pretix.event.order.approved,
pretix.event.order.denied,pretix.event.checkin,pretix.event.checkin.reverted,pretix.event.added,pretix.event.changed,
pretix.event.deleted,pretix.subevent.added,pretix.subevent.changed,pretix.subevent.deleted | | api.pretix.webhook.base-url | URL de K-Sup pour le webhook pretix | | l’URL du site principal | | api.pretix.webhook.enabled | Active les webhook | true / false | true | | pretix-webhook.mapping | le chemin de l'URL du webhook K-Sup | texte | /inscription/pretix/webhook |

Configuration des valeurs par défaut de l'évènement

CléDescriptionValeurs possiblesValeur par défaut
evenement.courriel.contact.defautCourriel utilisé pour l’initialisation de l’écran de saisie d’un évènementemailpas de valeur par défaut
evenement.courriel.expediteur.defautCourriel utilisé pour l’initialisation de l’écran de saisie d’un évènementemail type no-reply@acme.compas de valeur par défaut
evenement.modeleformulaire.defautCode du formulaire par défaut pour l’inscriptioncode d'un formulaire disponibleidentite
evenement.type.widget.defautType d’affichage (pretix) par défaut des évènement dans le widgetlist / (week) / calendarcalendar
event.setting.default.max_items_per_orderNombre maximun de place par inscription, tous produits confondusnumérique100
event.setting.default.cancel_allow_user_untilDélai d’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementRELDATE/x/date_from|date_to|presale_startRELDATE/7/-/date_from/
event.setting.default.cancel_allow_userAutorise l’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementtrue / falsetrue
event.setting.default.change_allow_user_addonsAutorise la modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementtrue / falsetrue
event.setting.default.change_allow_user_untilDélai de modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènementRELDATE/x/date_from|date_to|presale_startRELDATE/7/-/date_from/
event.setting.default.localesListe des langues supportéescode languefr
event.setting.default.localeLangue par défaut dans les langues supportéescode languefr
event.setting.default.regionRegion par défaut (utilisé pour les dates notamment)code paysFR
inscription.limite.categorie.defautValeur par défaut utilisée lors de l’activation d’une restriction par catégorienombre positif strictement2

Configuration des valeurs par défaut de l'évènement - champs imposés par l'API

CléDescriptionValeurs possiblesValeur par défaut
event.setting.change_allow_user_priceValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Supany / gt / gte/ eqany
event.setting.payment_term_accept_lateValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Suptrue / falsefalse
event.setting.cancel_allow_user_paid_refund_as_giftcardValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Supon / offoff
event.setting.redirect_to_checkout_directlyValeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Suptrue / falsetrue

Configuration de l'anonymisation d'une inscription

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- |------------------------| -------- | | anonymous.attendee.address.street | Valeur utilisée pour anonymiser une ligne d’adresse | texte libre | ${anonymous.default} | | anonymous.attendee.email | Valeur utilisée pour anonymiser un email | texte libre | ${anonymous.default} | | anonymous.attendee.name_parts.family_name | Valeur utilisée pour anonymiser le nom de famille | texte libre | ${anonymous.default} | | anonymous.attendee.name_parts.given_name | Valeur utilisée pour anonymiser le prénom | texte libre | ${anonymous.default} | | anonymous.attendee.phone | Valeur utilisée pour anonymiser le téléphone | texte libre | ${anonymous.default} | | anonymous.default | Valeur par défaut pour l’anonymisation des données | texte libre | ******** |

Configuration de l'export des inscrits via API

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- |----------------------------------------------------------------------------------------------------------------------------------| -------- | | api.pretix.event.exporter.identifier | Identifiant de l’export à récupérer via l’API | Dépend de l'installation pretix (cf page "Export" du menu "Réservations" d'un évènement dans pretix | orderlist | | api.pretix.event.exporter.format | Format de l’export pretix via l’API | Dépend de l'installation pretix et de l'identifier précédent (cf page "Export" du menu "Réservations" d'un évènement dans pretix | positions:semicolon |

Configuration de l'export des inscrits via K-Sup

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- |----------------------------------------------------------------| -------- | | inscription.export.delimiter | Délimiteur utilisé pour l’export CSV | tout caractère de séparation sauf le caractère d'échappement | ; | | inscription.export.quote-mode | Mode d’échappement des champs | ALL / ALL_NON_NULL / MINIMAL / NON_NUMERIX / NONE | MINIMAL (cf org.apache.commons.csv.QuoteMode) | | inscription.export.quote | Caractère utilisé pour l’échappement des champs | tout caractère d'échappement, sauf le caractère de séparation | " | | inscription.export.trim | Active le nettoyage (trim) des valeurs exportées | true / false | true |

Configuration du batch

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- |---------------|----------------------------| -------- | | import.event.max_error_count | Nombre d’erreur maximum dans l’import avant arrêt du batch | entier strictement positif | 100 |

Configuration du widget

| Clé | Description | Valeurs possibles | Valeur par défaut | | -------- | -------- | -------- | -------- | | api.pretix.server.widget-css-url | URL de la CSS pretix pour le widget | dépends de l'installation pretix | ${api.pretix.server.base-url}/${api.pretix.organizer}/widget/v1.css | | api.pretix.server.widget-script-url | URL du script Javascript pretix pour le widget | dépends de l'installation pretix | ${api.pretix.server.base-url}/widget/v1.fr.js | | api.pretix.server.widget.skip_ssl_check | Désactive la vérification SSL sur le widget | true / false | false | | api.pretix.server.widget.use-custom-css | Active l’utilisation des CSS custom pretix pour le widget | true / false | true | | api.pretix.server.widget.use-custom-iframe-css | Active l’utilisation des CSS custom pretix pour le widget liste | true / false | true | | api.pretix.server.widget.use-pretix-css | Active l’utilisation des CSS pretix pour le widget | true / false | true | | inscription.widget.css.cache | Durée du cache des CSS du widget | cf durations | PT1H | | url.inscription.widget.css.mapping | URL du endpoint qui fournit la CSS utilisée dans le widget (CSS utilisant des couleurs UAS) | toute URL K-Sup non réservée | /inscription/widget/widget.css | | url.inscription.widget.iframe.css.mapping | URL du endpoint qui fournit la CSS utilisée dans l’iframe (CSS utilisant des couleurs UAS) | toute URL K-Sup non réservée | /inscription/widget/iframe.css |

Modifier le fichier

Documentation technique de la fiche lieu


  • Code objet : 9100
  • Table : LIEU
  • Type du libellé "Catégorie" : 9101
  • Type du libellé "Agglomération" : 9102

Ajout de l'extension au projet

<version.lieu>7.0-SNAPSHOT</version.lieu>
...
<overlay>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>lieu-webapp</artifactId>
    <targetPath>/extensions/lieu</targetPath>
</overlay>
....
<dependency>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>lieu-core</artifactId>
    <version>${version.lieu}</version>
</dependency>
<dependency>
<groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>lieu-core</artifactId>
    <classifier>sources</classifier>
    <version>${version.lieu}</version>
</dependency>
<dependency>
<groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>lieu-webapp</artifactId>
    <type>war</type>
    <version>${version.lieu}</version>
</dependency>
<dependency>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>lieu-api</artifactId>
    <version>${version.lieu}</version>
</dependency>

Activation du plugin de fiches

Par défaut, le plugin Lieu est activée sur les fiches actualité, annuaireksup, defaultsructureksup et laboratoire. Pour l'activer sur d'autres fiches, il faut définir un bean ListToAddBean et ajouter les classes de fiches.

<bean id="lieuPluginFicheProjet" class="com.kportal.core.context.ListToAddBean">
    <property name="idExtensionToMerge">
        <util:constant static-field="com.univ.objetspartages.util.LieuUtil.ID_EXTENSION"/>
    </property>
    <property name="idBeanToMerge" value="lieuPluginFiche"/>
    <property name="listToMerge" value="classes"/>
    <property name="add">
        <list>
            <value>com.univ.objetspartages.om.Article</value>
        </list>
    </property>
</bean>

Activation du plugin cartographie

Par défaut le plugin cartographie est activé sur la fiche Lieu. Pour l'activer sur une autre fiche, il faut déclarer un ListToAddBean sur le plugin et y ajouter la classe.

<bean id="pluginLieuCartographie" class="com.kportal.core.context.ListToAddBean">
    <property name="idExtensionToMerge">
        <util:constant static-field="com.univ.objetspartages.util.LieuUtil.ID_EXTENSION"/>
    </property>
    <property name="idBeanToMerge" value="infosCartographiePlugin"/>
    <property name="listToMerge" value="classes"/>
    <property name="add">
        <list>
            <value>com.univ.objetspartages.om.actualite</value>
        </list>
    </property>
</bean>

Il faut déclarer dans le fichier application_lieu.properties les paramètres suivants :

# Activation de la cartographie
cartographie.active=true
# Nombre de catégories
infosCartographie.categorie.nombre=4

# Option d'affichage de la cartographie
cartographie.center.latitude=49.0390822
cartographie.center.longitude=2.0741167
cartographie.initial.zoom=16

Activation des agrégations supplémentaires pour la fiche Lieu

Lorsque la cartographie est activée il est possible d'ajouter les catégories de cartographie dans la liste d'aggrégation de la fiche Lieu.

<bean id="aggregationsLieu" class="com.kportal.core.context.SetToAddBean">
    <property name="idExtensionToMerge">
        <util:constant static-field="com.univ.objetspartages.util.LieuUtil.ID_EXTENSION"/>
    </property>
    <property name="idBeanToMerge" value="searchFicheLieuConfiguration"/>
    <property name="setToMerge" value="filters"/>
    <property name="add">
        <set>
            <ref bean="searchAggregationAgglomeration"/>
            <ref bean="categorie1Aggregation"/>
            <ref bean="categorie2Aggregation"/>
            <ref bean="categorie3Aggregation"/>
            <ref bean="categorie4Aggregation"/>
        </set>
    </property>
</bean>

Ajout d'un catégorie de cartographie

Pour ajouter une nouvelle catégorie de cartographie, il faut déclarer le nouveau type de libellé correspondant à la catégorie dans le fichier tables/lieu_type_libelle.dat

(Lieu) Catégorie 5                                    CC05

Il faut également modifier le nombre de catégories dans le fichier application_lieu.properties

infosCartographie.categorie.nombre=5

Il est possible de déclarer une aggrégation pour cette nouvelle catégorie, et de l'ajouter dans la liste d'aggrégations définies plus haut.

<bean id="categorie5Aggregation" class="com.kosmos.search.query.configuration.SearchAggregationTermLabelConfiguration" parent="searchAggregationTermLabel">
    <property name="name" value="categorie5"/>
    <property name="field" value="plugins.infosCartographiePlugin.categorie5.label_code"/>
    <property name="labelType" value="CC05"/>
    <property name="label" value="INFOS_CARTOGRAPHIE.FO.CATEGORIE5"/>
</bean>

Modifier le fichier

Paramétrage de l'extension Lieu

Le paramétrage de l'extension est réalisé dans le fichier lieu.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_lieu.properties dans les sources d'un projet
  • env_lieu.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

ParamètreDescriptionValeurs possiblesValeur par défault
maps.google.useSensorUtilisation de la géolocalisationtrue / falsetrue
maps.defaultMapWidthLargeur par défaut de la carte400
maps.defaultMapHeightHauteur par défaut de la carte400
fiche.LIEU.recherche_avanceeSupport de la recherche avancée1
fiche.LIEU.recherche_anonymeSupport de la recherche anonyme0
fiche.LIEU.encadre_rechercheEncadré de recherche avancée pour les fiches Lieu1
fiche.LIEU.style_affichageUtilisation d'un template spécifique ou de celui par défaut0
LIEU.GOOGLE.API_KEYhttps://developers.google.com/maps/documentation/javascript/get-api-key(vide)
LIEU.GOOGLE.SCRIPT_URLChemin vers le script de l'APIhttps://maps.googleapis.com/maps/api/js?key=${LIEU.GOOGLE_MAPS_API.KEY}&callback=lieu_GoogleMapsAPI_Loaded
LIEU.LEAFLET.PROVIDERFournisseur de fond de carteOpenStreetMap
LIEU.LEAFLET.GEOCODERConvertisseur de coordonnéesOpenStreetMap
LIEU.ROUTE.PROVIDERTemplate d'URL vers un itinéraire${LIEU.ROUTE.PROVIDER.OPENSTREETMAP}
LIEU.ROUTE.PROVIDER.GOOGLEMAPSTemplate d'URL vers un itinéraire pour Google Mapshttps://maps.google.com/maps?z=14&daddr=
LIEU.ROUTE.PROVIDER.OPENSTREETMAPTemplate d'URL vers un itinéraire pour OSMhttps://www.openstreetmap.org/directions?to=
LIEU.MAP.APIAPI utilisée pour l'affichage des cartesLEAFLET
cartographie.lien_fiche_lieu.affichageIndique si le lien vers la fiche Lieu doit être affiché sur la cartographie1
cartographie.fiches_liees_lieu.affichageIndique si la liste des fiches liées à un lieu doit être affichée sur la cartographie1
cartographie.activeActivation la cartographietrue / false(vide / false)
infosCartographie.categorie.nombreDéfinition du nombre de catégoriesEntier positif0
infosCartographie.categorie.bo.ordreOrdre des catégories pour la saisie en back-office1 à ${infosCartographie.categorie.nombre}n
infosCartographie.categorie.fo.ordreOrdre des catégories pour l'affichage en front-office1 à ${infosCartographie.categorie.nombre}n
infosCartographie.categorie.map.ordreOrdre des catégories pour l'affichage dans le plan interactif1 à ${infosCartographie.categorie.nombre}n
cartographie.center.latitudeLatitude du point central par défaut sur lequel sera centré le plan interactif à l'ouverturevide
cartographie.center.longitudeLongitude du point central par défaut sur lequel sera centré le plan interactif à l'ouverturevide
cartographie.initial.zoomNiveau de zoom par défaut à l'ouverture du plan interactifvide

Modifier le fichier

Taglink

Ajout de l'extension au projet

<dependency>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>taglink</artifactId>
    <version>${taglink.version}</version>
</dependency>

Activation du plugin de fiches

Par défaut, l'extension Taglink n'est activée sur aucune fiche (liste vide). Pour l'activer sur des fiches, il faut définir un bean AttributeToOverrideBean et ajouter les classes de fiches.

<bean id="taglinkPluginFiche" class="com.kportal.core.context.AttributeToOverrideBean">
    <property name="idExtensionToMerge" value="taglink"/>
    <property name="attributes">
        <map>
            <entry key="classes">
                <list>
                    <value>com.univ.objetspartages.om.Actualite</value>
                    <value>com.ksup.objetspartages.om.Annuaireksup</value>
                </list>
            </entry>
        </map>
    </property>
</bean>

Il est possible d'activer le plugin sur l'ensemble des fiches (convention "null") sur les classes concernées : 

<bean id="taglinkPluginFiche" class="com.kportal.core.context.AttributeToOverrideBean">
    <property name="idExtensionToMerge" value="taglink"/>
    <property name="attributes">
        <map>
            <entry key="classes">
                <null/>
            </entry>
        </map>
    </property>
</bean>

Paramétrage des types de libellés correspondant aux tags

Il est également possible d'ajouter de nouveaux types de tag. Il faut pour cela ajouter le nouveau libellé dans un fichier typ_libelle.dat spécifique au projet. Il faut ensuite configurer le tags. Deux possibilités pour cela :

  • S'il n'y a pas de différenciation de fiches autorisées par type de tag.
    • Créer un fichier application_taglink.properties dans src/main/resources du projet
    • Et ajouter les types autorisés (exemple : type de base 9876 (Tag), fournis avec l'extension, et l'ajout d'un nouveau type de libellé, et nouveau tag 9900).
      tags.types=9876,9900
    
  • S'il y a un différenciation des fiches autorisées par type de tag, utiliser la fonctionnalité d'AttributeToOverrideBean (exemple : type de base 9876 (Tag), fournis avec l'extension, sans classe, et l'ajout d'un nouveau type de libellé, 9900, avec la classe CoursBean).
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="pluginTagLinkConf" class="com.kportal.core.context.AttributeToOverrideBean">
        <property name="idExtensionToMerge" value="taglink"/>
        <property name="idBeanToMerge" value="pluginTagLinkConfiguration"/>
        <property name="attributes">
            <map>
                <entry key="typesLibelles" value-ref="typesLibelleList" />
            </map>
        </property>
    </bean>

    <util:list id="typesLibelleList" value-type="com.kosmos.taglink.bean.TagLinkTypeBean">
        <bean id="transversal" class="com.kosmos.taglink.bean.TagLinkTypeBean">
            <constructor-arg name="type" value="9876"/>
        </bean>

        <bean id="diplomaLevel" class="com.kosmos.taglink.bean.TagLinkTypeBean">
            <constructor-arg name="type" value="9900"/>
            <constructor-arg name="classes">
                <list>
                    <value>com.ksup.objetspartages.bean.CoursBean</value>
                </list>
            </constructor-arg>
          </bean>
    </util:list>
</beans>

Activation de la fonctionnalité d'insertion d'une liste à partir de tags

Pour activer le tag de toolbox, il faut effectuer la modification suivante dans l'extension contexte du projet.

<!-- Activation du tag de toolbox permettant de faire une liste de fiches à partir de tags taglink -->
<bean id="projetTagListToolboxTag" class="com.kportal.core.context.AttributeToOverrideBean">
    <property name="idExtensionToMerge">
        <util:constant static-field="com.kosmos.taglink.util.TagLinkUtils.ID_EXTENSION"/>
    </property>
    <property name="idBeanToMerge" value="tagListToolboxTag"/>
    <property name="attributes">
        <map>
            <entry key="category" value="list"/>
        </map>
    </property>
</bean>

Intégration des tags transversaux dans la recherche

Activer la recherche par tags sur la recherche transverse

Afin de pouvoir filtrer les résultats de recherche par tags sur la recherche transverse, il faut ajouter l'aggrégation searchTaglinkAggregation au bean de configuration de recherche :

<bean id="searchFicheConfigurationTags" class="com.kportal.core.context.SetToAddBean">
    <property name="idExtensionToMerge" value="core"/>
    <property name="idBeanToMerge" value="searchFicheConfiguration"/>
    <property name="setToMerge" value="postFilters"/>
    <property name="add">
        <set>
            <ref bean="searchTaglinkAggregation"/>
        </set>
    </property>
</bean>

Ajout d'un lien vers la recherche dans l'entête de la fiche

cf intégration projet

Patch lien tag - resultat de recherche

cf intégration projet

Priorisation des résultats de recherche

Il est possible de prioriser les résultats de recherche à partir de termes prédéfinis. Au niveau de la recherche, lorsqu'on recherchera sur le terme "Premier tag", par exemple, les fiches affectées à ce libellé remonteront en priorité.

En back-office, il existe deux manières de procéder :

  • Via Outils > Tags > Création d'un nouveau tag de type 'Priorisation des résultats de recherche' et affectation des fiches liées ;
  • Via Contenus > {mon-type-de-fiche} > Onglet 'Plugins' > Tags.

Configuration

On commence par déclarer le type Priorisation de recherche (9875) puis on ajoute une pondération sur ce type de libellé, dans un fichier application_taglink.properties :

# Définition des types de tags
tags.types=9876,9875
# Pondération sur Priorisation des résultats de recherche
search.boost.plugins.taglinkPluginFiche.items.9875.label_value=10000

Ensuite, dans le fichier {mon-projet}-taglink.xml, on vient déclarer le boost, qui sera ajouté au bean exactPhraseQuery :

<!-- Boost pour la priorisation des résultats de recherche -->
<bean id="tagLinkPriorisationValueFieldConfiguration" class="com.kosmos.search.query.configuration.SearchFieldConfiguration">
    <property name="fieldName" value="plugins.taglinkPluginFiche.items.9875.label_value"/>
    <property name="boost" value="${search.boost.plugins.taglinkPluginFiche.items.9875.label_value:10000}"/>
</bean>
 
<bean id="tagLinkQueryListToAdd" class="com.kportal.core.context.ListToAddBean">
    <property name="idExtensionToMerge">
        <util:constant static-field="com.jsbsoft.jtf.core.ApplicationContextManager.DEFAULT_CORE_CONTEXT"/>
    </property>
    <property name="idBeanToMerge" value="exactPhraseQuery"/>
    <property name="listToMerge" value="extraFields"/>
    <property name="add">
        <list>
            <ref bean="tagLinkPriorisationValueFieldConfiguration"/>
        </list>
    </property>
</bean>

Modifier le fichier

Paramétrage de l'extension Taglink

Le paramétrage de l'extension est réalisé dans le fichier taglink.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_taglink.properties dans les sources d'un projet
  • env_taglink.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
tags.typesListe des types de libellés à prendre en compte dans la configuration générique du plugin.Codes des types de libellés espacés par des virgules : Exemple : tags.types=9876,99009876

Modifier le fichier

Paramétrage de l'extension Connecteur HAL

Le paramétrage de l'extension est réalisé dans le fichier connecteurhal.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_connecteurhal.properties dans les sources d'un projet
  • env_connecteurhal.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
HAL_URL_RECHERCHE_APIURL de l'API de rechercheURLhttps://api.archives-ouvertes.fr/
HAL_URL_RECHERCHEURL de la liste des résultats de recherche sur le site HALURLhttps://hal.archives-ouvertes.fr/
HAL_NB_RESULTATSNombre de résultats à afficher dans la pageEntier positif10
HAL_CHAMPS_RETOUR_JSONListe des champs que l'on souhaite récupérer de l'API HALListe des champs HAL séparés par des virgulesdocid,docType_s,title_s,authFullName_s,doiId_s,volume_s,page_s,producedDate_tdate,producedDateD_i,producedDateM_i,producedDateY_i,peerReviewing_s,journalTitle_s,uri_s
HAL_TRI_RESULTATTri des résultats retournés par l'API HALasc | descdesc
HAL_TYPES_DOCUMENTSTypes de documents renvoyé par l'apiCf. lien valeurs possiblesNONE
api.hal.authentificationMode d'authentificationCf. fr.kosmos.web.projets.extensions.AbstractJaxRsDAO.ModeNONE
documenthal.mappingChemin d'écoute du controllerUrl relative/documentHal
api.hal.connecttimeoutTimeout de connexion à l'APIEntier positif30000
api.hal.readtimeoutTimeout de lecture de l'APIEntier positif30000
api.hal.searchChemin de l'API de rechercheChaîne de caractèressearch
api.hal.libelleChemin de l'API permettant de récupérer la "Liste des référentiels"Chaîne de caractèresref/doctype
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.disabledDésactivation du cache "Récupération d'une liste des documents HAL pour une fiche annuaire"true | falsefalse
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.eternalIndique si le cache "Récupération d'une liste des documents HAL pour une fiche annuaire" est éterneltrue | falsefalse
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.maxEntriesLocalHeapNombre d'entrées max dans le cache "Récupération d'une liste des documents HAL pour une fiche annuaire"Entier positif100
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.timeToIdleSecondsDurée max (en secondes) pendant laquelle un élément est conservé dans le cache "Récupération d'une liste des documents HAL pour une fiche annuaire" sans être accédéEntier positif120
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.timeToLiveSecondsDurée max (en secondes) pendant laquelle un élément est conservé dans le cache "Récupération d'une liste des documents HAL pour une fiche annuaire"Entier positif120
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.evictionPolicyPolitique d'éviction du cache "Récupération d'une liste des documents HAL pour une fiche annuaire"Cf. net.sf.ehcache.store.MemoryStoreEvictionPolicyLFU
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.disabledDésactivation du cache "Récupération d'une liste des documents HAL pour une fiche structure"true | falsefalse
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.eternalIndique si le cache "Récupération d'une liste des documents HAL pour une fiche structure" est éterneltrue | falsefalse
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.maxEntriesLocalHeapNombre d'entrées max dans le cache "Récupération d'une liste des documents HAL pour une fiche structure"Entier positif100
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.timeToIdleSecondsDurée max (en secondes) pendant laquelle un élément est conservé dans le cache "Récupération d'une liste des documents HAL pour une fiche structure" sans être accédéEntier positif3600
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.timeToLiveSecondsDurée max (en secondes) pendant laquelle un élément est conservé dans le cache "Récupération d'une liste des documents HAL pour une fiche structure"Entier positif43200
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.evictionPolicyPolitique d'éviction du cache "Récupération d'une liste des documents HAL pour une fiche structure"Cf. net.sf.ehcache.store.MemoryStoreEvictionPolicyLFU
cache.ServiceDoctypeHal.getDoctypes.disabledDésactivation du cache "Récupération des libellés HAL"true | falsefalse
cache.ServiceDoctypeHal.getDoctypes.eternalIndique si le cache "Récupération des libellés HAL" est éterneltrue | falsefalse
cache.ServiceDoctypeHal.getDoctypes.maxEntriesLocalHeapNombre d'entrées max dans le cache "Récupération des libellés HAL"Entier positif10
cache.ServiceDoctypeHal.getDoctypes.timeToIdleSecondsDurée max (en secondes) pendant laquelle un élément est conservé dans le cache "Récupération des libellés HAL" sans être accédéEntier positif3600
cache.ServiceDoctypeHal.getDoctypes.timeToLiveSecondsDurée max (en secondes) pendant laquelle un élément est conservé dans le cache "Récupération des libellés HAL"Entier positif86400
cache.ServiceDoctypeHal.getDoctypes.evictionPolicyPolitique d'éviction du cache "Récupération d'un libellé HAL"Cf. net.sf.ehcache.store.MemoryStoreEvictionPolicyLFU

Modifier le fichier

Principes d'intégration FO

Ce plugin est activé par défaut pour les fiches Annuaireksup et Laboratoire.

Procédure d'installation :

Ajouter les dépendances dans le pom.xml du projet.

<!-- Connecteur HAL -->
<dependencies>
    <dependency>
        <groupId>fr.kosmos.web.projets.extensions</groupId>
        <artifactId>connecteurhal</artifactId>
        <version>${connecteurhal.version}</version>
        <type>war</type>
    </dependency>
    <dependency>
        <groupId>fr.kosmos.web.projets.extensions</groupId>
        <artifactId>connecteurhal</artifactId>
        <version>${connecteurhal.version}</version>
        <type>jar</type>
        <classifier>classes</classifier>
    </dependency>
</dependencies>

Ajouter l'overlay dans les plugins du build.

<!-- Connecteur HAL -->
<overlay>
    <groupId>fr.kosmos.web.projets.extensions</groupId>
    <artifactId>connecteurhal</artifactId>
    <targetPath>/extensions/connecteurhal</targetPath>
</overlay>

Affichage dans les fiches

Par défaut, le plugin s'affiche en dessous de la fiche.

Voir la personnalisation.

Dans le footer, vérifier que les appels aux js sont bien présents.

<resources:script group="scriptsFo" locale="<%= ctx.getLocale().toString() %>" />
<resources:script group="jQuery" />

Si le projet utilise le thème psk, il faudra ajouter le groupe css dans le fichier wro.xml du projet.

    <group name="styles-theme">
        <css>/extensions/connecteurhal/resources/styles/extension-connecteurhal.css</css>
    </group>

Modifier le fichier

Internationalisation

Le connecteur HAL gère les langues françaises et anglaises par défaut.

Ajouter des langues

Modification du wro.xml. Exemple pour l'ajout de l'espagnol.

    <group name="scriptsFo_es">
    	<group-ref>i18nFo_es</group-ref>
        <group-ref>jQuery</group-ref>
        <group-ref>styles</group-ref>
    </group>

    <group name="i18nFo_es" abstract="true">
		<js>/resources/scripts/messages.js</js>
    </group>

Ajout du fichier de traduction dans le dossier resources. Exemple de nommage : Application_connecteurhal_es.properties.

CONNECTEURHAL.EXTENSION_LIBELLE=Conector HAL

HAL_VOIR_AUTRES_DOCUMENTS=Ver todas las publicaciones en el sitio HAL

ERROR_AJAX_HAL=Ocurrió un error al recuperar la lista de documentos HAL

HAL_DETAIL_TITLE=Más información
HAL_DETAIL_VOLUME=Volumen :
HAL_DETAIL_PAGE=Página/Identificador :
HAL_DETAIL_TITRE_JOURNAL=Título del periódico :
HAL_DETAIL_LIEUX=Lugares de publicación :
HAL_DETAIL_EDITEURS=Editores :
HAL_DETAIL_ID=Identificador :


HAL_COMMITE_LECTURE=Comité de lectura :
HAL_COMMITE_LECTURE_OUI=sí
HAL_COMMITE_LECTURE_NON=no

HAL_LISTE_VIDE=No se encontraron documentos HAL
LISTE_DOCUMENT_HAL=Publicaciones
HAL_PLUS_D_INFO=Ver publicación

Modifier le fichier

Personnalisation du connecteur

Personnalisation de l'affichage en FO

L'affichage des documents HAL est géré par Tiles et paramétré dans tiles-connecteurhal.xml. Il est possible de supprimer l'affichage automatique du plugin pour une fiche en ajoutant la propriété classesAffichageSpecifique avec la liste des fiches.


<bean id="beanOverridden" class="com.kportal.core.context.AttributeToOverrideBean">
    <property name="idExtensionToMerge" value="com.kosmos.connecteurhal.util.ConnecteurHalUtil.ID_EXTENSION"/>
    <property name="idBeanToMerge" value="pluginPublicationHalPlugin"/>
    <property name="attributes">
        <entry key="classesAffichageSpecifique">
            <list>
                <value>com.ksup.objetspartages.om.Formation</value>
            </list>
        </entry>
    </property>
</bean>

Pour une simple modification de l'affichage, il suffit de surcharger documentsHal.jsp.

Personnalisation des champs

Pour récupérer plus de champs de l'API, il faut personnaliser plusieurs classes du connecteur.

Créer un ViewModel spécifique implémentant DocumentHalDefaultViewModel

public class ProjetDocumentViewModel extends DocumentHalDefaultViewModel {

    /**
     * La donnée à ajouter.
     */
    private String data;

    public String getData() {
        return data;
    }

    public void setData(final String data) {
        this.data = data;
    }
}

Créer un preparer héritant de HalDefaultViewPreparer.

public class ProjetHalViewPreparer extends HalDefaultViewPreparer<ProjetDocumentViewModel, DocumentHalDefaultBean> {

    /**
     * {@inheritDoc}
     */
    public ProjetDocumentViewModel createViewModel() {
        return new ProjetDocumentViewModel();
    }

    /**
     * {@inheritDoc}
     */
    public void populateViewModel(final DocumentHalDefaultBean documentHalBean, final ProjetDocumentViewModel documentHalDefaultViewModel) {
        super.populateViewModel(documentHalBean, documentHalDefaultViewModel);
        final Map<String, Object> champsAutres = documentHalBean.getOtherfields();
        if (champsAutres.containsKey("champ_a_ajouter")) {
            documentHalDefaultViewModel.setData((TypeData) champsAutres.get("champ_a_ajouter"));
        }
    
}

Créer un bean héritant de DocumentHalDefaultBean. Ajouter les champs à récupérer de l'API.

public class ProjetDocumentHalBean extends DocumentHalDefaultBean {

    @JsonProperty("champ_a_ajouter")
    private String champAAjouter;

    /**
     * Constructeur.
     */
    public ProjetDocumentHalBean() {
        super();
    }
    

    public void setChampAAjouter(final String champAAjouter) {
        this.champAAjouter = champAAjouter;
    }

    public String getChampAAjouter() {
        return champAAjouter;
    }
}

Dans le fichier resources/application_connecteurhal.properties, déclarer la propriété HAL_CHAMPS_RETOUR_JSON pour ajouter le nom du champ supplémentaire à récupérer de l'API.

#Liste des champs retournés dans le flux json
HAL_CHAMPS_RETOUR_JSON=docid,docType_s,title_s,authFullName_s,doiId_s,volume_s,page_s,producedDate_tdate,producedDateD_i,producedDateM_i,producedDateY_i,peerReviewing_s,journalTitle_s,uri_s,publicationLocation_s,publisher_s,champ_a_ajouter

Personnaliser la définition Tiles pour référencer le ViewPreparer spécifique.

<!DOCTYPE tiles-definitions PUBLIC
        "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
        "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
    <definition name="tiles.connecteurhal.documents" template="/extensions/connecteurhal/WEB-INF/jsp/connecteurHal/documentsHal.jsp"
                preparer="fr.projet.connecteurhal.ProjetHalViewPreparer"/>
</tiles-definitions>

Personnaliser le css

Les variables suivantes peuvent être personnalisées.

--hal-accent-color: #AB1919; /* Couleur de mise en avant (titre et bouton) */
 
--hal-tag-background-color: #cecece; /* Couleur de fond de la pastille du type de publication */
--hal-tag-color: #020202; /* Couleur du texte de la pastille du type de publication */
--hal-tag-border-radius: 5px; /* Arrondi de la pastille du type de publication */
 
--hal-document-background-color-hover: #ccc; /* Couleur de fond d'un document au survol */
--hal-document-background-color-even: #dfdfdf; /* Couleur de fond d'un document ligne pair */
--hal-document-background-color-odd: #efefef; /* Couleur de fond d'un document ligne impair */
--hal-document-border-radius: 5px; /* Arrondi d'un document */

Exemple :

.documents_hal {
    --hal-accent-color: #ff00dd;
}

Modifier le fichier

Activation de l'extension Fichiers joints

Ajout de l'extension au projet

Pour ajouter l'extension dans votre projet, il faut ajouter les éléments suivants dans le POM :

  • Version
<properties>
    [...]
    <fichierjoint.version>7.00.00</fichierjoint.version>
</properties>
  • Dépendances
<dependencies>
    [...]
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>fichierjoint</artifactId>
        <version>${fichierjoint.version}</version>
    </dependency>
</dependencies>

Activation du plugin

Pour activer le plugin sur un type de fiche (ex : Formation), il faut déclarer un bean dans le contexte de votre projet (ex : dans projetExtensionContext.xml) :

<bean id="fichierjointPluginFiche" class="com.kportal.core.context.ListToAddBean">
    <property name="idExtensionToMerge">
        <util:constant static-field="fr.kosmos.fichierjoint.util.FichierjointUtils.ID_EXTENSION"/>
    </property>
    <property name="listToMerge" value="classes"/>
    <property name="add">
        <list>
            <value>com.ksup.objetspartages.om.Formation</value>
        </list>
    </property>
</bean>

Modifier le fichier

Paramétrage de l'extension Fichiers joints

Le paramétrage de l'extension est réalisé dans le fichier fichierjoint.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_fichierjoint.properties dans les sources d'un projet
  • env_fichierjoint.properties dans le storage d'un environnement

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut

Modifier le fichier

Chaîne de build Front K-Sup

Ce document présente la chaine de build front de K-Sup.

Historique

Jusqu'à la version 7.0 de K-Sup, le build était géré par WRO et Compass.

Ceux-ci s'occupaient de plusieurs choses :

  • transpilation des fichiers scss vers des fichiers css avec Compass1
  • interprétation des messages spring pour l'i18n présents dans les fichiers javascript
  • constuction de bundles, appelés group css et js
  • minification du code
  • possibilité d'étendre les fonctionnalités précedentes pour les extensions et projets

Cas d'usage général de la chaîne de build front

@startuml
    skinparam actorStyle awesome

    rectangle Utilisateur {
        :dév:
        :inté:
        :jenkins:
    }

    rectangle Résultat {
        :Front < 7.0:
        :Front > 7.0:
        :Back office:
    }

    usecase build as "Compiler du CSS
    ==
    Compiler du JS
    ==
    Traduire des messages JS
    "

    :dév: -down-> build
    :inté: -down-> build
    :jenkins: -down-> build

    build --down->> :Front < 7.0:
    build -down->> :Front > 7.0:
    build -down->> :Back office:

@enduml

Fonctionnement

Le produit et chacune des extensions qui exposent des ressources publient un jar classes qui contient les ressources nécessaires ainsi que le fichier de configurations permettant de regrouper ces ressources en bundle.

Pour construire un projet, un package npm nommé ksup-front-build prend en charge le build des JS et des CSS. Ce package a la responsabilité de récupérer les différentes configurations nécessaires au projet et à ses dépendances.

Une fois récupérées, fusionnées et consolidées, ces configurations servent à la construction des bundles qui sont nécessaires au fonctionnement du front de K-Sup.

Pour le JS : webpack est utilisé
Pour le CSS : postcss et sass (sass-dart) sont utilisés

Ces bundles sont référencés dans les jsp ; soit par les tags jstl <resources:link> et <resources:script>, soit directement avec les tags HTML <script> et <link>.

La résolution en URL vers les fichiers générées est faite par la classe ResourceUtils.

Vue générale

@startuml

skinparam arrow {
    FontSize 10
    FontName Impact
    Color #FF6655
    FontColor #444
}

skinparam actor {
    FontColor #333
}

skinparam package {
    FontSize 14
    BackgroundColor #ccc
    BorderColor #888
    FontName Arial
}

skinparam frame {
    BackgroundColor #fff
}

frame "Build Front sans WRO" {

    package "ksup" {
        rectangle "ksup-jsp"
        rectangle "ksup-js"
        rectangle "ksup-scss"
        rectangle "ksup-conf"
        "ksup-conf" -up-> "ksup-js"
        "ksup-conf" -up-> "ksup-scss"

        note right of "ksup-conf"
            Définit des bundles
        endnote
    }

    package "extension#n" {
        rectangle "ext-jsp"
        rectangle "ext-js"
        rectangle "ext-scss"
        rectangle "ext-conf"
        "ext-conf" -up-> "ext-js"
        "ext-conf" -up-> "ext-scss"

        note right of "ext-conf"
            Étends les bundles produit
            En définit de nouveaux
        endnote

    }

    package "projet" {
        rectangle "projet-jsp"
        rectangle "projet-js"
        rectangle "projet-scss"
        rectangle "projet-conf"

        "projet-conf" -up-> "projet-js"
        "projet-conf" -up-> "projet-scss"

        note right of "projet-conf"
            Étends les bundles produit
            En définit de nouveaux
        endnote

        (npm)

        note top of (npm)
            La construction du livrable
            par maven lance ce build
        endnote
    }

    package "build-front-ksup" {
        [script de build] -up-> "ksup-conf": 1
        [script de build] -up-> "ext-conf": 2
        [script de build] -up-> "projet-conf": 3
    }

    package "livrable" {
        rectangle "bundles css + manifest"
        rectangle "bundles js + manifest"
    }

    "bundles css + manifest" <-up- [script de build]
    "bundles js + manifest" <-up- [script de build]

    (npm) --> [script de build]: Build

}

@enduml

Développement d'un projet

Le parent-projects en plus des dépendances maven déclare des profiles récupérant les bundles des différents extensions présentes.

Développement des extensions K-Sup

Le kore-dependencies ajoute toutes les ressources css et js au jar classes ainsi que le fichier de défninition des bundles bundle.json.

i18n

La responsabilité de l'internationnalisation est dans les fichiers JS au plus proche de la fonctionnailté. Une entrée i18n dans la définition des bundles permet de préciser les fichiers qui seront chargés pour chacune des langues définies.


Modifier le fichier

Paramétrage de Node et NPM dans IntelliJ

Paramétrer node et npm dans IntelliJ permet plusieurs choses :

  • démarrer les scripts du fichier package.json via l'IDE
  • avoir de l'introspection sur l'API node

Repérer la version de Node et NPM à utiliser

Projet sans frontend-maven-plugin

La version dépend du projet et doit être installée via nvm ou asdf.

Projet avec frontend-maven-plugin

Le chemin de node et npm est donc relatif au projet. Il faut d'abord avoir lancé mvn clean package pour les avoir récupérés. Les exécutables de node et npm se trouveront alors dans le dossier node du projet (pas node_modules) Si le dossier node est présent, le projet (ou un de ses parents) a le plugin.

Settings

Dans les settings d'IntelliJ (ctrl+alt+s), aller dans Languages & Frameworks.

Settings

Modifier le fichier

Développement front projet

Ce document précise le fonctionnement de la chaîne de build K-Sup pour le développement sur un projet.

Prérequis

Le projet est basé sur la chaîne de build front, s'il est toujours construit par WRO : voir la migration

Mode production

Le mode par défaut du script est dit de production. Il transpile tous les fichiers nécessaires au fonctionnement du front de ksup (frontoffice et backoffice).

La commande à lancer est la suivante :

npm run build

Il existe un profil maven nommé front-build, activé par la présence du fichier package.json qui lance ce build avec le build maven.

Mode développement

Le script de build front peut-être lancé dans un mode de development. Ce mode permet d'avoir des fichiers non minifiés et une écoute des modifications des sources afin de mettre à jour les fichiers générés concernés par la modification.

npm run build:dev

Il existe un profil maven nommé front-dev, à activer manuellement et en désactivant manuellement front-build, qui lance ce build avec le build maven.

Utilitaire

Un profil maven nommé front-clean permet de supprimer node_modules et package-lock.json.

NOTE : Il n'est pas activé par défaut.

Normes de dév

Dossiers préconisés

TypeFrontLegacy
Scripts/src/main/resources/scripts//src/main/resources/scripts-legacy/
Styles/src/main/resources/sass//src/main/resources/sass-legacy/

NOTE : dans tous les cas, ils doivent être dans /src/main/resources pour être visible par la chaîne de build.

Modifier le fichier

Développement front extension/produit

Ce document présente l'environnement de développement utilisant les sources d'une extension pour la faire évoluer.

Installation

Travailler en local sur une extension/le produit

Comme pour le reste du travail sur une extension, il faut utiliser maven clean install.

Travailler sur une version publiée

Les assets venant avec la dépendences maven, c'est transparent.

Modifier le fichier

Migration d'une extension utilisant WRO

Initialisation

Créer un fichier bundle.json dans src/main/resources si des assests doivent être compilés en bundle.

Exemple avec l'extension agenda

{
  "name": "@ksup/extensions-agenda",
  "version": "7.0.0-0",
  "description": "Plugin permettant de présenter des événements sous forme calendaire (jour, semaine, mois).",
  "repository": {
    "type": "git",
    "url": "ssh://git@git.kosmos.fr/extension/agenda.git"
  },
  "devDependencies": {
    "husky": "^3.1.0",
    "node-cli-ksup": "^1.0.1"
  },
  "husky": {
    "hooks": {
      "commit-msg": "commit-msg -E HUSKY_GIT_PARAMS"
    }
  },
  "keywords": [],
  "author": "Kosmos",
  "license": "Apache-2.0"
}

Il faut supprimer les scripts en référence à sass:watch et le hook husky post-merge si présents.

Exemple d'éléments à supprimer :

{
  "scripts": {
    "scss": "mvn sass:update-stylesheets",
    "watch": "mvn sass:watch"
  },
  "husky": {
    "hooks": {
      "post-merge": "update-style"
    }
  }
}

Conf wro -> conf front-build

  • si le projet a une conf wro, il faut la transcrire vers la conf front-build.
  • si des scripts font référence à des libraires (jQuery notamment) il faut les déclarer en externals
  • Si le groupe contient des fichiers de traduction
    • Créer un fichier par langue avec comme suffixe la langue en question : frontoffice-i18n.js devient frontoffice-i18n_en.js et frontoffice-i18n_fr_FR.js.
    • Traduire chacune des valeurs présentes dans ces fichiers (avec les valeurs des fichiers *.properties de Spring)
    • Ajouter une section lang avec les différentes langues traduites et référencer les fichiers comme dans cet exemple : scripts/collaboratif_i18n_${lang}.js
  • si des fichiers sont référencés dans la webapp, il faut ajouter le chemin relatif à la racine du module :
    • /resources/styles/no-theme/verifications.css => styles/no-theme/verifications.css

bundle.json

Exemple de fichier

{
  "externals": {
    "jQuery": "/adminsite/scripts/libs/jquery-1.11.0.js"
  },
  "styles": {
    "styles": {
      "files": [
        "src/main/webapp/resources/styles/sass/extension-agenda.scss"
      ]
    }
  },
  "scripts": {
    "scriptsBo_fr_FR": {
      "files": [
        "src/main/webapp/resources/scripts/saisie.js"
      ],
      "externals": {
        "jQuery": "jQuery"
      }
    },
    "scriptsBo_en": {
      "files": [
        "src/main/webapp/resources/scripts/saisie.js"
      ]
    },
    "scriptsFo_fr_FR": {
      "files": [
        "src/main/webapp/resources/scripts/saisie_front.js",
        "src/main/webapp/resources/scripts/saisie.js"
      ]
    }
  }
}

Pour plus d'information sur le fichier bundle.json

Modifier le fichier

Chaîne de build front K-Sup (Package ksup-front-build)

Présentation technique du package ksup-front-build.

Objectif

L'objectif de ce package est de réaliser le build des ressources front de K-Sup d'un projet.

Fonctionnement

@startuml

skinparam arrow {
    Color #FF6655
}

autonumber

actor projet order 1
participant maven order 5
participant "script de build (npm)" order 10
participant "kore-parent" order 20
participant frontgen order 30
participant extensions order 35
entity livrable order 40

"projet" -> maven: maven clean package
maven -> "script de build (npm)": npm run build

group Chargement des configurations
    "script de build (npm)" -[#blue]-> "kore-parent": le produit en premier
    "script de build (npm)" -[#blue]-> frontgen: frontgen en première extension
    "script de build (npm)" -[#blue]-> extensions: ensuite les autres extensions
    "script de build (npm)" -[#blue]-> projet: le projet en dernier
end

"script de build (npm)" -> maven: Génération des fichiers js + css + manifest.json
maven -> livrable: war

@enduml

Un mode watch permet de travailler sans minification et avec écoute des modifications des fichiers.

Le script se base sur des fichiers de configuration présents dans le projet et ses dépendances.

Dépendances

Le fonctionnement du build CSS est basé sur :

  • postcss
    • postcss-sass : transpile le scss/sass vers le css
    • postcss-import : résoud les @import
    • postcss-hash : ajoute un hash dépendant du contenu du fichier afin de mieux gérer le cache sur les navigateurs
    • postcss-url : résoud les url() présents dans les fichiers css afin de conserver les chemins relatifs
    • autoprefixer : ajoute les préfixes vendor en fonction de la compatibilité, basé sur Can I Use
    • cssnano : minification
  • chokidar : pour mettre en place les watchers

Le fonctionnement du build JS est basé sur :

  • webpack
    • webpack-manifest-plugin : génère les fichiers manifest pour les hash (les hash sont natifs)
    • externals (interne à webpack) : permet de référencer et donc de résoudre des dépendances JS dans le scope global.

Utilisation

Le script peut être lancé avec plusieurs paramètres.

Mode

Ce paramètre permet de choisir le mode de construction des bundles.
Valeur par défaut : production

ValeurCommentaire
productionC'est le mode nominal, les fichiers produits sont minifiés, leur nom comporte un hash et des fichiers manifest.json sont générés pour permettre à K-Sup de les résoudre.
developmentLa minification est désactivé et les fichiers ne comportent pas de hash. Seul, le manifest concernant les externals est généré.

Watch

Il s'agit d'un flag --watch qui active la surveillance des modifications des fichiers.
Si un fichier d'un bundle est modifié le bundle est rebuildé, et seulement lui.

Ce flag n'est pas compatible avec le mode production. Les hash et les manifests changeraient rendant la résolution des fichiers inopérante.

Build

Ce paramètre permet de choisir ce que l'on va construire.
Valeur par défaut : all

ValeurCommentaire
allLance la construction de tous les bundles
jsLance la construction du JS seulement
cssLance la construction du CSS seulement

Modifier le fichier

Fichier bundle.json

Le fichier bundle.json représente les bundles à générer par la chaîne de build front.

Niveau pricipal

Il y a trois entrées principales dans ce fichier :

Bundle

Les sections styles et scripts ont presque tout en commun.

Il s'agit d'un objet dont chaque entrée définit un bundle.

Chaque bundle contient les informations suivantes :

  • extends (string) : référence un autre bundle dont les fichiers et les langues seront ajoutés au bundle courant (en premier)
  • abstract (boolean) : un bundle abstrait ne génère pas de fichier, il doit être étendu dans un autre bundle (il n'est pas nécessaire qu'un bundle soit abstrait pour être étendu)
  • files (string[]) : la liste des fichiers js ou css contenu dans le bundle
  • target (string) : le dossier cible (défaut : 'static/css' ou 'static/js')
  • hash (boolean) : permet d'empêcher la création du hash dans le nom du fichier
  • lang (string[]) : permet de décliner les bundles en plusieurs langues, les fichiers de langue doivent être référencés avec ${lang}

Externals

Les scripts js pouvant dépendre d'une librairie externe, il faut déclarer celle-ci ici.

Le bundle l'utilisant devra alors le déclarer :

Il s'agit alors d'un objet dont les clés sont les références dans le script et les valeurs le nom de l'external.

{
  "externals": {
    "jQuery": "jQuery",
    "jquery": "jQuery",
    "jquery-ui": "jquery-ui",
    "jquery.validate": "jQuery.validate"
  }
}

Exemple de fichier

Ceci n'est qu'un exemple recomposé de plusieurs fichiers

{
  "externals": {
    "jQuery": "/adminsite/scripts/libs/jquery-1.11.0.js",
    "jquery-ui": "/adminsite/scripts/libs/jquery-ui-1.10.3.custom.js",
    "jquery.validate": "/adminsite/scripts/libs/validate/jquery.validate-1.13.1.js",
    "Choices": "/adminsite/scripts/libs/choices/assets/scripts/dist/choices.min.js",
    "Sortable": "/adminsite/scripts/libs/Sortable.js",
    "CKEDITOR_HELPER": "/adminsite/scripts/libs/ckeditor-helper.js"
  },
  "styles": {
    "common": {
      "abstract": true,
      "files": [
        "sass/common.scss"
      ]
    },
    "styles-dsi": {
      "extends": "common",
      "files": [
        "sass/jsp/styles/mode_connecte.scss"
      ]
    },
    "styles-print": {
      "files": [
        "sass/jsp/styles/impression.scss"
      ]
    },
    "complement": {
      "files": [
        "sass/jsp/styles/complement.scss"
      ],
      "target": "/jsp/styles",
      "hash": false
    },
    "screen": {
      "files": [
        "sass/jsp/styles/screen.scss"
      ],
      "target": "/jsp/styles",
      "hash": false
    },
    "jQueryCSS": {
      "files": [
        "webapp/jsp/scripts/libs/css/magnific-popup.css"
      ]
    }
  },
  "scripts": {
    "scriptsFo": {
      "files": [
        "webapp/adminsite/scripts/libs/css/jquery-ui-1.10.4.custom.css",
        "webapp/adminsite/scripts/libs/css/jquery.kmultiselect-0.1.1.css",
        "webapp/adminsite/scripts/libs/css/jquery.kmonoselect-0.1.0.css",
        "webapp/adminsite/scripts/libs/css/jquery.dataTables.css",
        "webapp/adminsite/scripts/libs/css/jquery.dataTables_themeroller.css",
        "webapp/jsp/scripts/frontoffice-i18n_${lang}.js",
        "webapp/jsp/scripts/libs/tabindex-override.js",
        "webapp/jsp/scripts/menu.js",
        "webapp/jsp/scripts/validation.js",
        "webapp/jsp/scripts/frontoffice.js",
        "webapp/jsp/scripts/galeries.js",
        "webapp/jsp/scripts/medias.js",
        "webapp/jsp/scripts/headPage.js",
        "webapp/jsp/scripts/onglets.js"
      ],
      "lang": [
        "en",
        "fr_FR"
      ],
      "externals": {
        "jQuery": "jQuery",
        "jquery": "jQuery",
        "jquery-ui": "jquery-ui",
        "jquery.validate": "jQuery.validate",
        "Choices": "Choices",
        "Sortable": "Sortable",
        "CKEDITOR_HELPER": "CKEDITOR_HELPER"
      }
    }
  }
}

Modifier le fichier

Activation du mode legacy

Pour activer le mode legacy du front K-Sup sur un projet, il suffit de créer un fichier à la racine du projet nommé .legacy

Fonctionnement

Le profil maven front-legacy est activé par la présence du fichier. Ce profil utilise le plugin maven dependency:copy-dependencies pour récupérer les différentes extensions installées.

Il existe ensuite un profil par extension (si celle-ci possède un mode legacy) qui ajoute l'overlay et la dépendance. Chacun de ces profils est activé par la présence de la dépendance copiée par dependency:copy-dependencies.


"maven" -> "front-legacy": .legacy
activate "front-legacy"
"front-legacy" -> "dependency" :copy-dependencies
activate "dependency"
return Tous les jar nécessaire
group 1 profil par extension ayant une webapp legacy
    "front-legacy" --> "front-legacy-acme": ajoute le jar legacy et l'overlay
    activate "front-legacy-acme"
    return overlay + jar
end
return

Modifier le fichier

Démarrage d'un environnement produit

Ce document décrit comment installer et démarrer un environnement produit K-Sup pour la version 7.x du produit K-Sup. Cette installation nécessite un projet à déployer. Les commandes décrites ci-dessous, sont indiquées avec l'utilisation d'un environnement Linux (pour windows, certains outils seront à adapter).

Installation de K-Sup et des composants

Pour faciliter la gestion des versions des composants, ceux-ci seront installés via l'outil SDKMAN.

Installation de SDKMAN

L'installation de l'utilitaire SDKMAN est un pré-requis nécessaire pour les installations suivantes, c'est un outil permettant de gérer les différentes versions de vos SDK (java, maven, tomcat...).

Lien vers la documentation d'installation

Installation de Java

Installer la dernière version 17 de Java (zulu) (ne pas la mettre en version par défaut si vous en utilisez une autre).

# Pour obtenir la dernière version
sdk list java
# Pour installer la version
sdk install java 17.x-zulu

Installation du serveur tomcat

Installer la dernière version de tomcat 9.

# Pour obtenir la dernière version
sdk list tomcat
# Pour installer la version
sdk install tomcat 9.x

Installation de maven

  • Installer la dernière version de maven 3.9.
# Pour obtenir la dernière version
sdk list maven
# Pour installer la dernière 
sdk install maven 3.9.x
  • Créer un dossier .m2 s'il n'existe pas.
ll ~/.m2
mkdir ~/.m2

Récupérer le fichier settings_private_nexus_all.xml et le déposer dans le dossier ~/.m2. Renommer le fichier en settings.xml

mv ~/.m2/settings_private_nexus_all.xml ~/.m2/settings.xml 

Paramétrage de NPM

Créer un fichier ~/.npmrc contenant la valeur suivante :

registry=https://nexus.nantes.kosmos.fr/nexus/content/groups/public.npm/

Installation de MariaDB

K-Sup V7 utilise la version LTS 10.11 de Mariadb. Créer un dossier contenant les volumes des bases de données

mkdir -p ~/database-volumes/mariadb10

Il n'y a pas d'installation de mariadb, le serveur doit être démarré au démarrage du projet (cf Démarrage de mariadb)

Installation d'opensearch

Opensearch ne nécessite pas d'installation, celui-ci est à lancer au démarrage du projet via docker. (cf Démarrage d'opensearch)

Installation du projet de test

Le projet dev-ksup est utilisé pour tester les développements en cours sur le produit K-Sup et ses extensions.

Ce projet contient les modules bas niveaux (Koreparent et autres) ainsi que la totalité des extensions produit.

  • Récupérer le projet sur Gitlab
cd ~/ksup-projets/
git clone ssh://git@gitlab.kosmos.fr:22226/solutioning/ksup/produit/environnements/dev-ksup.git

Configuration de l'environnement de développement

Création du répertoire storage

Un répertoire storage doit être créé dans votre espace de développement ~/ksup-projets/dev-ksup/storage, celui-ci contiendra :

  • La configuration de votre projet
  • Les logs applicatives
  • Les fichiers d'import/export
  • Les médias
  • ...

Un sous-répertoire storage/conf doit être créé, celui-ci contiendra les différents fichiers de configuration de votre projet.

Création de la base de données

Créer un nouveau schéma dev-ksup sur votre base de données MariaDB

create database devksup70 CHARACTER SET utf8 COLLATE utf8_general_ci;

Au premier démarrage, dev-ksup va créer et initialiser les tables nécessaires au fonctionnement de K-Sup.

Configuration du projet

Afin de démarrer, la configuration minimale doit être déclarée au sein du fichier storage/conf/env.properties. Celle-ci décrit le chemin vers les différents répertoires de données de K-Sup, la configuration du serveur SMTP ainsi que les informations de connexion à la base de données.

### Chemin vers le répertoire storage
storage.dir=/home/<votre user>/ksup-projets/dev-ksup/storage

logs.path=${storage.dir}/logs

### Port utilisé pour le protocole http
site.port.http=8080
site.secure=false
 
mail.host=localhost
mail.port=1025
mail.from=mail@test.fr
mail.webmaster=mail@test.fr
 
datastore.default.user=root
datastore.default.password=root
datastore.default.url=jdbc:mysql://127.0.0.1:3310/devksup70

### Désactivation de la securité (liée au HTTPS et au CORS) en local pour les cookies 
cookie.processor.secure=false
cookie.processor.samesite=UNSET

Configuration de tomcat

Une fois le projet dev-ksup importé dans IntelliJ, l'exécution de la commande mvn clean package permet de générer le war exploded qui sera déployé sur le serveur tomcat.

Dans la configuration serveur, le chemin vers le répertoire storage/conf doit être déclaré dans les VM options :

-Dconf.dir=/home/<votre user>/ksup-projets/dev-ksup/storage/conf

Dans la partie deployment, il faut sélectionner dev-ksup:war exploded et bien positionner le champ Application context sur /.

Configuration d'OpenSearch

Ajouter la configuration OpenSearch dans le fichier ~/ksup-projets/dev-ksup/storage/conf/env_opensearch.properties

vi /home/storage/conf/env_opensearch.properties
search.nodes = 127.0.0.1:32788

Démarrage

Démarrage de mariadb

Lancer le conteneur mariadb 10.11

# Déploie un conteneur mariadb 10.11
# Le port exposé est le 3310 avec les identifiants root/root
docker run -dit -e MYSQL_ROOT_PASSWORD=root --name mariadb10 -v ~/database-volume/mariadb-10/:/var/lib/mysql -p 3310:3306 mariadb:10.11 mysqld --sql_mode="STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" --character-set-server=utf8 --collation-server=utf8_general_ci

Possibilité de le lancer automatiquement au démarrage de la session avec la commande suivante

docker run -dit --restart unless-stopped -e MYSQL_ROOT_PASSWORD=root --name mariadb1010 -v ~/database-volume/mariadb-10/:/var/lib/mysql -p 3310:3306 mariadb:10.11 mysqld --sql_mode="STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" --character-set-server=utf8 --collation-server=utf8_general_ci

Démarrage d'OpenSearch

Lancer le conteneur OpenSearch (se référer à la documentation complète du docker opensearch ksup-docker-elasticsearch) :

docker pull nexus.nantes.kosmos.fr:18445/keep/ksup/opensearch
docker run \
        -v opensearch:/usr/share/opensearch/data \
        --rm \
        -p 32788:9200 \
        --name opensearch24 \
        nexus.nantes.kosmos.fr:18445/keep/ksup/opensearch

Démarrer tomcat

Compilation du projet

mvn clean package

Lancer tomcat et regarder les logs ~/ksup-projets/dev-ksup/storage/logs/default-webapp.log

less ~/ksup-projets/dev-ksup/storage/logs/default-webapp.log

Modifier le fichier

Intégration d'un projet

Rechargement des styles à chaud

Prérequis

  • Afin de pouvoir effectuer un rechargement "à chaud" des styles css, compass doit être installé sur le poste de développement.
sudo apt-get install ruby-dev
sudo gem install sass compass
  • Dans la configuration du serveur tomcat, définir l'action "Update classes and resources" pour les sections "On 'Update' action" et "On frame deactivation". configuration de tomcat
  • Aoutez dans le fichier "storage/conf/env.properties" la ligne suivante afin de ne pas avoir les fichiers css minifiés et ainsi pouvoir debugger en mode inspecteur dans les navigateurs. wro.debug=true

Compilation en temps réel des css

  • Le fichier de configuration pour la compilation des scss est "src/main/sass/jsp/styles/config.rb"
  • Ouvrir un terminal et se positionner sur le projet dans le répertoire styles
  • Tapez "compass watch", pour voir en temps réel les modifications css que vous faites.
compass watch

>>> Compass is watching for changes. Press Ctrl-C to Stop.

INFO :

  • Le fichier "src/main/sass/jsp/styles/screen.scss" regroupe tous les scss qui sont importés.
  • Le fichier css final est dans webapp, screen.css.

Modifier le fichier

Initialisation d'une extension

Prérequis

  • Créer un dépôt sur Gitlab dans le groupe solutioning/ksup/produit/extensions et le cloner en local.
  • Ajouter un fichier LICENCE.md à la racine avec le contenu suivant
    Copyright 2013 - Kosmos
    
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at  
      
    [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)  
      
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
  • Ajouter un fichier README.md à la racine avec le contenu suivant (en adaptant le contenu)
    #Mon Extension
    Cette extension permet ...
    
    ##Questions, problèmes, suggestions
    Les produits K-Portal et K-Sup sont Open Source. Vous pouvez ainsi participer à leur évolution : [Guide de contribution Open-Source](http://www.ksup.org/fr/communaute/contribuez/).   
    En cas d'anomalie, merci de saisir un ticket sur la plate-forme d'assistance [JIRA](https://issues.kosmos.fr/browse/MONEXTENSION).
    
    ##Contribution au développement
    Si vous souhaitez proposer des développements merci de consulter le [processus de contribution](http://www.ksup.org/fr/communaute/contribuez/guide-de-contribution-sur-bitbucket-20805.kjsp).
    
    ## Installation hooks GIT
    Ce projet contient des Hooks préconfiguré.
    Lors de l'installation de ce projet lancer la commande
    ```
    npm install
    ```
    Voir ssh://git@gitlab.kosmos.fr:22226/solutioning/ksup/outils/node-cli-ksup.git
  • Ajouter un fichier package.json à la racine avec le contenu suivant (en adaptant le contenu)
    {
      "name": "@ksup/extensions-monextension",
      "version": "7.2-SNAPSHOT",
      "description": "Cette extension permet de ...",
      "main": "index.js",
      "devDependencies": {
        "husky": "^3.1.0",
        "node-cli-ksup": "^1.0.1"
      },
      "husky": {
        "hooks": {
          "commit-msg": "commit-msg -E HUSKY_GIT_PARAMS"
        }
      },
      "repository": {
        "type": "git",
        "url": "ssh://git@gitlab.kosmos.fr:22226/solutioning/ksup/extensions/monextension.git"
      },
      "keywords": [],
      "author": "",
      "license": "Apache-2.0",
      "dependencies": {}
    }
  • Ajouter un fichier Jenkinsfile à la racine avec le contenu suivant (en adaptant le contenu)
    #!/usr/bin/env groovy
    @Library('KSup_Pipeline') _
    
    ksupTemplatePipeline(
            repoKey : 'monextension',
            mavenProfile: 'generate-license',
            jdkVersion: 'jdk_17',
            mvnVersion: 'maven_3.8.6',
            DEPLOY: true
    )

Création du pom.xml

  • Ajouter un fichier pom.xml à la racine avec le contenu suivant (en adaptant le contenu)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    ...
</project>
  • Déclarer le parent Koreparent
<parent>
    <groupId>fr.kosmos.web.extensions</groupId>
    <artifactId>parent-extensions</artifactId>
    <version>7.2-SNAPSHOT</version>
</parent>
  • Déclarer les information relatives à l'extension
<name>Mon extension</name>
<description>Artifact parent de l'extension Mon extension</description>

<scm>
    <connection>scm:git:git@gitlab.kosmos.fr:22226/solutioning/ksup/extensions/monextension.git</connection>
    <tag>HEAD</tag>
</scm>
  • Déclarer les modules de l'extension
 <modules>
    <module>monextension-core</module>
    <module>monextension-webapp-contrib</module>
    <module>monextension-webapp-front</module>
</modules>

Ajout des modules

Module core

Le module core contient le code applicatif métier de l'extension (beans, dao, service...). Il est obligatoire.

Il contient également le fichier de contexte Spring permettant de déclarer l'extension au sein de K-Sup.

  • Créer un dossier monextension-core à la racine du projet
  • Ajouter un dossier docs à ce dossier (pour la documentation)
  • Ajouter un fichier pom.xml à ce dossier avec le contenu suivant (en adaptant le contenu)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>monextension</artifactId>
        <version>7.2-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>monextension-core</artifactId>
    <packaging>jar</packaging>

    <name>Mon extension Core</name>
    <description>Module contenant les services métiers de l'extension Mon extension</description>

</project>
  • Ajouter un dossier src/main/java à ce dossier (pour le code Java)
  • Ajouter un dossier src/main/resources à ce dossier (pour les ressources)
  • Créer un fichier monextension.properties dans src/main/resources avec le contenu suivant (en adaptant le contenu)
core.version=${core.version}
extension.version=${project.version}
  • Créer un fichier monextension_fr_FR.properties dans src/main/resources avec le contenu suivant (en adaptant le contenu)
MONEXTENSION.EXTENSION_LIBELLE=Mon extension
MONEXTENSION.EXTENSION_DESCRIPTION=Description de l'extension Mon extension
  • Ajouter le dossier com/kosmos/monextension/context/ à src/main/resources
  • Créer un fichier ExtensionContext.xml dans ce dossier avec le contenu suivant (en adaptant le contenu)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.kosmos.monextension.context"/>

    <bean id="monextension" class="com.kportal.extension.DefaultExtensionImpl">
        <property name="libelle" value="MONEXTENSION.EXTENSION_LIBELLE"/>
        <property name="version" value="${extension.version}"/>
        <property name="coreVersion" value="${core.version}"/>
        <property name="externe" value="true"/>
        <property name="type" ref="TYPE_PARAMETRABLE"/>
        <property name="relativePath" value="/extensions/monextension"/>
        <property name="description" value="MONEXTENSION.EXTENSION_DESCRIPTION"/>
        <property name="logo" value="/resources/images/extension.png"/>
        <property name="auteur" value="Kosmos"/>
    </bean>

    <bean id="monextensionProperties" class="com.kportal.core.config.PropertyConfigurer">
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="ignoreResourceNotFound" value="true" />
        <property name="locations">
            <list>
                <value>classpath*:monextension.properties</value>
                <value>classpath*:monextension-override.properties</value>
                <value>classpath*:application_monextension.properties</value>
                <value>${path.properties}env_monextension.properties</value>
            </list>
        </property>
        <property name="fileEncoding" value="UTF-8"/>
    </bean>

    <bean id="monextensionMessages" class="com.kportal.core.config.MessageLoader" init-method="init">
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="ignoreResourceNotFound" value="true" />
        <property name="locations">
            <list>
                <value>classpath*:monextension_*.properties</value>
                <value>classpath*:Application_monextension_*.properties</value>
            </list>
        </property>
        <property name="fileEncoding" value="UTF-8"/>
    </bean>

    <!-- Chargement des statistiques -->
    <import resource="classpath:com/kosmos/monextension/context/monextension-statistics.xml"/>

    <!-- Chargement des contextes externes-->
    <import resource="classpath*:com/kosmos/monextension/context/monextension-webapp-*.xml"/>

    <!-- Ouverture à la surchage projet -->
    <import resource="classpath*:spring/monextension-override.xml"/>
</beans>
  • Ajouter un dossier migration dans src/main/resources/com/kosmos/monextension
  • Ajouter un fichier V7_1_0__init.sql (numéro à adapater) dans ce dossier avec le script d'initialisation de la base de données.

Module webapp-contrib

Le module webapp-contrib contient le code applicatif de la partie contribution de l'extension.

  • Créer un dossier monextension-webapp-contrib à la racine du projet
  • Ajouter un fichier pom.xml à ce dossier avec le contenu suivant (en adaptant le contenu)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>monextension</artifactId>
        <version>7.2-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>monextension-webapp-contrib</artifactId>
    <packaging>jar</packaging>

    <name>Application Mon extension Contrib</name>
    <description>Module contenant la webapp de contribution de l'extension Mon extension</description>

    <dependencies>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>monextension-core</artifactId>
        </dependency>
    </dependencies>

</project>
  • Ajouter un dossier src/main/java à ce dossier (pour le code Java)
  • Ajouter un dossier src/main/resources à ce dossier (pour les ressources)
  • Ajouter un dossier com/kosmos/monextension/context à src/main/resources
  • Créer un fichier monextension-webapp-contrib.xml dans ce dossier avec le contenu suivant (en adaptant le contenu)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="monextensionContributionProperties" class="com.kportal.core.config.PropertyConfigurer">
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="ignoreResourceNotFound" value="true"/>
        <property name="locations">
            <list>
                <value>classpath*:monextension-contrib.properties</value>
                <value>classpath*:monextension-contrib-override.properties</value>
                <value>classpath*:application_monextension-contrib.properties</value>
            </list>
        </property>
        <property name="fileEncoding" value="UTF-8"/>
    </bean>

    <bean id="monextensionContributionMessages" class="com.kportal.core.config.MessageLoader" init-method="init">
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="ignoreResourceNotFound" value="true"/>
        <property name="locations">
            <list>
                <value>classpath*:monextension-contrib_*.properties</value>
                <value>classpath*:Application_monextension-contrib_*.properties</value>
            </list>
        </property>
        <property name="fileEncoding" value="UTF-8"/>
    </bean>

<!--    TODO entrée de menu...-->
</beans>
  • Ajouter un dossier META-INF/resources/extensions/monextension à src/main/resources
  • Ajouter un dossier resources/images dans le dossier META-INF/resources/extensions/monextension
  • Ajouter un dossier WEB-INF/jsp/monextension dans le dossier META-INF/resources/extensions/monextension, ce dossier contiendra les JSP de saisie de l'extension
  • Ajouter les fichiers monextension-contrib.properties et monextension-contrib_fr_FR.properties dans src/main/resources

Module webapp-front

Le module webapp-front contient le code applicatif de la partie front de l'extension (ViewModels, ViewPreparers, jsp...).

  • Créer un dossier monextension-webapp-front à la racine du projet
  • Ajouter un fichier pom.xml à ce dossier avec le contenu suivant (en adaptant le contenu)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>monextension</artifactId>
        <version>7.2-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>monextension-webapp-front</artifactId>
    <packaging>jar</packaging>

    <name>Mon extension Webapp Front</name>
    <description>Module contenant la webapp front de l'extension monextension</description>

    <dependencies>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>monextension-core</artifactId>
        </dependency>
    </dependencies>
</project>
  • Ajouter un dossier src/main/java à ce dossier (pour le code Java)
  • Ajouter un dossier src/main/resources à ce dossier (pour les ressources)
  • Ajouter un fichier bundle.json à src/main/resources, ce fichier contiendra les bundles à générer par la chaîne de build front (cf fichier bundle.json)
  • Ajouter un dossier com/kosmos/monextension/context à src/main/resources
  • Créer un fichier monextension-webapp-front.xml dans ce dossier, ce fichier contiendra les beans Spring spécifiques à la webapp front de l'extension
  • Ajouter un dossier META-INF/resources/extensions/monextension/WEB-INF/jsp à src/main/resources, ce dossier contiendra les JSP de l'extension

Modifier le fichier

CKEDITOR 4

Le produit fusionne par défaut les templates produit et les templates projet.

Exemple template de styles projet

Pour déclarer des templates projet, créer le fichier /static/js/ckeditor/projectTemplates.js

(function () {
    'use strict';

    const templates = [
            {
                title: 'Mon style',
                image: 'template1.gif',
                class: 'emphasis--skin-style-projet'
            }
        ]
    ;

    // Register a template definition set named "default".
    if (CKEDITOR.getTemplates('default')) {
        CKEDITOR.getTemplates('default').templates.push(...templates);
    } else {
        // Register a template definition set named "default".
        CKEDITOR.addTemplates('default',
            {
                // The name of the subfolder that contains the preview images of the templates.
                imagesPath: CKEDITOR.getUrl(CKEDITOR.plugins.getPath('templates') + 'templates/images/'),
                // Template definitions.
                templates
            });

    }
})();

Dossier contenant les aperçus

Le dosier contenant les aperçus est le suivant : adminsite/scripts/libs/ckeditor/plugins/templates/templates/images/

Pour remplacer tous les styles

Il faut surcharger le fichier de conf (defaultConfig.json par exemple)

  "templates_files": [ "/adminsite/scripts/productTemplates.js", "/static/js/ckeditor/projectTemplates.js" ],

devient :

  "templates_files": [ "/static/js/ckeditor/projectTemplates.js" ],

Modifier le fichier

Tutoriel de migration d'un projet de K-Sup 6.7 vers 7.0

Données et environnements de références

  • Récupérer dump et médias récents
  • Mettre en place environnement Rancher 6.7 de référence
  • Mettre en place environnement local 6.7 de référence
  • Identifier les sites de référence (1 par template / modèle) et les fonctionnalités principales

Pré-requis

  • Préparer l'environnement de développement de la 6.7 et 7
  • Réaliser la montée de version sur la dernière version de la 6.7
  • Identifier les patchs et déterminer la pertinence de leur maintien
  • Supprimer les patchs obsolètes (ou qui le seront suite à la migration)
  • Faire le ménage dans les branches, et de quelle branche démarrer la migration vers la 7
  • Installation de npm sur le poste

Trame

  1. Mise à jour du socle technique (MAJ Pom, java, tomcat, mariadb, passage a opensearch) => Le projet doit compiler sur le poste & Jenkins
    1. Migration du pom
    2. Résoudre les problèmes de compilation
    3. Résoudre les impacts sur les objets spécifiques
    4. Mettre à jour les fichiers de l'usine logicielle (jenkinsfile & IC/values.yaml) pour qu'il utilise les bonnes versions de java & tomcat
    5. Supprimer les patchs et les fonctionnalités devenues obsolètes
  2. Mise à jour de la configuration Spring
    1. Démarrer le projet
  3. Impacts API K-SUP
  4. Suppression du wro et mise en place des bundles => On voit l'écran avec les styles et les scripts
  5. Impacts Opensearch => la recherche doit fonctionner
  6. Flyway
  7. Analyse des corrections à faire sur Sonar et les effectuer
  8. Activation de la recherche anonyme
  9. Tester les fonctionnalités principales

Ressources

Modifier le fichier

Mise à jour du pom

Avec la version 7.0, le pom.xml du projet devrait être considérablement allégé. Liens utiles :

Modification du parent

Mettre à jour la version du parent-projects.

    <parent>
        <groupId>fr.kosmos.web.projets</groupId>
        <artifactId>parent-projects</artifactId>
        <version><!--la version qui aura été décidée--></version>
    </parent>

Gestion des dépendances

Projet K-Sup

Il n'est plus nécessaire d'ajouter une dépendance quelconque au produit K-Sup, parce que les librairies concernées sont déjà importées dans le pom parent. Pour les importer, il faut ajouter un fichier .ksup vide à la racine du projet. Ce fichier va activer un profil maven du pom parent, qui va tirer toutes les dépendances du produit. Nous pouvons donc supprimer <produit>ksup</produit> du pom ainsi que les références au produit dans les dépendances :

<dependencies>
    <dependency>
        <groupId>fr.kosmos.web.produit</groupId>
        <artifactId>${produit}</artifactId>
        <version>${core.version}</version>
        <type>war</type>
    </dependency>
    <dependency>
        <groupId>fr.kosmos.web.produit</groupId>
        <artifactId>${produit}</artifactId>
        <version>${core.version}</version>
        <type>pom</type>
        <scope>provided</scope>
        <exclusions>
            <exclusion>
                <groupId>fr.kosmos.web.extensions</groupId>
                <artifactId>importExport</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

Properties

Supprimer les versions des extensions, elles sont portées par le parent. Ne garder que les versions des dépendances externes.

    <!-- Propriétés -->
    <properties>
        <!-- Version du produit et des extensions -->
        <core.version>6.07.74</core.version>
        <agenda.version>2.07.21</agenda.version>
        <accueil.version>1.07.08</accueil.version>
        <ldap.version>1.07.10</ldap.version>
        <fichelink.version>1.07.04</fichelink.version>
        <uas.version>2.07.06</uas.version>
        <supervision.version>1.07.12</supervision.version>
        <importExport.version>1.07.30</importExport.version>
        <eprivacy.version>2.07.09</eprivacy.version>
        <!-- Utilisation du PSK comme utilitaire -->
        <psk.version>2.07.05</psk.version>
        <!-- Dépendances externes -->
        <pac4j.version>3.3.0</pac4j.version>
        <spring-webmvc-pac4j.version>3.2.0</spring-webmvc-pac4j.version>
    </properties>

devient :

    <!-- Propriétés -->
    <properties>
        <!-- Dépendances produit -->
        <psk.version>7.0-SNAPSHOT</psk.version>
        <!-- Dépendances externes -->
        <pac4j.version>3.3.0</pac4j.version>
        <spring-webmvc-pac4j.version>3.2.0</spring-webmvc-pac4j.version>
    </properties>

Récupération du front-legacy

Pour récupérer le front-legacy, il est nécessaire d'ajouter un fichier .ksup-legacy (vide) à la racine du projet. La présence de ce fichier permet d'activer un profil maven spécifique (front-legacy), qui tire des dépendances prédéfinies du pom parent. Ces dépendances peuvent donc être supprimées du pom.xml du projet.

Déclarer les dépendances des extensions ksup

La déclaration des dépendances des extensions a changé dans la V7, maintenant nous n'avons plus besoin de déclarer les dépendances jar, war, etc... mais seulement les artéfacts maven des extensions. Donc un pom en v6 ressemblant à ça :

<dependencies>
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>${nom-extension}</artifactId>
        <version>${nom-extension.version}</version>
        <type>war</type>
    </dependency>
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>${nom-extension}</artifactId>
        <version>${nom-extension.version}</version>
        <type>jar</type>
        <classifier>classes</classifier>
    </dependency>
</dependencies>

Va devenir en V7 :

<dependencies>
    <!-- Dépendance pas systématiquement nécessaire parce que tirée par les autres dépendances de l'extension -->
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>${nom-extension}-core</artifactId>
    </dependency>
    <!-- dépendance qui contient les références à l'affichage du front V7 -->
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>${nom-extension}-webapp-front</artifactId>
    </dependency>
    <!-- dépendance qui contient le code lié à la contribution -->
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>${nom-extension}-webapp-contrib</artifactId>
    </dependency>
    
    <!-- dépendances optionnelles -->
    <!-- dépendance qui permet la rétro compatibilité des libs front 6.7 sur les projets-->
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>${nom-extension}-webapp-legacy</artifactId>
    </dependency>
    <!-- dépendance qui contient le code relatif à l'api de l'extension -->
    <dependency>
        <groupId>fr.kosmos.web.extensions</groupId>
        <artifactId>${nom-extension}-api</artifactId>
    </dependency>
</dependencies>

Note : Ces extensions étant dans le dependencyManagement, il n'est plus nécessaire de déclarer leur numéro de version dans la balise properties.

Dépendances externes

Il n'y a normalement pas de changement au niveau des dépendances externes (sauf si celles-ci sont désormais tirées par le pom.xml du parent-projects.)

Changement de la construction des groupes de styles et scripts

Il faut supprimer le plugin unpack-sass qui n'aura plus d'utilité par la suite. Il faut le remplacer par deux choses :

  • d'abord on ajoute un projet npm. Cette action va créer un fichier package.json qui active un autre plugin pour générer les bundles.
      npm init
      npm install @ksup/ksup-front-build --dev
      npm install
    
    et ajouter au package.json dans l'objet scripts :
    {
      "build": "npx ksup-front-build --pom",
      "build:dev": "LOG_LEVEL=debug npx ksup-front-build --mode development --dump --pom",
      "build:watch": "LOG_LEVEL=debug npx ksup-front-build --mode development --watch --pom"
    }
    
  • ensuite on crée un fichier bundle.json dans src/main/resources
    {
      "styles": {},
      "scripts": {}
    }
    
  • on doit avoir un package.json sous cette forme
{
  "name": "${nomProjet}",
  "version": "2.0.0",
  "description": "Description du projet",
  "scripts": {
    "build": "npx ksup-front-build --pom",
    "build:dev": "LOG_LEVEL=debug npx ksup-front-build --mode development --dump --pom",
    "build:watch": "LOG_LEVEL=debug npx ksup-front-build --mode development --watch --pom"
  },
  "repository": {
    "type": "git",
    "url": "ssh://git@git.kosmos.fr/kspjt/${nomProjet}.git"
  },
  "keywords": [],
  "author": "Kosmos",
  "license": "Apache-2.0",
  "devDependencies": {
    "@ksup/ksup-front-build": "0.0.11",
    "npx": "^10.2.2"
  },
  "dependencies": {}
}
  • puis il faut ajouter le plugin ci-dessous pour pouvoir récupérer les dépendances javascript et scss du psk en v7.
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>${maven-dependency-plugin.version}</version>
    <executions>
        <execution>
            <id>unpack psk</id>
            <phase>generate-resources</phase>
            <goals>
                <goal>unpack-dependencies</goal>
            </goals>
            <configuration>
                <includeGroupIds>fr.kosmos.projet.projectsk</includeGroupIds>
                <includeTypes>war</includeTypes>
                <includes>**/*.js, **/*.svg, **/*.scss, **/*.css, bundle.json</includes>
                <stripVersion>true</stripVersion>
                <stripType>true</stripType>
                <stripClassifier>true</stripClassifier>
                <useSubDirectoryPerArtifact>true</useSubDirectoryPerArtifact>
                <prependGroupId>false</prependGroupId>
                <silent>false</silent>
                <outputDirectory>${front.build.jar.assets}/preproject</outputDirectory>
                <!-- groupe de marker séparé pour permettre plusieurs unpack du même jar -->
                <!-- https://lists.apache.org/thread/x04m9xlyy09oc8qr3slfvv4xqh3cllp6 -->
                <markersDirectory>${project.build.directory}/markers/preproject</markersDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>
  • enfin ajouter le plugin suivant pour y intégrer les polices de caractères.
     <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-resources-plugin</artifactId>
           <configuration>
                 <nonFilteredFileExtensions>
                     <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                     <nonFilteredFileExtension>eot</nonFilteredFileExtension>
                     <nonFilteredFileExtension>woff</nonFilteredFileExtension>
                     <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
                      <nonFilteredFileExtension>svg</nonFilteredFileExtension>
                 </nonFilteredFileExtensions>
           </configuration>
     </plugin>
    

Overlays

Les overlays liés au produit et ses extensions est à supprimer.

ATTENTION : L'overlay qui concerne les contenus du PSK sont toujours nécessaires à ce jour, il faut donc les laisser.

Pour que les jsp soient bien prises en compte dans le projet il faudra ajouter à la fin de la liste des overlay, l'overlay suivant :

<overlay>
    <groupId>fr.kosmos.web.kore</groupId>
    <artifactId>frontgen</artifactId>
    <targetPath>/jsp</targetPath>
</overlay>

Gitignore

Il faut de modifier le .gitignore du projet, pour rajouter des dossiers/fichiers qui ne doivent pas être commités. Soit :

/node_modules/
/node/
package-lock.json

Impacts mise à jour JDK 17

Java 17 apporte de nouvelles fonctions et syntaxes, à l’instar de java 8 avec les lambdas. Cependant, la migration vers Java 17 impacte certains imports, ce qui crée des problèmes de compilations au moment du build maven.

  • Certaines classes ont été migrées de javax. vers jakarta..
    • Par exemple : javax.validation.machin.truc devient jakarta.validation.machin.truc.

Changements liés a Opensearch

  • Faire le ticket MIGRATION-125 si le projet ne doit pas utiliser la refonte du flux.

Exemple

Soit le pom.xml suivant en 6.7 :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>fr.kosmos.web.projets</groupId>
        <artifactId>parent-projects</artifactId>
        <version>1.07.02</version>
    </parent>

    <groupId>fr.kosmos.projet.upvd</groupId>
    <artifactId>upvd</artifactId>
    <version>1.03.15-SNAPSHOT</version>

    <packaging>war</packaging>

    <!-- Description -->
    <name>Université de Perpignan Via Domitia</name>
    <description>Site et sous-sites de l'Université de Perpignan Via Domitia</description>

    <!-- Propriétés -->
    <properties>
        <produit>ksup</produit>
        <repo.git>ssh://git@git.kosmos.fr/kspjt/upvd.git</repo.git>

        <!-- Version du produit et des extensions -->
        <core.version>6.07.74</core.version>
        <agenda.version>2.07.21</agenda.version>
        <accueil.version>1.07.08</accueil.version>
        <ldap.version>1.07.10</ldap.version>
        <fichelink.version>1.07.04</fichelink.version>
        <uas.version>2.07.06</uas.version>
        <supervision.version>1.07.12</supervision.version>
        <importExport.version>1.07.30</importExport.version>
        <eprivacy.version>2.07.09</eprivacy.version>
        
        <!-- Exclusion des sources non ksup dans sonar -->
        <sonar.exclusions>
            src/main/webapp/confortplus/**/*
        </sonar.exclusions>
    </properties>

    <!-- Emplacement SVN -->
    <scm>
        <connection>scm:git:ssh://git@git.kosmos.fr/kspjt/upvd.git</connection>
        <tag>HEAD</tag>
    </scm>

    <!-- Description des étapes de build -->
    <build>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <plugins>
            <!-- Définit la construction du war -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration combine.self="append">
                    <overlays>
                        <!-- Descente du produit et des extensions -->
                        <overlay>
                            <groupId>fr.kosmos.web.produit</groupId>
                            <artifactId>${produit}</artifactId>
                            <targetPath>/</targetPath>
                            <excludes>
                                <exclude>**/extensions/importExport/**/*</exclude>
                                <exclude>**/WEB-INF/lib/importExport*</exclude>
                            </excludes>
                        </overlay>
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>agenda</artifactId>
                            <targetPath>/extensions/agenda</targetPath>
                        </overlay>
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>accueil</artifactId>
                            <targetPath>/extensions/accueil</targetPath>
                        </overlay>
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>fichelink</artifactId>
                            <targetPath>/extensions/fichelink</targetPath>
                        </overlay>
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>ldap</artifactId>
                            <targetPath>/extensions/ldap</targetPath>
                        </overlay>
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>uas</artifactId>
                            <targetPath>/extensions/uas</targetPath>
                        </overlay>
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>supervision</artifactId>
                            <targetPath>/extensions/supervision</targetPath>
                        </overlay>
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>importExport</artifactId>
                            <targetPath>/extensions/importExport</targetPath>
                        </overlay>
                        <!-- Extension eprivacy -->
                        <overlay>
                            <groupId>fr.kosmos.web.extensions</groupId>
                            <artifactId>eprivacy</artifactId>
                            <targetPath>/extensions/eprivacy</targetPath>
                        </overlay>
                    </overlays>
                </configuration>
            </plugin>

            <plugin>
                <groupId>nl.geodienstencentrum.maven</groupId>
                <artifactId>sass-maven-plugin</artifactId>
                <configuration combine.self="append">
                    <gems>
                        <gem>${basedir}/utils/gems/zengrids-1.4/lib/zen-grids.rb</gem>
                    </gems>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!-- Gestion des dépendances (notamment pour les overlays) -->
    <dependencies>
        <!-- Dépendances produit -->
        <dependency>
            <groupId>fr.kosmos.web.produit</groupId>
            <artifactId>${produit}</artifactId>
            <version>${core.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.produit</groupId>
            <artifactId>${produit}</artifactId>
            <version>${core.version}</version>
            <type>pom</type>
            <scope>provided</scope>
            <exclusions>
                <exclusion>
                    <groupId>fr.kosmos.web.extensions</groupId>
                    <artifactId>importExport</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Extension fichelink -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>fichelink</artifactId>
            <version>${fichelink.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>fichelink</artifactId>
            <version>${fichelink.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>

        <!-- Extension ldap -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>ldap</artifactId>
            <version>${ldap.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>ldap</artifactId>
            <version>${ldap.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>

        <!-- Extension agenda -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>agenda</artifactId>
            <version>${agenda.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>agenda</artifactId>
            <version>${agenda.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>

        <!-- Extension accueil -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>accueil</artifactId>
            <version>${accueil.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>accueil</artifactId>
            <version>${accueil.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>

        <!-- Extension Usine à sites -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>uas</artifactId>
            <version>${uas.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>uas</artifactId>
            <version>${uas.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>

        <!-- Extension Supervision (liée au contrat TMA) -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>supervision</artifactId>
            <version>${supervision.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>supervision</artifactId>
            <version>${supervision.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>importExport</artifactId>
            <version>${importExport.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>importExport</artifactId>
            <version>${importExport.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>
        <!-- Extension eprivacy -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>eprivacy</artifactId>
            <version>${eprivacy.version}</version>
            <type>war</type>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>eprivacy</artifactId>
            <version>${eprivacy.version}</version>
            <type>jar</type>
            <classifier>classes</classifier>
        </dependency>
    </dependencies>
</project>

On retrouverait donc, en 7.0 :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>fr.kosmos.web.projets</groupId>
        <artifactId>parent-projects</artifactId>
        <version>7.0-SNAPSHOT</version>
    </parent>

    <groupId>fr.kosmos.projet.upvd</groupId>
    <artifactId>upvd</artifactId>
    <version>2.00.00-SNAPSHOT</version>

    <packaging>war</packaging>

    <!-- Description -->
    <name>Université de Perpignan Via Domitia</name>
    <description>Site et sous-sites de l'Université de Perpignan Via Domitia</description>

    <!-- Propriétés -->
    <properties>
        <repo.git>ssh://git@git.kosmos.fr/kspjt/upvd.git</repo.git>
        <!-- Exclusion des sources non ksup dans sonar -->
        <sonar.exclusions>
            src/main/webapp/confortplus/**/*
        </sonar.exclusions>
    </properties>
  
    <!-- Gestion des dépendances (notamment pour les overlays) -->
    <dependencies>
        <!-- Extension accueil -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>accueil-webapp-contrib</artifactId>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>accueil-webapp-legacy</artifactId>
        </dependency>
        <!-- Extension agenda -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>agenda-webapp-contrib</artifactId>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>agenda-webapp-legacy</artifactId>
        </dependency>
        <!-- Extension ePrivacy -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>eprivacy-webapp-legacy</artifactId>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>eprivacy-webapp-contrib</artifactId>
        </dependency>
        <!-- Extension fichelink -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>fichelink-webapp-contrib</artifactId>
        </dependency>
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>fichelink-webapp-legacy</artifactId>
        </dependency>
        <!-- Extension ldap -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>ldap-webapp-contrib</artifactId>
        </dependency>
        <!-- Extension Supervision (liée au contrat TMA) -->
        <dependency>
            <groupId>fr.kosmos.web.extensions</groupId>
            <artifactId>supervision-webapp-contrib</artifactId>
        </dependency>
    </dependencies>
</project>

Modifier le fichier

Migration des objets spécifiques

Pour chaque objet spécifique, faire les modifications suivantes.

Impacts java des changements sur les encadrés des objets spéciaux

Liens utiles :

  • MIGRATION-170 impacts de migrations à faire
  • MIGRATION-169 impact sur la génération de fichiers d'imports (rien à faire c'est un impact client).

Saisie de l'objet

Dans la classe de saisie de l'objet :

  • Supprimer "throws Exception" de la signature de la méthode preparerRECHERCHE
  • Implémenter la méthode prepareRestrictedSearch
  • Implémenter la méthode getObjectCode

La méthode prepareRestrictedSearch réalise la même opération que preparerRECHERCHE mais dans un contexte de "libellé restreint", c'est pour cette raison qu'une rubrique est passée en paramètre de la méthode. Il convient donc de récupérer les libellés de façon restreinte.

Par exemple :

public class SaisieObjetSpe extends SaisieFiche {

    @Override
    protected void preparerRECHERCHE() {
        infoBean.set("LISTE_LANGUES", LangueUtil.getListeLangues(getLocale()));
        infoBean.set("LISTE_TYPE_EVENEMENT", LabelUtils.getLabelCombo("03", getLocale()));
        infoBean.set("LISTE_THEMATIQUES", LabelUtils.getLabelCombo("04", getLocale()));
        infoBean.set("LISTE_SITES", LabelUtils.getLabelCombo("08", getLocale()));
    }

    @Override
    protected void prepareRestrictedSearch(String sectionCode) {
        infoBean.set("LISTE_LANGUES", LangueUtil.getListeLangues(getLocale()));
        infoBean.set("LISTE_TYPE_EVENEMENT", LabelUtils.getLabelForComboForSection("03", getLocale(), sectionCode));
        infoBean.set("LISTE_THEMATIQUES", LabelUtils.getLabelForComboForSection("04", getLocale(), sectionCode));
        infoBean.set("LISTE_SITES", LabelUtils.getLabelForComboForSection("08", getLocale(), sectionCode));
    }
}

Dans la jsp de saisie BO de la fiche, remplacer la saisie de la rubrique et de la langue par :

<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
...
<tiles:insertDefinition name="contentContext">
    <tiles:putAttribute name="model" value="${infoBean.get(\"CONTEXT_DIPLAY_MODEL\")}"/>
</tiles:insertDefinition>

Il faut également retirer les initialisations des codes et libellés de rubriques à l'initialisation de la page, sous peine de perdre les éventuelles modifications faites sur les rubriques. (voir la méthode preparerPRINCIPAL sur l'objet java Saisie.java)

Déclaration du bean de l'objet

Précédemment, on avait la forme suivante de bean de déclaration :

    <!-- Déclaration de l'objet -->
    <bean id="objetSpe" class="com.kportal.cms.objetspartages.Objetpartage">
        <property name="nomClasse" value="fr.acme.spe.om.ObjetSpe"/>
        <property name="classeBean" value="fr.acme.spe.bean.ObjetSpeBean"/>
        <property name="parametreProcessus" value="SAISIE_OBJET_SPE"/>
        <property name="nomObjet">
            <util:constant static-field="fr.acme.spe.om.ObjetSpe.NOM_OBJET"/>
        </property>
        <property name="codeObjet">
            <util:constant static-field="fr.acme.spe.om.ObjetSpe.CODE_OBJET"/>
        </property>
        <property name="libelleObjet" value="ST_OBJET_SPE"/>
        <property name="ordre" value="20"/>
        <property name="idMenuBoParent" value="menuContenu"/>
        <property name="libelle" value="ST_OBJET_SPE"/>
        <property name="type" ref="TYPE_PARAMETRABLE"/>
        <property name="actions" ref="actionsDefautFiche"/>
        <property name="actionDefaut" ref="actionAccueil"/>
        <property name="ficheDAO" ref="objetSpeDAO"/>
    </bean>

La déclaration de l'emplacement de l'objet dans le menu BO a changé. Créer un nouveau composant de menu avec le bean suivant :

    <bean id="objetSpMenu" class="com.kportal.extension.module.menu.ComponentItemMenu">
        <property name="label" value="ST_NOM_OBJET_SPE"/>
        <property name="order" value="80"/>
        <property name="component" ref="objetSpeBean"/>
        <property name="parent" ref="menu_parent"/>
    </bean>

Utiliser les valeurs ordre et idMenuBoParent précédemment dans le bean de déclaration de l'objet spe pour indiquer la référence du menu et l'ordre de la fiche dans le menu.

L'idMenuBoParent ne correspond pas à la référence du menu parent, il faut faire une conversion. Il faut voir avec le PO pour déterminer l'emplacement du menu de l'objet spé en V7. Les valeurs possibles sont listées dans core-menubo.xml.

Le bean doit maintenant ressembler à ça :

    <!-- Déclaration de l'objet -->
    <bean id="objetSpe" class="com.kportal.cms.objetspartages.Objetpartage">
        <property name="nomClasse" value="fr.acme.spe.om.ObjetSpe"/>
        <property name="classeBean" value="fr.acme.spe.bean.ObjetSpeBean"/>
        <property name="parametreProcessus" value="SAISIE_OBJET_SPE"/>
        <property name="nomObjet">
            <util:constant static-field="fr.acme.spe.om.ObjetSpe.NOM_OBJET"/>
        </property>
        <property name="codeObjet">
            <util:constant static-field="fr.acme.spe.om.ObjetSpe.CODE_OBJET"/>
        </property>
        <property name="libelleObjet" value="ST_OBJET_SPE"/>
        <property name="libelle" value="ST_OBJET_SPE"/>
        <property name="type" ref="TYPE_PARAMETRABLE"/>
        <property name="actions" ref="actionsDefautFiche"/>
        <property name="actionDefaut" ref="actionAccueil"/>
        <property name="ficheDAO" ref="objetSpeDAO"/>
    </bean>

Modifier le fichier

Mise a jour de Java et API K-SUP

Dans cette partie, nous allons lister les impacts de compilations liés à la montée de version vers Java 17.

API flyway

Changer les scripts java :

import org.flywaydb.core.api.migration.MigrationChecksumProvider;
import org.flywaydb.core.api.migration.jdbc.JdbcMigration;

public class nom_de_la_classe_du_script implements JdbcMigration, MigrationChecksumProvider {
    //....

    @Override
    public void migrate(final Connection connection) throws Exception {
        //....
    }
}

par

import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;

public class nom_de_la_classe_du_script extends BaseJavaMigration {
    //....

    @Override
    public void migrate(final Context context) throws Exception {
        //....
    }
}

Recherche

  • Modification de SaisieFiche#preparerRecherche décrite dans le ticket MIGRATION-123
  • Modification de l'api liée à la récupération des libellés décrite dans le ticket MIGRATION-111

Changer le type de l'attribut 'typesFiches'

L'attribut typesFiches de SearchOptions a été modifié. Désormais, il prend une Map de <String, SearchFicheConfiguration>. Le code suivant :

final List<String> typeFiches = new ArrayList<>();
typeFiches.add("formation");
searchOptions.setTypesFiches(typeFiches);

devient :

final Map<String, SearchFicheConfiguration> typeFiches = new HashMap<>();
typeFiches.put("formation", new SearchFicheConfiguration("formation"));
searchOptions.setTypesFiches(typeFiches);

Modifier la signature de la méthode generateIndexingCommand

Certaines méthodes de la classe ServiceIndexerFicheBeanImpl ont été mises à jour, notamment au niveau des signatures.

  • Ainsi, la méthode generateIndexingCommand
protected IndexingCommand generateIndexingCommand(final MetatagBean metatag, final String type, final FormationBean bean, Map<String, CompletionBean> completions) {

devient

public SearchCommand generateIndexingCommand(final MetatagBean metatag, final String type, final FormationBean bean, Map<String, CompletionBean> completions) {
  • Pour retourner une commande de création, il faudra ensuite réaliser un cast dans le code. Soit :
final IndexingCommand indexingCommand = super.generateIndexingCommand(metatag, type, formationUPVDBean, completions);

devient :

final CreateIndexingCommand indexingCommand = (CreateIndexingCommand) super.generateIndexingCommand(metatag, type, formationUPVDBean, completions);

Impacts java 17

Si nécessaire, modifier les imports javax vers jakarta.

import javax.validation.Constraint;

devient

import jakarta.validation.Constraint;

Impacts API Ksup

Java

Migrations diverses

  • Si l'appel à la méthode FicheUnivHelper.getFiche() est utilisé pour afficher une fiche en front, elle doit être remplacée par FicheUnivHelper.getFicheFrontOffice().
  • La méthode ValidateurCAS.getURLCasAuthentification() n'existe plus, elle a été remplacée par ValidateurCAS.getURLAuthentificationCallback().
  • Ajout d'un tag "média"
  • Indexation des champs booléens ksup
  • La propriété affichebloc des CardViewModel est remplacé par displayBloc.

Modifier le fichier

Usine logicielle

.npmrc

Il est nécessaire de créer un fichier .npmrc à la racine du projet, afin que celui-ci compile correctement sur Jenkins, avec le contenu suivant :

registry=https://nexus.nantes.kosmos.fr/nexus/content/groups/public.npm/

Jenkinsfile

Le Jenkinsfile du projet doit être mis à jour pour compiler en 7.0 avec Java 17. Soit le fichier suivant :

#!/usr/bin/env groovy
@Library('KSup_Pipeline') _
// https://confluence.kosmos.fr/display/KOUTILS/Pipeline+Projets
projectDeployTemplatePipelineV2(
    PROJECT_ROOM: 'monProjet',
    MAVEN_VERSION: 'maven_3.8.6',
    MAVEN_JDK_VERSION: 'jdk_17',
    HELM_TEMPLATE_VERSION_APP: '2.0.3',
    HELM_TEMPLATE_VERSION_VOL: '0.2.1')
)

Faire attention à la version du Helm disponible lors de la modification du Jenkinsfile.

IC/values.yaml

Le fichier IC/values.yaml de votre projet doit être mis à jour pour utiliser Java 17 et Tomcat 9 lors des déploiements. Soit les lignes suivantes à déclarer :

tomcat:
  # Personnalisation de la version du serveur Tomcat
  image:
    name: nexus.nantes.kosmos.fr:18445/solutioning/tomcat
    tag: 9-jdk17

Modifier le fichier

Mise à jour de la configuration Spring

Déplacement des contextes, des fichiers .dat et .properties

Organisation des jsp

Les modules des extensions ont été modifiés dans le produit. Les fichiers jsp présents dans jsp/core sont à déplacer dans un dossier avec le nom de l'extension.

Par exemple jsp/extensions/core/article/fiche.jsp est à déplacer dans jsp/extensions/article/article/fiche.jsp.

Gestion des fichiers .dat

Les fichiers .dat sont à déplacer du répertoire src/main/webapp/WEB-INF/ dans le répertoire : src/main/resources/.

Suite au ticket CORE-6810, la propriété "table.repertoire.specific=/tables_specific a été ajoutée aux propriétés du produit dans le fichier core.properties.

  • S'il est présent avec la valeur "/tables_specific" sur un projet à migrer, il faut le supprimer des propriétés du projet.
  • Si la propriété a une valeur autre que "/table_specific", il faut laisser cette propriété telle quelle dans le projet.
  • La propriété table.repertoire.specific a été supprimée du PSK et les fichiers .dat du psk ont été déplacés dans le ticket PROJZERO-901.
  • Impact de migration : MIGRATION-351

Fichiers .properties

  • Mettre les propriétés spécifiques au projet dans ${extension_projet}.properties. Vérifier dans le fichier ExtensionContext.xml du projet si c'est ce nom qui est spécifié dans le bean de définition des propriétés. Sinon utiliser celui qui y est référencé.
  • Déplacer les propriétés qui sont des surcharges des propriétés du core dans le fichier du projet application_${extension_projet}.properties.

Contexte des extensions

Renommer le fichier ${nom_projet}ExtensionContext.xml en ExtensionContext.xml et le déplacer dans un dosser src/main/resources/com/project/${nom_projet}/context.

Effectuer les actions décrites dans MIGRATION-301

Exemple de modification sur UPVD.

Attention ! Il ne faut pas overrider le ExtensionContext du projet. On ne s'override pas soi-même.

Cloisonnement des contextes

  • Si le projet possède des controllers : voir le résumé des actions à faire dans MIGRATION-160

Impacts des fichiers spring liés a Opensearch

Impacts liés aux objets & plugins spécifiques

Si le projet possède des objets spécifiques ou des plugins indexés :

Gestion des impacts liés au CKEditor

  • Voir MIGRATION-240

  • Voir MIGRATION-320 si le projet possède des styles de lien bouton

  • Avec le cloisonnement des contextes, il faut déclarer les configurations CKEditor différemment : désormais, il faut faire un application_typefiche_ckeditor.properties par type de fiche.

    • En 6.7, on avait, dans un fichier application_monprojet_ckeditor.properties :
DEFAULT=/adminsite/ckeditor/configurations/monprojetDefaultConfig.json
SAISIE_PAGELIBRE=/adminsite/ckeditor/configurations/monprojetStyleParagraphe.json
SAISIE_PAGELIBRE.COMPLEMENTS=/adminsite/ckeditor/configurations/monprojetDefaultConfig.json
SAISIE_PAGELIBRE.CONTENU_ENCADRE=/adminsite/ckeditor/configurations/monprojetStyleEncadre.json
  • Désormais, on a, dans un fichier application_pagelibre_ckeditor.properties :
SAISIE_PAGELIBRE=/adminsite/ckeditor/configurations/monprojetStyleParagraphe.json
SAISIE_PAGELIBRE.COMPLEMENTS=/adminsite/ckeditor/configurations/monprojetDefaultConfig.json
SAISIE_PAGELIBRE.CONTENU_ENCADRE=/adminsite/ckeditor/configurations/monprojetStyleEncadre.json
  • Et le fichier application_acme_ckeditor.properties devient :
DEFAULT=/adminsite/ckeditor/configurations/monprojetDefaultConfig.json

Impact sur les templates

Renommage du bean templateSiteDefaut : MIGRATION-314

Si la modification n'est pas effectuée, l'erreur suivante survient :

2024-07-11_15:53:56.913 [RMI TCP Connection(2)-127.0.0.1] ERROR c.j.j.core.ApplicationContextManager - Erreur extension id=pec : le bean id=templateSiteDefaut à merger sur l'application n'existe pas
2024-07-11_15:53:56.913 [RMI TCP Connection(2)-127.0.0.1] ERROR c.j.j.core.ApplicationContextManager - Erreur extension id=pec : le bean id=templateSiteDefaut impossible à merger sur l'application
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'templateSiteDefaut' available

Nouveaux droits

Demander au CPK s'il faut ajouter les droits "Dupliquer, Exporter et Importer". Si oui, ajouter la propriété dans le fichier src/main/resources/application_uas.properties :

uas.fonctionnaliteAvancee = 1

Environnement local

Si vous n'avez pas de .localhost dans vos noms de domaines, ajouter les lignes suivantes dans l'env.properties :

cookie.processor.secure=false
cookie.processor.samesite=UNSET

Cela permet de désactiver le flag "secure" sur les cookies retournés par le K-Sup au navigateur. Ce paramètre empêchera la fonctionnalité de SSO en local sur votre poste (qui nécessite un cookie "SameSite=None" + secure).

Modifier le fichier

Identifier les problèmes de démarrage et corriger

Erreurs lors du build maven

Erreur d'encoding

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources) on project upvd: Input length = 1 -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.

Un fichier du projet a été enregistré dans le mauvais encoding.

Dans un premier temps, vérifier l'encoding des fichiers de son projet dans Settings > Editor > File encodings. Ils doivent être en UTF-8.

Il faut ensuite déterminer quels fichiers posent problème. Ne pas hésiter à revenir à une étape sans cette erreur pour voir quels fichiers ont été modifiés ou déplacés.

Erreurs opensearch

Erreur lors de l'indexation

En fonction de l'utilisation d'opensearch en local, si vous n'utilisez qu'un seul docker, les données sont communes à tous les projets.

Si vous avez l'erreur suivante, c'est que les tables de batch ne sont plus en phase : les tables séquences contiennent les prochains id à utiliser. Si ces id existent déjà, la contrainte n'est pas respectée.

PreparedStatementCallback; SQL [INSERT into BATCH_JOB_EXECUTION(JOB_EXECUTION_ID, JOB_INSTANCE_ID, START_TIME, END_TIME, STATUS, EXIT_CODE, EXIT_MESSAGE, VERSION, CREATE_TIME, LAST_UPDATED, JOB_CONFIGURATION_LOCATION) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; Duplicate entry '0' for key 'PRIMARY'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '0' for key 'PRIMARY'

Le script suivant remet à zéro les id. Il faut ensuite lancer un script d'indexation.

use NOM_DATABASE;
SET FOREIGN_KEY_CHECKS=0;
truncate table `BATCH_JOB_EXECUTION`;
truncate table `BATCH_JOB_EXECUTION_CONTEXT`;
truncate table `BATCH_JOB_EXECUTION_PARAMS`;
truncate table `BATCH_JOB_EXECUTION_SEQ`;
truncate table `BATCH_JOB_INSTANCE`;
truncate table `BATCH_JOB_SEQ`;
truncate table `BATCH_STEP_EXECUTION`;
truncate table `BATCH_STEP_EXECUTION_CONTEXT`;
truncate table `BATCH_STEP_EXECUTION_SEQ`;
SET FOREIGN_KEY_CHECKS=1;
insert into `BATCH_JOB_EXECUTION_SEQ` values ('1', '0');
insert into `BATCH_JOB_SEQ` values ('1', '0');
insert into `BATCH_STEP_EXECUTION_SEQ` values ('1', '0');

Erreur lors du lancement Tomcat

Erreur [${preview.mapping:/apercu/}*]

L'exception ci dessous apparait lors du passage en 7 car le build exige la présence d'un package.json pour procéder.

Caused by: java.lang.IllegalArgumentException: <url-pattern> [${preview.mapping:/apercu/}*] invalide dans l'association de filtre (filter mapping)

Comme la documentation le précise maintenant, il faut ajouter ce package.json, créer un .npmrc a la racine du projet (et dans son poste de développement) pour indiquer le registre, et ajouter un bundle. Détails sur npm.md.

Erreurs front

Aucune style appliqué sur le site / Aucune interaction possible sur le site

Si aucun style n'est appliqué sur le site et/ou qu'aucune interaction n'est possible (les menus dropdown ne fonctionnent pas, les menus latéraux sont cassés sur les petits écrans). Vous êtes au bon endroit.

Analyse du problème

Généralement, ce problème est visible a l'oeil mais on peut le confirmer en allant dans l'onglet network de la console de debug de son navigateur, et des fichiers en rouge passent à travers les lignes.

Solutions

Oubli du script.js dans le header

Vérifier si le script.js est bien ajouté sur le header.jsp du projet.

Pour la v7, il faut voir la ligne ci-dessous, et s'assurer de sa présence dans le dossier target/<projet>-<version>/static/js/scripts_<code_langue>.js

<resources:script group="scripts" locale="<%= ContexteUtil.getContexteUniv().getLocale().toString() %>"/><%
Analyser le bundle final

En v7, si la console développeur, indique une librairie manquante, il peut être utile de vérifier si le bundle.json effectif du projet le descend correctement. Pour faire cette analyse, il faut insérer le script suivant dans le package.json:

  "scripts": {
    ...
    "build:dev": "LOG_LEVEL=debug npx ksup-front-build --mode development --dump --pom",
    ...  
}

Après l'avoir lancé, un fichier target/tmp/bundles_dump.json est généré et il faut regarder ici, si les dépendances sont bien disponibles. Sinon, il y a un problème avec la construction du bundle.json.

Certains styles ne sont pas appliqués correctement

Il y a un impact de migration dans la mise a jour du moteur SASS, où l'utilisation de variables dans les styles est un peu différent

Le site ne trouve pas certains groupes après la migration en bundle

Si le groupe que vous recherchez est <nom_groupe> et est défini de la forme suivante :

{
  "<nom_groupe>": {
    "files": [
      "des scripts ou styles"
    ]
  }
}

Et qu'il existe dans un autre bundle, un autre groupe défini de la forme suivante :

{
  "<nom_groupe>": {
    "files": [
      "d'autres scripts ou styles"
    ],
    "lang": [
      "des langues"
    ]
  }
}

Alors, il est possible que le groupe que vous cherchez ait été fusionné aux groupes du même nom avec des langues.

La solution est d'ajouter le paramètre locale="<%=ContexteUtil.getContexteUniv().getLocale().toString()%> à la balise d'appel du groupe.

<resources:script group="<nom_groupe>" />

Devient :

<resources:script group="<nom_groupe>" locale="<%= ContexteUtil.getContexteUniv().getLocale().toString()%>"/>

Erreurs SQL / Flyway

Erreur liée à la montée de version 10.2 vers version supérieure.

Si vous rencontrez l'erreur SQL : Caused by: java.sql.SQLException: Column count of mysql.proc is wrong. Expected 21, found 20. Created with MariaDB 100244, now running 100525. Please use mariadb-upgrade to fix this error

Caused by: com.kosmos.migration.exception.MigrationException: Error during flyway migration for table SCHEMA_VERSION_CORE
      ...
Caused by: org.flywaydb.core.api.FlywayException: Error while executing beforeMigrate callback: Migration beforeMigrate.sql failed
----------------------------------
SQL State  : HY000
Error Code : 1558
Message    : Column count of mysql.proc is wrong. Expected 21, found 20. Created with MariaDB 100244, now running 100525. Please use mariadb-upgrade to fix this error
Location   : com/kosmos/core/migration/beforeMigrate.sql (/usr/local/tomcat/file:/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/core-7.1-SNAPSHOT.jar!/com/kosmos/core/migration/beforeMigrate.sql)
Line       : 20
Statement  : -- Création de la procédure stockée utilisée pour ajouter une colonne
-- Avant de créer la colonne, cette procédure teste si elle est déjà présente
DROP PROCEDURE IF EXISTS ADD_COLUMN

  ...
Caused by: org.flywaydb.core.internal.sqlscript.FlywaySqlScriptException: Migration beforeMigrate.sql failed
----------------------------------
SQL State  : HY000
Error Code : 1558
Message    : Column count of mysql.proc is wrong. Expected 21, found 20. Created with MariaDB 100244, now running 100525. Please use mariadb-upgrade to fix this error
Location   : com/kosmos/core/migration/beforeMigrate.sql (/usr/local/tomcat/file:/usr/local/tomcat/webapps/ROOT/WEB-INF/lib/core-7.1-SNAPSHOT.jar!/com/kosmos/core/migration/beforeMigrate.sql)
Line       : 20
Statement  : -- Création de la procédure stockée utilisée pour ajouter une colonne
-- Avant de créer la colonne, cette procédure teste si elle est déjà présente
DROP PROCEDURE IF EXISTS ADD_COLUMN
...
Caused by: java.sql.SQLException: Column count of mysql.proc is wrong. Expected 21, found 20. Created with MariaDB 100244, now running 100525. Please use mariadb-upgrade to fix this error
...
2024-07-22_08:59:27.074 [main] [] INFO  o.s.i.endpoint.EventDrivenConsumer - Removing {logging-channel-adapter:channel.out.log.adapter} as a subscriber to the 'channel.out.log' channel
2024-07-22_08:59:27.074 [main] [] INFO  o.s.i.channel.DirectChannel - Channel 'org.springframework.web.context.WebApplicationContext:.channel.out.log' has 0 subscriber(s).
2024-07-22_08:59:27.075 [main] [] INFO  o.s.i.endpoint.EventDrivenConsumer - stopped bean 'channel.out.log.adapter'; defined in: 'class path resource [publish/publish-context.xml]'; from source: ''int:logging-channel-adapter' with id='channel.out.log''

Il faut alors lancer la commande suivante sur le serveur de base de données :

/usr/bin/mysql_upgrade -u root -p

DuplicateKeyException

Si vous rencontrez l'erreur suivante : Caused by: org.springframework.dao.DuplicateKeyException: PreparedStatementCallback;

487131 [http-nio-8087-exec-6] INFO  c.u.o.a.impl.ServiceAspectMetatag - Detect [ServiceMetatag.save] action on id [74141] > [74141] 
487153 [http-nio-8087-exec-6] INFO  c.u.u.p.UrlProviderChainController - Impossible de trouver une URL pour le contexte site:ksup, rubrique:, idMetatag:74141 
487195 [searchSubscribeTaskExecutor-4] INFO  c.k.s.i.b.c.CreateIndexingCommand - Indexation effectuée [index : content_fr, type : fiche, id : 74141] 
487196 [searchSubscribeTaskExecutor-4] INFO  c.k.s.i.s.i.ServiceIndexerIndexerMetatag - Mise à jour de l'ensemble des données indexées référençant la fiche (code=1747820981155, type=Page libre) 
487208 [searchSubscribeTaskExecutor-4] ERROR o.s.i.handler.LoggingHandler - org.springframework.messaging.MessageHandlingException: error occurred during processing message in 'MethodInvokingMessageProcessor' [org.springframework.integration.handler.MethodInvokingMessageProcessor@79da98c]; nested exception is org.springframework.dao.DuplicateKeyException: PreparedStatementCallback; SQL [INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]; Duplicate entry '74143' for key 'PRIMARY'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '74143' for key 'PRIMARY', failedMessage=GenericMessage [payload=74141, headers={action=save, id=bd03d110-870b-2e1b-a22a-88345aead357, targetId=74141, target=ControleurUniv, timestamp=1747830349876}]
	at org.springframework.integration.support.utils.IntegrationUtils.wrapInHandlingExceptionIfNecessary(IntegrationUtils.java:191)
	at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:113)
	at org.springframework.integration.handler.ServiceActivatingHandler.handleRequestMessage(ServiceActivatingHandler.java:105)
[...]
Caused by: org.springframework.dao.DuplicateKeyException: PreparedStatementCallback; SQL [INSERT into BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]; Duplicate entry '74143' for key 'PRIMARY'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '74143' for key 'PRIMARY'
	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:244)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
	at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1577)
[...]
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '74143' for key 'PRIMARY'
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:118)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)
[...]

Il faut lancer les commandes suivantes pour mettre à jour les contraintes :

insert into BATCH_JOB_EXECUTION_SEQ (select max(JOB_EXECUTION_ID),0 from BATCH_JOB_EXECUTION);
insert into BATCH_STEP_EXECUTION_SEQ (select max(STEP_EXECUTION_ID),0 from BATCH_STEP_EXECUTION);
insert into BATCH_JOB_SEQ (select max(JOB_INSTANCE_ID),0 from BATCH_JOB_INSTANCE);

Modifier le fichier

Impacts divers du framework K-Sup

Réaliser les migrations suivantes :

Offre de formation

Le tag offreformation:autocomplete n'existe plus. Il doit être remplacé par autocompletion:autocomplete avec l'import :

<%@ taglib prefix="autocompletion" uri="http://kportal.kosmos.fr/tags/autocompletion" %>

Modifier le fichier

Chaine de build

Profile maven

Vérifier que le profile maven front-build est activé, ou le front-dev.

Emplacement des fichiers scss

Déplacer le contenu du dossier /src/main/sass dans src/main/resources/sass.

Fichiers config.rb

À supprimer. Il est normalement présent dans sass/jsp/styles.

Fichiers fonts

Des fichiers fonts peuvent être présents dans le dossier sass. Il faut les déplacer au même endroit que les fichiers scss, et ajouter le plugin suivant dans le pom.xml en listant les extensions présentes dans le projet.

<build>
    <plugins>
        ...
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <configuration>
                <nonFilteredFileExtensions>
                    <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                    <nonFilteredFileExtension>eot</nonFilteredFileExtension>
                    <nonFilteredFileExtension>woff</nonFilteredFileExtension>
                    <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
                    <nonFilteredFileExtension>svg</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
            </configuration>
        </plugin>
    </plugins>
</build>

Modification du Pom

Plugins pour générer les bundles

Pour que le bundle puisse se compiler correctement, on peut supprimer le plugin unpack-sass s'il est toujours présent (ne sera plus utile pour la suite) et ajouter les plugins suivant :

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <configuration>
            <nonFilteredFileExtensions>
                <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                <nonFilteredFileExtension>eot</nonFilteredFileExtension>
                <nonFilteredFileExtension>woff</nonFilteredFileExtension>
                <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
                <nonFilteredFileExtension>svg</nonFilteredFileExtension>
            </nonFilteredFileExtensions>
        </configuration>
    </plugin>

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>${maven-dependency-plugin.version}</version>
        <executions>
            <execution>
                <id>unpack psk</id>
                <phase>generate-resources</phase>
                <goals>
                    <goal>unpack-dependencies</goal>
                </goals>
                <configuration>
                    <includeGroupIds>fr.kosmos.projet.projectsk</includeGroupIds>
                    <includeTypes>war</includeTypes>
                    <includes>**/*.js, **/*.svg, bundle.json</includes>
                    <stripVersion>true</stripVersion>
                    <stripType>true</stripType>
                    <stripClassifier>true</stripClassifier>
                    <useSubDirectoryPerArtifact>true</useSubDirectoryPerArtifact>
                    <prependGroupId>false</prependGroupId>
                    <silent>false</silent>
                    <outputDirectory>${front.build.jar.assets}/preproject</outputDirectory>
                    <!-- groupe de marker séparé pour permettre plusieurs unpack du même jar -->
                    <!-- https://lists.apache.org/thread/x04m9xlyy09oc8qr3slfvv4xqh3cllp6 -->
                    <markersDirectory>${project.build.directory}/markers/preproject</markersDirectory>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>

Ajout des resources dans le build maven

Ajouter, dans la partie déjà existante du plugin maven-war-plugin, la webResource suivante :

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-war-plugin</artifactId>
  <configuration>
    <webResources>
      <resource>
        <targetPath>/</targetPath>
        <directory>target/front-build</directory>
      </resource>
    </webResources>
  </configuration>
</plugin>

Suppression de wro

Transformation de wro.xml en bundle.json

  • Créer un fichier bundle.json minimal dans src/main/resources
  • Reprendre tous les groupes wro et les répartir dans les sections styles et scripts
    • Ajouter une section "files" dans chacun des groupes
    • Reporter les fichiers du groupe dans cette section files
    • Les chemins doivent être modifiés pour être relatifs à la racine du projet : /resources/styles/no-theme/verifications.css devient styles/no-theme/verifications.css
    • Si le fichier résultat est référencé directement dans une jsp (e.g. : screen.css)
      • Désactiver le calcul du hash
      • Préciser le chemin où générer le fichier (target)
  • Si le groupe contient des fichiers de traduction
    • Créer un fichier par langue avec comme suffixe la langue en question : frontoffice-i18n.js devient frontoffice-i18n_en.js et frontoffice-i18n_fr_FR.js.
    • Traduire chacune des valeurs présentes dans ces fichiers (avec les valeurs des fichiers *.properties de Spring)
    • Ajouter une section lang avec les différentes langues traduites et référencer les fichiers comme dans cet exemple : scripts/collaboratif_i18n_${lang}.js
  • Si des librairies Javascript externes sont utilisées et ont besoin d'être déclarées dans le scope global (e.g. : jQuery, Leaflet, ...), ou si les scripts mettent à disposition des ressources pour les autres scripts (fonctions utilitaires, définition d'objets, etc...) :
    • Ajouter une section "externals" au même niveau que styles et scripts
    • Ajouter une entrée par librairie avec, comme clé, son nom et, comme valeur, son url (relative à la racine du site ou absolue pour un CDN)
    • Ajouter une entrée "externals" au bundle
    • Ajouter une entrée dans cette dernière avec, comme clé, le nom référencé dans le fichier utilisant la librairie et, comme valeur, la clé de l'external déclaré plus haut.

Si le script est dans le projet, le chemin à indiquer correspond au chemin depuis src/main/resources/. Par exemple pour le fichier src/main/resources/js/upvd/recherche_fiches.js on aura :

{
  "scripts": {
    "nomGroup": {
      "files": [
        "js/upvd/recherche_fiches.js"
      ]
    }
  }
}

Si un script utilise jquery, il faut l'ajouter dans la partie externals. Par exemple, le fichier wro suivant :


<group name="styles">
    <css>/resources/styles/extension-commentaire-upvd.css</css>
</group>
<group name="styles-theme1">
    <css>/jsp/styles/screen.css</css>
    <css>/extensions/eprivacy/resources/styles/orejime.css</css>
    <css>/extensions/eprivacy/resources/styles/overload-orejime.css</css>
</group>

<group name="styles-print-theme1">
    <css>/jsp/styles/impression.css</css>
    <css>/jsp/styles/theme1_upvd/impression.css</css>
</group>

        <!-- JS FO -->
<group name="i18nFo" abstract="true">
    <js>/adminsite/scripts/libs/datepicker/localization/messages.js</js>
    <js>/adminsite/scripts/libs/validate/localization/messages.js</js>
    <js>/jsp/scripts/frontoffice-i18n.js</js>
    <!-- Inclusion des ressources des modules -->
    <js>/resources/fo/scripts/i18n/responsive_previsualization-i18n.js</js>
</group>

<group name="scriptsFo-theme1_ca">
    <group-ref>scriptsFo_psk_theme1_common</group-ref>
</group>
<group name="scriptsFo-theme1_es">
    <group-ref>scriptsFo_psk_theme1_common</group-ref>
</group>

<group name="i18nFo_fr" abstract="true">
    <group-ref>i18nFo</group-ref>
</group>

        <!-- Scripts FO -->
<group name="scriptsFo_psk_theme1_common" abstract="true">
    <js>/resources/fo/scripts/frontoffice-theme1.js</js>
</group>

<group name="i18nFo_en" abstract="true">
    <group-ref>i18nFo</group-ref>
</group>

<group name="i18nFo_es" abstract="true">
    <group-ref>i18nFo</group-ref>
</group>

<group name="i18nFo_ca" abstract="true">
    <group-ref>i18nFo</group-ref>
</group>

<group name="jQuery" abstract="true">
    <js>/adminsite/scripts/libs/jquery-1.11.0.js</js>
    <js>/adminsite/scripts/libs/jquery-ui-1.10.3.custom.js</js>
    <js>/adminsite/scripts/kosmos-utils.js</js>
    <js>/adminsite/scripts/libs/jquery.iframePopin-0.0.1.js</js>
    <js>/adminsite/scripts/libs/jquery.transit-0.9.12.js</js>
    <js>/adminsite/scripts/libs/jquery.sort-functions-0.1.0.js</js>
    <js>/adminsite/scripts/libs/jquery.kmultiselect-0.1.1.js</js>
    <js>/adminsite/scripts/libs/jquery.kmonoselect-0.1.0.js</js>
    <js>/adminsite/scripts/libs/validate/jquery.validate-1.13.1.js</js>
    <js>/adminsite/scripts/libs/validate/jquery.validate_additional-methods-1.13.1.js</js>
    <js>/adminsite/scripts/libs/jquery.jstree-pre1.0fix2.js</js>
    <js>/adminsite/scripts/libs/jquery.cookie.js</js>
    <js>/adminsite/scripts/libs/jquery.jstreeactions-0.1.1.js</js>
    <js>/adminsite/scripts/libs/jquery.jstreekcheckboxes-0.1.0.js</js>
    <js>/adminsite/scripts/libs/jquery.jstreefilter-0.1.0.js</js>
    <js>/adminsite/scripts/libs/bootstrap.js</js>
    <js>/adminsite/scripts/libs/jquery.pwstrength-bootstrap-1.0.0.js</js>
    <js>/adminsite/scripts/libs/mediaElement/mediaelement.js</js>
    <js>/adminsite/scripts/libs/mediaElement/me-i18n.js</js>
    <js>/adminsite/scripts/libs/mediaElement/me-i18n-locale-fr.js</js>
    <js>/adminsite/scripts/libs/mediaElement/mediaelementplayer.js</js>
    <js>/jsp/scripts/libs/jquery.magnific-popup.js</js>
    <js>/jsp/scripts/libs/collapsable-common.js</js>
    <js>/jsp/scripts/libs/tabindex-override.js</js>
    <js>/adminsite/scripts/libs/jquery.autocomplete-1.2.24.js</js>
</group>


<group name="scriptsUpvd" abstract="true">
    <js>/resources/scripts/upvd.js</js>
    <js>/resources/scripts/menu.js</js>
    <js>/resources/scripts/outils.js</js>
</group>

<group name="scriptsUpvdAccueil">
    <js>/resources/scripts /accueil.js</js>
</group>

<group name="jQueryUpvd" abstract="true">
    <js>/resources/scripts/libs/jquery.cookiecuttr.js</js>
    <js>/resources/scripts/libs/jquery.cycle2.js</js>
    <js>/resources/scripts/libs/jquery.cycle2.swipe.js</js>
    <js>/resources/scripts/libs/jquery.mediahelpers.js</js>
</group>

<group name="scriptsBase" abstract="true">
    <js>/adminsite/scripts/iFrames.js</js>
    <js>/adminsite/scripts/multiSelect.js</js>
    <js>/adminsite/scripts/monoSelect.js</js>
    <js>/jsp/scripts/dropdown-button.js</js>
    <js>/jsp/scripts/menu.js</js>
    <js>/jsp/scripts/validation.js</js>
    <js>/jsp/scripts/galeries.js</js>
    <js>/jsp/scripts/medias.js</js>
    <js>/jsp/scripts/collaboratif.js</js>
    <js>/resources/scripts/upvd_i18n.js</js>
    <js>/jsp/scripts/headPage.js</js>
    <js>/jsp/scripts/onglets.js</js>
    <js>/jsp/scripts/accessible-autocomplete.js</js>
    <js>/jsp/scripts/search.js</js>
    <js>/adminsite/scripts/autocomplete.js</js>
    <!-- Inclusion des ressources des modules -->
    <js>/resources/fo/scripts/*.js</js>
    <group-ref>jQueryUpvd</group-ref>
    <group-ref>scriptsUpvd</group-ref>
    <js>/jsp/scripts/recherche_fiches.js</js>
</group>


<group name="scriptsFrontPlus" abstract="true">
    <js>/extensions/ofin/resources/scripts/data-toggle.js</js>
    <js>/extensions/ofin/resources/scripts/modal.js</js>
    <js>/extensions/ofin/resources/scripts/animate-function.js</js>
    <js>/extensions/ofin/resources/scripts/libs/clipboard.js</js>
    <js>/extensions/ofin/resources/scripts/extension-offreformation.js</js>
</group>
        <!-- Scripts FO -->
<group name="common_psk">
    <js>/extensions/psk/resources/fo/scripts/libs/donutty/donutty.js</js>
    <js>/extensions/psk/resources/fo/scripts/libs/owl.carousel.js</js>
    <js>/extensions/psk/resources/fo/scripts/libs/data-toggle.js</js>
    <js>/extensions/psk/resources/fo/scripts/libs/responsive-container.js</js>
    <js>/extensions/psk/resources/fo/scripts/ckeditor-fo-accordion.js</js>
    <js>/extensions/psk/resources/fo/scripts/ckeditor-fo-tabs.js</js>
    <js>/extensions/psk/resources/fo/scripts/frontoffice-psk.js</js>
    <js>/extensions/psk/resources/fo/scripts/responsive_previsualization.js</js>
</group>
<group name="scriptsFo_fr_FR">
    <group-ref>jQuery</group-ref>
    <group-ref>i18nFo_fr</group-ref>
    <group-ref>scriptsBase</group-ref>
</group>

<group name="scriptFo_full">
    <js>/resources/scripts/formulaire.js</js>
    <js>/resources/scripts/formulaire_i18n.js</js>
    <js>/resources/scripts/libs/orejime.js</js>
    <js>/resources/scripts/eprivacy.js</js>
    <js>/jsp/scripts/dropdown-button.js</js>
    <js>/jsp/scripts/menu.js</js>
    <js>/jsp/scripts/validation.js</js>
    <js>/jsp/scripts/frontoffice.js</js>
    <js>/jsp/scripts/galeries.js</js>
    <js>/jsp/scripts/medias.js</js>
    <js>/jsp/scripts/headPage.js</js>
    <js>/jsp/scripts/onglets.js</js>
    <js>/jsp/scripts/accessible-autocomplete-1636551838733.min.js</js>
    <js>/jsp/scripts/search.js</js>
</group>


<group name="scriptsFo_en">
    <group-ref>jQuery</group-ref>
    <group-ref>i18nFo_en</group-ref>
    <group-ref>scriptsBase</group-ref>
</group>

<group name="scriptsFo_es">
    <group-ref>jQuery</group-ref>
    <group-ref>i18nFo_es</group-ref>
    <group-ref>scriptsFrontPlus</group-ref>
    <group-ref>scriptFo_full</group-ref>
    <group-ref>common_psk</group-ref>
    <group-ref>scriptsBase</group-ref>
</group>

<group name="scriptsFo_ca">
    <group-ref>jQuery</group-ref>
    <group-ref>i18nFo_ca</group-ref>
    <group-ref>scriptsFrontPlus</group-ref>
    <group-ref>scriptFo_full</group-ref>
    <group-ref>common_psk</group-ref>
    <group-ref>scriptsBase</group-ref>
</group>

devient :

  {
  "styles": {
    "styles": {
      "files": [
        "META-INF/resources/extensions/connecteurhal/resources/styles/extension-connecteurhal.css"
      ]
    }
  },
  "scripts": {
    "scriptsFo": {
      "files": [
        "META-INF/resources/extensions/connecteurhal/resources/scripts/extension-connecteurhal.js"
      ],
      "lang": [
        "fr_FR",
        "en"
      ],
      "externals": {
        "jQuery": "jQuery"
      }
    },
    "i18nFo": {
      "files": [
        "META-INF/resources/extensions/connecteurhal/resources/scripts/messages.js"
      ],
      "lang": [
        "fr_FR",
        "en"
      ]
    }
  }
}

Tout mettre dans le même groupe, attention à l'ordre. Mettre les externals dans l'external.

Fichiers non trouvés

Certains imports ne fonctionnent plus, exemple :

@import url("sass/jsp/styles/fonts/icones/IcoMoon.css");

Solution : vérifier la présence des fichiers Icomoon dans le war généré dans jsp/styles/fonts/icones (normalement on y retrouve le fichier Icomoon.css) et dans jsp/styles/fonts/icones/fonts (pour des fichiers autres Icomoons). S'ils sont présents, on peut modifier l'URL en /jsp/styles/fonts/icones/IcoMoon.css. Sinon voir avec l'équipe produit.

Ajouter les bundles

Les bundles sont ajoutés par les balises <resources:scripts>. Pareil pour les styles avec la balise <resources:styles>.

Ajout des scripts

Ajouter <resources:getScripts/> dans le footer, cette balise ajoute tous les imports qui ont été référencés par les addScript précédemment ajoutés.

Il faut également aller vérifier où sont utilisés les scripts JS pour éventuellement les sortir des bundles s'il s'avère qu'ils ne sont utilisés que dans des JSP précises.

Une fois qu'on a déterminé les scripts qui doivent absolument rester dans des bundles, il faudra les déplacer dans le dossier src/resources/js et utiliser la balise <resources:script group="<nom du groupe dans le bundle>" /> Si des scripts sont ajoutés dans les jsp directement, il faut remplacer les balises <script src="..."></script> par des balises <resources:addScript type="text/javascript" path="<path vers le script>" /> et ce sans devoir déplacer les scripts de place.

E-privacy

Les scripts d'e-privacy gérés précédemment par wro sont maintenant à ajouter dans le fichier footer.jsp. (cf MIGRATION-348)

<resources:addScript path="/extensions/eprivacy/resources/scripts/eprivacy.js" async="false"/>
...
<resources:getScript />

Les styles sont gérés par les bundles. Vérifier la présence de l'appel au groupe styles dans header.jsp et l'ajouter s'il n'est pas présent.

<resources:link media="screen" group="styles"/>

Problème d'encoding

Il peut arriver que certains scripts js aient des problèmes d'encoding. Lors du build maven, on a le message d'erreur suivant :

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources) on project upvd: Input length = 1 -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.

Dans un premier temps, vérifier l'encoding des fichiers de son projet dans Settings > Editor > File encodings. Ils doivent être en UTF-8. Dans ce cas, il faut chercher les scripts mal encodés. Lors de l'ouverture du script dans Intellij, on a le message suivant: img.png Solution pour l'instant : Copier le contenu du script dans un nouveau script. Supprimer l'ancien script. Renommer le nouveau script avec l'ancien nom.

Modifier le fichier

Recherche

Ce fichier repertorie les modifications à faire pour les modules de recherche lors de la migration d'un projet K-SUP 6.7 vers 7.

Migration d'Elasticsearch vers Opensearch

Le changement de license d'Elasticsearch a poussé de nombreuses entreprises, dont Kosmos, de regarder ailleurs pour continuer a faire de la recherche indéxée. Kosmos a choisi Elasticsearch, un fork open source d'Elasticsearch

Configuration

Changements sur les fichiers XML

Changer le mot clé raw pour éviter qu'un champ soit impliqué dans la recherche

Le mot clé raw qui était utilisé pour éviter que le champ soit pris en compte dans les recherches, doit être modifié a keyword qui est celui qui a le comportement voulu en V7 (lié à la montée de version Opensearch).

Exemple :

<property name="field" value="fiche.metier.chiffreMeteoCleor.raw"/>

devient:

<property name="field" value="fiche.metier.chiffreMeteoCleor.keyword"/>
Mise en place de l'indexation automatique sur Rancher
  • Impact de migration ici (concerne uniquement le Rancher)

Modifications du code

Appliquer les impacts de migration pour le passage d'Elasticsearch a opensearch

Voir les impacts de migration sur les tickets MIGRATION-183 et MIGRATION-333 et appliquer les actions demandées dessus.

Supprimer l'attribut 'score'

La classe SearchResultDoc a été modifiée dans le produit. Le score n'est plus retourné, car jugé peu pertinent à l'usage. Il faut donc le supprimer si utilisé (appels Java et affichage dans les JSP).

S'il y avait un affichage du score, bien penser à prévenir le chef de projet.

Indexer les booleens

Voir l'impact de migration ici et appliquer les actions demandées dessus.

Ajouter une langue a Opensearch

Par défaut, les langues françaises, anglaises et espagnoles sont définies dans le produit. Si le projet a d'autres langues, il faudra ajouter plusieurs éléments.

Il faut ajouter un template par langue autre que les langues par défaut dans le fichier ApplicationContext.xml. Par exemple, pour ajouter le catalan :

    <!-- Déclaration de template de langue -->
    <bean id="templateCa" class="com.kosmos.search.loader.template.IndexTemplateReference">
        <constructor-arg index="0" value="template_ca"/>
        <constructor-arg index="1" value="com/kosmos/search/mapping/index_template/template_ca.json"/>
        <constructor-arg index="2" value="203"/>
        <constructor-arg index="3">
            <list>
                <value>*_ca</value>
            </list>
        </constructor-arg>
    </bean>

Les correspondent au constructeur suivant :

    /**
     * Constructeur de la classe.
     * @param name nom du template
     * @param filePath Chemin dans le classpath vers le fichier décrivant le template
     * @param priority Priorité du template
     * @param indexPatterns Liste des patterns d'index
     */
    public IndexTemplateReference(final String name, final String filePath, final long priority, final String... indexPatterns) {
        this.filePath = filePath;
        this.name = name;
        this.priority = priority;
        this.indexPatterns = indexPatterns;
    }

Il faut ensuite ajouter le fichier détaillant le template d'indexation de la langue. C'est grâce à ce template qu'Opensearch peut générer l'indexation des fiches. Il est spécifique à chaque langue. Dans notre exemple, il s'agit de com/kosmos/search/mapping/index_template/template_ca.json. Le plus simple est de s'inspirer des templates existants, comme template_fr.json. Il faut ensuite adapter en fonction de la langue. Vérifier sur le site d'Opensearch les propriétés possibles.

{
  "settings": {
    "analysis": {
      "filter": {
        "catalan_stop": {
          "type": "stop",
          "stopwords": "_catalan_"
        },
        "catalan_stemmer": {
          "type": "stemmer",
          "language": "catalan"
        },
        "nGram_filter": {
          "type": "edge_ngram",
          "min_gram": 3,
          "max_gram": 20,
          "token_chars": [
            "letter",
            "digit",
            "punctuation",
            "symbol"
          ]
        }
      },
      "char_filter": {
        "keyword_truncate": {
          "type": "pattern_replace",
          "pattern": "^(.{255})(.*)$",
          "replacement": "$1"
        }
      },
      "analyzer": {
        "analyzer_core_ca": {
          "char_filter": [
            "html_strip"
          ],
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "catalan_stop",
            "catalan_stemmer",
            "asciifolding"
          ]
        },
        "analyzer_core_ngram_ca": {
          "char_filter": [
            "html_strip"
          ],
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "catalan_stop",
            "catalan_stemmer",
            "asciifolding",
            "nGram_filter"
          ]
        },
        "analyzer_core_raw_ca": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "asciifolding"
          ]
        },
        "analyzer_core_raw_stemmer_ca": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "catalan_stop",
            "asciifolding"
          ]
        }
      },
      "normalizer": {
        "keyword_normalizer": {
          "type": "custom",
          "char_filter": ["keyword_truncate"],
          "filter": ["lowercase", "asciifolding"]
        }
      }
    }
  },
  "mappings": {
    "properties" : {
      "fulltext": {
        "type": "text",
        "term_vector": "with_positions_offsets",
        "store": true,
        "fields": {
          "raw": {
            "type": "text",
            "analyzer": "analyzer_core_raw_ca",
            "term_vector": "with_positions_offsets"
          },
          "ngram": {
            "type": "text",
            "analyzer": "analyzer_core_ngram_ca",
            "term_vector": "with_positions_offsets"
          }
        }
      }
    },
    "dynamic_templates": [
      {
        "stringses": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "analyzer": "analyzer_core_ca",
            "term_vector": "with_positions_offsets",
            "copy_to": "fulltext",
            "fields": {
              "keyword" :{
                "type": "keyword",
                "normalizer": "keyword_normalizer"
              },
              "raw": {
                "type": "text",
                "analyzer": "analyzer_core_raw_ca",
                "term_vector": "with_positions_offsets"
              },
              "ngram": {
                "type": "text",
                "analyzer": "analyzer_core_ngram_ca",
                "term_vector": "with_positions_offsets"
              }
            }
          }
        }
      }
    ]
  }
}

Impacts de recherche API K-SUP

Configuration

Appliquer les impacts de migration décrits ici

Supprimer les surcharges

Certains beans qui surchargent la configuration peuvent ne plus servir pour la V7. Si c'est le cas, il faut les supprimer.

Exemples (à compléter, si d'autres beans dans le même cas sont rencontrés) :

  • ara-sdio: searchFicheAccueilConfiguration supprimé de arasio-accueil.xml.

Modifier le fichier

Scripts Flyway

À la fin de cette étape, démarrer le projet avec une base vide et avec une base du projet avant migration.

Faire la configuration pour Flyway

Suivre l'impact de migration décrit sur le ticket MIGRATION-308.

Effectuer les modifications sur les scripts Java

  • Supprimer les scripts obsolètes.

  • La baseline est utilisée pour pouvoir démarrer un projet sur une base vide tout en incorporant les modifications spécifiques au projet, que ce soit l'ajout ou la modification de tables, ou l'ajout de données. Il est donc possible de spécifier une version dans un script Flyway Java de la façon suivante :

/**
 * Version à partir de laquelle le script doit être exécuté.
 */
private static final String MIGRATION_FROM_1_X="1.3.1";
@Override 
public void migrate(final Context context) throws Exception {
    if (!FlywayMigrationUtils.isScriptWithVersionExecuted(context.getConnection(),${extensionProjet}Utils.ID_EXTENSION,MIGRATION_FROM_1_X)) {
        saveLabels();
    } else {
        LOG.info("Migration non effectuée car la version {} a été exécutée",MIGRATION_FROM_1_X);
    }
}
  • Dans le cas de script versionné d'insertion de données, il risque d'y avoir des soucis dans l'ordonnancement de l'exécution du script par rapport au chargement du contexte de l'application. Donc si c'est possible, il est préférable de transformer les scripts d'insertion de données en script rejouables (R__<timestamp>_<fonction>).

  • Si le script java est un script projet rejouable, il est à déplacer dans src/main/java/com/kosmos/ksup/migration. Sinon, il doit être mis dans le dossier src/main/java/${extensionProjet}/migration. Les scripts rejouables doivent avoir un timestamp dans leur nom. Par exemple, le script R__ajout_libelles.java devient R__1664359142_ajout_libelles.java avec 1664359142 le timestamp.

  • Les scripts Rejouables n'implémentent plus JdbcMigration & MigrationChecksumProvider, ils doivent donc être supprimées. Les scripts doivent maintenant étendre la classe org.flywaydb.core.api.migration.BaseJavaMigration.

Effectuer la mise à jour sur les scripts SQL

  • Supprimer les scripts qui corrigent des données. Par exemple, sur UPVD : supprimer V1_2_0_1__renommage_card_inscription.sql (son but était de renommer la carte inscription).

  • Pour les scripts SQL qui sont conservés, il est nécessaire de s'assurer qu'ils soient rejouables (présence d'un IF NOT EXISTS lors des créations de table, par exemple).

  • Déplacer les fichiers SQL en suivant la matrice de choix suivante :

    • script dans src/main/webapp/WEB-INF/conf/sql/mysql
      • est-il rejouable ?
        • oui
          • Déplacer dans src/main/resources/com/kosmos/ksup/migration.
        • non
          • Déplacer src/main/resources/com/kosmos/${mon_projet}/migration.
    • script dans src/main/webapp/WEB-INF/conf/sql/core/mysql
      • Déplacer dans src/main/resources/com/kosmos/core/migration
    • Ne pas oublier de faire un répertoire des choix effectués dans le fichier upgrade-version_6.07.XX_7.XX.XX.
  • Fusionner les scripts versionnés qui traitent les sujets similaires pour réduire le nombre de scripts à exécuter au démarrage, et les positionner dans src/main/resources/com/kosmos/${extensionProjet}/migration.

Modifier le fichier

Gestion de la recherche anonyme

Voir ce guide pour plus de détails.

Paramétrage

La recherche anonyme s'active pour chaque type de fiche avec la propriété fiche.NOM_OBJET.recherche_anonyme, les valeurs possibles sont les suivantes :

  • 0: pas de recherche anonyme autorisée
  • 1: recherche anonyme autorisée

Modifier le fichier

Cahier de test des fonctionnalités principales à tester

  • Liste de fiches
  • Encadrés de fiche, de rubrique et encadrés génériques

Opensearch

Pour s'assurer qu'Opensearch fonctionne suite a la migration, il faut:

  1. Lancer l'indexation automatique depuis adminsite
  2. Accéder au '/search'

Modifier le fichier

Différences 6.7 et 7.0

Ce tableau a pour objectif de regrouper les modifications entre 6.7 et 7.0.

Type de fichier6.77.0
.dat (lien doc)src/main/webapp/WEB-INFsrc/main/resources
scripts flyway SQL core (lien doc)src/main/webapp/WEB-INF/conf/sql/core/mysqlsrc/main/resources/com/kosmos/core/migration
scripts flyway SQL projet (lien doc)src/main/webapp/WEB-INF/conf/sql/mysqlsrc/main/resources/com/kosmos/ksup/migration ou src/main/resources/com/project/${mon_projet}/migration
fichiers extensionsContext (lien doc)src/main/webapp/WEB-INF/{nom_extension}ExtensionContext.xmlsrc/main/resources/spring/{nom_extension}-override.xml
jsp produit (lien docsrc/main/webapp/jsp/extensions/core/TYPE_FICHE/src/main/webapp/jsp/extensions/TYPE_FICHE/TYPE_FICHE/
.scss (lien docsrc/sasssrc/main/resources/sass
.js (lien doc)src/main/resources/js
style de lienajouté dans les fichiers style.json (ex: encadreStyle.json)doc des liens

Modifier le fichier

Glossaire

  • Indicateur de volume : Indicateur d'usage généré par des requêtes réalisées en base de données (Combien de fiche formations en ligne au mois de mars ?)
  • Indicateur d'action : Indicateur généré par l'écoute en temps réel de certaines méthodes de l'applicatif (Combien d'action de création de fiche formation au mois de mars)
  • Indicateur de performance : Indicateur technique permettant de signaler les requêtes anormalement longues de l'application.
  • North Star : Indicateur permettant de mesurer l'usage du produit. Sur K-Sup cet indicateur mesure le taux de rafraîchissement des données visibles d'une application. Cela mesure donc :
    • Le passage en ligne d'une fiche
    • La modification d'une fiche en ligne
    • Le dépublication d'une fiche en ligne
  • booléen ksup : Chaîne de caractères qui peut prendre soit "0" soit "1" et est interprétée comme un booléen. Est représenté par un objet java dédié KsupBoolean pour facilité l'indexation de la recherche.

Modifier le fichier

Licence K-Sup

Modifier le fichier

Guide de contribution sur le produit K-Sup

Pour contribuer au produit K-Sup, il est nécessaire de réaliser les développements sur un environnement produit afin de s'affranchir d'une éventuelle surcharge projet. Il faut donc disposer d'un environnement produit fonctionnel (cf : Environnement de développement).

Tous les développements doivent être réalisés en relation avec un ticket Jira validé par l'équipe produit. Si votre développement n'a pas de ticket, il faut donc en créer un.

Le code doit être réalisé sur une branche dédiée en respectant les règles suivantes pour le nommage : Règles d'utilisation de Git

Après avoir poussé votre code, il faut créer une Pull Request afin que celui-ci soit relu par l'équipe produit.

Modifier le fichier

Guide de contribution à la documentation

La documentation K-Sup est réalisée en utilisant le langage Markdown (cf syntaxe Markdown Bitbucket). Il est possible de la modifier directement à partir d'un navigateur. En revanche pour l'ajout d'un nouveau fichier, il faudra passer par un poste de développement.

Mise à jour de la documentation via l'interface web

  1. En haut de chaque page, un lien permet d'accéder au fichier source de la page pour mettre à jour la documentation. Contribuer
  2. Copier l'ensemble du fichier
  3. Aller sur l'éditeur web approprié (https://onlinemarkdowneditor.dev/)
  4. Coller le contenu dans la toolbox de l'éditeur
  5. Si vous avez besoin d'ajouter des images il faut dans l'éditeur web :
    1. Ajouter l'image dans la toolbox
    2. Cliquer sur l'image pour faire apparaître les boutons contextuels Outils images
    3. Cliquer sur le bouton de droite pour ajouter un texte alternatif et le valider Texte alternatif images
  6. Après avoir modifié le contenu, il faut copier le contenu modifier à partir du mode source de la toolbox
  7. Il faut ensuite retourner à l'emplacement des sources sur Gitlab et accéder aux sources en cliquant sur le bouton Modifier

Modifier 4. Remplacer le contenu du fichier dans la page web avec celui copié 5. Pour valider la modification il faut

  1. Cliquer sur le bouton commit en bas de la page

Commit 2. Renseigner un message sur le fenêtre de validation. Attention, le message doit toujours commencer par docs : xxxx. Validation message 1. Si vous n'avez pas ajouté d'image, vous pouvez valider en cliquant sur le bouton commit 2. Si vous avez ajouté des images, il faut alors créer une pull request pour les joindre afin que des développeurs puissent les intégrer. Pour ce faire il faut : 1. Sur la fenêtre de validation, cocher la case Créer une pull request pour ce changement 2. Puis cliquer sur le bouton Créer une pull request (laisser le nom de la branche généré) Création de la pull request 3. Sur la fenêtre de création d'une pull request, ajouter les différentes images dans le champ description 4. Cliquer sur le bouton créer 5. Demander à un développeur d'intégrer les images au markdown (utiliser des chemins relatifs) Validation de la pull request

Ajout de nouveaux fichiers à la documentation

La construction du site de documentation est réalisée en se basant sur une convention pour l'emplacement des fichiers mardown. Pour ajouter un ficher, il faut donc respecter cette convention.

  1. Les fichiers doivent être placés dans le repository du module K-Sup concerné. Si le fichier concerne une problématique générique au produit K-Sup, il doit être placé dans ce projet (K-Sup Documentation).
  2. L'emplacement du fichier dépend de sa portée
    1. Si le fichier a une portée fonctionnelle, il doit être placé en respectant la cartographie K-Sup (cf K-Sup - Référentiel produit)
      1. Dans un répertoire docs/func/contenus pour les composants liés aux contenus
      2. Dans un répertoire docs/func/modules pour les composants liés aux modules
      3. Dans un répertoire docs/func/socle pour les composants liés au socle
    2. Si le fichier a une portée technique, il doit être placé dans un répertoire docs/tech. Organisation du répertoire de documentaion
  3. Dans chaque répertoire, il faut créer un fichier SUMMARY.md contenant l'organisation du répertoire et le lien vers les différents fichiers. Exemple :
    1. Exemple pour de la documentation fonctionnelle
- [Fiche Article](func/module/article-func.md)
  1. Exemple pour de la documentation technique
- [Article]()
  - [Article](tech/article/article-tech.md)
  - [Propriétés](tech/article/article-properties.md)

Attention, pour la documentation des différents modules K-Sup, les chemins des fichiers doivent contenir le nom du repository du projet sur git.

Gestion des ressources

Les images et autre ressources doivent être rangées dans un répertoire resources (il est possible d'ajouter des sous-répertoires)

Templates de documentation

Modifier le fichier

Paramétrage de l'extension xxxxx

Le paramétrage de l'extension est réalisé dans le fichier xxx.properties.

Surcharge

La surcharge des propriétés peut se faire en créant un fichier :

  • application_xxx.properties dans les sources d'un projet
  • env_xxx.properties dans le répertoire de configuration d'un environnement (référencé par la propriété conf.dir)

Propriétés

PropriétéDescriptionValeurs possiblesValeur par défaut
Propriété 1Description 1Valeur possibleValeur 1

Modifier le fichier

Liens externes à la documentation