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 :
- La documentation fonctionnelle
- La documentation technique
- Guide de développement sur le produit
- Licence : Les informations liées à la licence du produit
- Guide de contribution au produit
- Guide de contribution à la documentation
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
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
- Actualité
- Carte
- Connecteur CAS
- Connecteur LDAP
- Cycle de vie d'une fiche (B)
- Datagrid
- Editeur de contenus (toolbox)
- Eléments de contribution : multiselect ...
- Formation
- Formulaires
- Groupes
- Import / Export XML
- Inscription
- Liste dynamique
- Page d'accueil
- Recherche fulltext
- Recherche SQL
- Restriction d'accès
- RGPD
- Rubriques
- Scripts automatisés
- Sites
- Statistiques
- Urls
- Utilisateur
Composants Critiques
- Annuaire
- Article
- Cartographie
- Comparaison de fiches
- Document
- E-privacy
- Elément pédagogique
- Libellés
- Liste manuelle
- Mediathèque
- Multilingue
- Newsletter
- Page libre
- Panier
- Parcours
- Référencement (SEO)
- Rôles
- Sécurité
- Structure
- Structures (arbre)
- Workflow de validation d'une fiche
Composant Majeurs
- Accueil module d'administration
- Agenda
- Aperçu
- Cours
- Création de compte
- Encadrés
- Espaces collaboratifs
- HAL
- Laboratoire
- Lien
- Profils
- Services externes
- Supervision
Composants Simples
- Ancien étudiant
- API
- Arbre
- Association étudiante
- Billet de blog
- Blog
- CDM-fr
- Commentaire
- Connecteur PHP
- Construction de parcours
- Etudiant
- Gestionnaire d'extensions
- Indexation sites externes
- Lieu
- Offre stage / emploi
- Petite annonce
- Rebonds entre fiches
- Social
- Taglink
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 :
- le temps total passé pour la génération de la page
- le temps total passé pour l'exécution des requêtes SQL
- le nombre de requêtes SQL exécutées
- 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 :
- la requête fait partie des requêtes à auditer
- 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.
- Un filtre d'exclusion de type "l'URI commence par".
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
Module Groupes
Statistiques du composant Groupes
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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}} |
Module Liste dynamique
Statistique du composant Liste dynamique
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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}} |
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
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.

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.

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.

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.

Module Profils
Statistiques du composant Profils
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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}} |
Module Rubriques
Statistiques du composant Rubriques
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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}} |
Module Scripts automatisés
Statistiques du composant Scripts automatisés
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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.
| Indicateur | Message 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"}}} |
Module Services externes
Statistiques du composant Services externes
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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}} |
Module Utilisateurs
Statistique du composant Utilisateurs
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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}} |
Module Agenda
Statistique du composant agenda
Ce fichier présente les statistiques du composant agenda (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message type |
|---|
Statistique d'usage
Indicateur de type compteurs
| Indicateur | Message 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
| Indicateur | Message 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;]"}} |
Extension Arbre
Statistique de l'extension Arbre
Ce fichier présente les statistiques de l'extension Arbre
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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}} |
Extension CatalogueLien
Statistique de l'extension CatalogueLien
Ce fichier présente les statistiques de l'extension CatalogueLien
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Composant Espaces collaboratifs
Statistique du composant Espaces collaboratifs
Ce fichier présente les statistiques du composant espaces collaboratifs (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Extension FicheLink - Rebonds
Statistique de l'extension FicheLink
Ce fichier présente les statistiques de l'extension FicheLink
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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"}} |
Extension Formulaire
Statistique de l'extension Formulaire
Ce fichier présente les statistiques de l'extension Formulaire.
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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}} |
Module Import / Export XML
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
| Indicateur | Message 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
| Indicateur | Message 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"}}} |
Extension OFIN
Statistique de l'extension OFIN
Ce fichier présente les statistiques de l'extension OFIN.
Indication d'installation / d'utilisation
| Indicateur | Message 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}} |
Module Supervision
Statistique du composant supervision
Ce fichier présente les statistiques du composant supervision (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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 :
| Indicateur | Message 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"}} |
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"

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
| Champ | Description | Format | Exemple |
|---|---|---|---|
| Code | Code unique de l'évènement. Ce code sera visible lors de la réservation | Obligatoire, 50 caractères alphanumériques, pas de caractères spéciaux sauf "-" | JS-2022, po-lettres-2023 |
| Nom de l'évènement | Nom de l'évènement | Texte libre sur une seule ligne, 255 caractères max | Portes ouvertes 2023 |
| Rubrique | Code de la rubrique de rattachement | Obligatoire, prendre le code de la rubrique indiqué dans K-Sup | |
| Structure | Code de la structure de rattachement | Facultatif, prendre le code de la structure indiqué dans K-Sup | |
| Catégorie | Code 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 | |
| Type | Code 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 | |
| Lieu | Description du lieu de l'évènement | Facultatif, 4096 caractères | |
| Date de début | Date de début de l'évènement | Obligatoire, Date au format JJ/MM/AAAA | 15/02/2023 |
| Heure de début | Heure de début de l'évènement | Obligatoire, Heure au format HH24:MI:SS | 08:30:00 |
| Date de fin | Date de fin de l'évènement | Optionnel, Date au format JJ/MM/AAAA | 22/02/2023 |
| Heure de fin | Heure de fin de l'évènement | Optionnel, Heure au format HH24:MI:SS | 18:45:00 |
| Date d'ouverture des inscriptions | Date d'ouverture des inscriptions | Obligatoire, Date au format JJ/MM/AAAA | 15/01/2023 |
| Heure d'ouverture des inscriptions | Heure d'ouverture des inscriptions | Obligatoire, Heure au format HH24:MI:SS | 00:00:00 |
| Date de fermeture des inscriptions | Date de fermeture des inscriptions | Optionnel, Date au format JJ/MM/AAAA | 10/02/2023 |
| Heure de fermeture des inscriptions | Heure de fermeture des inscriptions | Optionnel, Heure au format HH24:MI:SS | 23:59:59 |
| Description | Texte de présentation de l'évènement. | Facultatif, 4096 caractères | |
| Intitulé de la place / article | Intitulé de la place | Obligatoire, | |
| Nombre de places / articles par inscription | Nombre de place maximum par inscription | Numérique, facultatif | |
| Texte de confirmation | Texte 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éditeur | Adresse électronique à utiliser pour l'émetteur des courriels | ||
| Attacher le fichier calendaire | Facultatif | ||
| Adresse mail de contact | Adresse électronique à utiliser pour obtenir des informations sur l'évènement |
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.
| Champ | Description | Format | Exemple |
|---|---|---|---|
| Code de l'évènement de rattachement | Code de l'évènement auquel rattacher ce créneau | Obligatoire, utiliser un des code évènement | |
| Code du créneau | Code du créneau | Obligatoire, 50 caractères alphanumériques, pas de caractères spéciaux sauf "-" | |
| Intitulé du créneau | Texte libre sur une seule ligne, 255 caractères max | ||
| Structure | Code de la structure de rattachement | Facultatif, prendre le code de la structure indiqué dans K-Sup | |
| Catégorie | Code 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 | |
| Type | Code 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ébut | Date de début de l'évènement | Obligatoire, Date au format JJ/MM/AAAA | 15/02/2023 |
| Heure de début | Heure de début du créneau | Obligatoire, Heure au format HH24:MI:SS | 08:30:00 |
| Date de fin | Date de fin du créneau | Optionnel, Date au format JJ/MM/AAAA | 22/02/2023 |
| Heure de fin | Heure de fin du créneau | Optionnel, Heure au format HH24:MI:SS | 18:45:00 |
| Lieu | Description du lieu du créneau | Facultatif, 4096 caractères | |
| Description | Texte de présentation du créneau. | Facultatif, 4096 caractères | |
| Nombre de places disponibles | Nombre de place disponibles pour le créneau | Numérique, obligatoire | |
| Gestionnaire 1 | Code utilisateur du premier getionnaire | Facultatif, (cf Admisnistration > Utilisateurs) | |
| Gestionnaire 2 | Code utilisateur du second getionnaire | Facultatif, (cf Admisnistration > Utilisateurs) | |
| Gestionnaire 3 | Code 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
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:
- 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. - 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. - 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é.
Extension Lieu
Statistique du module Cartographie
Ce fichier présente les statistiques du module Cartographie
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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}} |
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.
Statistique du composant Article
Ce fichier présente les statistiques du composant article (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
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
Module Accueil
Statistique du composant accueil
Ce fichier présente les statistiques du composant accueil (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Module Actualite
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
Statistique du composant actualité
Ce fichier présente les statistiques du composant actualité (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message type |
|---|
Module AnnuaireSup
Statistique du composant annuairesup
Ce fichier présente les statistiques du composant annuairesup (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message type |
|---|
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
Statistique du composant structure
Ce fichier présente les statistiques du composant structure (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message type |
|---|
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 :
| Nom | Type | Description |
|---|---|---|
| Titre | Texte simple | Le titre |
| Sous-titre | Texte simple | Le sous-titre |
| Catégorie | Sélection multiple | Choix 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"
Statistique du composant Application
Ce fichier présente les statistiques du composant application.
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Extension Blog
Statistique de l'extension Blog
Ce fichier présente les statistiques de l'extension blog (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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}} |
Module Article de blog
Statistique du composant Article de blog
Ce fichier présente les statistiques du composant article de blog (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message type |
|---|
North Star
| Indicateur | Message 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
| Indicateur | Message 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}} |
Module Blog
Statistique du composant Blog
Ce fichier présente les statistiques du composant blog (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message type |
|---|
North Star
| Indicateur | Message 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
| Indicateur | Message 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}} |
Module Commentaire
Statistique du composant commentaire
Ce fichier présente les statistiques du composant commentaire (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message type |
|---|
Extension étudiant et alumni
Statistique de l'extension étudiant alumni
Ce fichier présente les statistiques de l'extension étudiant alumni (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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}} |
Module Annuaire Anciens Etudiants
Statistique du composant annuaire anciens etudiants
Ce fichier présente les statistiques du composant annuaire anciens etudiants (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message type |
|---|
Module étudiant
Statistique du composant annuaire étudiants
Ce fichier présente les statistiques du composant annuaire étudiants (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message type |
|---|
Module association étudiant
Statistique du composant association étudiant
Ce fichier présente les statistiques du composant association étudiant (cf ).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message type |
|---|
Extension GED
Statistique de l'extension GED
Ce fichier présente les statistiques de l'extension GED (Gestion électronique des Documents).
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
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.
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).
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.
Module Formation
Statistique du composant Formation
Ce fichier présente les statistiques du composant Formation
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Module Parcours
Statistique du composant Parcours
Ce fichier présente les statistiques du composant Parcours.
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Module Elément Pédagogique
Statistique du composant Element Pédagogique
Ce fichier présente les statistiques du composant Element Pédagogique.
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Module Cours
Statistique du composant Cours
Ce fichier présente les statistiques du composant Cours.
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Extension Offre Stage Emploi
Statistique de l'extension OffreStageEmploi
Ce fichier présente les statistiques de l'extension OffreStageEmploi
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Extension Petite Annonce
Statistique de l'extension Petite Annonce
Ce fichier présente les statistiques de l'extension Petite Annonce
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Extension Recherche et Labo
Statistique de l'extension Recherche et Labo
Ce fichier présente les statistiques de l'extension Recherche et Labo
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
Extension Lieu
Statistique de l'extension Lieu
Ce fichier présente les statistiques de l'extension Lieu
Indication d'installation / d'utilisation
| Indicateur | Message 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
| Indicateur | Message 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
| Indicateur | Message 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}} |
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
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.propertiesdans les sources d'un projetenv.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)Message_xxx.propertiesdans 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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| BO.HEADER.MESSAGE | Contenu du texte du message | Chaine de caractères internationalisée | Serveur de production (fr)/Production server (en) |
| BO.HEADER.MESSAGE.COLOR.TEXT | Couleur du texte du message | Couleur CSS¹ | #56040a |
| BO.HEADER.MESSAGE.COLOR.BACKGROUND | Couleur du fond du message | Couleur CSS¹ | #ffd6d0 |
| BO.HEADER.SHOW_MESSAGE | Activation du message | true/false | true |
1 les couleurs CSS s'expriment de plusieurs manières : documentatiton Mozilla sur les couleurs en css
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
class ActionsBandeauFicheViewPreparer
note left of ActionsBandeauFicheViewPreparer
main.jsp va attendre un bandeau-action-fiche
end note
class ActionsBandeauCardViewPreparer
note right of ActionsBandeauCardViewPreparer
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
AbstractFavorisActionViewPreparer -right-|> AbstractActionViewPreparer class FavorisActionViewModel extends AbstractActionViewModel{ }
class FavorisActionFicheViewPreparer
}
note bottom of FavorisActionFicheViewPreparer
Dans le core-front-sitetemplates, le preparer :
FavorisActionFicheViewPreparer --* FavorisActionViewModel
class FavorisActionCardViewPreparer
FavorisActionCardViewPreparer --* FavorisActionViewModel
ActionsBandeauFicheViewPreparer --* ActionsBandeauViewModel AbstractFavorisActionViewPreparer --* FavorisActionViewModel
ActionsBandeauViewModel --*"1..n" AbstractActionViewModel : actions @enduml
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
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
| Classe | Description | Ordre |
|---|---|---|
| CharEncodingFilter | ce filtre a pour fonction de pallier à l'absence d'encodage dans le requete ou la réponse. | HIGHEST_PRECEDENCE |
| MDCFilter | ce filtre positionne l'identifiant de session dans le contexte MDC | + 10000 |
| HoneyPotFilter | ce filtre permet de retourner une réponse 200 si un bot est détecté lors de la soumission d'un formulaire | + 20000 |
| HttpsRedirectFilter | ce filtre gére la demande de bascule HTTP > HTTPS au navigateur si le site courant est définit en HTTPS. | + 30000 |
| SiteAliasResolutionFilter | ce filtre gére la demande de redirection vers l'URL principal du site si un alias de site est détecté. | + 30000 |
| HttpMarkerFilter | ce filtre initilise le recueil des informations statistiques de la requête | + 40000 |
| ContexteFilter | ce filtre positionne le contexte univ à partir des informations de la session de l'utilisateur | + 50000 |
| PDFFilter | ce filtre gére la génération en PDF d'une page du site. | + 60000 |
- Filtres sur le répartiteur (dispatcher) FORWARD
| Classe | Description | Ordre |
|---|---|---|
| ForwardContexteFilter | ce filtre transmet le contexte en cas de forward | HIGHEST_PRECEDENCE |
- Filtres sur le répartiteur (dispatcher) ERROR
| Classe | Description | Ordre |
|---|---|---|
| HttpsRedirectFilter | ce filtre gére la demande de bascule HTTP > HTTPS au navigateur si le site courant est définit en HTTPS. | + 30000 |
| SiteAliasResolutionFilter | ce filtre gére la demande de redirection vers l'URL principal du site si un alias de site est détecté. | + 30000 |
| ContexteFilter | ce 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
| Classe | Description | Ordre |
|---|---|---|
| FrontOfficeFilter | ce filtre est utilisé lors de la préparation de l'affichage front | HIGHEST_PRECEDENCE + 60000 |
Ajout d'un filtre HTTP
- Créer une classe qui implémente ExtensionFilter
...
public class AcmeFilter implements ExtensionFilter {
...
}
- Ajouter les annotations Spring
@Component
@CoreContext
@Order(Ordered.HIGHEST_PRECEDENCE + 35000)
@WebFilter(urlPatterns = FILTER_PATTERN_DEFAULT, filterName = "acme-filter")
public class AcmeFilter implements ExtensionFilter {
...
}
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
mail.debug | Permet de passer l'envoi de mail en mode debug. Au lieu d'être envoyé, l'e-mail est loggé | true / false | false |
mail.host | Définit le nom d'hote ou ip du serveur de messagerie | une chaine de caractère | smtp.xxxx.fr |
mail.port | Définit le port sur le serveur de messagerie (habituellement, 25 ou 587 ou 465) | un entier | 25 |
mail.user | Définit le le nom d'utilisateur si le serveur de mail nécessite une authentification | une chaine de caractère | |
mail.password | Définit le le mot de passe utilisateur si le serveur de mail nécessite une authentification | une chaine de caractère | |
mail.from | Définit l'adresse mail de l'expediteur des mail système. Utiliser de préférence une adresse en no-reply@xxx.fr | une chaine de caractère | kportal@xxxx.fr |
mail.webmaster | Définit l'adresse mail de contact. Utilisé adresse d'émetteur pour certains e-mails de gestion | une chaine de caractère | webmaster@xxxx.fr |
mail.enabletls | Permet d'activer le mode TLS. Le TLS est une évolution plus sécurisé du SSL | true / false | false |
mail.enablessl | Permet d'activer le mode SSL. Si activé, est prioritaire au TLS | true / false | false |
mail.sender | Définit l'emétteur du mail (à défaut, utilise la valeur de mail.from | une chaine de caractère | |
mail.sleep | Permet de préciser un délai entre chaque envoi de mail en ms | un entier | 100 |
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| servletDispatcherEnabled | Activation du Dispatcher Servlet | Booléen (true, false) | false |
| dispatcherServletBaseUrl | Préfixe des URLs de contrôleurs de l'extension | Chaîne de caractères | idenfiant de l'extension |
| servletPriority | Permet de définir un ordre dans le chargement de la servlet de cette extension par rapport aux autres extensions | integer | com.kportal.extension.IExtensionServlet#EXTENSION_SERVLET_PRIORITY |
| servletName | Nom de la servlet utilisée dans la déclaration Spring (pas utile à modifier) | Chaîne de caractères | idExtensionDispatcherServlet |
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 {
//
}
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é | Valeur | Description |
|---|---|---|
| sentry.dsn | Data Source Name permettant d'envoyer les logs vers le sentry souhaité | |
| sentry.environment | ex: dev-ksup-master | Nom de l'environnement dans sentry |
| sentry.release | ex: 7.00.00 | Version de la release courrante |
| sentry.shutdownTimeout | 2000 par défaut | Durée en ms pour le timeout |
| sentry.flushTimeoutMillis | 15000 par défaut | Durée en ms pour le timeout |
| sentry.connectionTimeoutMillis | 5000 par défaut | Durée en ms pour le timeout |
| sentry.readTimeoutMillis | 5000 par défaut | Durée en ms pour le timeout |
| sentry.debug | true ou false (false par défaut) | Active le mode debug sur sentry |
| sentry.maxBreadcrumbs | 100 par défaut | Nombre d'éléments présents dans le breadcrumb |
| sentry.sampleRate | de 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.attachThreads | true ou false (false par défaut) | Défini si les threads doivent être attachés aux évènements envoyés à sentry |
| sentry.attachStacktrace | true ou false (false par défaut) | Défini si la stack trace doit être attachée aux évènements envoyés à sentry |
| sentry.minimumEventLevel | ERROR par défaut | Niveau de log minimum à envoyer à sentry |
| sentry.minimumBreadcrumbLevel | ERROR par défaut | Niveau 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>
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

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.
| Champ | Description |
|---|---|
| ID_URL | Identifiant de l'URL |
| FIRST_VISIBLE_SECTION | Première rubrique visible de l'URL |
| CODE_SITE | Code du site |
| URL | URL de l'URL |
| HASH | Identifiant de l'URL fabriqué en hachant celui-ci |
| TYPE | Type de l'URL |
| STATUS | Statut de l'URL |
| ID_METATAG | Identifiant de la fiche liée à cette URL, peut être null |
| CREATION_DATE | Date de création |
| UPDATE_DATE | Date de mise à jour |
| ID_TARGET_URL | Dans le cas d'une URL de redirection, identifiant de l'URL vers lequel cette URL redirige |
| ID_CANONICAL_URL | Identifiant de l'URL canonique du contenu |
| SECTION | Rubrique du contenu |
Table URL_REDIRECT
Table permettant de tracer les appels des URLs de redirection.
| Champ | Description |
|---|---|
| ID_URL_REDIRECT | Identifiant de l'URL de redirection |
| ID_URL | Identifiant de l'URL qui lui est lié |
| LAST_USED | Dernière date à laquelle l'URL a été utilisée |
| HITS | Nombre de fois où l'URL a été appelée |
Services et accès à la base de données

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

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.

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.

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 :

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

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ètre | Valeur | Commentaire |
|---|---|---|
| url.purge.maxdays | Par défaut : 365 | Permet de définir le nombre de jours de rétention des urls de redirection non utilisées |
Configuration dans l'application
| Paramètre | Valeur | Commentaire |
|---|---|---|
| Niveau montant max | Entier | À 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 max | Entier | À définir dans la configuration de site, permet de paramétrer le nombre de rubrique devant apparaître au maximum depuis le slug |
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 possibles | Valeur par défaut |
|---|---|---|
| dsi.activation | 0 ou 1 | 1 (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()
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 :
- 0 degrees: the correct orientation, no adjustment is required.
- 0 degrees, mirrored: image has been flipped back-to-front.
- 180 degrees: image is upside down.
- 180 degrees, mirrored: image has been flipped back-to-front and is upside down.
- 90 degrees, mirrored: image has been flipped back-to-front and is on its side.
- 90 degrees: image is on its side.
- 270 degrees: image has been flipped back-to-front and is on its far side.
- 270 degrees, mirrored: image is on its far side.

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.
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.propertiesdans les sources d'un projetenv.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Propriété | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| mediatheque.secure.typeMedias | Indique 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.format | Indique le type MIME de réécriture des fichiers Image de la médiathèque | image/png, image/webp | image/webp |
| mediatheque.photo.excluded.format | Indique les types MIME d'images non concernées par la réécriture, séparés par de ',' | image/gif, ... | image/gif |
| mediatheque.photo.output.quality | Indique le facteur de compression pour la réécriture des fichier Image de la médiathèque | un nombre en entre 0 et 1 | 0.75F |
| mediatheque.photo.output.compression | Indique le type de compression pour la réécriture des fichier Image de la médiathèque | Dépend du format. Pour un webp : Lossy / LossLess | Lossy |
| mediatheque.TYPE_RESSOURCE.max_file_size | Donne la taille limite à ne pas dépasser pour un media de type TYPE_RESSOURCE (photo, audio, video, fichier, ...) | 123456 ou 100K ou 2M | photo: 5M video: 150M fichier: 10M audio: 2M |
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) {
...
}
}
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
site.secure | Définit si le https est activé sur l'environnment. | true ou false (false uniquement sur les environnement de dev | true |
en ajoutant la valeur
site.secure=false
dans votre env.properties, le HTTPS sera désactivé par défaut.
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;
}
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
endpointpermettant 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.)
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>
Gestion des libellés
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| table.repertoire.specific | Emplacement des fichiers .dat | - chemin relatif dans le storage ou dans le classpath (dans le répertoire src/main/resources du projet) | /tables_specific |
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.
- Si la valeur est un JSON “wrappeur” (string commençant par
- 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)
-
Initialisation Flyway
- La classe étend
BaseJavaMigrationet implémentemigrate(Context). Flyway l’exécute au moment des migrations (au démarrage).
- La classe étend
-
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 viaPropertyHelper) peut contenir une liste de tables à ignorer, séparées par des virgules.- Exemple de configuration (fichier
env.propertiesde l’environnement):text_column_processor.excluded_tables=HISTORIQUE_LOGS,TMP_IMPORT
- Exemple de configuration (fichier
-
Détection des colonnes texte
listTextColumns(Connection, String table):- Tente un
SELECT * FROM {table} LIMIT 1et lit leResultSetMetaData. - Est considérée comme “texte” une colonne dont le type JDBC est
LONGVARCHAR, ou de typeVARCHARd’une taille supérieure à un seuil minimal, ou dont le type contientTEXT. - 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}.
- Tente un
-
Construction des requêtes SQL
buildQuerySelect(table, columns): construit leSELECTavecID_{table}+ colonnes texte, et y ajoute une clause WHERE construite parbuildColumnPredicat(table, columns)(spécifique à la classe fille).buildQueryUpdate(table, columns): construit unUPDATEavec paramètres, du styleUPDATE {table} SET col1=?, col2=?, ... WHERE ID_{table}=?.buildColumnPredicat(Set<String> columns, String needle): utilitaire générique pour produire une clauseWHERE (col LIKE '%needle%' OR ...), avec échappement des'dansneedle.
-
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.decodeStringJSonToClassenMap<String, Object>. - Pour chaque valeur de type
String, appliqueprocessHtmlContent(html). - Re-encode le JSON via
CodecJSon.encodeObjectToJSonInString.
- Parse via
- Sinon:
processHtmlContent(value)directement.
- JSON wrappeur ? (
- Si la valeur a changé et que l’intégrité est validée, on
UPDATEla ligne.
- On lit l’ID (
- Pour chaque ligne du
-
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
applyIntegrityVerificationest àtrue(par défaut), la migration pour cette ligne est annulée (on écrit la valeur d’origine), avec unWARN. - Une classe fille peut désactiver ce garde-fou en réglant
applyIntegrityVerification=falsesi elle doit effectuer des transformations modifiant intentionnellement le texte visible.
- Convertit HTML -> texte via
Points d’attention et bonnes pratiques
-
Prédicats SQL efficaces
- Évitez un scan complet des tables: construisez un
WHEREqui restreint aux contenus suspects (viabuildColumnPredicat(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).
- Évitez un scan complet des tables: construisez un
-
Intégrité du texte visible
- Conservez
applyIntegrityVerification=truetant que possible. Ne désactivez que si vos transformations changent volontairement le texte (ex. suppression de décorations textuelles devenues obsolètes).
- Conservez
Créer un nouveau script de migration: guide pas-à-pas
-
Étendre
AbstractTextColumnsProcessor- Implémenter:
protected String processHtmlContent(String htmlValue).protected String buildColumnPredicat(String tableName, Set<String> columns).
- Implémenter:
-
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
- Exemple:
-
Implémenter
buildColumnPredicat -
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; }
- Pattern recommandé:
-
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
minimumColumnSizesi votre migration doit couvrir desVARCHARplus petits/grands.
- Si la transformation modifie le texte visible (suppression de libellés), envisagez
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 ?
isJsonWrappedne reconnaît que les strings démarrant/finissant par{/}. Si vous avez du JSON tableau ou un wrapper différent, ajustezisJsonWrappeddans une sous-classe (ou surchargezprocessColumn).
-
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
processColumnpour décider au cas par cas si l’intégrité doit bloquer la migration et retournez la valeur d’origine si besoin.
- Le garde-fou est global à l’instance. Pour un contrôle plus fin, surchargez
-
Et si
ID_{TABLE}n’existe pas ?getIdValuetenteID_{TABLE}puis, en fallback, la première colonne duResultSet. Si aucune n’est accessible, la ligne est ignorée (pas d’UPDATE possible).
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.
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)
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.
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| saisie.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].INVISIBLE | Masque le champ en saisie | 0 (visible) ou 1 (invisible) | 0 |
| saisie.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].MODIFIABLE | N'autorise la saisie du champ qu'à la création | 0 (non modifiable) ou 1 (modifiable) | 1 |
| saisie.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].FACULTATIF | Indique le caractère optionnel du champ | 0 (obligatoire) ou 1 (facultatif) | 1 |
| saisie.{{NOM_DU_CHAMP}}.TAILLEMAX | Indique le nombre maximum de caractères autorisés du champ | entier positif | aucune |
| saisie.THEMATIQUE.INVISIBLE | Active la saisie du champs THEMATIQUE par défaut | 0 (visible) ou 1 (invisible) | 0 |
| saisie.THEMATIQUE.MODIFIABLE | Autorise la modification du champ THEMATIQUE par défaut | 0 (non modifiable) ou 1 (modifiable) | 1 |
| saisie.THEMATIQUE.FACULTATIF | Rend facultative la saisie du champ THEMATIQUE par défaut | 0 (obligatoire) ou 1 (facultatif) | 1 |
| saisie.RESUME.INVISIBLE | Active la saisie du champs RESUME par défaut | 0 (visible) ou 1 (invisible) | 0 |
| saisie.RESUME.MODIFIABLE | Autorise la modification du champ RESUME par défaut | 0 (non modifiable) ou 1 (modifiable) | 1 |
| saisie.RESUME.FACULTATIF | Rend facultative la saisie du champ RESUME par défaut | 0 (obligatoire) ou 1 (facultatif) | 1 |
| saisie.RESUME.TAILLEMAX | Limite la saisie du champ RESUME par défaut | entier positif | 2048 |
| saisie.PHOTO.INVISIBLE | Active la saisie du champ PHOTO par défaut | 0 (visible) ou 1 (invisible) | 0 |
| saisie.PHOTO.MODIFIABLE | Autorise la modification du champ PHOTO par défaut | 0 (non modifiable) ou 1 (modifiable) | 1 |
| saisie.PHOTO.FACULTATIF | Rend facultative la saisie du champ PHOTO par défaut | 0 (obligatoire) ou 1 (facultatif) | 1 |
Les propriétés peuvent être adaptées par objet pour surcharger les valeurs par défaut
| Propriété | Description | Valeurs possibles |
|---|---|---|
| saisie.{{OBJET}}.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].INVISIBLE | Masque le champ en saisie pour un objet spécifique | 0 (visible) ou 1 (invisible) |
| saisie.{{OBJET}}.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].MODIFIABLE | N'autorise la saisie du champ qu'à la création pour un objet spécifique | 0 (non modifiable) ou 1 (modifiable) |
| saisie.{{OBJET}}.{{NOM_DU_CHAMP}}[.{{SOURCE_IMPORT}}].FACULTATIF | Indique le caractère optionnel du champ pour un objet spécifique | 0 (obligatoire) ou 1 (facultatif) |
| saisie.{{OBJET}}.{{NOM_DU_CHAMP}}.TAILLEMAX | Indique le nombre maximum de caractères autorisés du champ pour un objet spécifique | entier positif |
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ètre | Description | Valeur possible | Valeur par défaut |
|---|---|---|---|
| jobDetails | Liste des beans de type JobDetailFactoryBean | ||
| type | Type de script | Voir IModule.TYPE_* | TYPE_NON_PARAMETRABLE_NON_AFFICHABLE |
| libelle | Libellé du script | ||
| description | Description du script | ||
| permissions | Liste des permissions nécessaires pour le script | ||
| etat | Etat par défaut du script | Voir 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ètre | Description | Valeur possible | Valeur par défaut |
|---|---|---|---|
| jobClass | La classe du job | Ex : com.kosmos.search.batch.job.IndexerJob | |
| durability | Indique si le job doit rester disponible dans le job store | true ou false | false |
| description | Description 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.

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 :
- 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.
- 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.
- 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");
}
...
}
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".

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.
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
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
healthController.mapping | path du endpoint de la sonde | path d'URL | /health |
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="<a"/>
<property name="baliseFermante" value="</a>"/>
</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 :
- Le tag [centresinteret], ordre=900
- Le tag [media;video;], ordre=950
- Le tag [media;audio;], ordre=950
- Le tag [media;pdfviewer;], ordre=950
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
fichiergw.maxsize | Définit la taille limite des fichiers uploadés par défaut. | un nombre (en Ko) | 50000 |
fichiergw.uas.maxsize | Définit la taille limite des fichiers uploadés dans l'usine à sites. | un nombre (en Ko) | 5000 |
fichiergw.<<EXT>>.maxsize | Dé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.
Configuration des toolbox
Documentation fonctionnelle
La configuration d'une toolbox peut être définie à plusieurs niveaux :
- avec l'attribut
configurationdu 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.
-
Création du fichier :
src/main/resources/article_ckeditor-override.properties→ il sera pris en compte uniquement par l’extension Article. -
Clé concernée (d’après
CkeditorConfigurerUtils) :CORPS. -
Contenu du fichier :
SAISIE_ARTICLE.CORPS=/adminsite/ckeditor/configurations/styleParagrapheSpe.json SAISIE_ARTICLE_FRONT.CORPS=/adminsite/ckeditor/configurations/styleParagrapheSpe.json -
Le style par défaut est
styleParagraphe. On crée donc une variantestyleParagrapheSpe.json, sans Accordéon, dans le dossiersrc/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).
-
Création du fichier :
src/main/resources/application_article_ckeditor.properties→ visible par le core. -
Clé concernée :
CONTENU_ENCADRE. -
Contenu du fichier :
SAISIE_ARTICLE.CONTENU_ENCADRE=/adminsite/ckeditor/configurations/styleParagrapheSpe2.json SAISIE_ARTICLE_FRONT.CONTENU_ENCADRE=/adminsite/ckeditor/configurations/styleParagrapheSpe2.json -
Le style par défaut étant
styleParagraphe, on crée une version adaptéestyleParagrapheSpe2.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.
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| tiles.configuration.refresh.activation | Active le controle du rechargement de la configuration | true : rechargement activé false : rechargement desactivé | false |
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
ToolboxCardBeanetSearchCardBean
Structure et slots
- Le template expose 15 slots verticaux numérotés de
0à14(soit 15 positions). - Chaque slot est un bean
DragNDropSlotnommésiteWeb-slotX. - Pour chaque slot, la liste des types de cards autorisées est fournie par un bean
CardClassListnommé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 lelistToAddBean. - 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
- 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.
- 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.
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 :
- ingest-attachment : plugin utilisé pour l'indexation de pièce jointe (utilisation de Tika).
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 possible | Valeur Défaut | Commentaire |
|---|---|---|---|
| cluster.name | String | core | Nom du cluster |
| name | String | ${cluster.name}.node.default | Nom du noeud |
| index.number_of_shards | int | 1 | Nombre de shard de l'index par défaut |
| index.number_of_replicas | int | 0 | Nombre de replicas de l'index par défaut |
| http.enabled | boolean | true | Activation du HTTP par défaut |
| client.transport.sniff | boolean | true | Heartbeat du client |
| discovery.zen.ping.multicast.enabled | boolean | true | Heartbeat 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
- 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 :
- 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 possible | Valeur Défaut | Commentaire |
|---|---|---|---|
| search.inclusion_objet | Chaîne de caractères : Code d'objet séparés par des , (exemple : CODE_OBJET1,CODE_OBJET2) | vide | Liste des codes objets a indexer (exécutée avant search.exclusion_objet). Si vide alors tous les objets sont pris en compte. |
| search.exclusion_objet | Chaîne de caractères : Code d'objet séparés par des , | vide | Liste des objets à exclure de l'indéxation |
| search.restriction_etat | Chaîne de caractères : Code des états séparés par des , | 0005,0006 | noms des états à exclure au moment de l'indexation |
| search.read.scroll.timeout | int | 10 | Durée du scroll Opensearch |
| search.read.scroll.timeout.unit | TimeUnit | MINUTES | Unité de la durée du scroll Opensearch |
| search.batch.indexation.commit-interval | int | 50 | commit-interval pour le job de réindexation complet |
| search.batch.indexation.throttle-limit | int | 4 | nombre maximum d'exécution en parallèle pour le job de réindexation complet (nombre de thread) |
| search.batch.indexation.skip-limit | int | 10000 | nombre maximum d'éléments ignorés pour le job de réindexation complet |
| batch.indexation.pagesize | int | 10 | nombre de documents récupéré par le reader pour le job de réindexation complet |
| search.batch.indexation.fiche.metatag.commit-interval | int | 10 | commit-interval pour le job de réindexation suite à la modification d'une fiche |
| search.batch.indexation.fiche.metatag.skip-limit | int | 10000 | nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'une fiche |
| search.batch.indexation.user.commit-interval | int | 10 | commit-interval pour le job de réindexation suite à la modification d'un utilisateur |
| search.batch.indexation.user.skip-limit | int | 10000 | nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'un utilisateur |
| search.batch.indexation.rubrique.commit-interval | int | 10 | commit-interval pour le job de réindexation suite à la modification d'une rubrique |
| search.batch.indexation.rubrique.skip-limit | int | 10000 | nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'une rubrique |
| search.batch.indexation.label.commit-interval | int | 10 | commit-interval pour le job de réindexation suite à la modification d'un label |
| search.batch.indexation.label.skip-limit | int | 10000 | nombre maximum d'éléments ignorés pour le job de réindexation suite à la modification d'un label |
| search.batch.indexation.media.commit-interval | int | 10 | commit-interval pour le job de réindexation suite à la modification d'un média |
| search.batch.indexation.media.skip-limit | int | 10000 | nombre 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.
- 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.
- Appel du service de recherche :
- Le service de recherche (
FulltextSearchFactory) est appelé pour obtenir unServiceSearcherapproprié. - Si un
ServiceSearcherest trouvé, il exécute la recherche et obtient unSearchResultBean.
- Initialisation du contexte de recherche :
- La méthode
initContextinitialise unSearchContextavec les informations de la requête, les résultats de la recherche, et d'autres métadonnées.
- Gestion des résultats de recherche :
- Si des résultats de recherche sont obtenus, le servlet parcourt les
AbstractSearchHandlerdisponibles pour trouver celui qui peut gérer la requête et la réponse. - Le
AbstractSearchHandlerapproprié est utilisé pour construire et retourner unModelAndView.
- Gestion des erreurs :
- Si aucun
ServiceSearcherou résultat de recherche n'est trouvé, une exception est levée.
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 :
-
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
- Emplacement :
-
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
- Définis via des beans Spring
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 :
-
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
-
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 :
- 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
- Bootstrap ClassLoader
- FileSystemFileListReader : Pour lire les fichiers depuis le système de fichiers local
- Webapp ClassLoader (ParallelWebappClassLoader)
- Fichiers WEB-INF/classes/
- Fichiers WEB-INF/lib/*.jar
- Webapp ClassLoader (ParallelWebappClassLoader)
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 :
-
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
-
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 :
-
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.jsondu JAR koreparent, placer un fichier avec le même nom au même chemin dans le projet
-
Surcharge par Priorité (pour les Index Templates)
- En définissant un bean
IndexTemplateReferenceavec une priorité plus élevée - Les templates avec une priorité plus élevée sont appliqués en priorité par OpenSearch
- En définissant un bean
Diagramme du Processus de Chargement

Ordre de Priorité des Templates
Component Templates
Pour les component templates, l'ordre de priorité est déterminé par l'ordre de chargement :
- Les templates définis dans le projet ont priorité sur ceux définis dans les JARs dépendants
- 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 :
- Plus la valeur de priorité est élevée, plus le template est prioritaire
- 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 :
-
Identifiez le template à surcharger, par exemple :
com/kosmos/search/mapping/component_template/content/template_fiche.json -
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 -
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 :
-
Créez un fichier JSON dans le répertoire approprié :
src/main/resources/com/kosmos/search/mapping/component_template/custom/mon_template.json -
Le template sera automatiquement découvert et chargé.
Surcharge d'un Index Template
Pour surcharger un index template existant :
-
Créez un bean
IndexTemplateReferenceavec 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" ); } -
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 keywordDateTemplateField: Classe de base abstraite pour créer des champs de type dateMultiPathKeywordTemplateField: Implémentation concrète de KeywordTemplateField pour définir plusieurs chemins via XMLMultiPathDateTemplateField: 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 :
- Le
DynamicTemplateGeneratorgénère le template JSON en scannant tous les beansTemplateField - Le
ComponentTemplateLoadervérifie si le template existe déjà sur OpenSearch - S'il existe, le loader compare la version existante avec la nouvelle version générée
- 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
-
Nommage Unique
- Utilisez des préfixes spécifiques à votre projet pour éviter les collisions de noms
- Exemple :
projet_template_fiche.jsonau lieu detemplate_fiche.json
-
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
- Utilisez des plages de priorités cohérentes :
-
Documentation
- Documentez les templates que vous ajoutez ou surchargez
- Indiquez clairement les modifications apportées aux templates existants
-
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 :
- Les component templates sont chargés avant les index templates
- Les fichiers sont chargés selon l'ordre du classpath Java
- Les templates avec le même nom sont dédupliqués
- Les index templates peuvent définir des priorités explicites pour contrôler l'ordre d'application
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);
}
}
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.
- 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);
...
- 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);
...
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
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é.
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"

- Activer les modules "Statistique" et "Script automatisé de génération des statistiques"

- Depuis l'écran de gestion des traitement planifiés, activer le traitement planifié "Batch d'extraction des statistiques"

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,
A l'aide de l'attribut order, vous pouvez insérer votre bean dans la liste des deciders à l'endroit adapté à vos besoins.... <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> ... - 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é.
Il suffit ensuite de déclarer le bean dans votre fichier de contexte... 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; } } ...
A l'aide de l'attribut order, vous pouvez insérer votre bean dans la liste des deciders à l'endroit adapté à vos besoins.... <bean id="siteManagerProfileDecider" class="com.acme.statistics.util.decider.XxxxxxManagerProfileDecider"> <property name="order" value="42000"/> </bean> ...
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 contexte — l'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>
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.

Les tokens sont générés via un script interne à Kosmos dont voici le diagramme de séquence.

Voici la liste des variables les propriétés qui leur correspondent ainsi qu'une valeur d'exemple :
| Nom de la variable dans le diagramme | Description | Propriété correspondante | Valeur d'exemple |
|---|---|---|---|
| idDashboard | Identifiant du tableau de bord qu'on souhaite visualiser | dashboard.id | 4adbc771-5b67-48d7-a656-8447cd4db8f0 |
| RLS | Restrictions d'accès aux données du tableau de bord | organisation='Kosmos' | |
| hostUrl | URL de l'hébergeur d'Apache Superset | dashboard.provider.hostname | https://ksup.analytics.skolengo.com |
| apiLoginPath | Path de l'API d'Apache Superset, qui renvoie un jeton d'accès et un refresh token | dashboard.provider.api.login | "/api/v1/security/login" |
| apiGuestTokenPath | Path 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" |
| username | Nom d'utilisateur pour le profil ayant le droit de générer un guest token | dashboard.provider.username | |
| password | Mot de passe de l'utilisateur pour le profil ayant le droit de générer un guest token | dashboard.provider.password | |
| provider | Provider du tableau de bord | dashboard.provider.provider | db |
| refresh | Vrai si on souhaite obtenir un refresh token lors de la connexion à l'API, faux sinon | dashboard.provider.refresh | true |
| TYPE_DASHBOARD | Type de ressource demandée à Superset, variable statique de com/kosmos/statistics/api/superset/model/RessourceForGuestToken.java | TYPE_DASHBOARD | dashboard |
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é.

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.
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.propertiesdans les sources d'un projetenv.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Propriété | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| statistics.client.id | Valeur par défaut du code client (fallback) | Chaîne de caractères | DEFAULT |
| statistics.http.duration.threshold | Temps 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 seuil | 3000 |
| statistics.performance.exclude.uri.start.with | Pattern 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.with | Pattern 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.dir | Chemin de génération des fichiers de statistiques | Path sur le disque de l'application | ${storage.dir}/statistics |
| statistics.batch.filename | Nom du fichier généré par le batch d'export des statistiques | Nom de fichier | statistics-volume.jsonl |
| job_params.blacklist | Liste des paramètres de job à exclure de la payload | Liste de chaînes de caractères | fireTime,nextFireTime,previousFireTime,noResumeAfterComplete |
| messaging.statistics.subscribe.taskexecutor.size | Taille du pool d'exécution du job des statistiques | Entier positif | 5 |
| messaging.statistics.subscribe.taskexecutor.maxsize | Taille maximum du pool d'exécution du job des statistiques | Entier positif | 5 |
| scheduler.statistics.cronExpression | Expression cron pour l'exécution automatisée du script d'export des statistiques | Expression cron | 0 5 0 1 * ? |
| statistics.performance.maxhistory | Durée de conservation (en mois) des logs de performances | Entier positif | 6 |
| dashboard.ui.hideTitle | Vrai si on veut cacher le titre du tableau de bord, faux sinon | true ou false | true |
| dashboard.ui.hideChartControls | Vrai si on veut cacher les contrôles du tableau de bord (rafraîchir les données, exporter le graphique, mettre en plein écran) faux sinon | true ou false | false |
| dashboard.ui.filters.expanded | Vrai 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 false | false |
| dashboard.provider.hostname | URL de l'hébergeur d'Apache Superset | Chaîne de caractères | https://ksup.analytics.skolengo.com |
| dashboard.provider.api.login | Path de l'API d'Apache Superset, qui renvoie un jeton d'accès et un refresh token | Chaîne de caractères | /api/v1/security/login |
| dashboard.provider.api.guest_token | Path 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.username | Nom 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.password | Mot 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.provider | Provider du tableau de bord | Chaîne de caractères | db |
| dashboard.provider.refresh | Vrai si on souhaite obtenir un refresh token lors de la connexion à l'API, faux sinon | true ou false | true |
| dashboard.id | Identifiant du tableau de bord qu'on souhaite visualiser | Chaîne de caractères | FIXME : pas encore disponible |
| dashboard.guestToken | Jeton 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ères | FIXME : dépend des organisations |
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.
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.
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>
| template1 | template2 | template3 | |
|---|---|---|---|
| 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>
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 :
| Variable | Description | Valeur par défaut |
|---|---|---|
--page-layout-columns-count | Nombre de colonnes du layout de base | 12 * |
--page-layout-offset | Première colonne du layout de base | 2 ** |
--page-layout-use-same-row-content | 0: empiler les zones, 1: permettre d'avoir les asides sur la même ligne que le contenu | 0 |
--page-layout-sidebar-columns-count | Nombre de colonnes de la sidebar de navigation | 12 |
--page-layout-boxes-columns-count | Nombre de colonnes de la sidebar des encadrés | 12 |
--page-layout-main-content-columns-count | Nombre de colonnes du contenu principal | 12 |
--page-layout-main-content-offset | Position de la première colonne du contenu principal | 2 |
--page-layout-pre-content-columns-count | Nombre de colonnes du contenu principal et des sidebars | 12 |
--page-layout-pre-content-offset | Position de la première colonne du contenu principal et des sidebars | 2 |
* 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 :
-
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-contentprend 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 de0viendra donc annuler la multiplication. Si c'est1, on additionne donc le nombre de colonnes occupées par la sidebar et par la zone d'encadrés. -
Le nombre de colonne du contenu principal central (
--page-layout-main-content-columns-count) est recalculé :main-content-columns-count = columns-count - aside-columns-countIl s'agit du nombre total de colonnes de la grille moins le nombre de colonnes occupées par la sidebar et les encadrés.
-
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-countOn 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.
-
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 -
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
bodyqui 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é
spanindique que la valeur utilisée désigne un nombre de colonnes et non un indice de référence. Le mot-clécontentdé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 fiche | Emplacement | Pré-contenu | Sidebar | Encadrés | Contenu principal | Même ligne ? **** |
|---|---|---|---|---|---|---|
| Accueil *** | Page d'accueil | 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) | Non |
| " | " | 12 (@2‑14) | ✅ 12 (@2‑14) | ✅ 12 (@2‑14) | 12 (@2‑14) | Non |
| Accueil *** | Page intérieure | 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) | Non |
| " | " | 12 (@2‑14) | ✅ 12 (@2‑14) | ✅ 12 (@2‑14) | 12 (@2‑14) | Non |
| Autre | Page d'accueil | 12 (@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 |
| Autre | Page intérieure | 10 (@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)
Ldésigne le nombre de colonnes occupées par la zone (L pour Length)Sdésigne l'indice de la colonne du début de la zone (S pour Start)Edé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) :
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.
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 lesCardViewBuilder - 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 viaAbstractViewPreparer.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 :
- De créer un bean dans un fichier
-override.xml - De le rendre prioritaire via la propriété
order - D’utiliser un
parentpour 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.
Principes d'intégration des vues
Déclaration d'une nouvelle vue
- Créer la jsp de la vue
- Créer un viewModel hériant de AbstractViewModel qui contient les données de la vue
public class MyViewModel extends AbstractViewModel {
...
}
- 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;
}
}
}
- 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
- 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
- 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
- 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
- Création d'un nouveau viewPreparer qui étend le viewPreparer de la vue à surcharger
- Override de la méthode prepare pour changer la construction du modèle
- 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.
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).
Intégration des encadrés
4 types d'encadrés sont disponibles dans K-Sup :
| Type d'encadré | Description | Code template |
|---|---|---|
| encadré de contenu | encadré saisissable dans l'onglet "Encadrés" de l'interface d'administration | contentBox |
| encadré de rubrique | encadré saisissable dans l'onglet "Encadré(s)" de l'interface de saisie de rubrique | sectionBox |
| encadré de générique | encadré 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().
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.
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>
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.
- 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);
}
}
- Création du viewModel, l'id du viewModel des fiches est par défaut mainViewModel (défini dans MainViewModel).
public class ArticleTemplateViewModel extends AbstractContentViewModel {
}
- 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>
- 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
- 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;
}
}
- 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<>();
...
}
- 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>
- 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>
...
- Répéter les étapes 1 à 4 pour créer les sous-vues qui composeront la page
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.
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.
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
DefaultTemplateViewPrepareret indiquer le code du template dans la propriétéincludedSiteTemplatedu 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>
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
orderdans 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>
...
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 :
- Classe d'accueil de rubrique : Hérite de
DefaultPageAccueilRubrique - Bean de page d'accueil : Implémente
BeanPageAccueil - View Model : Modèle de vue pour le front
- View Preparer : Prépare les données pour la vue
- 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,getUrlModificationetgetLibelleAffichbleé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.
- Les méthodes
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_RUBRIQUErepré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)).
- 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
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
AbstractSectionHomeViewPreparerpuisqu'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).
- La classe étend de
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) ;
- 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
- 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.
- Déclaration du préparateur de vue
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
trimDirectiveWhitespacespermet 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
}
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 obligatoire | Description | Format de donnée | Exemple de valeur |
|---|---|---|---|
| pointName | Identifiant permettant de cibler le point d'extension. | Chaine de caractère | applicationcontent-tools |
| context | Le contexte dans lequel recherche l'addon pour le pointName | IViewModel | ${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>
...
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>
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| header.front.referrer-policy | Valeur du header Referrer-Policy | Chaîne de caractères (cf MDN) | no-referrer |
| header.front.x-content-type-options | Valeur du header X-Content-Type-Options | Chaîne de caractères (cf MDN) | nosniff |
| header.front.content-security-policy | Valeur du header Content-Security-Policy | Chaî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
AbstractHttpHeaderPrepareret implémenter la méthodeacceptet/ouprepareselon 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>
...
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
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>
Surcharge de tags
Mise en œuvre
- Activer le profil maven
override-tags - 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.
Gestion des Liens en Front
Table des matières
- Vue d'ensemble
- Architecture du système de liens
- LinkRule - Règles de traitement des liens
- Types de liens prédéfinis
- Affichage en front-office
- Surcharge et personnalisation côté projet
- 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èglesLinkContext.java: Contexte de calcul contenant les donnéesLinkUtils.java: Utilitaires (détection liens externes, etc.)rules/LinkRule.java: Classe abstraite de baserules/ExternalLinkRule.java: Règle pour liens externesrules/NotSameSiteLinkRule.java: Règle pour liens vers autres sitesrules/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éthode | Description | Usage |
|---|---|---|
accept(LinkContext) | Détermine si la règle s'applique au contexte | Override pour ajouter des conditions |
apply(LinkContext, ILink) | Applique les modifications au lien | Override 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 i18nFO.LINK.EXTERNAL_SITE.TITLErel: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 i18nFO.LINK.NOT_SAME_SITE.TITLEclass: 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 relativesAbsoluteUrlHrefLangLinkRule: 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
- Toujours appeler
super.accept()etsuper.apply()dans les surcharges - Utiliser des noms de type uniques pour éviter les conflits
- Définir un ordre approprié selon la spécificité de la règle
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.
- Résout la base d’URL du DispatcherServlet de l’extension via le bean
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.javakoreparent/webapp-front/src/main/java/com/kosmos/common/CoreControllerUrlBuilder.javademarche/demarche-webapp-front/src/main/java/com/kosmos/demarche/DemarcheControllerUrlBuilder.javakoreparent/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
coreControllerUrlBuilderdans vos beans. -
Démarche: Récupérer (ApplicationContextManager.getBean()) / injecter le bean
demarcheControllerUrlBuilderdans vos beans.
Construire des URLs: les 3 surcharges de buildUrl
AbstractControllerUrlBuilder expose:
buildUrl(String path)
String url = urls.buildUrl("/users");
// -> "/<base-dispatcher>/users" (préfixée par l’extension)
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"
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"
- 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
ControllerUrlResolvern’est pas trouvé pour l’extension, uneIllegalArgumentExceptionest levée parAbstractControllerUrlBuilder.getUrlResolver(...). - Extension désactivée:
ControllerUrlResolverpeut renvoyer""(vide). Tenez-en compte si vous affichez l’URL telle quelle dans une vue. - Variables non fournies: si
pathcontient{var}mais que la clé manque dansvariables,UriComponentsBuilder.buildAndExpand(...)lèvera uneIllegalArgumentException. - Encodage:
UriComponentsBuildergère l’encodage des query params et des segments de chemin lors de la transformation en chaîne.
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
Intégration d'un template de site
Création d'un nouveau template
- 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
- 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>
- 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>
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) |
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>
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 :
SearchResultsViewPreparer: Classe principale qui prépare les résultats de rechercheListItemStyleResolver: Interface définissant les resolvers de styleAbstractSearchResultListItemStyleResolver: 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é
- Lorsqu'un utilisateur effectue une recherche,
SearchResultsViewPreparerest appelé pour préparer les résultats. - Pour chaque résultat, un sous-contexte est créé dans
createSubcontext. - La méthode
getStyleResolvers()récupère tous les beans de typeListItemStyleResolverdéclarés dans le contexte Spring. - Le système parcourt ces resolvers pour trouver le premier qui accepte le contexte (méthode
accept). - Le resolver sélectionné détermine le style à utiliser (méthode
resolve). - Le style est stocké dans le sous-contexte avec la clé
ContentListTagProcessor.STYLE. - 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 :
- Créer un resolver de style qui implémente
ListItemStyleResolver - Déclarer ce resolver comme bean Spring
- 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.
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.
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.
Documentation technique de la fiche article
Code objet : 0015 Table : ARTICLE
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.propertiesdans les sources d'un projetenv_article.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeurs possibles | Valeur par défault |
|---|---|---|---|
| fiche.ARTICLE.sitemap | Activation de la présente des fiches article dans le fichier sitemap | booléen K-Sup : 0 (non affichées) / 1 (affichées) | 1 |
| fiche.ARTICLE.recherche_avancee | Activation de la recherche avancée | booléen K-Sup : 0 (désactivée) / 1 (activée) | 1 |
| fiche.ARTICLE.style_affichage | Activation des styles d'affichage dans la toolbox | booléen K-Sup : 0 (désactivée) / 1 (activée) | 1 |
| export_rss.article.class | Définition d'un export spécifique pour les fiches article | String -> Classe java de l'export | com.univ.rss.ExportRssArticle |
| search.article.index.boost | Boost de la fiche dans ElasticSearch | numérique | 25 |
Documentation technique de la fiche page libre
Code objet : 0016 Table : PAGELIBRE
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.propertiesdans les sources d'un projetenv_pagelibre.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeur par défault |
|---|---|---|
| fiche.PAGELIBRE.sitemap | Définit que les fiches page libre seront affichées dans le fichier sitemap | 1 |
| fiche.PAGELIBRE.recherche_avancee | Activation de la recherche avancée | 1 |
| search.pagelibre.boost | Boost de la fiche dans ElasticSearch | 10 |
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 :
- Modèle :
ActualiteCardBean- Stocke les données de configuration de la carte - Modèle de Vue :
ActualiteCardViewModel- Prépare les données pour l'affichage dans la vue - Constructeur de Vue :
ActualiteCardViewBuilder- Transforme le modèle en modèle de vue - Vues : Fichiers JSP pour le rendu front-office et back-office
- 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 cartesubtitle: Sous-titre de la cartetypes: Types d'actualités sélectionnés à affichernewsPerTab: Nombre d'actualités par ongletdisplayStyle: Style d'affichagedisplayContext: Contexte de filtrage des actualitéslinkLabel: 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
- Front-office :
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 cartesubtitle: Sous-titre de la cartenewsTypes: Liste des types d'actualités avec leurs libellés et URLs de recherchenewsPerTab: Nombre d'actualités par ongletsearchAllNewsUrl: URL pour rechercher toutes les actualitéssearchAllNewsResultsUrl: URL pour afficher tous les résultats de recherchelinkLabel: 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 cartegetNewsTypes: Récupère et filtre les types d'actualités à afficherfilterTypesFromSearch: Filtre les types d'actualités selon les résultats de recherchebuildSearchUrl: Construit les URLs de recherche pour les types d'actualitésgetTypeLabel: 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énementDATE_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éeupdateView: 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 chargementhandleTabClick: Configure les écouteurs d'événements pour les clics sur les ongletsinitAllNewsTabs: Initialise l'onglet "Toutes les actualités" au chargement de la page
Flux de Données
- Configuration : Les administrateurs configurent la carte via le formulaire d'édition back-office
- Création du Modèle : Le système crée un
ActualiteCardBeanavec les données de configuration - Construction du Modèle de Vue :
ActualiteCardViewBuildertransforme le bean enActualiteCardViewModel- Récupère et filtre les types d'actualités
- Construit les URLs de recherche pour chaque type d'actualités
- Rendu : Le JSP front-office affiche la structure initiale de la carte avec les onglets
- Chargement Dynamique :
- Au chargement de la page,
lastNewsCard.jscharge 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
- Au chargement de la page,
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.propertiesdans les sources d'un projetenv_actualite.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeurs possibles | Valeur par défault |
|---|---|---|---|
| lastNewsCard.allowed.styles | Liste 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ée | 0001,0013 |
| lastNewsCard.default.style | Style d'affichage à utiliser par défaut | chaine | 0001 |
Localisation
La fonctionnalité supporte la localisation via des fichiers de propriétés :
actualite-contrib_fr_FR.properties: Chaînes en françaisactualite-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
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 )
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.propertiesdans les sources d'un projetannuairesup-override.propertiesdans les sources d'un projetenv_annuairesup.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeurs possibles | Valeur par défault |
|---|---|---|---|
| style.annuaire.avatar.path | chemin de la vignette par défaut dans les styles de liste | un chemin absolu vers une image | /static/images/avatar-default.png |
Documentation technique de la fiche application
Code objet : 0030 Table : APPLICATION
Libellés
| Type | Code |
|---|---|
| (Application) Catégorie(s ) | APP1 |
| (Application) Type(s) | APP2 |
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.propertiesdans les sources d'un projet.env_application.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeurs possibles | Valeur par défault |
|---|---|---|---|
| fiche.APPLICATION.style_affichage=1 | Activation des styles d'affichage dans la toolbox | booléen K-Sup : 0 (désactivée) / 1 (activée) | 1 |
Documentation technique de l'extension message
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.propertiesdans les sources d'un projetenv_message.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeur par défault |
|---|
Documentation technique de l'extension Newsletter
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.propertiesdans les sources d'un projetenv_newsletter.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeurs possibles | Valeur par défault |
|---|---|---|---|
| newsletter.style | Styles CSS utilisés pour la newsletter | un 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_max | Taille maximale autorisée pour les fichiers joints (en Ko) | entier positif (Ko) | 1024 |
| newsletter.liste_archives | Active le listing des newsletters archivées (non personnalisées et non purgées) | 0 = désactivé, 1 = activé | 1 |
| newsletter.template | Chemin du template JSP utilisé pour l’aperçu d’une newsletter | chemin vers une JSP du projet (classpath/webapp) | /WEB-INF/jsp/fo/newsletter.jsp |
| mailing.template | Chemin du template JSP utilisé pour l’aperçu d’un mailing | chemin vers une JSP du projet (classpath/webapp) | /WEB-INF/jsp/fo/mailing.jsp |
| newsletter.purge.delai | Délai de purge des envois personnalisés (en jours) | entier positif (jours) | 15 |
| newsletter.dsi.restriction | Restriction de l’arbre des groupes DSI au périmètre de l’utilisateur lors de la diffusion des envois | 0 = désactivé, 1 = activé | 1 |
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>
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.propertiesdans les sources d'un projetenv_panier.propertiesdans 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écifique | Description | Paramètre | Format de donnée | Valeur par défaut |
|---|---|---|---|---|
| taille | Définit la taille maximum du panier. | panier.TYPE_PANIER.taille | Nombre entier. Valeur "-1" si la taille du panier est infinie. | -1 |
| actif | Dé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.actif | Booléen qui est soit false (le panier est désactivé) ou true (le panier est activé). | true |
| modale | Définit si le panier utilise les modales de renommage et de confirmation de suppression d'un item. | panier.TYPE_PANIER.modale | Booléen qui est soit false (le panier n'utilise pas les modales) ou true (le panier utilise les modales). | false |
| drag-n-drop | Dé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-drop | Booléen qui est soit false (le panier n'utilise pas le drag'n drop) ou true (le panier utilise le drag'n drop). | false |
| comportement | Dé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.comportement | Chaîne de caractère qui est soit BDD ou SESSION. | BDD (favoris)/SESSION (boutique) |
| quantifiable | Définit si le panier est quantifiable. Cela signifie qu'on peut ajouter plusieurs fois le même item dans un panier. | panier.TYPE_PANIER.quantifiable | Booléen qui est soit false (le panier n'est pas quantifiable) ou true (le panier est quantifiable). | false (favoris)/true (boutique) |
| typeFicheAutorise | Définit la liste des objets pouvant etre ajoutés au panier. | panier.TYPE_PANIER.type-fiche-autorise | Liste de codes fiche séparées par des virgules sans espaces. | |
| typeFicheExclu | Définit la liste des objets étant par défaut exclu du panier. | panier.TYPE_PANIER.type-fiche-exclu | Liste 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.
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>
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.propertiesdans les sources d'un projetapplication_uas-override.propertiesdans les sources du pskenv_uas.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)application_uas-contrib.propertiesdans les sources d'un projetenv_uas-contrib.propertiesdans 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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
uas.fonctionnaliteAvancee | Dé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é | Description | Valeurs possibles | Valeur par défaut |
|---|
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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| eprivacy.services.list | Liste des services présent sur le projet | analytics_cookies,facebook,twitter,instagram,linkedin,youtube | |
| eprivacy.services.list.hidden | Liste des services à masquer | essentials_cookies | |
| eprivacy.configuration.cache | Durée du cache sur le fichier configuration.js | PT1H | |
| eprivacy.configuration.privacypolicy | Chemin 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.cookieexpiresafterday | 365 | ||
| eprivacy.configuration.mustconsent | false | ||
| eprivacy.configuration.mustnotice | false | ||
| eprivacy.configuration.debug | false | ||
| eprivacy.configuration.declineLink | false |
Paramètres d'un service
| Propriété | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| eprivacy.configuration.app.{SERVICE_ID}.category | Identifiant de la catégorie (CATEGORY_ID) | ||
| eprivacy.configuration.app.{SERVICE_ID}.optOut | Ce service est-il en opt-out ? | true ou false | false |
| eprivacy.configuration.app.{SERVICE_ID}.default | Ce service est-il actif par défaut ? | true ou false | false |
| eprivacy.configuration.app.{SERVICE_ID}.cookies | Liste de cookies qui seront supprimés à la désactivation du service par l'utilisateur | cookies séparés par des virgules |
Messages
Catégories de services
| Clé ({CATEGORY_ID} à remplacer) | Exemple |
|---|---|
| EPRIVACY.CONFIGURATION.CATEGORIE.{CATEGORY_ID}.TITLE | Cookies de réseaux sociaux |
| EPRIVACY.CONFIGURATION.CATEGORIE.{CATEGORY_ID}.DETAILS | Détails... |
| EPRIVACY.CONFIGURATION.CATEGORIE.{CATEGORY_ID}.DESCRIPTION | Les 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}.TITLE | Youtube |
| EPRIVACY.CONFIGURATION.APP.{SERVICE_ID}.DESCRIPTION | Consultez 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.
Dépôts à récupérer
- inscription
- dev-ksup
- pretix
- pretix-openapi-generator
- et éventuellement koreparent
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
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.
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
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.propertiesdans les sources d'un projetenv_inscription.propertiesdans lestoraged'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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| evenement.courriel.contact.defaut | Courriel utilisé pour l’initialisation de l’écran de saisie d’un évènement | pas de valeur par défaut | |
| evenement.courriel.expediteur.defaut | Courriel utilisé pour l’initialisation de l’écran de saisie d’un évènement | email type no-reply@acme.com | pas de valeur par défaut |
| evenement.modeleformulaire.defaut | Code du formulaire par défaut pour l’inscription | code d'un formulaire disponible | identite |
| evenement.type.widget.defaut | Type d’affichage (pretix) par défaut des évènement dans le widget | list / (week) / calendar | calendar |
| event.setting.default.max_items_per_order | Nombre maximun de place par inscription, tous produits confondus | numérique | 100 |
| event.setting.default.cancel_allow_user_until | Délai d’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | RELDATE/x/date_from|date_to|presale_start | RELDATE/7/-/date_from/ |
| event.setting.default.cancel_allow_user | Autorise l’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | true / false | true |
| event.setting.default.change_allow_user_addons | Autorise la modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | true / false | true |
| event.setting.default.change_allow_user_until | Délai de modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | RELDATE/x/date_from|date_to|presale_start | RELDATE/7/-/date_from/ |
| event.setting.default.locales | Liste des langues supportées | code langue | fr |
| event.setting.default.locale | Langue par défaut dans les langues supportées | code langue | fr |
| event.setting.default.region | Region par défaut (utilisé pour les dates notamment) | code pays | FR |
| inscription.limite.categorie.defaut | Valeur par défaut utilisée lors de l’activation d’une restriction par catégorie | nombre positif strictement | 2 |
Configuration des valeurs par défaut de l'évènement - champs imposés par l'API
| Clé | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| event.setting.change_allow_user_price | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | any / gt / gte/ eq | any |
| event.setting.payment_term_accept_late | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | true / false | false |
| event.setting.cancel_allow_user_paid_refund_as_giftcard | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | on / off | off |
| event.setting.redirect_to_checkout_directly | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | true / false | true |
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 |
Dépôts à récupérer
- inscription
- dev-ksup
- pretix
- pretix-openapi-generator
- et éventuellement koreparent
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
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.
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
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.propertiesdans les sources d'un projetenv_inscription.propertiesdans lestoraged'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é | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| evenement.courriel.contact.defaut | Courriel utilisé pour l’initialisation de l’écran de saisie d’un évènement | pas de valeur par défaut | |
| evenement.courriel.expediteur.defaut | Courriel utilisé pour l’initialisation de l’écran de saisie d’un évènement | email type no-reply@acme.com | pas de valeur par défaut |
| evenement.modeleformulaire.defaut | Code du formulaire par défaut pour l’inscription | code d'un formulaire disponible | identite |
| evenement.type.widget.defaut | Type d’affichage (pretix) par défaut des évènement dans le widget | list / (week) / calendar | calendar |
| event.setting.default.max_items_per_order | Nombre maximun de place par inscription, tous produits confondus | numérique | 100 |
| event.setting.default.cancel_allow_user_until | Délai d’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | RELDATE/x/date_from|date_to|presale_start | RELDATE/7/-/date_from/ |
| event.setting.default.cancel_allow_user | Autorise l’annulation d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | true / false | true |
| event.setting.default.change_allow_user_addons | Autorise la modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | true / false | true |
| event.setting.default.change_allow_user_until | Délai de modification d’une inscription par un inscrit pour l’initialisation de l’écran de saisie d’un évènement | RELDATE/x/date_from|date_to|presale_start | RELDATE/7/-/date_from/ |
| event.setting.default.locales | Liste des langues supportées | code langue | fr |
| event.setting.default.locale | Langue par défaut dans les langues supportées | code langue | fr |
| event.setting.default.region | Region par défaut (utilisé pour les dates notamment) | code pays | FR |
| inscription.limite.categorie.defaut | Valeur par défaut utilisée lors de l’activation d’une restriction par catégorie | nombre positif strictement | 2 |
Configuration des valeurs par défaut de l'évènement - champs imposés par l'API
| Clé | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| event.setting.change_allow_user_price | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | any / gt / gte/ eq | any |
| event.setting.payment_term_accept_late | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | true / false | false |
| event.setting.cancel_allow_user_paid_refund_as_giftcard | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | on / off | off |
| event.setting.redirect_to_checkout_directly | Valeur utilisée pour des champs obligatoires de l’API et non exposés dans K-Sup | true / false | true |
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 |
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>
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.propertiesdans les sources d'un projetenv_lieu.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Paramètre | Description | Valeurs possibles | Valeur par défault |
|---|---|---|---|
| maps.google.useSensor | Utilisation de la géolocalisation | true / false | true |
| maps.defaultMapWidth | Largeur par défaut de la carte | 400 | |
| maps.defaultMapHeight | Hauteur par défaut de la carte | 400 | |
| fiche.LIEU.recherche_avancee | Support de la recherche avancée | 1 | |
| fiche.LIEU.recherche_anonyme | Support de la recherche anonyme | 0 | |
| fiche.LIEU.encadre_recherche | Encadré de recherche avancée pour les fiches Lieu | 1 | |
| fiche.LIEU.style_affichage | Utilisation d'un template spécifique ou de celui par défaut | 0 | |
| LIEU.GOOGLE.API_KEY | https://developers.google.com/maps/documentation/javascript/get-api-key | (vide) | |
| LIEU.GOOGLE.SCRIPT_URL | Chemin vers le script de l'API | https://maps.googleapis.com/maps/api/js?key=${LIEU.GOOGLE_MAPS_API.KEY}&callback=lieu_GoogleMapsAPI_Loaded | |
| LIEU.LEAFLET.PROVIDER | Fournisseur de fond de carte | OpenStreetMap | |
| LIEU.LEAFLET.GEOCODER | Convertisseur de coordonnées | OpenStreetMap | |
| LIEU.ROUTE.PROVIDER | Template d'URL vers un itinéraire | ${LIEU.ROUTE.PROVIDER.OPENSTREETMAP} | |
| LIEU.ROUTE.PROVIDER.GOOGLEMAPS | Template d'URL vers un itinéraire pour Google Maps | https://maps.google.com/maps?z=14&daddr= | |
| LIEU.ROUTE.PROVIDER.OPENSTREETMAP | Template d'URL vers un itinéraire pour OSM | https://www.openstreetmap.org/directions?to= | |
| LIEU.MAP.API | API utilisée pour l'affichage des cartes | LEAFLET | |
| cartographie.lien_fiche_lieu.affichage | Indique si le lien vers la fiche Lieu doit être affiché sur la cartographie | 1 | |
| cartographie.fiches_liees_lieu.affichage | Indique si la liste des fiches liées à un lieu doit être affichée sur la cartographie | 1 | |
| cartographie.active | Activation la cartographie | true / false | (vide / false) |
| infosCartographie.categorie.nombre | Définition du nombre de catégories | Entier positif | 0 |
| infosCartographie.categorie | Ordre des catégories pour la saisie en back-office | 1 à ${infosCartographie.categorie.nombre} | n |
| infosCartographie.categorie | Ordre des catégories pour l'affichage en front-office | 1 à ${infosCartographie.categorie.nombre} | n |
| infosCartographie.categorie | Ordre des catégories pour l'affichage dans le plan interactif | 1 à ${infosCartographie.categorie.nombre} | n |
| cartographie.center.latitude | Latitude du point central par défaut sur lequel sera centré le plan interactif à l'ouverture | vide | |
| cartographie.center.longitude | Longitude du point central par défaut sur lequel sera centré le plan interactif à l'ouverture | vide | |
| cartographie.initial.zoom | Niveau de zoom par défaut à l'ouverture du plan interactif | vide |
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
Patch lien tag - resultat de recherche
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>
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.propertiesdans les sources d'un projetenv_taglink.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Propriété | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
tags.types | Liste 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,9900 | 9876 |
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.propertiesdans les sources d'un projetenv_connecteurhal.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Propriété | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
HAL_URL_RECHERCHE_API | URL de l'API de recherche | URL | https://api.archives-ouvertes.fr/ |
HAL_URL_RECHERCHE | URL de la liste des résultats de recherche sur le site HAL | URL | https://hal.archives-ouvertes.fr/ |
HAL_NB_RESULTATS | Nombre de résultats à afficher dans la page | Entier positif | 10 |
HAL_CHAMPS_RETOUR_JSON | Liste des champs que l'on souhaite récupérer de l'API HAL | Liste des champs HAL séparés par des virgules | 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 |
HAL_TRI_RESULTAT | Tri des résultats retournés par l'API HAL | asc | desc | desc |
HAL_TYPES_DOCUMENTS | Types de documents renvoyé par l'api | Cf. lien valeurs possibles | NONE |
api.hal.authentification | Mode d'authentification | Cf. fr.kosmos.web.projets.extensions.AbstractJaxRsDAO.Mode | NONE |
documenthal.mapping | Chemin d'écoute du controller | Url relative | /documentHal |
api.hal.connecttimeout | Timeout de connexion à l'API | Entier positif | 30000 |
api.hal.readtimeout | Timeout de lecture de l'API | Entier positif | 30000 |
api.hal.search | Chemin de l'API de recherche | Chaîne de caractères | search |
api.hal.libelle | Chemin de l'API permettant de récupérer la "Liste des référentiels" | Chaîne de caractères | ref/doctype |
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.disabled | Désactivation du cache "Récupération d'une liste des documents HAL pour une fiche annuaire" | true | false | false |
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.eternal | Indique si le cache "Récupération d'une liste des documents HAL pour une fiche annuaire" est éternel | true | false | false |
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.maxEntriesLocalHeap | Nombre d'entrées max dans le cache "Récupération d'une liste des documents HAL pour une fiche annuaire" | Entier positif | 100 |
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.timeToIdleSeconds | Duré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 positif | 120 |
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.timeToLiveSeconds | Duré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 positif | 120 |
cache.ServiceConnecteurHalImpl.getDocumentHalAnnuaire.evictionPolicy | Politique d'éviction du cache "Récupération d'une liste des documents HAL pour une fiche annuaire" | Cf. net.sf.ehcache.store.MemoryStoreEvictionPolicy | LFU |
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.disabled | Désactivation du cache "Récupération d'une liste des documents HAL pour une fiche structure" | true | false | false |
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.eternal | Indique si le cache "Récupération d'une liste des documents HAL pour une fiche structure" est éternel | true | false | false |
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.maxEntriesLocalHeap | Nombre d'entrées max dans le cache "Récupération d'une liste des documents HAL pour une fiche structure" | Entier positif | 100 |
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.timeToIdleSeconds | Duré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 positif | 3600 |
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.timeToLiveSeconds | Duré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 positif | 43200 |
cache.ServiceConnecteurHalImpl.getDocumentHalStructure.evictionPolicy | Politique d'éviction du cache "Récupération d'une liste des documents HAL pour une fiche structure" | Cf. net.sf.ehcache.store.MemoryStoreEvictionPolicy | LFU |
cache.ServiceDoctypeHal.getDoctypes.disabled | Désactivation du cache "Récupération des libellés HAL" | true | false | false |
cache.ServiceDoctypeHal.getDoctypes.eternal | Indique si le cache "Récupération des libellés HAL" est éternel | true | false | false |
cache.ServiceDoctypeHal.getDoctypes.maxEntriesLocalHeap | Nombre d'entrées max dans le cache "Récupération des libellés HAL" | Entier positif | 10 |
cache.ServiceDoctypeHal.getDoctypes.timeToIdleSeconds | Duré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 positif | 3600 |
cache.ServiceDoctypeHal.getDoctypes.timeToLiveSeconds | Durée max (en secondes) pendant laquelle un élément est conservé dans le cache "Récupération des libellés HAL" | Entier positif | 86400 |
cache.ServiceDoctypeHal.getDoctypes.evictionPolicy | Politique d'éviction du cache "Récupération d'un libellé HAL" | Cf. net.sf.ehcache.store.MemoryStoreEvictionPolicy | LFU |
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.
Appel des scripts dans le footer
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>
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
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;
}
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>
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.propertiesdans les sources d'un projetenv_fichierjoint.propertiesdans lestoraged'un environnement
Propriétés
| Propriété | Description | Valeurs possibles | Valeur par défaut |
|---|
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
messagesspring pour l'i18n présents dans les fichiers javascript- constuction de bundles, appelés
groupcss 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.
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.

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
| Type | Front | Legacy |
|---|---|---|
| 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.
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.
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.jsdevientfrontoffice-i18n_en.jsetfrontoffice-i18n_fr_FR.js. -
Traduire chacune des valeurs présentes dans ces fichiers (avec les valeurs des fichiers
*.propertiesdeSpring) -
Ajouter une section
langavec les différentes langues traduites et référencer les fichiers comme dans cet exemple :scripts/collaboratif_i18n_${lang}.js
-
Créer un fichier par langue avec comme suffixe la langue en question :
-
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
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
vendoren 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
| Valeur | Commentaire |
|---|---|
| production | C'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. |
| development | La 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
| Valeur | Commentaire |
|---|---|
| all | Lance la construction de tous les bundles |
| js | Lance la construction du JS seulement |
| css | Lance la construction du CSS seulement |
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 bundletarget(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 fichierlang(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"
}
}
}
}
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
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).
- Démarrage d'un environnement produit
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
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".

- 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.
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
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" ],
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
- Mise à jour du socle technique (MAJ Pom, java, tomcat, mariadb, passage a opensearch) => Le projet doit compiler sur le poste & Jenkins
- Migration du pom
- Résoudre les problèmes de compilation
- Résoudre les impacts sur les objets spécifiques
- Mettre à jour les fichiers de l'usine logicielle (jenkinsfile & IC/values.yaml) pour qu'il utilise les bonnes versions de java & tomcat
- Supprimer les patchs et les fonctionnalités devenues obsolètes
- Mise à jour de la configuration Spring
- Impacts API K-SUP
- Suppression du wro et mise en place des bundles => On voit l'écran avec les styles et les scripts
- Impacts Opensearch => la recherche doit fonctionner
- Flyway
- Analyse des corrections à faire sur Sonar et les effectuer
- Activation de la recherche anonyme
- Tester les fonctionnalités principales
Ressources
- Différences 6.7 et 7.0
- Identifier les problèmes de démarrage et corriger : Cette ressource doit être consultée afin de corriger les problèmes qui peuvent apparaitre durant une migration vers KSUP-7.
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 baliseproperties.
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.jsonqui active un autre plugin pour générer les bundles.
et ajouter au package.json dans l'objetnpm init npm install @ksup/ksup-front-build --dev npm installscripts:{ "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.jsondanssrc/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.versjakarta..- Par exemple :
javax.validation.machin.trucdevientjakarta.validation.machin.truc.
- Par exemple :
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>
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
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>
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
- La méthode
FrontUtil.calculerListeFichiersJoints()a été déplacée dansPSKFrontUtil.calculerListeFichiersJoints. - Impacts sur les méthodes utilitaires manipulant des libellés
- Impact suite à la sortie des encadrés dans une table dédiée
- Changement sur les constructeurs de cartes de page d'accueil
- Impacts sur le formatage html des toolbox
Java
Migrations diverses
- Si l'appel à la méthode
FicheUnivHelper.getFiche()est utilisé pour afficher une fiche en front, elle doit être remplacée parFicheUnivHelper.getFicheFrontOffice(). - La méthode
ValidateurCAS.getURLCasAuthentification()n'existe plus, elle a été remplacée parValidateurCAS.getURLAuthentificationCallback(). - Ajout d'un tag "média"
- Indexation des champs booléens ksup
- La propriété
afficheblocdesCardViewModelest remplacé pardisplayBloc.
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
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
- Voir 3. du ticket : Migration vers OpenSearch MIGRATION-286
Impacts liés aux objets & plugins spécifiques
Si le projet possède des objets spécifiques ou des plugins indexés :
- Les champs renseignants des id de médias doivent avoir l'annotation @GetterAnnotation(isIdMedia=true) MIGRATION-148 et MIGRATION-157
- Indexation des booléens MIGRATION-196
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.propertiespar type de fiche.- En 6.7, on avait, dans un fichier
application_monprojet_ckeditor.properties:
- En 6.7, on avait, dans un fichier
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.propertiesdevient :
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).
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);
Impacts divers du framework K-Sup
Réaliser les migrations suivantes :
- Modification du tag champPhoto MIGRATION-280
- Modification de l'api liée à la récupération des libellés MIGRATION-111
- Modification de l'affichage des fichiers joints MIGRATION-311
- Modification des encadrés MIGRATION-170
- Modification des plugins tag dans ksup MIGRATION-307
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" %>
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
stylesetscripts-
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.cssdevientstyles/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)
-
Désactiver le calcul du
-
Ajouter une section
-
Si le groupe contient des fichiers de traduction
-
Créer un fichier par langue avec comme suffixe la langue en question :
frontoffice-i18n.jsdevientfrontoffice-i18n_en.jsetfrontoffice-i18n_fr_FR.js. -
Traduire chacune des valeurs présentes dans ces fichiers (avec les valeurs des fichiers
*.propertiesdeSpring) -
Ajouter une section
langavec les différentes langues traduites et référencer les fichiers comme dans cet exemple :scripts/collaboratif_i18n_${lang}.js
-
Créer un fichier par langue avec comme suffixe la langue en question :
-
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 questylesetscripts -
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'
externaldéclaré plus haut.
-
Ajouter une section
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:
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.
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
/**
* 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:
searchFicheAccueilConfigurationsupprimé dearasio-accueil.xml.
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.javadevientR__1664359142_ajout_libelles.javaavec1664359142le timestamp. -
Les scripts Rejouables n'implémentent plus
JdbcMigration&MigrationChecksumProvider, ils doivent donc être supprimées. Les scripts doivent maintenant étendre la classeorg.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 EXISTSlors 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.
- oui
- est-il rejouable ?
- 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.
- script dans src/main/webapp/WEB-INF/conf/sql/mysql
-
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.
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
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:
- Lancer l'indexation automatique depuis adminsite
- Accéder au '/search'
Différences 6.7 et 7.0
Ce tableau a pour objectif de regrouper les modifications entre 6.7 et 7.0.
| Type de fichier | 6.7 | 7.0 |
|---|---|---|
| .dat (lien doc) | src/main/webapp/WEB-INF | src/main/resources |
| scripts flyway SQL core (lien doc) | src/main/webapp/WEB-INF/conf/sql/core/mysql | src/main/resources/com/kosmos/core/migration |
| scripts flyway SQL projet (lien doc) | src/main/webapp/WEB-INF/conf/sql/mysql | src/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.xml | src/main/resources/spring/{nom_extension}-override.xml |
| jsp produit (lien doc | src/main/webapp/jsp/extensions/core/TYPE_FICHE/ | src/main/webapp/jsp/extensions/TYPE_FICHE/TYPE_FICHE/ |
| .scss (lien doc | src/sass | src/main/resources/sass |
| .js (lien doc) | src/main/resources/js | |
| style de lien | ajouté dans les fichiers style.json (ex: encadreStyle.json) | doc des liens |
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.
Licence K-Sup
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.
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
- En haut de chaque page, un lien permet d'accéder au fichier source de la page pour mettre à jour la documentation.

- Copier l'ensemble du fichier
- Aller sur l'éditeur web approprié (https://onlinemarkdowneditor.dev/)
- Coller le contenu dans la toolbox de l'éditeur
- Si vous avez besoin d'ajouter des images il faut dans l'éditeur web :
- Ajouter l'image dans la toolbox
- Cliquer sur l'image pour faire apparaître les boutons contextuels

- Cliquer sur le bouton de droite pour ajouter un texte alternatif et le valider

- Après avoir modifié le contenu, il faut copier le contenu modifier à partir du mode source de la toolbox
- Il faut ensuite retourner à l'emplacement des sources sur Gitlab et accéder aux sources en cliquant sur le bouton Modifier
4. Remplacer le contenu du fichier dans la page web avec celui copié
5. Pour valider la modification il faut
- Cliquer sur le bouton commit en bas de la page
2. Renseigner un message sur le fenêtre de validation. Attention, le message doit toujours commencer par docs : xxxx.
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é)
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)

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.
- 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).
- L'emplacement du fichier dépend de sa portée
- 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)
- Dans un répertoire docs/func/contenus pour les composants liés aux contenus
- Dans un répertoire docs/func/modules pour les composants liés aux modules
- Dans un répertoire docs/func/socle pour les composants liés au socle
- Si le fichier a une portée technique, il doit être placé dans un répertoire docs/tech.

- 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)
- 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 :
- Exemple pour de la documentation fonctionnelle
- [Fiche Article](func/module/article-func.md)
- 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
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.propertiesdans les sources d'un projetenv_xxx.propertiesdans le répertoire de configuration d'un environnement (référencé par la propriétéconf.dir)
Propriétés
| Propriété | Description | Valeurs possibles | Valeur par défaut |
|---|---|---|---|
| Propriété 1 | Description 1 | Valeur possible | Valeur 1 |
Liens externes à la documentation
- docs.ksup.org : Documentation publique du produit (version 6.7 et 6.6)
- Référentiel produit : Liste des composants K-Sup
- Roadmap 7.0 : Chantier en cours sur la version 7.0
- Règles de développement : Règles de développement (Java, Front, Git, FLyway)
- Tutoriels développement : FAQ Solutioning
- Formations K-Sup : Liens vers les différents supports de formation

Structure par défaut des pages K-Sup V7