InitProcessComposantCooperative - 08/08/2026 17:12:03

Partagée entre composants et base hôte

Capable de process préemptif

      #DECLARE($objetClass : Object; $functionID : Text; $params : Object)
// initialise un process thread-safe (la méthode a la propriété "thread-safe")
// ici on va utiliser "traceHandler" du composant
var $texte : Text

// init du worker, préemptif ou non
ON ERR CALL(Formula(traceHandler).source; ek local)

$texte:=cs.xSDK.Outils.me.getTextDeTypeProcess(Current process)
CALL WORKER(Worker Services; Formula(traceHandler); [msgk_event; msgk_log]; "Information"; Current method name; $texte; New object("nomProcess"; Current process name; "numProcess"; Current process))

If (Count parameters>2)
	Storage.Host.processUser.call(Null).ExecuterDansProcess($objetClass; $functionID; $params)
End if 

    

Bac a sable WEBMO - 10/08/2026 10:10:39

Partagée entre composants et base hôte

      #DECLARE($numProcess : Integer)

var $numproc; $commande; $i : Integer
var $o; $oo : Object
var $c : Collection

If (Count parameters=0)
	
	//APPELER WORKER("WK_Services";"initProcessWorker")
	//$bool:=Lire activation assertions
	//  $x:=""
	//  UserIDentification ("EnregistrerIdentifiants";->$x)
	//  APPELER WORKER("WK_Services";Inscrire au Journal;"Ouvrir_BDD";0;0;0;$x)
	$numproc:=New process(Current method name; 0; "tester"; Current process)
	
Else 
	
	//cs._CommunFunctions.me.InitProcess()
	
	$o:=New object
	$oo:=New object
	
	
	$c:=New collection
	//$c.push(0)
	//$c.push(31)
	//$c.push(4)
	//$c:=New collection(0; 3)
	$commande:=0
	For each ($i; $c)
		$commande:=$commande ?+ $i
	End for each 
	
	
	
	//cs.$serveur.new().InstallerPageSupport("support")
	
	If ($commande ?? 0)
		// afficher la console de l'application
		var $params : Object
		
		$params:=New object
		$params.Commande:="Afficher"
		$params.sourceLogs:=ALV BDD mère
		$params.wndTitre:="Logs application ALV"
		cs.xSDK.ResourceALV.me.SetObjet(Est Ressource APP; "Ressources_Communes/nbrMaxLogs"; Is longint; $params; "nbrMaxLogs")
		
		cs.xSDK.EvenementsALV.me.AfficherEditeur($params)
		cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "WK libellé 1"; Current method name; "test ALB : WK description 1")
	End if 
	
	
	If ($commande ?? 1)
		cs._composant.new().InstallerServeur()
		cs.$serveur.new().Demarrer()
		$c:=WEB Server list
	End if 
	
	
	If ($commande ?? 2)
		//cs._maintenance.new().CréerBDDmobile()
	End if 
	
	If ($commande ?? 3)
		cs.xALB._maintenance.new().Démarrer()
		cs._maintenance.new().InstallerAlbums()
		
		// lire la présence de la tâche du composant ALB et son état
		$c:=cs.xSDK.RegistreTaches.new().LireInscriptions()
	End if 
	
	
	If ($commande ?? 4)
		var $data : cs._SF_Sessions
		$data:=cs._SF_Sessions.me
		SET MENU BAR(1)
		$i:=Open form window("SF_Sessions"; Plain form window; Horizontally centered; Vertically centered; *)
		DIALOG("SF_Sessions"; $data)
		CLOSE WINDOW
		RELEASE MENU(Get menu bar reference)
	End if 
	
	
	If ($commande ?? 31)
		cs.xSDK.TraductionsEditeur.new().ModifierTraductions()
		
	End if 
End if 
    

EstDansCollection - 30/07/2026 14:41:11

Disponible via SQL

      #DECLARE($nomFiltre : Text; $valeur : Integer)->$result : Integer
// test si $valeur est dans le filtre de nom $nomFiltre
var $c : Collection

$result:=-1
$c:=cs._filtrageDonnees.me[$nomFiltre]

Case of 
	: ($c=Null)
	: ($c.length=0)
	Else 
		$result:=$c.indexOf($valeur)
End case 

    

WEBMO_EcrireElement - 28/10/2025 18:37:02

Disponible via les balises HTML et les URLs 4D (4DACTION...)

Capable de process préemptif

      #DECLARE($url : Text)->$result : Text
var $serveur : cs.$serveur
var $albums : cs.$albums

// dans l'ordre !
Case of 
	: ($url="@WebALB@")
		// ici une requête d'album
		// attention le nom de la function peut être dans cs.$albums
		$albums:=cs.$albums.new($url)
		
		Case of 
			: ($albums.url="")
				
			: (OB Is defined($albums; $albums.url))
				// url reconnue, lancer la function locale
				$result:=$albums[$albums.url]()
				
			: (OB Is defined($albums.webALB; $albums.url))
				// url du composant ALB, lancer la function
				$albums.webALB[$albums.url]()
				$result:=$albums.webALB.result.resultat
				
			: (OB Is defined($albums.reqALB; $albums.url))
				// url du composant ALB, lancer la requête
				$albums.reqALB[$albums.url]()
				// ALB a fait l'envoi
		End case 
		
	Else 
		// traiter une url envoyée par un formulaire en construction
		
		$serveur:=cs.$serveur.new()
		Case of 
				// les url XXXXX/nomClasse/nomFunction?params renvoie un text dans $result
				// la classe $pagenomClasse doit exister
			: ($serveur.TraiterURLpage($url))
				// renvoyer le résultat
				$result:=$serveur.result.resultat
				
				// les url XXXXX/nomClasse/nomFunction?params renvoie directement un text par WEB SEND TEXT
				// ici nomClasse = ACTION toujours
			: (cs.$action.new().TraiterURL($url))
				// action traitée
		End case 
End case 

    

EstDansTableauNumeric - 30/07/2026 14:49:42

Disponible via SQL

Capable de process préemptif

      #DECLARE($nomTableau : Text; $valeur : Integer)->$result : Integer
// test si $2 est dans le tableau de nom $1
var $ptr : Pointer

$ptr:=Get pointer($nomTableau)
If (Type($ptr->)=LongInt array)
	$result:=Find in array($ptr->; $valeur)
	
Else 
	$result:=-1
End if 


    

traceHandler - 08/08/2026 12:18:44

Capable de process préemptif

      cs._Trace.me.Intercepter()

    

COMPILER_WEB - 07/09/2025 12:46:08

      wwwRacineRessources:="../../"

    

InitProcessComposantThreadSafe - 08/08/2026 14:24:22

Partagée entre composants et base hôte

Capable de process préemptif

      #DECLARE($objetClass : Object; $functionID : Text; $params : Object)
// initialise un process thread-safe (la méthode a la propriété "thread-safe")
// ici on va utiliser "traceHandler" du composant
var $texte : Text

// init du worker, préemptif ou non
ON ERR CALL(Formula(traceHandler).source; ek local)

$texte:=cs.xSDK.Outils.me.getTextDeTypeProcess(Current process)
CALL WORKER(Worker Services; Formula(traceHandler); [msgk_event; msgk_log]; "Information"; Current method name; $texte; New object("nomProcess"; Current process name; "numProcess"; Current process))

If (Count parameters>2)
	Storage.Host.processUser.call(Null).ExecuterDansProcess($objetClass; $functionID; $params)
End if 

    

[class]SitesSelect - 04/06/2026 14:24:49

      Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("SitesSelect"; $requête)
	
	This.selection:=Null
	
	
	
	// ----------------------
	//MARK:PageHTML
	// ----------------------
	
Function CreerBDDmobile($tâche : cs.xSDK.Tache)->$result : Collection
	var $entité : cs.Sites
	
	// créer la sélection d'entités
	This.Créer()
	
	// créer les entités de la BDD mobile
	$result:=New collection
	If (This.selection.length>0)
		// ordonner
		This.selection:=This.selection.orderBy("nom asc")
		
		For each ($entité; This.selection) While (Not($tâche.Tuer.signaled))
			$result.push($entité.CreerBDDentity())
		End for each 
	End if 
	
    

[class]_composant - 10/08/2026 09:45:41

      property environnement : cs.xSDK.EnvironnementALV
property document : cs._document
property trace : cs._Trace
property rsc : cs.xSDK.ResourceALV
property cFct : cs._CommunFunctions
// propriétés des appelants
property RacineXML : Text
property listeDataClass : Collection


Class constructor()
	
	// environnement
	This.environnement:=cs.xSDK.EnvironnementALV.new()
	
	This.document:=cs._document.new()
	This.trace:=cs._Trace.me
	This.rsc:=cs.xSDK.ResourceALV.me
	This.cFct:=cs._CommunFunctions.me
	
	This.listeDataClass:=New collection("Personnes"; "Communes"; "Medias"; "Sites")
	
	
	//---------------------
	// MARK: Installation
	//---------------------
	
Function InitVariablesWEB()
	var $data : Object
	
	Use (Storage)
		Storage["System"]:=New shared object
		Storage["Host"]:=New shared object
		
		Storage["Application"]:=New shared object
		Storage["Processes"]:=New shared object
		
		Storage["BDD"]:=New shared object
		Storage["BDD"]:=New shared object
		Storage["ServeurHTTP"]:=New shared object
		
	End use 
	
	Use (Storage.System)
		// sert, en particulier, pour les process de type monitoring
		Storage.System.ArrêtAPP:=False
		
		Storage.System.estExécutéDansHôte:=False
		Storage.System.estExécutéDansAPP:=False
		// tout le monde n'est pas initialisé ; on ne sait pas si on est serveur Web
		Storage.System.estServeurWeb:=False
		
		Storage.System.typeApplication:=This.environnement.typeApplication()
		Storage.System.estServeur:=This.environnement.estServeur()
		Storage.System.estClient:=This.environnement.estClient()
		
	End use 
	
	Use (Storage.Host)
		Storage.Host.Session_Etat:=0
	End use 
	
	// pour la navigation
	Use (Storage.BDD)
		Storage.BDD.navAll:=New shared object
	End use 
	
	Use (Storage.ServeurHTTP)
		// fixer le nom du serveur Web
		Storage.ServeurHTTP.nom:=File(Structure file; fk platform path).name
	End use 
	
	// partager des ressources
	$data:=New object
	// fichier de ressources
	$data.chemin:=Get 4D folder(Current resources folder)+"DataServeur.xml"
	This.rsc.Inscrire(Est Ressource WEBMO; $data)
	
	
Function Installer()
	
	// installer les ressources du composant
	Partager Ressources("Installer Ressources Composant"; New object("dossier"; Get 4D folder(Current resources folder); "IDnom"; "WEBMO"))
	
	This.InstallerDonnéesHote()
	This.IntegerDansHote()
	cs._maintenance.new().ChargerBDD()
	
	Case of 
		: (Storage.System.estServeur)
			// on est sur un serveur HTTP
			
			This.InstallerServeur()
			// lancer le serveur Web
			cs.$serveur.new().Demarrer()
			
			// lancer la maintenance
			cs._maintenance.new().Démarrer()
			
		Else 
			// BDDmère ou APP ou client
	End case 
	
	
Function InstallerDonnéesHote()
	// lire les données d'installation
	var $data : Object
	var $attribut : Text
	
	Case of 
		: (Not(Storage.System.estExécutéDansHôte))
		: (Storage.System.typeApplication=ALV Client APP)
		: (Storage.System.typeApplication=4D Remote mode)
			// filtrer
		Else 
			$data:=New object
			EXECUTE METHOD(Lire Données Hôte Partagées; $data)
			
			// recopier les données reçues
			Use (Storage.Host)
				For each ($attribut; OB Keys($data))
					Storage.Host[$attribut]:=$data[$attribut]
				End for each 
			End use 
	End case 
	
	// utile pour les réactiver en mode compilé
	SET ASSERT ENABLED(Storage.Host.estDebugAPP())
	
	
Function InstallerServeur()
	// placer les pages HTML et ressources du serveur dans le dossier du site
	// rappel : ne concerne pas l'APP autonome (composant non présent)
	var $dossierDestination : Object
	
	// dossier d'installation
	$DossierDestination:=This.document.getRacineHTMLFolder()
	If ($dossierDestination=Null)
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; Current method name; "InstallerSurServeur [KO], pas de serveur Web actif")
		
	Else 
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; Current method name; "InstallerSurServeur [OK] dans "+$dossierDestination.platformPath)
		
		$dossierDestination.delete(Delete with contents)
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; Current method name; "Vidage du dossier racine [OK] "+$dossierDestination.platformPath)
		
		This.InstallerPagesDynamiques($dossierDestination)
		This.InstallerRessources($dossierDestination)
		This.InstallerRessourcesMEDIA($dossierDestination)
		This.InstallerRessourcesCARTO($dossierDestination)
		This.InstallerRessourcesSSL($dossierDestination)
		
		// installer les ressources Web albums
		cs.xALB.$albums.new().InstallerRessourcesSurServeur($dossierDestination)
	End if 
	
	
Function InstallerPagesDynamiques($dossierDestination : Object)
	// Ajouter les pages dynamiques
	Dupliquer ContenuDeDossier(Folder(Get 4D folder(Current resources folder); fk platform path).folder("TemplatesPagesWeb"); $dossierDestination)
	
	
Function InstallerRessources($dossierDestination : Object)
	var $dossier : Object
	
	$dossier:=$dossierDestination.folder("ressources")
	$dossier.create()
	
	// Ajouter les feuilles de style
	COPY DOCUMENT(Folder(Get 4D folder(Current resources folder); fk platform path).folder("css").platformPath; $dossier.platformPath; *)
	// Ajouter les ressources images
	COPY DOCUMENT(Folder(Get 4D folder(Current resources folder); fk platform path).folder("Images").folder("HTML").platformPath; $dossier.platformPath; "images"; *)
	// Ajouter les fichiers Java
	COPY DOCUMENT(Folder(Get 4D folder(Current resources folder); fk platform path).folder("javascripts").platformPath; $dossier.platformPath; "js"; *)
	
	
Function InstallerRessourcesMEDIA($dossierDestination : Object)
	var $dossierSource : 4D.Folder
	
	$dossierSource:=This.document.getDataFolder()
	$dossierSource.copyTo($dossierDestination; Localized string("149"); fk overwrite)
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Medias"; Current method name; "Dossier source "+$dossierSource.platformPath)
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Medias"; Current method name; "Dossier destination "+$dossierDestination.platformPath)
	
	
Function InstallerRessourcesCARTO($dossierDestination : Object)
	
	If (Storage.System.estExécutéDansHôte)
		// ressources OL-IGN
		// mêmes ressources que la BDD mère
		cs.xCarto.$carte.new().InstallerRessources($DossierDestination.platformPath)
	End if 
	
	
Function InstallerRessourcesSSL($dossierDestination : Object)
	// recopier les certificats pour le serveur Web
	var $DossierSource : Object
	var $dataTexte : Text:=""
	
	If (Storage.System.estExécutéDansHôte)
		
		This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/nomDossierSSL"; Is text; ->$dataTexte)
		// rappel : $dataTexte dit quel est le certificat (certifié ou autosigné) à utiliser
		// situé dans les ressources hote
		$DossierSource:=Folder(Get 4D folder(Current resources folder; *); fk platform path)
		$DossierSource:=$DossierSource.folder("SSL").folder($dataTexte)
		// dans
		$DossierDestination:=This.document.getCertificatSSLFolder()
		// copier les certificats
		COPY DOCUMENT($DossierSource.platformPath; $DossierDestination.parent.platformPath; $DossierDestination.name; *)
	End if 
	
	
Function IntegerDansHote()
	// BDDmère ou APP ou client
	// intégrer le composant dans la base hôte, pour les interfaces :
	If (Storage.System.estExécutéDansHôte)
		// dans cette version rien à faire
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Intégration"; Current method name; "Intégrer dans base hôte ; application ALV "+String(Storage.System.typeApplication))
	End if 
	
	
	//----------------------------------
	// MARK:APP
	//----------------------------------
	
Function getAppInfo($requete : Object)
	var $data : Object
	
	$data:=cs.xSDK.$composant.new()
	$data.LireInfoPlist(Folder(fk resources folder).file("Info.plist"))
	
	$requete.application:=New object
	$requete.application.name:=$data.infoPlist.query("key = :1"; "CFBundleName")[0].string
	$requete.application.id:="ALV 16800"
	$requete.application.version:=$data.infoPlist.query("key = :1"; "CFBundleVersion")[0].string
	
	
	//----------------------------------
	//MARK:BDD
	//----------------------------------
	
	
Function ChargerBDD($dossier : 4D.Folder; $nomBDD : Text)
	// charger la BDD dans le storage
	var $fichier : 4D.File
	var $dataClassNom : Text
	var $c : Collection
	
	// initialiser le storage ; on doit avoir un dossier
	If (Not($dossier.exists))
		$dossier.create()
	End if 
	
	// charger la base de données locale (sur disque) dans Storage.BDD
	// avec ce qui existe dans ce dossier
	For each ($dataClassNom; This.listeDataClass)
		$fichier:=$dossier.file($dataClassNom+".json")
		
		If ($fichier.exists)
			$c:=JSON Parse($fichier.getText(); Is collection)
			
			// stocker
			Use (Storage[$nomBDD])
				Storage[$nomBDD][$dataClassNom]:=New shared collection
			End use 
			sharedCollection($c; Storage[$nomBDD][$dataClassNom])
		End if 
		
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"+Choose($fichier.exists; " [OK]"; " [KO]"); Current method name; "Lecture du fichier de la DataClass "+$dataClassNom+" : "+$fichier.platformPath)
	End for each 
	
	
	//----------------------------------
	//MARK:Variables contexte
	//----------------------------------
	
Function InitContexte($url : Text)
	var $vs4D : Text
	
	$vs4D:="4D4D"
	Case of 
		: ($url="@albums@")
			$vs4D:="4D4Dalbum"
	End case 
	
	If (Not(Session.isGuest()))
		// cas du serveur Web avec un user connu
		$vs4D:="4D4Dprivate"
	End if 
	
	If (Not(OB Is defined(Session.storage; "HTTPvars")))
		Use (Session.storage)
			Session.storage.HTTPvars:=New shared object
		End use 
	End if 
	// stocker
	Use (Session.storage.HTTPvars)
		Session.storage.HTTPvars.vs4D:=$vs4D
	End use 
	
	
	//----------------------------------
	// MARK:Utilitaires
	//----------------------------------
	
Function InitResult($Error : Integer; $ErrorDescription : Text; $success : Boolean)->$result : Object
	
	If (Count parameters=0)
		$Error:=0
		$ErrorDescription:=""
		$success:=True
	End if 
	$result:=New object("Error"; $Error; "ErrorDescription"; $ErrorDescription; "success"; $success)
	
	$result.nomMethode:=Current method name
	
	
Function InitStructureXML($ref : Text; $nomClass : Text)
	This.RacineXML:=DOM Create XML Ref($ref)
	DOM SET XML ATTRIBUTE(This.RacineXML; "class"; $nomClass)
	
	
Function ExporterStructureXML()->$result : Text
	var $datatexte : Text
	
	DOM EXPORT TO VAR(This.RacineXML; $datatexte)
	DOM CLOSE XML(This.RacineXML)
	
	$result:=This.Nettoyer($datatexte)
	
	
Function Nettoyer($texte : Text)->$result : Text
	// structure est le mot-clé pour renvoyer le corps du texte
	$texte:=Substring($texte; Position("<"; $texte; 2; *))
	
	If ($texte="@structure@")
		$texte:=Substring($texte; Position("<"; $texte; 2; *))
		$texte:=Substring($texte; 1; Position("</structure"; $texte)-1)
	End if 
	
	$result:=Replace string($texte; "\r\r"; "")
	
	
Function InitReponseRequete()->$result : Object
	$result:=New object("waiting"; False; "success"; True; "message"; "")
	
	
    

[class]Communes - 30/10/2025 17:08:08

      // attributs de la classe
property nom : Text
property ChampsExposés : Collection
property blason : Picture

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	
	Super("Communes"; $IDentité)
	
	
	
Function FixerParametresListe()->$result : Object
	$result:=New object
	$result.DataClassNom:="Communes"
	$result.Requete:="id>0"
	$result.nomFunction:="AfficherFORMdetail"
	$result.nomFORMlist:="detailSimple"
	
	
Function CreerBDDentity()->$result : Object
	$result:=New object
	$result.IDunique:=This.IDunique
	
	$result.Elementliste:=This.Elementliste()
	// les champs calculés
	Super.CreerBDDentity($result; ["Champ_1"; "Champ_2"; "Champ_3"; "Champ_4"; "Champ_5"; "Champ_boutonLibelle_1"; "Champ_boutonLibelle_2"; "Champ_titreIconed_1"; "Champ_titreIconed_2"; "Champ_titreIconedLinked_1"; "Champ_titreIconedLinked_2"; "Champ_titreIconedLinked_3"])
	
	
Function labelDataClass()->$result : Text
	$result:=Super.labelDataClass("la ")
	
	
Function Administration()->$result : Text
	// département, région et pays
	$result:=This.dataClass.leDepartement.Libellé(New object("Options"; 0x00C90000))
	
	
	// ----------------------
	//MARK:Sélection
	// ----------------------
	
Function LesEvents()->$result : Object
	var $c : Collection
	
	$c:=This.dataClass.LesEvents()
	$result:=cs.EventsSelect.new($c)
	$result.Créer()
	
	
Function LesMedias()->$result : Object
	// créer la collection des medias
	$result:=cs.MediasSelect.new(This.dataClass.LesMedias())
	$result.Créer()
	
	
Function LesSites()->$result : Object
	// créer la collection des medias
	$result:=cs.SitesSelect.new(This.dataClass.LesSites())
	$result.Créer()
	
	
	// ----------------------
	//MARK: FORM Liste
	// ----------------------
	
Function Elementliste()->$result : Text
	$result+="<article id='"+This.IDunique+"' class='element-listeEntity' nom='"+This.nom+"'>"
	
	$result+="<article>"
	$result+=This.LibelléLié(This._tagUrl+"WebDATA/<!--#4DEVAL $1.nomFunction-->?<!--#4DEVAL $1.nomFORMlist-->?Communes?"+This.IDunique; New object("Options"; 0))
	$result+="</article>"
	
	$result+="<article><span>"
	$result+=This.Administration()
	$result+="</span></article>"
	
	$result+="</article>"
	
	
	// ----------------------
	//MARK: FORM Detail
	// ----------------------
	
Function Champ_1()->$result : Text
	$result:=This.getBlasonURL(This.nomFichierIcone)
	
	
Function Champ_2()->$result : Text
	$result:=This.nom
	
	
Function Champ_3()->$result : Text
	$result:=This.Administration()
	
	
Function Champ_4()->$result : Text
	// rappel : un lieu type 60700 existes pour toutes les communes
	// il sert de commentaire à la commune
	var $entité : cs.Lieux
	var $ID : Integer
	
	$result:=""
	
	$ID:=This.dataClass.LieuParDefaut()
	$entité:=cs.Lieux.new($ID)
	$result:="<p>"+$entite.commentaire+"</p>"
	
	
Function Champ_5()->$result : Text
	var $selection : cs.DicoDesNomsSelect
	
	$selection:=This.LesEvents().LesProtagonistes().LesPatronymes()
	$result:=$selection.Ecrire()
	
	
Function Champ_boutonLibelle_1()->$result : Text
	$result:="16829.svg"
	
	
Function Champ_boutonLibelle_2()->$result : Text
	$result:=Localized string("145")
	
	
Function Champ_titreIconed_1()->$result : Text
	$result:="16830.svg"
	
	
Function Champ_titreIconed_2()->$result : Text
	$result:=Localized string("151")
	
	
Function Champ_titreIconedLinked_1()->$result : Text
	$result:="16831.svg"
	
	
Function Champ_titreIconedLinked_2()->$result : Text
	$result:=Localized string("152")
	
	
Function Champ_titreIconedLinked_3()->$result : Text
	$result:="16827.svg"
	
	
	// -----------------------------
	// MARK:Maintenance
	// -----------------------------
	
Function CréerBlasonMobile($dossier : 4D.Folder)
	var $pict : Picture
	var $cheminFichier : Text
	
	$pict:=This.blason
	If (Picture size($pict)=0)
		READ PICTURE FILE(Folder(Get 4D folder(Current resources folder); fk platform path).folder("Images").folder("HTML").file("blason_ico.png").platformPath; $pict)
	End if 
	
	// * créer son imagette Webinée
	// largeur max de l'image, hauteur max de l'image
	This.cFct.RetaillerImage(->$pict; WebMob Icon largeur; WebMob Icon hauteur)
	
	// ajouter le fichier image au dossier
	$cheminFichier:=$dossier.file(This.nomFichierIcone).platformPath
	WRITE PICTURE FILE($cheminFichier; $pict; WebMob Icon format)
	
	
Function get nomFichierIcone()->$result : Text
	$result:="blason_ico.png"
	
	Case of 
		: (Not((Storage.System.estExécutéDansHôte)))
		: (Picture size(This.blason)=0)
		Else 
			
			$result:=String(This.IDcodé())+"_ico"+WebMob Icon format
	End case 
	
	
Function getBlasonURL($nomFichier : Text)->$result : Text
	$result:="../../ressources/Images/mobileData/"
	
	Case of 
		: (Not(Storage.System.estExécutéDansHôte))
		: (Picture size(This.blason)=0)
		Else 
			
			$result:=This.document.getDataURL()
	End case 
	$result+=Localized string("150")+"/"+$nomFichier
	
	
    

[class]_document - 09/08/2026 10:02:15

      Class constructor()
	
	
	
Function getAppWorkSpaceFolder()->$result : Object
	$result:=Storage.Host.$document.call(Null).GetAppWorkSpace()
	
	
Function getDataServeurFolder()->$result : Object
	$result:=Storage.Host.$document.call(Null).getDataServeurFolder()
	
	
	//------------------
	//mark: installation
	//------------------
	
Function getSessionFolder()->$result : Object
	// le dossier de la session Web
	$result:=This.getRacineHTMLFolder().folder(Localized string("129")).folder(Session.id)
	
	
Function getRacineHTMLFolder()->$result : 4D.Folder
	var $texte : Text:=""
	
	Case of 
		: (Not(cs.xSDK.ResourceALV.me.SetVariable(Est Ressource WEBMO; "Serveur_web/nomDossier_RacineWeb"; Is text; ->$texte)))
		: (Storage.System.typeApplication=ALV Client APP)
			// filtrer (pas concerné pour l'instant)
			
		: (Storage.System.typeApplication=ALV Serveur APP)
			// ouverture de l'APP serveur
			$result:=Folder(Structure file(*); fk platform path).parent.parent.parent.parent.folder($texte)
			
		: (Storage.System.typeApplication=ALV Serveur HTTP)
			// ouverture du projet avec un 4D serveur non compilé
			// dossier dans les document utilisateur
			$result:=Folder(fk documents folder).parent.folder("Sites").folder($texte)
			
		: (Storage.System.typeApplication=ALV BDD mère)
			// composant dans la base mère
			// dossier dans les document utilisateur
			$result:=Folder(fk documents folder).parent.folder("Sites").folder($texte)
			
		Else 
			// composant exécuté seul
			$result:=Folder(Structure file(*); fk platform path).parent.parent.parent.folder($texte)
	End case 
	
	// au besoin, créer le dossier
	$result.create()
	
	
Function getCertificatSSLFolder()->$result : 4D.Folder
	// fixe le chemin du dossier des certificats SSL du serveur Web
	var $texte : Text:=""
	
	Case of 
		: (Not(cs.xSDK.ResourceALV.me.SetVariable(Est Ressource WEBMO; "Serveur_web/nomDossier_CertificatSSL"; Is text; ->$texte)))
		: (Storage.System.typeApplication=ALV Client APP)
			// hors sujet
			$result:=Null
			
		Else 
			// serveurs APP ou HTTP
			// le dossier des cert est au même niveau que le dossier racine HTML
			$result:=This.getRacineHTMLFolder().parent.folder($texte)
			Case of 
				: ($result.exists)
					// c'est bon
				: ($result.create())
					// c'est créé
				Else 
					$result:=Null
			End case 
	End case 
	
	
Function CertInformations()->$result : Object
	var $params : Object
	
	$params:=New object("CertificatSSLFolderPath"; This.getCertificatSSLFolder().platformPath)
	cs.xSSL.$lectureCERT.new().getInformations($params)
	$result:=$params.infos
	
	
Function getMobileWebFolder()->$result : 4D.Folder
	Case of 
		: (Not(Storage.System.estExécutéDansHôte))
			// exécution en local ; utiliser un dossier de test
			$result:=Folder(fk resources folder)
			
		: (Storage.System.typeApplication=ALV Serveur APP)
			// mode opérationnel
			$result:=Folder(fk data folder; *)
			
		: (Storage.System.typeApplication=ALV BDD mère)
			// test
			$result:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug")
			
		Else 
			// ??
	End case 
	$result:=$result.folder("mobileWeb")
	// au cas ou
	$result.create()
	
	
	// -----------------------------
	// MARK:BDD
	// -----------------------------
	
Function getBDDFolder()->$result : 4D.Folder
	// installation : dossier des fichiers BDD pour le composant (nomDossier ID_155)
	Case of 
		: (Storage.System.typeApplication=ALV Serveur APP)
			// cas opérationnel
			$result:=This.getDataServeurFolder().folder(Localized string("156"))
			$result.create()
			
		: (Storage.System.typeApplication=ALV BDD mère)
			// pour test local
			$result:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug").folder(Localized string("156"))
			$result.create()
			
		Else 
			// rien
			$result:=Null
	End case 
	
	
	// -----------------------------
	// MARK:Medias
	// -----------------------------
	
Function getMediasFolder()->$result : 4D.Folder
	$result:=This.getDataFolder().folder(Localized string("147"))
	// au cas ou
	$result.create()
	
	
Function getVignettesFolder()->$result : 4D.Folder
	$result:=This.getDataFolder().folder(Localized string("148"))
	// au cas ou
	$result.create()
	
	
Function getBlasonsFolder()->$result : 4D.Folder
	$result:=This.getDataFolder().folder(Localized string("150"))
	// au cas ou
	$result.create()
	
	
Function getDataFolder()->$result : 4D.Folder
	// installation : dossier des medias pour le composant (nomDossier ID_155)
	Case of 
		: (Storage.System.typeApplication=ALV Serveur APP)
			// cas opérationnel
			$result:=This.getDataServeurFolder().folder(Localized string("155"))
			$result.create()
			
		: (Storage.System.typeApplication=ALV Serveur HTTP)
			// 4D Server pour debug
			$result:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug").folder(Localized string("155"))
			$result.create()
			
		: (Storage.System.typeApplication=ALV BDD mère)
			// pour test local
			$result:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug").folder(Localized string("155"))
			$result.create()
			
		Else 
			// rien
			$result:=Null
	End case 
	
	
Function getServeurDataFolder()->$result : 4D.Folder
	// serveur Web : en opérationnel, racine du site
	$result:=This.getRacineHTMLFolder().folder(Localized string("149"))
	// au cas ou
	$result.create()
	
	
	// -----------------------------
	// MARK:Albums
	// -----------------------------
	
Function getWEBalbumsFolder()->$result : Object
	// (v6.4.6 dans le dossier Web)
	$result:=This.getRacineHTMLFolder().folder("Albums")
	
	
	// -----------------------------
	// MARK:URL
	// -----------------------------
	
Function getVignetteNom($ID : Integer)->$result : Text
	$result:="vignette_"+String($ID; "00000")+WebMob Icon format
	
	
Function getVignetteURL($ID : Integer)->$result : Text
	If (Storage.System.estExécutéDansHôte)
		$result:=This.getDataURL()
		
	Else 
		$result:="../../ressources/Images/mobileData/"
	End if 
	$result+=Localized string("148")+"/"+This.getVignetteNom($ID)
	
	
Function getDataURL()->$result : Text
	$result:="../../"+Localized string("149")+"/"
	
	
	// -----------------------------
	// MARK:Notes
	// -----------------------------
	
Function getBDDnotesFolder($nomBDD : Text)->$result : Object
	// nom du fichier .4DB = nom dossier sans le séparateur final
	
	Case of 
		: (Not(Storage.System.estExécutéDansHôte))
			// exécution en local ; utiliser une BDD de test
			$result:=Folder(Structure file(*); fk platform path)
			$result:=$result.parent.parent.parent.folder("Test MOB").folder($nomBDD+".4dbase")
			$result:=$result.file($nomBDD)
			
		: (Storage.System.typeApplication=ALV Client APP)
		: (Storage.System.typeApplication=ALV BDD mère)
			// bof
			$result:=Folder(Structure file(*); fk platform path)
			$result:=$result.parent.parent.parent.folder("Composants").folder("Matrices").folder("Test MOB").folder($nomBDD+".4dbase")
			$result:=$result.file($nomBDD)
			
		Else 
			// serveur ALV
			$result:=This.getAppWorkSpaceFolder().folder(Localized string("112")).folder($nomBDD+".4dbase")
			$result.create()
			$result:=$result.file($nomBDD)
	End case 
	
    

[class]$session - 05/08/2026 10:03:15

      property trace : cs._Trace

singleton Class constructor()
	
	var parameters : Object
	This.trace:=cs._Trace.me
	
	
	
	// -----------------------------
	// MARK:Validation
	// -----------------------------
	
Function WEBmobileEmailChecker($request : Object)->$result : Object
	var $session : Object
	
	$result:=cs._composant.new().InitReponseRequete()
	
	//initialize shared variables
	This._InitSession()
	
	//check if the parameter file exists
	If (parameters.success)
		//get user session
		$session:=Storage.pendingSessions[$request.session.id]
		//check if the session exists
		If ($session#Null)
			//compare the current timestamp with that when creating the session
			If ((Milliseconds-$session.time)<=parameters.timeout)
				//if the difference is less than the value of the settings file: message to wait before sending an email again
				$result.statusText:=parameters.message.waitSendMailConfirmationMessage
				$result.success:=True
			Else 
				//the timeout value is exceeded and send a new email
				This.SendEmail($request; $result)
			End if 
		Else 
			//send a confirmation email the 1st time
			This.SendEmail($request; $result)
		End if 
		
		// put the session on pending
		$result.verify:=True
		
	Else 
		$result.success:=False
		$result.statusText:="Authentication failed, please contact the administrator for further assistance"
		$result.errors:=New collection("Configuration to send validation email is not available"; parameters.statusText)
	End if 
	
	
Function SendEmail($request : Object; $response : Object)
	var $htmlContent; $value; $stringError : Text
	var $status; $template; $o; $transporter; $result; $session : Object
	var $minutes : Integer
	
	$stringError:=""
	
	//get the path to the html email confirmation template
	$template:=Folder(fk resources folder).folder("WEB mobile Server").file(parameters.template.emailToSend)
	//check if the file exists
	If (Asserted($template.exists; "Missing file "+$template.platformPath))
		//method to send email
		$o:=New object
		$o.smtp:=New object
		$o.smtp.host:=parameters.smtp.host
		$o.smtp.acceptUnsecureConnection:=False
		$o.smtp.port:=Choose($o.smtp.acceptUnsecureConnection; 465; 587)
		$o.smtp.user:=parameters.smtp.user
		$o.smtp.password:=parameters.smtp.password
		$o.smtp.keepAlive:=True
		$o.smtp.connectionTimeOut:=30
		$o.smtp.sendTimeOut:=100
		
		$o.mail:=New object
		$o.mail.from:=parameters.smtp.from  //get "from" value from settings file
		$o.mail.to:=$request.email  //get "email" value from settings file
		$o.mail.subject:=parameters.emailSubject  //get "emailSubject" value from settings file
		$htmlContent:=$template.getText()
		//create server address for session activation
		//scheme: http or https
		// hostname: 172.0.0.1
		// port: 80
		// path: activation
		// session.id: ID de la session
		// otherParameters: More parameters
		$value:=parameters.activation.scheme+"://"+parameters.activation.hostname+":"+parameters.activation.port+"/"+parameters.activation.path+"?token="+$request.session.id+"?"+parameters.activation.otherParameters
		$htmlContent:=Replace string($htmlContent; "{{url}}"; $value)
		//convert milliseconde to Minute 
		$minutes:=(parameters.timeout/60000)
		//display the timeout value in the email
		$htmlContent:=Replace string($htmlContent; "{{expirationminutes}}"; String($minutes))
		$o.mail.htmlBody:=$htmlContent
		$transporter:=SMTP New transporter($o.smtp)
		$status:=$transporter.send($o.mail)
		//check if the email is sent
		
		If ($status.success)
			//get user session
			$session:=Storage.pendingSessions[$request.session.id]
			Use (Storage.pendingSessions)
				If ($session#Null)
					//update the timestamp if we send a new email after the expiration of the 1st connection
					$session.time:=Milliseconds
					
				Else 
					//add new session
					Storage.pendingSessions[$request.session.id]:=New shared object(\
						"team"; String($request.team.id); \
						"application"; $request.application.id; \
						"time"; Milliseconds)
				End if 
			End use 
		End if 
		
		$result:=New object("status"; $status.success; "statusText"; $status.statusText; "error"; $stringError)
	Else 
		$result:=New object("status"; False; "statusText"; $template.platformPath+" is not found"; "error"; $stringError)
	End if 
	
	If ($result.status)
		$response.statusText:=parameters.message.successConfirmationMailMessage
	Else 
		$response.statusText:=$result.statusText
	End if 
	
	$response.emailInfo:=$result
	$response.success:=$result.status
	
	
	// -----------------------------
	// MARK:Activation
	// -----------------------------
	
Function WebMobileActiveSession($url : Text)->$result : Object
	
	This._InitSession()
	
	//check if the parameter file exists
	If (parameters.success)
		If (Position(parameters.activation.path; $url)=2)
			$result:=This.ActivateSessions($url)
		End if 
		
	Else 
		WEB SEND TEXT("Please contact administrator to unlock your session")
		$result:=New object
		$result.success:=False
		$result.errors:=New collection("Configuration to active session is not available"; parameters.statusText)
	End if 
	
	
Function ActivateSessions($url : Text)->$result : Object
	var $template; $fichier : 4D.File
	var $sessionObjet; $session : Object
	var $bodyMessage; $htmlContent; $token : Text
	
	//get the path to the html email activation template
	$template:=Folder(fk resources folder).folder("WEB mobile Server").file(parameters.template.emailConfirmActivation)
	
	//return the information in to the database method
	$result:=New object("success"; False; "message"; ""; "error"; "")
	
	//check if the file exists
	If ($template.exists)
		//get token value from the url
		$token:=This.getTokenFromURL($url)
		
		//get user session
		$session:=Storage.pendingSessions[$token]
		//check if the session exists
		If ($session#Null)
			//compare the current timestamp with that when creating the session
			If ((Milliseconds-$session.time)<parameters.timeout)
				//if the difference is less than the value of the settings file: active the session
				// get the session file
				$fichier:=This.getSessionFile($token)
				If ($fichier#Null)
					$sessionObjet:=JSON Parse($fichier.getText())
					// update value
					$sessionObjet.status:="accepted"
					$fichier.setText(JSON Stringify($sessionObjet))
					
					Use (Storage.pendingSessions)
						//delete session
						OB REMOVE(Storage.pendingSessions; $token)
					End use 
					
					$bodyMessage:=parameters.message.successActiveSessionsMessage
					$result.success:=True
				End if 
				
			Else 
				$bodyMessage:=parameters.message.expireActiveSessionsMessage
			End if 
		Else 
			$bodyMessage:=parameters.message.expireActiveSessionsMessage
		End if 
	Else 
		$result.error:="Missing file "+$template.platformPath
	End if 
	$result.message:=$bodyMessage
	
	//write the value of $bodyMessage in the web page
	$htmlContent:=$template.getText()
	$htmlContent:=Replace string($htmlContent; "{{message}}"; $bodyMessage)
	WEB SEND TEXT($htmlContent)
	
	
	// -----------------------------
	// MARK:Mobile session
	// -----------------------------
	
Function InitialiserSessionInfos($infos : Object)
	var $data : Object
	var $result : Boolean
	
	This.InitialiserSession()
	$infos.userInfo.estGuest:=($infos.userInfo.IDfamille=-1008)  // famille des Invités
	
	// enregistrer les infos dans la session
	sharedObject($infos; Session.storage.mobileWebInfo)
	sharedObject($infos.userInfo; Session.storage.userInfo)
	
	// fixer les privilèges (on n'est plus Guest)
	$data:=New object
	$data.userName:=Session.storage.userInfo.userName
	// ne PAS utiliser les roles (pb dans fichier .json
	$data.privileges:=New collection("ReadRecords")  // tout le monde pareil, sinon lire les userInfo (attribut de UtilisateursALV) 
	$result:=Session.setPrivileges($data)
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Privilèves fixés "+Choose($result; "[OK]"; "[KO]"); Current method name; "Session id "+Session.id+" - idleTimeout "+String(Session.idleTimeout)+" mn - expirationDate "+String(Session.expirationDate)+" (voir détails dans logs)")
	This.trace.EnvoyerMessages([msgk_log]; "Privilèves fixés "+Choose($result; "[OK]"; "[KO]"); Current method name; "Session.storage.mobileWebInfo : "+JSON Stringify(Session.storage.mobileWebInfo))
	This.trace.EnvoyerMessages([msgk_log]; "Privilèves fixés "+Choose($result; "[OK]"; "[KO]"); Current method name; "Session.storage.userInfo : "+JSON Stringify(Session.storage.userInfo))
	
	// initialiser les variables session
	This.InitUserData()
	
	
Function setSessionFile($nomFichier : Text; $request : Object; $userInfo : Object)
	var $template; $texte : Text
	
	$template:=Folder(fk resources folder).folder("TemplatesMobileWeb").file("TemplateSession.json").getText()
	PROCESS 4D TAGS($template; $texte; $request; $userInfo)
	$texte:=Replace string($texte; ",}"; "}")  // nettoyer la fin des boucles 4DEACH
	
	cs._document.new().getMobileWebFolder().file($nomFichier).setText($texte)
	
	
Function getSessionObjet($nomFichier : Text)->$result : Object
	// renvoie le contenu du fichier $nomFichier situé dans le dossier des sessions mobileWEB
	var $fichier : 4D.File
	
	$result:=Null
	
	$fichier:=This.getSessionFile($nomFichier)
	
	If ($fichier#Null)
		$result:=JSON Parse($fichier.getText(); Is object)
	End if 
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Session MOB demandée "+Choose($result=Null; "[KO]"; "[OK]"); Current method name; $nomFichier+", (info : session.id = "+Session.id)
	This.trace.EnvoyerMessages([msgk_log]; "Session MOB demandée "+Choose($result=Null; "[KO]"; "[OK]"); Current method name; JSON Stringify($result))
	
	
Function getSessionFile($nomFichier : Text)->$result : 4D.File
	// renvoie le contenu du fichier $nomFichier situé dans le dossier des sessions mobileWEB
	var $fichier : 4D.File
	var $sessions : Collection
	
	$result:=Null
	
	// lister toutes les sessions disponibles
	$sessions:=cs._document.new().getMobileWebFolder().files(fk ignore invisible)
	
	For each ($fichier; $sessions) While ($result=Null)
		// chercher notre session
		If ($fichier.name=$nomFichier)
			// trouvé
			$result:=$fichier
		End if 
	End for each 
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Fichier session MOB demandé "+Choose($result=Null; "[KO]"; "[OK]"); Current method name; $nomFichier+" -"+String($sessions.length)+" sessions MOB enregistrées), (voir session dans logs)")
	
	
Function existeSession()->$result : Boolean
	$result:=(Not(Session.isGuest()))
	
	
Function getSessionDeCookie($data : Object)->$result : Boolean
	// renvoie vrai si le cookie contient un ID de session active
	var $token : Text
	var $session : Object
	
	$result:=False
	
	// lire l'id de session dans le cookie
	// il existe si la session a été créée par une activationByEmail
	$token:=cs.$cookie.new().getMobileWebID()
	If ($token#"")
		// il existe
		// lire le fichier session correspondant
		$session:=This.getSessionObjet($token)
		$result:=($session#Null)
		
		If ($result)
			// il existe, cas normal (par exemple, pas de ré initialisation du serveur...)
			// renvoyer les infos de la session
			$data.infos:=$session
		End if 
	End if 
	
	
Function getSessionDeBody($data : Object)->$result : Boolean
	var $corps; $token : Text
	var $params : Object
	
	$result:=False
	
	WEB GET HTTP BODY($corps)
	
	If ($corps#"")
		Try
			$params:=JSON Parse($corps; Is object)
		Catch
			// on a reçu autre chose qu'un objet
			$params:=New object
		End try
		
		If (OB Is defined($params; "sessionID"))
			$token:=$params.sessionID
			
			$result:=This.getSessionDeToken($token; $data)
		End if 
	End if 
	
	
Function getSessionDeToken($token : Text; $data : Object)->$result : Boolean
	// renvoyer les infos dans .infos
	var $session : Object
	
	$result:=($token#"")
	If ($result)
		// il existe
		// lire le fichier session correspondant
		$session:=This.getSessionObjet($token)
		$result:=($session#Null)
		
		If ($result)
			// il existe, cas normal (par exemple, pas de ré initialisation du serveur...)
			// renvoyer les infos de la session
			$data.infos:=$session
		End if 
	End if 
	
	
Function InitialiserSession()
	If (Not(OB Is defined(Session.storage; "userInfo")))
		Use (Session.storage)
			Session.storage.userInfo:=New shared object
		End use 
	End if 
	
	If (Not(OB Is defined(Session.storage; "mobileWebInfo")))
		Use (Session.storage)
			Session.storage.mobileWebInfo:=New shared object
		End use 
	End if 
	
	
	// -----------------------------
	// MARK:User Data
	// -----------------------------
	
	
Function InitUserData()
	var $data : Object
	// initialiser les variables session
	
	This.InitUserParams()
	This.InitHTTPvars()
	
	// créer le dossier des fichiers temporaires de la session
	$data:=cs._document.new().getSessionFolder()
	$data.create()
	
	
Function InitUserParams()
	// données de session
	// utilisées au gré de la navigation de l'utilisateur
	
	Use (Session.storage)
		Session.storage.UserParams:=New shared object
	End use 
	// initialiser les données de navigation
	cs.$pageWebNAV.new().Initialiser()
	
	
Function InitHTTPvars()
	var $data : Object
	// .HTTPvars contient 
	// - des variables, de nom wwwXXX, utilisées dans les pages Web de recherche ou de saisie pour recupérer les données utilisateur
	// RAPPEL : cette liste sert à initialiser TOUTES les variables process ; toutes ces variables doivent être dans cette liste
	// - des variables autres, de nom YYY, utilisées dans les templates de page Web pour la construction des pages
	
	Use (Session.storage)
		Session.storage.HTTPvars:=New shared object
	End use 
	$data:=Session.storage.HTTPvars
	Use ($data)
		// depuis v5.2 les 4DURL sont de la forme /4Dxxx/xxx/yyy
		$data.wwwRacineRessources:="../../"
		// fixer le contexte pour le composant CARTO
		$data.wwwContexteWeb:="ServeurWeb"
	End use 
	
	
	// -----------------------------
	// MARK:Utilitaires
	// -----------------------------
	
Function _InitSession()
	//create a share collection accessible from several processes
	If (Storage.pendingSessions=Null)
		Use (Storage)
			Storage.pendingSessions:=New shared object
		End use 
	End if 
	//create an object accessible from the current process
	If (parameters=Null)
		parameters:=This.getSetting()
	End if 
	
	
Function getSetting()->$result : Object
	var $settingFolder : 4D.Folder
	var $template; $file : 4D.File
	var $info : 4D.WebServer
	
	$settingFolder:=Folder(fk resources folder).folder("WEB mobile Server")
	$template:=$settingfolder.file("settings.json")
	
	If ($template.exists)
		$result:=JSON Parse($template.getText())
		
		If ($result.activation=Null)
			$result.activation:=New object
		End if 
		
		$info:=WEB Server(Web server database)
		If ($result.activation.scheme=Null)
			$result.activation.scheme:=Choose($info.security.HTTPSEnabled; "https"; "http")
		End if 
		
		If ($result.activation.hostname=Null)
			$result.activation.hostname:=$info.options.webIPAddressToListen[0]
		End if 
		
		If ($result.activation.port=Null)
			$result.activation.port:=String(Choose($info.security.HTTPSEnabled; $info.options.webHTTPSPortID; $info.options.webPortID))
		End if 
		
		If ($result.activation.path=Null)
			$result.activation.path:="activation"
		End if 
		
		If ($result.activation.otherParameters=Null)
			$result.activation.otherParameters:=""
		End if 
		
		If ($result.emailSubject=Null)
			$result.emailSubject:="Application Name: Sign in confirmation"
		End if 
		
		If ($result.timeout=Null)
			$result.timeout:=300000
		End if 
		
		If ($result.message=Null)
			$result.message:=New object
		End if 
		
		If ($result.message.successConfirmationMailMessage=Null)
			$result.message.successConfirmationMailMessage:="Verify your email address"
		End if 
		
		If ($result.message.waitSendMailConfirmationMessage=Null)
			$result.message.waitSendMailConfirmationMessage:="The mail is already sent thank you to wait before sending again"
		End if 
		
		If ($result.message.successActiveSessionsMessage=Null)
			$result.message.successActiveSessionsMessage:="You are successfully authenticated"
		End if 
		
		If ($result.message.expireActiveSessionsMessage=Null)
			$result.message.expireActiveSessionsMessage:="This email confirmation link has expired!"
		End if 
		
		If ($result.template=Null)
			$result.template:=New object
		End if 
		
		If ($result.template.emailToSend=Null)
			$result.template.emailToSend:="ConfirmMailTemplate.html"
		End if 
		
		$file:=$settingFolder.file($result.template.emailToSend)
		If (Not($file.exists))
			Folder(fk resources folder).folder("TemplatesMobileWeb").file("ConfirmMailTemplate.html").copyTo($settingFolder)
		End if 
		
		If ($result.template.emailConfirmActivation=Null)
			$result.template.emailConfirmActivation:="ActiveSessionTemplate.html"
		End if 
		
		$file:=$settingFolder.file($result.template.emailConfirmActivation)
		If (Not($file.exists))
			Folder(fk resources folder).folder("TemplatesMobileWeb").file("ActiveSessionTemplate.html").copyTo($settingFolder)
		End if 
		
		$result.success:=True
		
	Else 
		
		$result:=New object("success"; False; "statusText"; "Missing configuration files "+$template.platformPath)
		
		If (Not($settingFolder.exists))
			$settingFolder.create()
		End if 
		
		$file:=$settingFolder.file("settings.sample.json")
		If (Not($file.exists))
			Folder(fk resources folder).folder("TemplatesMobileWeb").file("settings.sample.json").copyTo($settingFolder)
		End if 
	End if 
	
	
Function getTokenFromURL($url : Text)->$result : Text
	var $texte : Text
	
	$texte:=Split string($url; "?")[1]
	$result:=Substring($texte; Position("="; $texte)+1)
	
	
Function getTokenFromSession()->$result : Text
	$result:=""
	Case of 
		: (Not(OB Is defined(Session.storage; "mobileWebInfo")))
		: (Not(OB Is defined(Session.storage.mobileWebInfo; "session")))
		: (Not(OB Is defined(Session.storage.mobileWebInfo.session; "id")))
		Else 
			$result:=Session.storage.mobileWebInfo.session.id
	End case 
	
    

[class]$pageWebLOGIN - 31/07/2026 09:05:26

      property check : cs.$authentificationEmail
property sessionUser; Utilisateur : cs.UtilisateursALV

Class extends $pageWeb

Class constructor()
	
	Super()
	
	This.check:=cs.$authentificationEmail.new()
	
	
	
	//----------------------------------
	// MARK:Traitement url
	//----------------------------------
	
Function seConnecter()
	// tout commence ici
	
	TEXT TO DOCUMENT(cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug/"+Current method name).file(Timestamp+"1.json").platformPath; "mobileWebInfo est null : "+String(Session.storage.mobileWebInfo=Null))
	TEXT TO DOCUMENT(cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug/"+Current method name).file(Timestamp+"2.json").platformPath; "mobileWebInfo est null : "+String(Session.storage.mobileWebInfo=Null)+Char(CR ASCII code)+JSON Stringify(cs.$cookie.me.Lire(); *))
	
	Case of 
		: (This.ReConnexion())
			// oui ; on a envoyé une page Web
			
		: (Not(This.ValiderDevice()))
			// erreur traitée
			
		: (This.paramsBody=Null)
			// se connecter depuis l'extérieur
			// envoyer la page de connexion
			This.Envoyer("LoginByEmail.shtml"; "accueil")
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Connexion"; Current method name; "Pas de paramètre de connexion ; la page 'LoginByEmail' a été envoyée")
			
		: (This.Authentifier(This.paramsBody).waiting)
			// une demande de confirmation d'authentication a été envoyée ; on attend la réponse
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Connexion"; Current method name; "Une adresse eMail a été traitée ; on attend la confirmation")
			
		: (This.Authentifier(This.paramsBody).success)
			// rappel : la page NAV et appelée par le javascript
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Connexion"; Current method name; "L'eMail a été traité ; la page a dû être envoyée")
			
		Else 
			// oups !
			This.Envoyer("LoginByEmail.shtml"; "accueil")
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Connexion"; Current method name; "Erreur ; adresse eMail non traitée, session mobileWeb non créée")
	End case 
	
	
Function Settings()
	This.Envoyer("Settings.shtml"; "accueil")
	
	
	//----------------------------------
	// MARK:Element HTML
	//----------------------------------
	
Function EcrireUserAuthentification()
	var $texte; $ElémentXML; $adresseEmail : Text
	
	This.getUser()
	
	This.InitStructureXML("article"; "saisieEmail")
	
	// email saisi
	$texte:="EmailSaisi"
	$ElémentXML:=DOM Create XML element(This.RacineXML; "p"; "class"; "elementLibelleSaisieEmail")
	$ElémentXML:=DOM Create XML element(This.RacineXML; "p/label"; "for"; $texte)
	DOM SET XML ELEMENT VALUE($ElémentXML; "EMAIL")
	
	$adresseEmail:=Choose(OB Is defined(Session.storage; "mobileWebInfo"); Session.storage.mobileWebInfo.email; "")
	$ElémentXML:=DOM Create XML element(This.RacineXML; "input"; "class"; "elementSaisieEmail"; "value"; $adresseEmail; "type"; "email"; "id"; $texte; "name"; $texte; "pattern"; "+@example\\.com"; "size"; "30"; "required"; "")
	
	$texte:=This.ExporterStructureXML()
	This.result.resultat:=Char(1)+$texte
	
	
Function getUser()
	This.sessionUser:=cs.UtilisateursALV.new(Session.userName)
	
	
	//----------------------------------
	// MARK:LOGIN par eMail
	//----------------------------------
	
Function Authentifier($request : Object)->$result : Object
	var $response; $data : Object
	
	// versionner
	This.getAppInfo($request)
	
	// renseigner le context
	This.getWebBrowserInfo($request)
	
	Case of 
		: (cs.$authentificationGuest.new().Authentification($request))
			// utilisateur étranger authentifié 
			$response:=This.InitReponseRequete()
			WEB SEND TEXT(JSON Stringify($response))
			
			
		: (This._finaliser())
			// le processus d'authentication est terminé, fixer les privilèges (on n'est plus Guest)
			
			Use (Session.storage.UserParams)
				Session.storage.UserParams.EtatNavigation:="Personnes"
			End use 
			
			// fixer les privilèges
			$data:=New object
			$data.userName:=Session.storage.userInfo.userName
			// ne PAS utiliser les roles (pb dans fichier .json
			$data.privileges:=New collection("ReadRecords")  // tout le monde pareil, sinon lire les userInfo (attribut de UtilisateursALV) 
			Session.setPrivileges($data)
			
			$response:=This.InitReponseRequete()
			WEB SEND TEXT(JSON Stringify($response))
			
		Else 
			// authentifier un utilisateur ALV
			
			// vérifier si l'adresse eMail est un identifiant connu et envoyer l'eMail
			$response:=This.check.Authentification($request)
			$response.message:=$response.statusText
			$response.waiting:=True & ($response.success)
			// si OK un eMail a été envoyé
			
			// ici on envoie .waiting= true et .succcess function d'état du processus
			WEB SEND TEXT(JSON Stringify($response))
	End case 
	$result:=$response
	
	
Function _finaliser()->$result : Boolean
	// vérifier que la session WEBMO $request.sessionID a été activée
	
	$result:=False
	
	Case of 
		: (Not(OB Is defined(Session.storage; "mobileWebInfo")))
		: (Not(OB Is defined(Session.storage.mobileWebInfo; "status")))
		: (Session.storage.mobileWebInfo.status#"accepted")
		Else 
			$result:=True
	End case 
	
	
Function ValiderDevice()->$result : Boolean
	var $template : 4D.File
	var $htmlContent : Text
	
	$result:=This.isValidDevice()
	
	If (Not($result))
		$template:=Folder(fk resources folder).folder("TemplatesMobileWeb").file("ErreurServeur.html")
		
		$htmlContent:=$template.getText()
		$htmlContent:=Replace string($htmlContent; "{{message}}"; Localized string("1022"))
		WEB SEND TEXT($htmlContent)
	End if 
	
	
    

[class]$notes - 14/10/2025 14:46:21

      property request; formats : Object

Class extends $pageWeb

Class constructor($request : Object)
	
	Super()
	
	This.request:=$request
	
	This.formats:=New object
	This.formats.Personnes:=New object("Options"; 0x0007)
	This.formats.Communes:=New object("Options"; 0x00010000)
	This.formats.Medias:=New object("Options"; 0x0000)
	
	
	
	// -----------------------------
	// MARK:Gestion de la BDD Notes
	// -----------------------------
	
Function ListerTables($data : Object)->$result : Collection
	// ici on est sur le serveur (ou la BDD mère en test)
	
	// ouvrir la BDD notes
	This.OuvrirBDD_externe()
	
	// lister les noms de tables de la requete
	ARRAY LONGINT($tabID; 0)
	ARRAY TEXT($tabNomTable; 0)
	Begin SQL
		SELECT id, nomTable FROM notes INTO :$tabID, :$tabNomTable;
	End SQL
	
	// passer en collection
	$result:=New collection
	If (Size of array($tabID)>0)
		ARRAY TO COLLECTION($result; $tabID; "ID"; $tabNomTable; "nomTable")
	End if 
	
	// fermer
	This.FermerBDD_externe()
	
	
Function LireBDD($data : Object)->$result : Collection
	// ici on est sur le serveur (ou la BDD mère en test)
	var $nomTable : Text
	
	// ouvrir la BDD notes
	This.OuvrirBDD_externe()
	
	// lister les notes de la requete
	$nomTable:=$data.DataClassNom
	ARRAY LONGINT($tabID; 0)
	ARRAY TEXT($tabUser; 0)
	ARRAY TEXT($tabDate; 0)
	ARRAY TEXT($tabNomTable; 0)
	ARRAY TEXT($tabIDunique; 0)
	ARRAY TEXT($tabObjet; 0)
	ARRAY TEXT($tabContenu; 0)
	Begin SQL
		SELECT id, auteur, noteDate, nomTable, IDunique, objet, contenu FROM notes WHERE nomTable = :$nomTable INTO :$tabID, :$tabUser, :$tabDate, :$tabNomTable, :$tabIDunique, :$tabObjet, :$tabContenu;
	End SQL
	
	// passer en collection
	$result:=New collection
	If (Size of array($tabID)>0)
		ARRAY TO COLLECTION($result; $tabID; "ID"; $tabUser; "auteur"; $tabDate; "noteDate"; $tabNomTable; "nomTable"; $tabIDunique; "IDunique"; $tabObjet; "objet"; $tabContenu; "contenu")
	End if 
	
	// fermer
	This.FermerBDD_externe()
	
	
Function SupprimerDansBDD($params : Object)
	var $ID : Integer
	
	Case of 
		: (Not(OB Is defined($params; "sélection")))
		: (Not(Value type($params.sélection)=Is collection))
		: ($params.sélection.length=0)
		Else 
			// on a tout
			
			// ouvrir la BDD notes
			This.OuvrirBDD_externe()
			
			Begin SQL
				START TRANSACTION; 
			End SQL
			
			For each ($ID; $params.sélection)
				Begin SQL
					DELETE FROM notes WHERE ID = :$ID;
				End SQL
			End for each 
			
			Begin SQL
				COMMIT TRANSACTION; 
			End SQL
			
			// fermer
			This.FermerBDD_externe()
	End case 
	
	
Function OuvrirBDD_externe()
	// Ouvrir (ou créer si elle n'existe pas) une BDD externe, via SQL
	var $sql_BDDpath; $sql_BDDcourantePath; $texte : Text
	var $table; $champ : Object
	var $c : Collection
	
	Case of 
		: (Storage.System.typeApplication=ALV Client APP)
		: (Storage.System.typeApplication=4D Remote mode)
			// filtrer
		Else 
			
			// fixer le nom de la BDD externe ouvrir
			$sql_BDDpath:=This.document.getBDDnotesFolder("BDD_NOTES").platformPath
			
			// lire la BDD externe ouverte
			$sql_BDDcourantePath:=""
			Begin SQL
				SELECT DATABASE_PATH() FROM _USER_SCHEMAS LIMIT 1 INTO :$sql_BDDcourantePath;
			End SQL
			If (Position("/"; $sql_BDDcourantePath)>0)
				$sql_BDDcourantePath:=Convert path POSIX to system($sql_BDDcourantePath)
			End if 
			
			// rappel : ouverture de la BDD en auto_close de sorte qu'elle puisse être ouverte par plusieurs applications (ex le serveur par AppMobile ou la BDD mère)
			Case of 
				: ($sql_BDDpath+".4DB"=$sql_BDDcourantePath)
					// déjà ouverte, ne rien faire
					
				: (Test path name($sql_BDDpath+".4DB")=Is a document)
					// ouverture d'une BDD existante
					Begin SQL
						USE DATABASE DATAFILE :$sql_BDDpath AUTO_CLOSE;
					End SQL
					// attention ici on est thread-safe
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation"; Current method name; "Ouverture de '"+$sql_BDDpath+"'")
					
				Else 
					Waiting(30)
					
					// nouvelle BDD externe
					//-- créer les fichiers $fichier.4DB et $fichier.4DD
					//-- créer / mettre à jour la structure
					//-- créer les index
					//-- envoyer les requêtes SQL vers la BDD externe
					
					// lire les données de création de la structure BDD
					$texte:=Document to text(Folder(Get 4D folder(Current resources folder); fk platform path).folder("Enumérations").file("Structure_BDD_Notes.json").platformPath)
					$c:=JSON Parse($texte; Is collection)
					Begin SQL
						CREATE DATABASE IF NOT EXISTS DATAFILE :$sql_BDDpath;
						USE DATABASE DATAFILE :$sql_BDDpath AUTO_CLOSE;
					End SQL
					
					For each ($table; $c)
						// on a une table
						Case of 
							: (Not(OB Is defined($table; "nomTable")))
							: (Not(OB Is defined($table; "champs")))
							: ($table.champs.length=0)
							Else 
								// ok on a une table et ses champs
								$texte:="CREATE TABLE IF NOT EXISTS "+$table.nomTable
								// lister les champs
								$texte:=$texte+" ("
								For each ($champ; $table.champs)
									$texte:=$texte+$champ.champ+" "+$champ.type+", "
								End for each 
								// fermer
								$texte:=Substring($texte; 1; Length($texte)-2)+");"
						End case 
						Begin SQL
							EXECUTE IMMEDIATE :$texte;
						End SQL
						
						// id automatique
						$texte:="ALTER TABLE "+$table.nomTable+" MODIFY id ENABLE AUTO_INCREMENT;"
						Begin SQL
							EXECUTE IMMEDIATE :$texte;
						End SQL
					End for each 
					
					This.trace.EnvoyerMessages([msgk_event]; "Process "+Current process name; Current method name; "Création de '"+$sql_BDDpath+"'")
					
			End case 
			// Maintenant toutes les requêtes SQL du process sont dirigées vers $sql_BDDpath
	End case 
	
	
Function FermerBDD_externe()
	// retour à la BDD mère
	Begin SQL
		USE DATABASE SQL_INTERNAL;
	End SQL
	
	
	// -----------------------------
	// MARK:Traitement des actions
	// -----------------------------
	
Function Traiter($response : Object)->$result : Boolean
	$result:=False
	
	Case of 
		: (This.request.action="")
		: (Not(OB Is defined(This; This.request.action)))
			// action est autre chose qu'une function de this ; laisser passer
		Else 
			$result:=True
			// exécuter l'url
			This[This.request.action]($response)
	End case 
	
	
Function AjouterNote($response : Object)
	// enregistrer This.request.EntitéRéduite
	This.EcrireNote()
	// demander la mise à jour FORM
	$response.dataSynchro:=True
	$response.statusTexte:=Localized string("13")
	
	
Function EcrireNote()
	// enregistrer la note de This.request
	var $nomTable; $IDunique; $noteDate; $auteur; $objet; $contenu : Text
	
	// ici on a une entité réduite
	$nomTable:=This.request.EntitéRéduite.DataClassNom
	$IDunique:=This.request.EntitéRéduite.IDunique
	$noteDate:=String(Current date; ISO date GMT; Current time)
	$objet:=This.request.note.objet
	$contenu:=This.request.note.contenu
	$auteur:=Localized string("14")
	Case of 
		: (Session=Null)
		: (Session.userName="")
		Else 
			$auteur:=Session.userName
	End case 
	
	// ouvrir la BDD notes
	This.OuvrirBDD_externe()
	
	// enregistrer et fermer
	Begin SQL
		START TRANSACTION;
		INSERT INTO notes (nomTable, IDunique, noteDate, auteur, objet, contenu) VALUES ( :$nomTable, :$IDunique, :$noteDate, :$auteur, :$objet, :$contenu); 
		COMMIT TRANSACTION;
		
		USE LOCAL DATABASE SQL_INTERNAL;
	End SQL
	
	This.FermerBDD_externe()
	
	// ici pas de retour
	
	
	// -----------------------------
	// MARK:Traitement des URL
	// -----------------------------
	
Function LireNotes($data : Object)->$result : Boolean
	// lire toutes les notes de la table .DataClassNom 
	var $auteur; $libellé : Text
	var $c; $c1; $c2 : Collection
	var $note : Object
	
	// lire la BDD notes
	$c:=This.LireBDD($data)
	$result:=($c.length>0)
	
	If ($c.length>0)
		// collection des user ayant créé une note pour $data
		$c1:=$c.extract("auteur").distinct()
		
		If ($c1.length>0)
			$data.notes:=""
			
			For each ($auteur; $c1)
				// lire les notes de $auteur
				$c2:=$c.query("auteur = :1"; $auteur).orderBy("noteDate asc")
				// les lire
				If ($c2.length>0)
					$data.notes:=$data.notes+"<h2>Par "+$auteur+"</h2>"
					For each ($note; $c2)
						$libellé:=This.Libellé($data.DataClassNom; $note.IDunique)
						$data.notes:=$data.notes+"<p>"+$libellé+" - "+$note.objet+" : "+$note.contenu+" (le "+String(Date($note.noteDate); System date abbreviated)+" à "+String(Time($note.noteDate); System time short)+")"+"</p></br>"
					End for each 
				End if 
			End for each 
			
		Else 
			$data.notes:="<h2>"+Localized string("1101")+"</h2>"
		End if 
		
	Else 
		// BDD vide
		$data.notes:="<h2>"+Localized string("1101")+"</h2>"
	End if 
	
	
Function Libellé($DataClassNom : Text; $IDunique : Text)->$result : Text
	var $entité : Object
	
	If ($DataClassNom="ALB_@")
		// un media d'album
		$result:=Session.storage.ALB_UserParams.Album.Medias.query("MED_IDunique = :1"; $IDunique)[0].MED_Nom
		$result:=$result+" (ID "+$IDunique+")"
		
	Else 
		$entité:=cs.xSQL[$DataClassNom].new($IDunique)
		
		If ($entité=Null)
			$result:=""
			
		Else 
			// entité de la BDD mère (ID existe)
			$result:=" (ID "+String($entité.ID)+")"
		End if 
	End if 
	
    

[class]Lieux - 04/10/2025 09:34:46

      property nom; label; commentaire : Text
property type : Integer
property lesEvenements : Object

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	// initialiser l'objet avec les données de l'entité $IDentité de la BDD
	
	Super("Lieux"; $IDentité)
	
	
	
	// ----------------------
	//MARK:Sélection
	// ----------------------
	
Function _LesEvents()
	// créer la collection des events
	var $selection : cs.EventsSelect
	
	$selection:=cs.EventsSelect.new(This.dataClass.LesEvents())
	$selection.Créer()
	Use (This)
		This.lesEvenements:=New shared object
	End use 
	This.cFct.AjouterObjetA($selection; This; "lesEvenements")
	
	
	// ----------------------
	//MARK: FORM Liste
	// ----------------------
	
Function NombreEvents()->$result : Text
	This._LesEvents()
	
	$result:=""
	$result+=This.AddEventsType(22000; 1220)
	$result+=This.AddEventsType(22300; 1223)
	$result+=This.AddEventsType(33600; 1236)
	$result+=This.AddEventsType(33700; 1237)
	$result+=This.AddEventsType(33800; 1238)
	$result+=This.AddEventsType(22100; 1221)
	$result+=This.AddEventsType(22200; 1222)
	
	If ($result="")
		$result:=Localized string("1073")
	End if 
	
	
Function AddEventsType($type : Integer; $typeLabel : Integer)->$result : Text
	var $c : Collection
	var $sélection : cs.EventsSelect
	
	$result:=""
	
	$c:=This.lesEvenements.selection.query("type >= :1 and type <= :2"; $type; $type+99)
	If ($c.length>0)
		// créer une sous selection d'events
		$sélection:=cs.EventsSelect.new()
		$sélection.selection:=$c
		
		$result:="<p>"+String($sélection.selection.length)+" "+Lowercase(This.cFct.LireLocatedSTR($typeLabel; New object("genre"; False; "plur"; ($sélection.selection.length>1))); *)+" :</p>"
		$result+="<p>"+$sélection.LesMembres()+"</p></br>"
	End if 
	
	
Function get labeledNom()->$result : Text
	$result:=This.nom+" ("+This.label+")"
	
	
Function get label()->$result : Text
	$result:=This.cFct.LireLocatedSTR(This.type)
	
	
	// ----------------------
	//MARK: FORM Detail
	// ----------------------
	
Function EtatCivil()->$result : Text
	var $c : Collection
	var $type : Integer
	var $selection : cs.EventsSelect
	
	$result:=""
	// lister tous les types d'events de ce lieu
	This._LesEvents()
	$c:=This.lesEvenements.selection.extract("type").distinct().orderBy(ck ascending)
	
	// pour chaque type, lister les events
	If ($c.length>0)
		For each ($type; $c)
			$result+=This.cFct.LireLocatedSTR($type)+", "+Localized string(String(16000000+$type))+Char(Line feed)
			
			$selection:=cs.EventsSelect.new()
			$selection.selection:=This.lesEvenements.selection.query("type = :1"; $type).orderBy("dateNum asc")
			
			$result+=$selection.EtatCivil()
		End for each 
	End if 
	
	
    

[class]_WEBMO_DataStore - 30/07/2026 17:44:57

      property cFct : cs._CommunFunctions
property dataClass : Object
property selection; collection : Collection
property length : Integer
property _tagUrl : Text:="/4DCGI/"
// attributs de classe
property DataClassNom : Text
property numTable : Integer
property ID : Integer
property IDunique : Text
property ChampsExposés : Collection

Class extends _composant

Class constructor($DataClassNom : Text; $data : Variant)
	
	Super()
	
	This.cFct:=cs._CommunFunctions.me
	
	This.dataClass:=cs.xSQL[$DataClassNom].new($data)
	// récupérer les attributs scalaires dans this : .DataClassNom, .collection et .length
	cs.xSDK.Outils.me.CopierAttributs(This.dataClass; This)
	This.Filtrer()
	
	
Function CreerBDDentity($data : Object; $c : Collection)
	var $attribut : Text
	
	For each ($attribut; $c)
		$data[$attribut]:=This[$attribut]()
	End for each 
	
	
Function labelDataClass($prefix : Text)->$result : Text
	$result:=$prefix+This.DataClassNom
	// supprimer le s final
	$result:=Substring($result; 1; Length($result)-1)
	$result:=Uppercase($result)
	
	
Function Titrer()->$result : Text
	$result:=This.cFct.LireLocatedSTR(This.numTable)
	
	
Function InformationEntité($data : Object)->$result : Text
	// créer le texte de l'entité $params; utilise un template
	var $event : cs.Events
	var $lesUnions : cs.UnionsSelect
	var $union : cs.Unions
	var $formats; $entité; $sélections; $sélection : Object
	var $dataClassNom; $texte; $soustexte; $Code : Text
	var $i; $numCode : Integer
	var $RacineXML; $ElementXML; $Xpath : Text
	
	// lire les formats
	$formats:=New object("Options"; 0)
	// options manquantes renseignées _FixerParamètresLibellé
	
	// lire les paramètres
	$formats.params:=0
	If (OB Is defined($data; "params"))
		$formats.params:=$data.params
	End if 
	$formats.séparateurComments:=". "
	
	$texte:=""
	Case of 
		: (Not(OB Is defined($data; "entité")))
		: ($data.entité=Null)
		Else 
			$entité:=$data.entité
			
			$dataClassNom:=$entité.DataClassNom
			
			// lire la structure des informations de l'entité $2
			$Xpath:=Folder(Get 4D folder(Current resources folder); fk platform path).folder("TemplatesMobileWeb").file("TemplateInfosEntité.xml").platformPath
			$RacineXML:=DOM Parse XML source($Xpath)
			$ElementXML:=DOM Find XML element by ID($RacineXML; $dataClassNom)
			If (ok=1)
				ARRAY TEXT($tabElements; 0)
				$ElementXML:=DOM Find XML element($ElementXML; "information"; $tabElements)
				
				If (Size of array($tabElements)>0)
					// pour chaque info
					For ($i; 1; Size of array($tabElements))
						// récupérer les éléments de l'info $i
						ARRAY TEXT($tabNoms; 1)
						ARRAY TEXT($tabValeurs; 1)
						// pour les erreurs
						$tabNoms{0}:="#Erreur"
						$tabValeurs{0}:="#Erreur"
						$ElementXML:=DOM Get first child XML element($tabElements{$i}; $tabNoms{1}; $tabValeurs{1})
						While (ok=1)
							INSERT IN ARRAY($tabNoms; 1; 1)
							INSERT IN ARRAY($tabValeurs; 1; 1)
							$ElementXML:=DOM Get next sibling XML element($ElementXML; $tabNoms{1}; $tabValeurs{1})
						End while 
						// lire les données nécessaires
						$Code:=$tabValeurs{indexTableau(Find in array($tabNoms; "code"))}
						// séparateur entre l'information courante et la suivante
						$formats.séparateur:=$tabValeurs{indexTableau(Find in array($tabNoms; "separateur"))}
						$formats.séparateurBloc:=Choose($data.params ?? 2; "</p><p></br><p>"; $formats.séparateur)
						
						$formats.Options:=Num($tabValeurs{indexTableau(Find in array($tabNoms; "format"))})
						
						If (Match regex("[0-9]{1,5}"; $Code))
							// un nombre de 1 à 5 chiffres
							$numCode:=Num($code)
							Case of 
								: ($numCode=0)
									If ($formats.params ?? 1)
										// nom de l'objet de la table courante
										$texte:=$texte+$entité.Libellé($formats)+$formats.séparateur
									End if 
									// ajouter le commentaire
									$formats.débutComment:=""
									$formats.finComment:=Choose($data.params ?? 2; $formats.séparateurBloc; ", ")
									$texte:=$texte+$entité.RédigerCommentaire($formats)
									
								: ($numCode=1)
									// les membres d'une union
									$texte:=$texte+$formats.séparateur+$entité.Libellé($formats)
									
								: ($numCode>=22000) & ($numCode<=22999)
									// évents persos
									$event:=$entité.EvenementPersonnel($numCode)
									If ($event#Null)
										// utiliser les formats par défaut
										$texte:=$texte+$event.Libellé($formats)
										// ajouter le commentaire
										$formats.débutComment:=Choose($data.params ?? 2; ". "; ", ")
										$formats.finComment:=""
										$texte:=$texte+$event.RédigerCommentaire($formats)
										$texte:=$texte+$formats.séparateurBloc
									End if 
									
								: ($numCode>=33000) & ($numCode<=33999)
									// évent fam : pour chaque union, éditer le premier Event Fam
									// attention : $entité peut concerner différents types de table : chercher la liste des Unions
									Case of 
										: ($dataClassNom="Personnes")
											$lesUnions:=$entité.LesUnions()
											
										: ($dataClassNom="Unions")
											$lesUnions:=$entité.LesUnions()
											
										Else 
											$lesUnions:=Null
									End case 
									
									// pour chaque union de la liste, éditer un event Fam
									Case of 
										: ($lesUnions=Null)
										: ($lesUnions.selection.length=0)
										Else 
											// rappel : les unions sont classées par date, et les conjoints sont liés aux unions par [Unions]ID
											For each ($union; $lesUnions.selection)
												// ajouter l'event, s'il existe
												$sélections:=$union.LesEvenementsFamiliaux()
												
												If ($sélections.selection.length=0)
													$texte:=$texte+Localized string("33611")
													
												Else 
													$soustexte:=""
													
													// utiliser les formats par défaut
													$formats.Options:=0x3F00
													// ici il peut y avoir 1 à plusieurs évents pour cette union (rappel : ordre chronos)
													If ($formats.params ?? 3)
														// on les prend tous
													Else 
														$sélections:=$sélections.selection.slice(0; 1)
													End if 
													For each ($sélection; $sélections.selection)
														$soustexte:=$soustexte+$sélection.Libellé($formats)+$formats.séparateur
													End for each 
													// nettoyer
													$texte:=$texte+Substring($soustexte; 1; Length($soustexte)-Length($formats.séparateur))
												End if 
												
												// ajouter le conjoint , s'il existe
												// on aurait pu utiliser l'option 15 du codage de l'évent
												Case of 
													: ($dataClassNom#"Personnes")
													: ($union.LeConjoint($entité.ID)=Null)
													Else 
														// ok on a un conjoint
														$formats.Options:=35
														$texte:=$texte+" "+$union.LeConjoint($entité.ID).Libellé($formats)
												End case 
												
												// ajouter les commentaires
												$soustexte:=""
												$formats.débutComment:=Choose($data.params ?? 2; "</p><p>"; ", ")
												$formats.finComment:=""
												For each ($sélection; $sélections.selection)
													$soustexte:=$soustexte+$sélection.RédigerCommentaire($formats)
												End for each 
												$texte:=$texte+$soustexte
												
												$texte:=$texte+$formats.séparateurBloc
											End for each 
									End case 
							End case 
							
						Else 
							// rien
						End if 
						
					End for 
					// nettoyer
					//dans cette version pas de nettoyage (les balises vides ne sont pas visibles à l'écran)
				End if 
			End if 
			DOM CLOSE XML($RacineXML)
	End case 
	// fixer le résultat
	$result:="<p>"+$texte+"</p>"
	
	
	// ----------------------
	//MARK:dataClass Wrappers
	// ----------------------
	
Function Libellé($formats : Object)->$libellé : Text
	// renvoie le nom formaté de this suivant les options $formats
	$libellé:=This.dataClass.Libellé($formats)
	
	
Function Le($DataClassNom : Text)->$result : Object
	// renvoie l'entité [$DataClassNom] de ce composant (la classe doit exister !)
	var $entité : Object
	
	$entité:=This.dataClass.Le($DataClassNom)
	// ajouter les attributs / functions WEB
	$result:=cs[$DataClassNom].new($entité.ID)
	
	
	// ----------------------
	//MARK:Sélections
	// ----------------------
	
Function Créer()
	// créer une collection d'entités ; resultat dans .selection
	This.setEntités()
	
	
Function Filtrer()
	var $c : Collection
	
	Case of 
		: (Not(OB Is defined(This; "collection")))
			// ce n'est pas une sélection
		: (This.collection.length=0)
			// sélection vide, rien à filtrer
		: (cs._filtrageDonnees.me["filtre"+This.DataClassNom]=Null)
			// pas de filtre défini
		Else 
			// filtrer
			$c:=cs._filtrageDonnees.me["filtre"+This.DataClassNom]
			// faire l'intersection entre This.collection et $c
			This.collection:=This.collection.filter(Formula($2.indexOf($1.value)>-1); $c)
	End case 
	
	
Function setEntités()
	// créer la sélection (collection d'entités de this)
	var $ID : Integer
	
	This.selection:=New collection
	Case of 
		: (This.DataClassNom="")
		: (Not(OB Is defined(This; "collection")))
		: (This.collection.length=0)
		Else 
			For each ($ID; This.collection)
				This.selection.push(cs[This.DataClassNom].new($ID))
			End for each 
			This.length:=This.selection.length
	End case 
	
	
Function getRelatedEntity($chemin : Text)->$entité : Object
	var $c : Collection
	var $dataTexte; $lien : Text
	
	
	// remonter le chemin $chemin de this
	$dataTexte:=$chemin
	If ($dataTexte="@_@")
		$c:=Split string($dataTexte; "_")
		// nom de l'attribut de l'end entité
		$dataTexte:=$c.pop()
		$entité:=This
		For each ($lien; $c)
			$entité:=$entité[$lien]
		End for each 
		
	Else 
		$entité:=This
	End if 
	
	
Function LibelléLié($url : Text; $formats : Object)->$result : Text
	// écrire le lien
	$result:="<a href="+Char(Double quote)+$url+Char(Double quote)+">"+This.Libellé($formats)+"</a>"
	
	
	// ----------------------
	//MARK:Utilitaires
	// ----------------------
	
Function ThisAjouter($objetORDA : 4D.Class; $nomObjet : Text)
	// stocker $classeObjet dans this, sous le nom .nomAttribut
	var $new_obj : Object
	
	Use (This)
		$new_obj:=This[$nomObjet]
		If ($new_obj=Null)
			This[$nomObjet]:=New shared object
		End if 
	End use 
	
	Use (This[$nomObjet])
		This[$nomObjet]:=OB Copy($objetORDA; ck shared; This[$nomObjet])
	End use 
	
	
Function _FixerParamètresLibellé($formats : Object)
	// fixer la valeur par défaut des $formats manquant
	var $data : Object
	var $i : Integer
	
	// valeurs par défaut
	$data:=New object("SymbolConjoints"; " & "; "SymbolDateLieu"; ","+Localized string("33"); "FormatDate"; 5; "FormatHeure"; 5; "FormatLieu"; 1; "FormatGeoLoc"; 1)
	
	// compléter $formats
	OB GET PROPERTY NAMES($data; $tabNoms)
	For ($i; 1; Size of array($tabNoms))
		If (Not(OB Is defined($formats; $tabNoms{$i})))
			$formats[$tabNoms{$i}]:=$data[$tabNoms{$i}]
		End if 
	End for 
	
	
Function IDcodé()->$ID : Integer
	$ID:=CodeEnreg(This.ID; [This.dataClass.numTable])
	
	
Function get entityDataClassNom()->$result : Text
	$result:=Replace string(This.DataClassNom; "Select"; "")
	
    

[class]$pageWeb - 31/07/2026 10:15:24

      property result; paramsBody : Object
property balisesUrl; paramsUrl; Descripteur : Collection
// propriétés des appelants
property RacineXML : Text

Class extends _composant

Class constructor()
	
	Super()
	
	This.result:=This.InitResult()
	
	
	
	//----------------------------------
	// MARK:Traitement url
	//----------------------------------
	
Function TraiterURL($url : Text)->$result : Object
	// traitement des url internes au serveur (appel des classes $pageXXX par les templates)
	// remarque : dans cette version, ces url ont le même format que celles des requêtes client HTTP
	This.result.nomMethode:=Current method name
	
	// récupérer les paramètres
	This.LireParametresUrl($url)
	
	// normalement .balise[2] est le nom d'une function de $pageXXX
	Case of 
		: (This.balisesUrl=Null)
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incomplète - pas de balises à traiter"
			
		: (This.balisesUrl.length<3)
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incorrecte - il manque la paramètres .balise[2]"
			
		: (Not(OB Is defined(This; This.balisesUrl[2])))
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incorrecte - .balise[2] '"+This.balisesUrl[2]+"' n'est pas une function de this "+OB Class(This).name
			
		Else 
			// on accepte la demande
			// le résultat, s'il y a, est dans this.result.resultat
			// exécuter la function, en cas d'erreur elle peut renseigner This.result
			This[This.balisesUrl[2]]()
	End case 
	$result:=This.result
	
	
Function LireParametresUrl($url : Text)
	// $url est de la forme /4Dxxx/yyy/nomFunction {?param1 {+?param2...}}, avec xxx = CGI ou ACTION ou SCRIPT, yyy le contexte WEB, APP ...
	var $corps : Text
	
	// lire les balises
	This.balisesUrl:=Split string($url; "/"; sk ignore empty strings)
	This.paramsUrl:=Null
	This.paramsBody:=Null
	
	If (This.balisesUrl.length>0)
		// analyser la function
		This.paramsUrl:=Split string(This.balisesUrl[This.balisesUrl.length-1]; "?")
		// virer function+params
		This.balisesUrl:=This.balisesUrl.slice(0; This.balisesUrl.length-1)
		// ajouter la function seule
		This.balisesUrl.push(This.paramsUrl[0])
		// lister les paramètres
		This.paramsUrl.shift()
		
		// nettoyer les paramètres
		Case of 
			: (This.paramsUrl.length<2)
				// rien à faire
			: (This.paramsUrl[0]="@+")
				// nettoyer les '+'
				This.paramsUrl:=Split string(This.paramsUrl.join(""); "+")
				ALERT(Current method name+".  +")
			: (This.paramsUrl[0]="@&")
				// l'url vient de javascript, le séparateur de params est '&' ('+' ne passe pas)
				This.paramsUrl:=Split string(This.paramsUrl.join(""); "&")
				ALERT(Current method name+".  &")
		End case 
		
		
		ARRAY TEXT($clésEntête; 0)
		ARRAY TEXT($valeursEntête; 0)
		WEB GET HTTP HEADER($clésEntête; $valeursEntête)
		
		// lister les paramètres
		Case of 
				// cas méthode GET
			: (Size of array($clésEntête)=0)
			: ($valeursEntête{Find in array($clésEntête; "X-METHOD")}="GET")
				// les paramètres viennent de l'URL : ils sont dans This.paramsUrl
				// rien à faire
				
				// cas méthode POST
			: ($valeursEntête{Find in array($clésEntête; "X-METHOD")}#"POST")
			: (Find in array($clésEntête; "Content-Type")=-1)
			: ($valeursEntête{Find in array($clésEntête; "Content-Type")}#"application/json")
			Else 
				// les paramètres sont dans le body
				// ok on a reçu un objet
				WEB GET HTTP BODY($corps)
				This.paramsBody:=JSON Parse($corps)
		End case 
		
	Else 
		This.balisesUrl:=Null
	End if 
	
	
	//----------------------------------
	// MARK:Connexion
	//----------------------------------
	
Function ServeurActif()
	This.result.resultat:=Char(1)+"4D4D"
	
	
Function estServeurActif()
	var $texte : Text
	
	$texte:=String(True)
	WEB SEND TEXT($texte; "text/html")
	
	
Function ReConnexion()->$result : Boolean
	// renvoie faux si une session user existe, une reconnexion automatique est faite
	// renvoie vrai si Guest, ou pas de fichier sessionWeb, ou le timeOut de la session est dépassée (plus d'un an!) ; va lancer la procédure de connexion
	var $data : Object
	
	$data:=New object
	Case of 
		: (Not(Session.isGuest()))
			// cas normal, la session est active
			$result:=False
			
		: (cs.$session.me.getSessionDeCookie($data))
			If ($data.infos.status="pending")
				// authentification en cours
				$result:=False
				
			Else 
				// la session a été fermée et une session enregistrée existe
				// la réactiver
				$result:=True
				This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Ré activation Session"; Current method name; $data.infos.session.id)
				
				cs.$session.me.InitialiserSessionInfos($data.infos)
				
				// se reconnecter
				This.Envoyer("loginByEmail.shtml"; "accueil")
			End if 
			
		Else 
			// ici on est guest ; l'appelant s'occupe de la suite
			$result:=False
	End case 
	
	
	//----------------------------------
	// MARK:Analyse des pages
	//----------------------------------
	
Function isValidDevice()->$result : Boolean
	var $request : Object:=New object
	
	$result:=False
	
	This.getWebBrowserInfo($request)
	Case of 
		: (Storage.System.typeApplication=ALV BDD mère)
			$result:=True  // pour test sur desktop
			
		: (Not(OB Is defined($request; "device")))
		: (Not(OB Is defined($request.device; "os")))
		: ($request.device.os="iPhone")
			$result:=True
			
		: ($request.device.os="Linux")
			$result:=($request.device.description="Android@")
			
			// what else?
	End case 
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Connexion"; Current method name; Choose($result; "OK"; "KO")+" ; device de la requête : "+Choose($result; $request.device.os; "inconnu")+", voir log")
	This.trace.EnvoyerMessages([msgk_log]; "Connexion"; Current method name; JSON Stringify($request))
	
	
Function getWebBrowserInfo($requete : Object)
	var $texte; $subTexte : Text
	var $c : Collection
	
	ARRAY TEXT($clésEntête; 0)
	ARRAY TEXT($valeursEntête; 0)
	WEB GET HTTP HEADER($clésEntête; $valeursEntête)
	
	$requete.device:=New object
	$texte:=$valeursEntête{Find in array($clésEntête; "user-Agent")}
	
	$texte:=Substring($texte; Position("("; $texte))
	$subTexte:=Substring($texte; 2; Position(")"; $texte)-2)
	$c:=Split string($subTexte; ";"; sk trim spaces)
	$requete.device.os:=$c[0]
	$requete.device.description:=$c[1]
	
	$texte:=Substring($texte; Position("version"; $texte))
	$texte:=Substring($texte; Position("/"; $texte)+1)
	$requete.device.version:=Substring($texte; 1; Position(" "; $texte)-1)
	
	$texte:=$valeursEntête{Find in array($clésEntête; "Accept-Language")}
	$requete.language:=New object
	$requete.language.id:=Substring($texte; 1; Position(","; $texte)-1)
	
	$requete.session:=New object
	// ici un objet mobileWEB a pour ID de session l'ID du cookie
	cs.$cookie.me.Lire()
	Case of 
		: (cs.$cookie.me.liste.length=0)
			$requete.session.id:="pas de cookie"
		: (cs.$cookie.me.liste.query("nom = :1"; "4DSID@").length=0)
			$requete.session.id:="pas de cookie 4DSID"
		Else 
			$requete.session.id:=cs.$cookie.me.liste.query("nom = :1"; "4DSID@")[0].valeur
	End case 
	$requete.session.ip:=$valeursEntête{Find in array($clésEntête; "Host")}
	// préciser qu'on vient d'un navigateur WEB
	$requete.client:="mobileWEB"
	// le status du processus d'authentification est initialisé 'en attente'
	$requete.status:="pending"
	
	
	//----------------------------------
	// MARK:Gestion des pages
	//----------------------------------
	
Function Envoyer($nomPageWeb : Text; $EtatNavigation : Text; $HTTPvars : Text)
	// variables process générales
	var vs4D; wwwRacineRessources; wwwEtatNavigation; wwwContexteWeb : Text
	
	// fixer les variables process
	Use (Session.storage.UserParams)
		Session.storage.UserParams.EtatNavigation:=$EtatNavigation
	End use 
	Use (Session.storage.HTTPvars)
		Session.storage.HTTPvars.wwwEtatNavigation:=$EtatNavigation
	End use 
	// définir les constantes process (et en mode interprété les variables process)
	
	// restaurer les variables process générales
	This.RestaurerHTTPvars("HTTPvars")
	// restaurer les variables process spécifiques du contexte (les variables sont déclarées dans ces contextes)
	If (Count parameters>2)
		This.RestaurerHTTPvars($HTTPvars)
	End if 
	
	//// fixer le cookie
	//If (cs.$cookie.me.getMobileWebCookie()="")
	//cs.$cookie.me.Ecrire("mobileWebID"; Session.id)
	//End if 
	//cs.$cookie.me.Ecrire("expire"; "01 Jan "+String(Year of(Current date)+2)+" 00:00:01 GMT")
	//cs.$cookie.me.Lire()
	
	WEB SEND FILE($nomPageWeb)
	
	
Function Rediriger($url : Text)
	WEB SEND HTTP REDIRECT($url)
	
	
	//----------------------------------
	// MARK:Variables process HTTP
	//----------------------------------
	
Function LireHTTPvars()
	var $data : Object
	var $i : Integer
	
	// depuis v14 les variables Form sont récupérées de cette façon
	ARRAY TEXT($tableauDeNoms; 0)
	ARRAY TEXT($tableauDeValeurs; 0)
	// les variables récupérées correspondent à des objets de formulaire Web ayant un attribut "name"
	WEB GET VARIABLES($tableauDeNoms; $tableauDeValeurs)
	For ($i; 1; Size of array($tableauDeNoms))
		// ne garder que les variables de type wwwXXX...
		If ($tableauDeNoms{$i}="www@")
			OB SET($data; $tableauDeNoms{$i}; $tableauDeValeurs{$i})
		End if 
	End for 
	ASSERT(This.trace.DebugerVariables("$commande"; Current method name; New object("variablesHTTP"; $data)))  //; "Options"; 0x0002
	
	sharedObject($data; Session.storage.HTTPvars)
	
	
Function RestaurerHTTPvars($contexte : Text)
	// copier $contexte dans les variables process
	var $sessionStorage; $data : Object
	var $attribut; $varNom : Text
	var $numTable; $numChamp : Integer
	var $ptr : Pointer
	
	$sessionStorage:=Session.storage
	
	If (OB Is defined($sessionStorage; $contexte))
		$data:=$sessionStorage[$contexte]
		
		For each ($attribut; $data)
			RESOLVE POINTER(Get pointer($attribut); $varNom; $numTable; $numChamp)
			Case of 
				: (($numTable=0) & ($numChamp=0))
					// pointeur NIL
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Variable indéfinie"; Current method name; $contexte+" : la variable '"+$attribut+"' n'a pas une variable")
				: (($numTable#-1) | ($numChamp#-1))
					// pas une variable
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Variable indéfinie"; Current method name; $contexte+" : absence de directive de compilation (var "+$attribut+" : xxx")
				Else 
					// on a une variable : l'initialiser
					$ptr:=Get pointer($attribut)
					$ptr->:=$data[$attribut]
			End case 
		End for each 
	End if 
	
	
    

[class]Sites - 30/10/2025 17:16:17

      property nom : Text
property type : Integer
property lesLieux : cs.LieuxSelect
property laCommune : cs.Communes

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	// initialiser l'objet avec les données de l'entité $IDentité de la BDD
	
	Super("Sites"; $IDentité)
	
	
	
Function FixerParametresListe()->$result : Object
	$result:=New object
	$result.DataClassNom:="Sites"
	$result.Requete:="id>0"
	$result.nomFunction:="AfficherFORMdetail"
	$result.nomFORMlist:="detailSimple"
	
	
Function Libellé($userFormats : Object)->$libellé : Text
	$libellé:=This.nom+" ("+This.cFct.LireLocatedSTR(This.type)+")"
	
	
Function CreerBDDentity()->$result : Object
	$result:=New object
	$result.IDunique:=This.IDunique
	
	$result.Elementliste:=This.Elementliste()
	// les champs calculés
	Super.CreerBDDentity($result; ["Champ_1"; "Champ_2"; "Champ_3"; "Champ_4"; "Champ_titreIconed_1"; "Champ_titreIconed_2"])
	
	
	// ----------------------
	//MARK:Sélection
	// ----------------------
	
Function _LesLieux()
	var $selection : cs.LieuxSelect
	
	$selection:=cs.LieuxSelect.new(This.dataClass.LesLieux())
	// créer .selection
	$selection.Créer()
	$selection.selection:=$selection.selection.orderBy("nom asc")
	Use (This)
		This.lesLieux:=New shared object
	End use 
	This.cFct.AjouterObjetA($selection; This; "lesLieux")
	
	
Function _LaCommune()
	var $entité : cs.Communes
	
	$entité:=cs.Communes.new(This.dataClass.laCommune)
	$entité.Créer()
	Use (This)
		This.lesLieux:=New shared object
	End use 
	This.cFct.AjouterObjetA($entité; This; "laCommune")
	
	
	// ----------------------
	//MARK: FORM Liste
	// ----------------------
	
Function Elementliste()->$result : Text
	$result+="<article id='"+This.IDunique+"' class='element-listeEntity' nom='"+This.nom+"' label='"+This.label+"'>"
	
	$result+="<article>"
	$result+=This.LibelléLié(This._tagUrl+"WebDATA/<!--#4DEVAL $1.nomFunction-->?<!--#4DEVAL $1.nomFORMlist-->?Sites?"+This.IDunique; New object("Options"; 0))
	$result+="</article>"
	
	$result+="<article class='ListeSites'><ul>"
	$result+=This.Events()
	$result+="</ul></article>"
	
	$result+="</article>"
	
	
Function Events()->$result : Text
	var $lieu : cs.Lieux
	
	This._LesLieux()
	
	$result:=""
	For each ($lieu; This.lesLieux.selection.orderBy("nom asc"))
		$result+="<li>"+$lieu.labeledNom+"</li>"
		$result:=$result+$lieu.NombreEvents()
	End for each 
	
	
Function get labeledNom()->$result : Text
	$result:=This.nom+" ("+This.label+")"
	
	
Function get label()->$result : Text
	$result:=This.cFct.LireLocatedSTR(This.type)
	
	
	// ----------------------
	//MARK: FORM Detail
	// ----------------------
	
Function Champ_1()->$result : Text
	This._LaCommune()
	$result:=This.laCommune.getBlasonURL(This.laCommune.nomFichierIcone)
	
	
Function Champ_2()->$result : Text
	$result:=This.nom
	
	
Function Champ_3()->$result : Text
	$result:=This.label
	
	
Function Champ_4()->$result : Text
	var $lieu : cs.Lieux
	
	This._LesLieux()
	
	$result:="<article class='ListeLieux'><ul>"
	For each ($lieu; This.lesLieux.selection)
		$result+="<li>"+$lieu.labeledNom+"</li>"
		$result+=$lieu.EtatCivil()
		$result+="</br>"
	End for each 
	$result+="</article></ul>"
	
	
Function Champ_titreIconed_1()->$result : Text
	$result:="16830.svg"
	
	
Function Champ_titreIconed_2()->$result : Text
	$result:=Localized string("152")
	
    

[class]$notesEditeur - 10/08/2026 09:35:58

      property serveurWeb : cs.$serveurWeb
property BDD : Object
property TableNom; nomBDD : Text

Class extends $notes


Class constructor()
	
	Super()
	
	This.serveurWeb:=cs.$serveur.new()
	
	
	
	// -----------------------------
	// MARK:Formulaire
	// -----------------------------
	
Function Modifier()
	// ouvrir l'éditeur de la BDD des journaux
	var $params : Object
	var $numProc : Integer
	
	$params:=New object
	$params.nomProcess:="U_Nav?NotesMobile"
	$params.nomTache:="Edition"
	$params.initProcess:=Formula(InitProcessComposantCooperative)
	
	$numProc:=Storage.Host.processUser.call(Null).NouveauProcessComposant("xWEBMO"; "$notesEditeur"; "OuvrirFormulaire"; $params)
	
	
Function OuvrirFormulaire()
	// méthode du process U_Nav Notes
	var $wndNum : Integer
	var $nomForm : Text
	
	// récupérer les paramètres (peuvent avoir changé depuis l'ouverture)
	This.InstallerDonnéesHote()
	
	NO DEFAULT TABLE
	// la fenêtre est ouverte en taille normale
	$nomForm:="U_Nav NotesMobile"
	
	$wndNum:=Open form window($nomForm; Palette form window; Horizontally centered; Vertically centered; *)
	
	// initialiser la valeur
	DIALOG($nomForm; This)
	CLOSE WINDOW
	CLEAR VARIABLE($wndNum)
	
	
Function estOccupé($état : Boolean)
	OBJECT SET VISIBLE(*; "AsynchroProgress"; $état)
	Form.AsynchroProgress:=Num($état)
	
	
Function CréerSélection()
	var $params : Object
	
	Case of 
		: (Not(OB Is defined(This; "BDD")))
		: (OB Is empty(This.BDD))
		: (Not(OB Is defined(This; "TableNom")))
		: (This.TableNom="")
		Else 
			
			// lister les notes de la table sélectionnée de la BDD sélectionnée
			$params:=OB Copy(This.BDD)
			$params.TableNom:=This.TableNom
			$params.wndRef:=Current form window
			$params.functionIDcallBack:="AfficherSelection"
			
			CALL WORKER("WK_Composant_MOB"; Formula from string("cs.$notesEditeur.new().SelectionnerNotes($1)"); $params)
			This.estOccupé(True)
	End case 
	
	
	// -----------------------------
	// MARK:Actions
	// -----------------------------
	
Function ListerBDDs($params : Object)
	// renvoyer la BDD notes du serveur
	// ici on est sur la BDDmère
	
	// méthode appelée dans un worker
	InitProcessComposantCooperative
	
	$params.functionID:="ListerBDDs"
	This.RequeterBDDnotes($params)
	// la liste est dans $params.reqRetour.ListeBDDs
	
	Case of 
		: ($params.reqRetour=Null)
			$params.reqRetour:=New object("ListeBDDs"; New collection)
			
		: (Not(OB Is defined($params.reqRetour; "ListeBDDs")))
			$params.reqRetour.ListeBDDs:=New collection
	End case 
	
	// au besoin, ajouter le journal local pour les tests du formulaire
	This.AjouterBDDdeTest($params.reqRetour.ListeBDDs)
	
	// retourner la liste ordonnée
	$params.reqRetour.ListeBDDs:=$params.reqRetour.ListeBDDs.orderBy("nomBDD asc")
	This.postCallBack($params)
	
	
Function AjouterBDDdeTest($c : Collection)
	// ajouter la BDD pour les tests
	// ici on est sur la BDDmère
	var $BDD : Object
	
	If ((Not(Storage.System.estExecuteDansHote)) | (Storage.Host.estDebugAPP()))
		// pour les tests depuis la BDD mère ou le composant
		$BDD:=New object
		$BDD.nomBDD:="BDDnotes_Test"
		$BDD.cheminBDD:=This.document.getBDDnotesFolder("BDD_NOTES").platformPath
		$BDD.methodeReq:="RequeterBDDnotesLocal"
		$c.push($BDD)
	End if 
	
	
Function AfficherListeBDDs($params : Object)
	// retour d'un call à un worker
	// méthode appelée dans le formulaire
	var $data : Object
	
	$data:=This.DécoderBase64($params.result)
	
	Form[$params.varName]:=New object
	Form[$params.varName].values:=$data.ListeBDDs.extract("nomBDD")
	Form[$params.varName].index:=-1
	Form[$params.varName].currentValue:=Choose($data.ListeBDDs.length=0; Localized string("1104"); Localized string("1105"))
	Form[$params.varName].params:=$data.ListeBDDs
	
	Form.estOccupé(False)
	
	
Function SelectionnerTables($params : Object)
	// on est dans la BDDmère
	// renvoyer les DataclassNoms de la BDD notes sélectionnée
	
	// méthode appelée dans un worker
	InitProcessComposantCooperative
	
	$params.functionID:="ListerTables"
	This[$params.methodeReq]($params)
	// la liste est dans $params.reqRetour.ListeTableNoms
	
	Case of 
		: ($params.reqRetour=Null)
			$params.reqRetour:=New object("ListeTables"; New collection)
			
		: (Not(OB Is defined($params.reqRetour; "ListeTables")))
			$params.reqRetour.ListeTables:=New collection
	End case 
	This.postCallBack($params)
	
	
Function AfficherListeTables($params : Object)
	// retour d'un call à un worker
	// méthode appelée dans le formulaire
	var $data : Object
	
	$data:=This.DécoderBase64($params.result)
	
	Form[$params.varName]:=New object
	Form[$params.varName].values:=$data.ListeTables.extract("nomTable").distinct().orderBy(ck ascending)
	Form[$params.varName].index:=-1
	Form[$params.varName].currentValue:=Choose($data.ListeTables.length>0; Localized string("1106"); Localized string("1107"))
	Form[$params.varName].params:=$data.ListeTables
	
	Form.estOccupé(False)
	
	
Function SelectionnerNotes($params : Object)
	// on est dans la BDDmère
	// renvoyer les enregistrements de la BDD notes sélectionnée
	// méthode appelée dans un worker
	
	InitProcessComposantCooperative
	
	$params.functionID:="ListerNotes"
	This[$params.methodeReq]($params)
	// la liste est dans $params.reqRetour.Notes
	
	Case of 
		: ($params.reqRetour=Null)
			$params.reqRetour:=New object("Notes"; New collection)
			
		: (Not(OB Is defined($params.reqRetour; "Notes")))
			$params.reqRetour.Notes:=New collection
	End case 
	This.postCallBack($params)
	
	
Function SupprimerSelection($params : Object)
	// supprimer les enregistrements de la BDD notes du serveur
	// méthode appelée dans un worker
	
	InitProcessComposantCooperative
	
	$params.functionID:="SupprimerNotes"
	This[$params.methodeReq]($params)
	
	// renvoyer la nouvelle liste
	This.SelectionnerNotes($params)
	
	
Function AfficherSelection($params : Object)
	// retour d'un call à un worker
	// méthode appelée dans le formulaire
	var $data : Object
	
	$data:=This.DécoderBase64($params.result)
	
	// alimenter le formulaire
	// fixer les titres de la LB Notes
	Case of 
		: (Not(OB Is defined($data; "NomsChamps")))
		: ($data.NomsChamps=Null)
		Else 
			For each ($params; $data.NomsChamps)
				OBJECT SET TITLE(*; "Notes_"+$params.libelle; $params.entete)
			End for each 
	End case 
	
	OBJECT SET VISIBLE(*; "grpNotes@"; False)
	Case of 
		: (Not(OB Is defined($data; "Notes")))
		: ($data.Notes=Null)
		Else 
			Form.Notes:=$data.Notes
			Form.NotesNbre:=String(Form.Notes.length)+" note"+Choose(Form.Notes.length>1; "s"; "")
			OBJECT SET VISIBLE(*; "grpNotes@"; Form.Notes.length>0)
	End case 
	
	Form.estOccupé(False)
	
	
	// -----------------------------
	// MARK:Requêtes BDD
	// -----------------------------
	
Function RequeterBDDnotes($params : Object)->$result : Object
	// exécuter la requête .function sur le serveur Web
	// ici c'est la BDDmère qui requête le serveur Web, la requête est traitée par une demande HTTP
	var $Error : Integer
	
	ASSERT(This.trace.DebugerMethode("Post requête"; Current method name; JSON Stringify($params; *)))
	$result:=Null
	
	$Error:=Storage.Host.RequeteHTTP.call().Requeter("/4DHTTP/xWEBMO/$serveurWeb/TraiterREQUETE"; "GET"; $params; "blob")
	// principe : la demande est envoyée au serveur Web Hôte ; 'sur connexion Web' appelle $serveurWeb.TraiterREQUETE() qui traite et renvoie le resultat
	$result:=$params.reqRetour
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Requête "+$params.functionID+" traitée "+Choose($Error=200; "[OK]"; "[KO]"); Current method name; "(resultat voir logs)")
	This.trace.EnvoyerMessages([msgk_log]; "Réponse Requête"; Current method name; JSON Stringify($result))
	
	
Function RequeterBDDnotesLocal($params : Object)->$result : Object
	// exécuter la requête .function sur le serveur local 
	// attention, ici c'est la BDD mère ou le Serveur
	
	$result:=Null
	
	Case of 
		: (Not(OB Is defined($params; "nomBDD")))
			This.trace.Créer(-15068; Current method name; "'nomBDD' n'est pas défini dans $1").LeverException([msgk_event; msgk_log])
		: (Not(OB Is defined($params; "functionID")))
			This.trace.Créer(-15068; Current method name; "'functionID' n'est pas défini dans $1").LeverException([msgk_event; msgk_log])
		: (Value type($params.functionID)#Is text)
			This.trace.Créer(-15068; Current method name; "'$1.functionID' n'est pas un texte").LeverException([msgk_event; msgk_log])
		: (Not(OB Is defined(This; $params.functionID)))
			This.trace.Créer(-15068; Current method name; "'$1.functionID' n'est pas une function de 'this'").LeverException([msgk_event; msgk_log])
		Else 
			// c'est ok
			// fixer la BDD
			This.nomBDD:=$params.nomBDD
			
			// exécuter la function
			This.serveurWeb[$params.functionID]($params)
			
			// le resultat est dans $params.reqRetour
			$result:=$params.reqRetour
	End case 
	
	
Function Exécuter($params : Object)
	// exécuter la requête en local ou sur le serveur, selon $params.methodeReq
	var $result : Object
	var $attribut : Text
	
	// requêter le serveur
	$result:=This[$params.methodeReq]($params)
	// mémoriser le resultat dans this
	For each ($attribut; OB Keys($result))
		This[$attribut]:=$result[$attribut]
	End for each 
	
	
Function postCallBack($params : Object)
	// appeler le formulaire $params.wndRef
	var $data : Object
	
	$data:=New object
	$data.params:=OB Copy($params)
	// il n'y a que le texte qui semble passer dans les .params
	$data.params.result:=This.EncoderBase64($params.reqRetour)
	// nettoyer
	OB REMOVE($data.params; "reqRetour")
	// construire l'appel au requêteur
	$data.execute:=Formula(cs.$notesEditeur.new()[$params.functionIDcallBack](This.params))
	
	CALL FORM($params.wndRef; Formula($data.execute().source))
	
	
	// -----------------------------
	// MARK:Utilitaires
	// -----------------------------
	
Function EncoderBase64($params : Object)->$result : Text
	var $dataBlob : Blob
	
	SET BLOB SIZE($dataBlob; 0)
	VARIABLE TO BLOB($params; $dataBlob)
	BASE64 ENCODE($dataBlob; $result)
	
	
Function DécoderBase64($dataTexte : Text)->$result : Object
	var $dataBlob : Blob
	
	SET BLOB SIZE($réception; 0)
	BASE64 DECODE($dataTexte; $dataBlob)
	BLOB TO VARIABLE($dataBlob; $result)
	
    

[class]$pageWebDATA - 31/07/2026 10:29:12

      property nav : cs.$pageWebNAV
property dataClassValide : Boolean
property DataClassNom : Text:=""
property listeDesEntites : Text:=""

Class extends $pageWeb

Class constructor()
	
	Super()
	
	This.nav:=cs.$pageWebNAV.new()
	
	
	
	//----------------------------------
	// MARK:Formulaires LISTE
	//----------------------------------
	
Function AfficherFORMliste()
	// This.paramsUrl[0] = nom du formulaire liste à envoyer
	// This.paramsUrl[1] = nom de la dataClass à utiliser
	
	Case of 
		: (Not(This.estSessionActive()))
		: (This.paramsUrl.length<2)
		Else 
			// cas nominal : tagger la liste
			Use (Session.storage.UserParams)
				Session.storage.UserParams.Contexte:="ALL"
			End use 
			
			// fixer la sélection courante
			This.nav.setSelectionNavAll(This.paramsUrl[1])
			This._AfficherFORMliste()
	End case 
	
	
Function AfficherFORMselection()
	// This.paramsUrl[0] = nom du formulaire liste à envoyer
	// This.paramsUrl[1] = nom de la dataClass à utiliser
	
	Case of 
		: (Not(This.estSessionActive()))
		: (This.paramsUrl.length<2)
		Else 
			// cas nominal : tagger la liste
			Use (Session.storage.UserParams)
				Session.storage.UserParams.Contexte:="SELECTION"
			End use 
			
			// fixer la sélection courante
			This.nav.setSelectionNavSelect(This.paramsUrl[1])
			This._AfficherFORMliste()
	End case 
	
	
Function RafraichirFORMselection()
	This._AfficherFORMliste()
	
	
Function _AfficherFORMliste()
	// This.paramsUrl[0] = nom du formulaire liste à envoyer
	// This.paramsUrl[1] = nom de la dataClass à utiliser
	var $nomPageWeb : Text
	
	If (This.paramsUrl.length>1)
		$nomPageWeb:=This.paramsUrl[0]+".shtml"
		This.Envoyer($nomPageWeb; This.paramsUrl[1])
	End if 
	
	
Function estSessionActive()->$result : Boolean
	// renvoyer true si la session est toujours active
	$result:=False
	
	Case of 
		: (This.ReConnexion())
			// une session WEBMO a été activée sur cookie : le logIn a été demandé
			// ou une session MOB est en cours et active
			
			// ici soit on est dans une session active (cas nominal, soit on est guest()
		: (Session.isGuest())
			// on demande une page liste mais on est guest (le cookie ou le fichier session sur Serveur n'existent plus)
			This.Envoyer("loginByEmail.shtml"; "accueil")
			
		Else 
			$result:=True
	End case 
	
	
Function Titrer()
	This.result.resultat:=Char(1)+This.Exécuter("Titrer")
	
	
Function listerEntities()
	var $texte : Text
	
	$texte:=This.InsérerAttente()
	
	Case of 
		: (Not(This.estDataClassValide()))
		: (Not(OB Is defined(cs; This.DataClassNom+"Select")))
			This.trace.Créer(-16801; Current method name; "Traitement URL :'la classe "+This.DataClassNom+"Select' n'existe pas").LeverException([msgk_event; msgk_log])
		Else 
			// liste à utiliser
			$texte:=This.nav.getListeHTML()
	End case 
	
	WEB SEND TEXT($texte; "text/html")
	
	
Function AfficherCollection()
	This.Envoyer("collection.shtml"; This.paramsUrl[0])
	
	
Function menuTrier()
	var $params; $data : Object
	
	If (This.paramsUrl.length>2)
		$params:=cs[This.paramsUrl[1]].new().FixerParametresListe()
		
		If (OB Is defined($params; This.paramsUrl[2]))
			// fixer le tri
			$data:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom]
			Use ($data)
				$data.tri:=$params[This.paramsUrl[2]]
			End use 
			
			// ré afficher (utilise paramsUrl[0])
			This.RafraichirFORMselection()
		End if 
	End if 
	
	
	//----------------------------------
	// MARK:Formulaires DETAIL
	//----------------------------------
	
Function AfficherFORMdetail()
	// This.paramsUrl[0] = nom du formulaire detail à envoyer
	// This.paramsUrl[1] = nom de la dataClass à utiliser
	// This.paramsUrl[2] = UUID de l'entité à afficher
	var $nomPageWeb : Text
	
	Case of 
		: (Not(This.estSessionActive()))
		: (This.paramsUrl.length<3)
		Else 
			// mémorisée l'entité sélectionnée
			This.nav.setCurrentEntity(This.paramsUrl[1]; This.paramsUrl[2])
			
			// nom de la page à envoyer
			$nomPageWeb:=This.paramsUrl[0]+".shtml"
			
			This.Envoyer($nomPageWeb; This.paramsUrl[1])
	End case 
	
	
Function getChamp()
	// retourner le champ This.paramsUrl[0] de l'entité courante de la classe courante
	var $entité : Object
	
	$entité:=This.nav.getCurrentEntity()
	$entité:=Storage[This.nav.nomBDD][$entité.DataClassNom].query("IDunique = :1"; $entité.IDunique)[0]
	This.result.resultat:=Char(1)+$entité["Champ_"+String(This.paramsUrl[0])]
	
	
	//----------------------------------
	// MARK:Utilitaires
	//----------------------------------
	
Function estDataClassValide()->$result : Boolean
	$result:=False
	
	Case of 
		: (Session=Null)
		: (Not(OB Is defined(Session.storage; "UserParams")))
		: (Not(OB Is defined(Session.storage.UserParams; "EtatNavigation")))
		: (Not(OB Is defined(cs; Session.storage.UserParams.EtatNavigation)))
		Else 
			$result:=True
			This.dataClassValide:=$result
			
			This.DataClassNom:=Session.storage.UserParams.EtatNavigation
	End case 
	
	This.trace.Créer(-16801*Num(Not($result)); Current method name; "Traitement URL :'la classe "+Session.storage.UserParams.EtatNavigation+"' n'existe pas").LeverException([msgk_event; msgk_log])
	
	
Function Exécuter($nomFunction : Text)->$result : Text
	// appel interne à la classe
	$result:=""
	Case of 
		: (Not(This.estDataClassValide()))
		: (Not(OB Is defined(cs[This.DataClassNom].new(); $nomFunction)))
			// function inconnue
			This.trace.Créer(-16801; Current method name; "Traitement URL :'"+$nomFunction+"' n'est pas une function de la classe '"+This.DataClassNom+"'").LeverException([msgk_event; msgk_log])
		Else 
			$result:=cs[This.DataClassNom].new()[$nomFunction]()
	End case 
	
	
Function InsérerAttente()->$result : Text
	var $texte : Text
	
	$texte:=Folder(fk resources folder).folder("TemplatesPagesWeb").file("attente.html").getText()
	$texte:=Replace string($texte; Char(Line feed); "")
	$texte:=Replace string($texte; "  "; "")
	
	$texte:=Substring($texte; Position("<body>"; $texte)+Length("<body>"))
	$texte:=Substring($texte; 1; Position("</body>"; $texte)-1)
	
	PROCESS 4D TAGS($texte; $result)
	
    

[class]$albums - 26/11/2025 09:14:45

      // _albums est indépendant des actions mobile
property webALB : cs.xALB.$pageWebAlbum
property reqALB : cs.xALB.$pageWebREQUETE
property UserParams : Object

Class extends $serveur

Class constructor($url : Text)
	
	Super()
	
	This.LireParametresUrl($url)
	// récupérer le nom de la function
	Case of 
		: (This.balisesUrl=Null)
		: (This.balisesUrl.length<3)
		Else 
			This.url:=This.balisesUrl[2]
	End case 
	
	This.InitSession()
	This.UserParams:=Session.storage.ALB_UserParams
	
	// on va utiliser les functions de ALB
	This.webALB:=cs.xALB.$pageWebAlbum.new($url)
	This.reqALB:=cs.xALB.$pageWebREQUETE.new($url)
	
	
	
Function labelDataClass()->$result : Text
	$result:=Uppercase("l'album")
	
	
Function InitSession()
	
	Super.InitSession("ALB")
	
	// paramètres propres ALB
	Case of 
		: (Session=Null)
		: (Not(OB Is defined(Session; "storage")))
		Else 
			
			If (Not(OB Is defined(Session.storage; "ALB_UserParams")))
				Use (Session.storage)
					Session.storage.ALB_UserParams:=New shared object
				End use 
			End if 
			
			// fixer le récepteur des albums
			If (Not(OB Is defined(Session.storage.ALB_UserParams; "Albums")))
				Use (Session.storage.ALB_UserParams)
					Session.storage.ALB_UserParams.Albums:=New shared collection
				End use 
			End if 
			
			// passer à ALB les noms de function / page html utilisés ici
			Use (Session.storage.ALB_Data)
				Session.storage.ALB_Data.urlOuvrirALB:="/$albums/OuvrirAlbum"
				Session.storage.ALB_Data.urlVisualiserALB:="/$albums/VisualiserAlbum"
				Session.storage.ALB_Data.pageAccueilALB:="mobileAlbumsAccueil.shtml"
				Session.storage.ALB_Data.pageVisuALB:="mobileAlbumVisualiser.shtml"
				Session.storage.ALB_Data.avecContact:=False
			End use 
			
	End case 
	
	
	// -----------------------------
	// MARK:Traitement des URL albums
	// point d'entrée des traitements et autres ALB
	// -----------------------------
	
Function Traiter()->$result : Boolean
	$result:=False
	Case of 
		: (This.url="")
		: (Not(OB Is defined(This; This.url)))
			// l'url est autre chose qu'une function de this ; laisser passer
		Else 
			$result:=True
			// exécuter l'url
			This[This.url]()
	End case 
	
	
Function mobileAlbumsConnecter()->$result : Boolean
	// envoyer la page de la liste des albums
	This.Envoyer(Session.storage.ALB_Data.pageAccueilALB; "Albums")
	$result:=True
	
	
Function OuvrirAlbum()->$result : Boolean
	// envoyer la page des vignettes de l'album sélectionné
	This.webALB.OuvrirAlbum()
	$result:=True
	
	// fixer les variables process (ID de l'album ouvert)
	Use (Session.storage.UserParams)
		Session.storage.UserParams.EtatNavigation:="ALB_"+This.paramsUrl[0]
	End use 
	
	
Function VisualiserAlbum()->$result : Boolean
	This.webALB.VisualiserAlbum()
	$result:=True
	
	
Function Envoyer($nomPageWeb : Text; $EtatNavigation : Text)
	// fixer les variables process
	// pour ici
	Use (Session.storage.UserParams)
		Session.storage.UserParams.EtatNavigation:=$EtatNavigation
	End use 
	
	// pour le composant ALB
	Use (Session.storage.ALB_HTTPvars)
		Session.storage.ALB_HTTPvars.wwwEtatNavigation:="Accueil"
	End use 
	
	WEB SEND FILE($nomPageWeb)
	
	
	// -----------------------------
	// MARK:Elements HTML spécifiques MOB
	// -----------------------------
	
Function LireSTR()->$result : Text
	$result:=""
	If (This.paramsUrl.length>0)
		$result:=Localized string(This.paramsUrl[0])
	End if 
	$result:=Char(1)+$result
	
	
Function ListerAlbums()->$result : Text
	var $dossiers : Collection
	
	$dossiers:=cs._document.new().getWEBalbumsFolder().folders()
	This.webALB.Lister($dossiers)
	
	$result:=This.webALB.result.resultat
	
	
	// -----------------------------
	// MARK:Requete HTML
	// -----------------------------
	
Function mobileAlbumAjouterNote()
	// traiter la requete
	// les données sont dans le corps
	var $corps : Text
	var $data; $requete : Object
	
	$requete:=New object("success"; False; "EntitéRéduite"; New object)
	
	ARRAY TEXT($tabChamps; 0)
	ARRAY TEXT($tabValeurs; 0)
	WEB GET HTTP HEADER($tabChamps; $tabValeurs)
	
	Case of 
		: (Find in array($tabChamps; "Content-Type")=-1)
		: ($tabValeurs{Find in array($tabChamps; "Content-Type")}#"application/json")
		Else 
			// ok on a reçu un objet
			WEB GET HTTP BODY($corps)
			$data:=JSON Parse($corps)
			
			Case of 
				: (Not(OB Is defined($data; "indexMedia")))
				: (Not(OB Is defined($data; "note")))
				: (Not(OB Is defined($data.note; "objet")))
				: (Not(OB Is defined($data.note; "contenu")))
				Else 
					$requete.success:=True
					
					$requete.EntitéRéduite.DataClassNom:="ALB_"+Session.storage.ALB_UserParams.Album.Identification_ID
					$requete.EntitéRéduite.IDunique:=Session.storage.ALB_UserParams.Album.Medias[$data.indexMedia].MED_IDunique
					$requete.EntitéRéduite.params:=$data.note
					
					//TODOcs.$notes.new($requete).EcrireNote()
					
			End case 
	End case 
	
	WEB SEND TEXT("traitement "+Choose($requete.success; "OK"; "KO"))
    

[class]Unions - 03/12/2025 11:19:50

      property leEvent : cs.Events
property lesMembres : Object
// attributs de la classe
property SansEnfant : Boolean

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	// initialiser l'objet avec les données de l'entité $IDunique de la BDD
	var $c : Collection
	
	Super("Unions"; $IDentité)
	
	// pour trier les unions, mettre l'event ici
	// attention il peut ne pas y en avoir
	This.leEvent:=Null
	$c:=This.dataClass.LesEvents()
	// prendre le premier
	If ($c.length>0)
		This.leEvent:=cs.Events.new($c[0])
	End if 
	
	
	
Function Libellé($formats : Object)->$result : Text
	// renvoie le nom formaté  des protagonistes suivant les options $formats
	// $formats
	// l'union (parentale par exemple) peut être nulle
	var $sélection : cs.PersonnesSelect
	var $entité : cs.Personnes
	var $lien : Text
	
	If (This.ID#0)
		// sélectionner les membres de l'union
		$sélection:=This.LesProtagonistes()
		
		// coder leur noms
		$result:=""
		$lien:=" & "
		For each ($entité; $sélection.selection)
			$result:=$result+$lien+$entité.Libellé($formats)
		End for each 
		
		// nettoyer
		$result:=Replace string($result; $lien; ""; 1)
	End if 
	
	
	// ----------------------
	//MARK:Sélection
	// ----------------------
	
Function LesEvenementsFamiliaux()->$result : cs.EventsSelect
	// créer la collection des events triée par date
	$result:=cs.EventsSelect.new(This.dataClass.LesEvents())
	$result.Créer()
	$result.selection:=$result.selection.orderBy("dateNum asc")
	
	
Function LesProtagonistes()->$result : cs.PersonnesSelect
	// créer la sélection de personnes
	var $c : Collection
	
	$result:=cs.PersonnesSelect.new(This.dataClass.LesProtagonistes())
	
	// ordonner les protagonistes : membre1 = homme, membre2 = femme
	$result.Créer()
	$c:=$result.selection  // le personnes
	
	This.lesMembres:=New object
	// rappel : homme sexe = faux, femme sexe = vrai
	// plusieurs cas
	Case of 
		: ($c.length=0)
			This.lesMembres:=Null
			
		: ($c.length=1)
			This.lesMembres.membre1:=Null
			This.lesMembres.membre2:=Null
			If ($c[0].sexe)
				This.lesMembres.membre2:=$c[0]
			Else 
				This.lesMembres.membre1:=$c[0]
			End if 
			
		Else 
			$c:=$c.orderBy("sexe asc")
			This.lesMembres.membre1:=$c[0]
			This.lesMembres.membre2:=$c[1]
	End case 
	
	
Function LeConjoint($ID : Integer)->$result : Object
	// trouver les membres de l'union
	This.LesProtagonistes()
	
	Try
		// chercher dans les membres celui dont l'ID n'est pas $ID
		If (This.lesMembres.membre1.ID=$ID)
			$result:=This.lesMembres.membre2
		Else 
			$result:=This.lesMembres.membre1
		End if 
		
	Catch
		// conjoint absent
		$result:=Null
	End try
	
    

[class]$serveur - 17/07/2026 15:23:56

      // pour optimiser les appels, cette classe n'étend pas $composant
property trace : cs._Trace
property fct : cs.xSDK.Outils
property rsc : cs.xSDK.ResourceALV
property balisesUrl; paramsUrl : Collection
property paramsBody : Object
// utiliser par les composants
property url : Text:=""
property result : Object:=Null

Class constructor()
	
	This.trace:=cs._Trace.me
	This.fct:=cs.xSDK.Outils.me
	This.rsc:=cs.xSDK.ResourceALV.me
	
	
	
	
	// ----------------------
	//MARK:Serveur
	// ----------------------
	
Function Demarrer()
	var $data; $params; $result : Object
	var $c : Collection
	var $webServer : 4D.WebServer
	
	$c:=WEB Server list.query("name = :1"; Storage.ServeurHTTP.nom)
	
	Case of 
		: ($c.length=0)
			// pas de serveur ; le créer (test du composant)
			$webServer:=WEB Server(Web server database)
			
		: ($c[0].isRunning)
			// déjà démarré ; abandonner
			$webServer:=Null
			
		Else 
			// récupérer le serveur
			$webServer:=$c[0]
	End case 
	
	If ($webServer=Null)
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Serveur Web"; Current method name; "Le serveur Web est déjà démarré")
		
	Else 
		// fixer les paramètres généraux du serveur Web
		$data:=New object
		This.FixerParametres($data)
		
		// par défaut, pas de certificat => pas https
		
		// Enable HTTP on your 4D Web server (no certificates)
		$data.HTTPEnabled:=True
		
		// Disable HTTPS on your 4D Web server
		$data.HTTPSEnabled:=False
		$data.HSTSEnabled:=False
		
		// état du certificat SSL
		$params:=cs._document.new().CertInformations()
		Case of 
			: (Not($params.Production.exists))
				// on n'a pas de certificat SSL
				This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Paramétrage Serveur Web : Cert"; Current method name; "certificat SSL en production non trouvé")
				
			: ($params.Production.Invalide)
				// on n'a pas de certificat SSL valide
				This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Paramétrage Serveur Web : Cert"; Current method name; "certificat SSL trouvé en production non valide, doit être renouvelé")
				
			Else 
				// options HTTPS
				// age maximal d'activation du HSTS pour une session
				$data.HSTSMaxAge:=7776000  //   7 776 000 = 3 * 30 * 24 * 3600, soit 3 mois.    31 536 000 = 365 * 86400 s = 365 days
				
				// Enable HSTS on the 4D Web server
				// rappel : HSTS activé permet au serveur web 4D de déclarer que les navigateurs ne doivent interagir avec lui que par des connexions HTTPS sécurisées
				// important : ici HTTP est aussi activé : le navigateur peut toujours basculer entre HTTPS et HTTP (par exemple, dans la zone URL du navigateur, l'utilisateur peut remplacer "https" par "http")
				// on pourra déactiver HTTP
				$data.HSTSEnabled:=True
				
				// Enable HTTP on your 4D Web server
				$data.HTTPEnabled:=True
				
				// Enable HTTPS on your 4D Web server
				$data.HTTPSEnabled:=True
		End case 
		
		// fixer le point d'entrée du site Web mobile
		This.InstallerURLdémarrage("mobile")
		
		// crééer la page RGPD (lien dans Apple Store Connect)
		This.InstallerPageSupport("support")
		
		// c'est ok, on démarre
		This.trace.EnvoyerMessages([msgk_log]; "Serveur Web"; Current method name; "$data "+JSON Stringify($data))
		$result:=$webServer.start($data)
		
		// message ALV
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "dossier racine Web"; Current method name; "<"+$data.rootFolder.platformPath+">")
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; Current method name; "Le serveur Web "+Choose($webServer.HTTPSEnabled; "[HTTPS]"; "[HTTP]")+" est démarré "+Choose($webServer.isRunning; "[OK]"; "[KO]")+", HSTS "+Choose($webServer.HSTSEnabled; "[OK]"; "[KO]"))
	End if 
	
	
Function Arreter()
	var $vb_stopped : Boolean
	
	If (WEB Server(Web server database).isRunning)
		ASSERT(This.trace.DebugerMethode("Stop"; Current method name; "stopping web server..."))
		
		WEB Server(Web server database).stop()
	End if 
	
	$vb_stopped:=Not(WEB Server(Web server database).isRunning)
	ASSERT($vb_stopped; "web server failed to stop")
	ASSERT(This.trace.DebugerMethode("Stop"; Current method name; "web server stopped. "+Choose($vb_stopped; "[OK]"; "[KO]")))
	
	// message ALV
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Serveur Web"; Current method name; "Le serveur Web est arrêté "+Choose($vb_stopped; "[OK]"; "[KO]"))
	
	
Function FixerParametres($params : Object)
	// fixer les paramètres du serveur Web de ce composant
	var $dataEntier : Integer:=0
	var $adresseIP : Text
	var $dossier : 4D.Folder
	
	
	// l'adresse IP du serveur Web 4D : lire l'adresse IP de la machine sur le réseau
	$adresseIP:=cs.xSDK.EnvironnementALV.new().infosSystème().IPadresse
	This.rsc.SetResourceALV(Est Ressource WEBMO; "Serveur_web/Web_Adresse_IP_ecoute"; ->$adresseIP)
	$params.IPAddressToListen:=$adresseIP
	
	// fixer les options APP
	//  surcharge les valeurs définies dans les propriétés de la base
	This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/portHTTP_sousDomaine"; Is longint; ->$dataEntier)
	$params.HTTPPort:=$dataEntier
	
	This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/portHTTPS_sousDomaine"; Is longint; ->$dataEntier)
	$params.HTTPSPort:=$dataEntier
	
	This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/Web_Timeout_process"; Is longint; ->$dataEntier)
	$params.inactiveProcessTimeout:=$dataEntier
	
	This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/Web_Timeout_session"; Is longint; ->$dataEntier)
	$params.inactiveSessionTimeout:=$dataEntier
	
	$params.scalableSession:=True
	//$params.sessionCookieName:="ALV_WEBmobile"  //  "" rétablit la valeur par défaut (4DSID)
	
	// fixer le dossier des certificats
	$dossier:=cs._document.new().getCertificatSSLFolder()
	If ($dossier.exists)
		$params.certificateFolder:=$dossier
	End if 
	
	// fixer le dossier racine
	$dossier:=cs._document.new().getRacineHTMLFolder()
	If ($dossier.exists)
		$params.rootFolder:=$dossier
	End if 
	
	
Function getAdresseServeur()->$result : Text
	var $dataTexte : Text:=""
	
	If (Storage.System.typeApplication=ALV Serveur APP)
		// url du serveur WEB opérationnel
		This.rsc.SetVariable(Est Ressource APP; "serveur_URL/Nom_sousDomaine"; Is text; ->$dataTexte)
		
	Else 
		// pour test 
		This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/Web_Adresse_IP_ecoute"; Is text; ->$dataTexte)
	End if 
	$result:=$dataTexte
	
	
Function LireInformationsServeur()->$result : Object
	// informations du serveur Web
	var $dataTexte : Text:=""
	var $i : Integer:=0
	var $texte : Text
	
	// en principe : ALV Serveur APP est interrogé par ALV Client APP, et ALV Serveur HTTP par 4D mode distant
	$result:=New object
	
	// *** les paramètres
	$result.params:=New object
	// dossier racine
	$result.params.DossierRacineHTML:=Get 4D folder(HTML Root folder; *)  //Lire Ressource ALV(Est Ressource WEB; "Chemins/Serveur_web/Dossier_racine_web"; Est un texte)->
	$result.params.estDémarré:=WEB Is server running
	
	// paramètres
	WEB GET OPTION(Web port ID; $i)
	$result.params.webPortID:=$i
	WEB GET OPTION(Web HTTPS port ID; $i)
	$result.params.webHTTPSPortID:=$i
	WEB GET OPTION(Web HTTPS enabled; $i)
	$result.params.HTTPSEnabled:=($i=1)
	WEB GET OPTION(Web HSTS enabled; $i)
	$result.params.HSTSEnabled:=($i=1)
	
	// URL serveur
	This.rsc.SetVariable(Est Ressource APP; "serveur_URL/Nom_sousDomaine"; Is text; ->$dataTexte)
	$texte:="://"+$dataTexte
	If ($result.params.HTTPSEnabled)
		$texte:="https"+$texte
		This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/portHTTPS_sousDomaine"; Is longint; ->$i)
		$texte:=$texte+":"+String($i)
	Else 
		$texte:="http"+$texte
		This.rsc.SetVariable(Est Ressource WEBMO; "Serveur_web/portHTTP_sousDomaine"; Is longint; ->$i)
		$texte:=$texte+":"+String($i)
	End if 
	$result.params.URLserveurWEB:=$texte
	
	// *** info serveur composant 
	$result.serveurWeb:=WEB Server(Web server database)
	
	// au cas ou, passer en objet pur
	$dataTexte:=JSON Stringify($result)
	$result:=JSON Parse($dataTexte)
	
	
	// ----------------------
	//MARK:Connexions
	// ----------------------
	
Function InstallerURLdémarrage($nomPage : Text)
	var $fichier; $fileFTP : 4D.File
	var $result : cs.xSDK.Traces
	var $texteOUT : Text
	var $texteIN:=""
	var $data : Object
	
	$data:=New object
	
	// fixer l'url du point d'entrée du serveur ; 2 cas : opérationnel et test local
	This.rsc.SetObjet(Est Ressource WEBMO; "Serveur_web/portHTTPS_sousDomaine"; Is longint; $data; "port")
	$data.adresse:=This.getAdresseServeur()
	$data.nomPage:=Choose(Storage.System.typeApplication=ALV Serveur APP; $nomPage+".html"; $nomPage+"test.html")
	$data.url:="https://"+$data.adresse+":"+String($data.port)+"/4DCGI/WebLOGIN/seConnecter"
	
	$fileFTP:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug").file($data.nomPage)
	
	$fichier:=Folder(fk resources folder).folder("TemplatesMobileWeb").file($nomPage+"Proto.html")
	If ($fichier.exists)
		$texteIN:=$fichier.getText()
		PROCESS 4D TAGS($texteIN; $texteOUT; $data)
		$fileFTP.setText($texteOUT)
		
		// transfert sur l'hébergeur
		This.rsc.SetObjet(Est Ressource HOST; "Chemins/siteWEBstatique/Path"; Is text; $data; "url")
		$result:=cs.xSDK.ServicesFTP.new().EnvoyerFichier($fileFTP.platformPath; $data.url)
		
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Intégration "+Choose($result.success; "[OK]"; "[KO]"); Current method name; "URL racine Web installée sur hébergeur : '"+$data.url+"'")
	End if 
	
	
Function InstallerPageSupport($nomPage : Text)
	var $fichier; $fileFTP : 4D.File
	var $result : cs.xSDK.Traces
	var $texteOUT : Text
	var $texteIN:=""
	var $data : Object
	
	$data:=New object
	
	// fixer l'url du point d'entrée du serveur ; 2 cas : opérationnel et test local
	This.rsc.SetObjet(Est Ressource WEBMO; "Serveur_web/portHTTPS_sousDomaine"; Is longint; $data; "port")
	$data.adresse:=This.getAdresseServeur()
	$data.nomPage:=$nomPage+".html"
	
	$fileFTP:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug").file($data.nomPage)
	
	$fichier:=Folder(fk resources folder).folder("TemplatesMobileWeb").file($nomPage+"Proto.html")
	If ($fichier.exists)
		$texteIN:=$fichier.getText()
		PROCESS 4D TAGS($texteIN; $texteOUT; $data)
		$fileFTP.setText($texteOUT)
		
		// transfert sur l'hébergeur
		This.rsc.SetObjet(Est Ressource HOST; "Chemins/siteWEBstatique/Path"; Is text; $data; "url")
		$result:=cs.xSDK.ServicesFTP.new().EnvoyerFichier($fileFTP.platformPath; $data.url)
		
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Intégration "+Choose($result.success; "[OK]"; "[KO]"); Current method name; "URL racine Web installée sur hébergeur : '"+$data.url+"'")
	End if 
	
	
	// ----------------------
	//MARK:Connexions
	// ----------------------
	
Function surAuthentificationWeb($url : Text; $entete : Text; $IPnavigateur : Text; $IPserveur : Text; $LogIn : Text; $motDePasse : Text)->$result : Boolean
	// Appelée à la réception d'une 4Daction (4DSCRIPT...), d'une url 4DCGI (pas seulement?)
	// retourne toujours vrai sinon ; l'action est validée
	// fixe un .userInfo pour la suite des opérations (vide = pas d'utilisateur identifié)
	var $data : Object
	var $pageWeb : cs.$pageWeb
	
	// pour les erreurs
	cs._CommunFunctions.me.InitProcess()
	
	$data:=New object
	$data.Commande:=$url
	$data.IPnavigateur:=$IPnavigateur
	$data.utilisateur:=$LogIn
	ASSERT(This.trace.DebugerVariables($url; Current method name; $data))  //; "Options"; 0x0002
	
	$pageWeb:=cs.$pageWeb.new()
	
	// normalement toujours vrai
	$result:=True
	
	Case of 
		: (cs.$session.me.existeSession())
			// une session Web est toujours en cours
			
		: (cs.$session.me.getSessionDeBody($data))
			// une session est en cours et active
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Session MOB en cours"; Current method name; $data.infos.session.id)
			
		: (cs.$session.me.getSessionDeCookie($data))
			// le cookie pointe une session enregistrée ; on a récupéré ses infos dans .infos
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Ré ouverture de la session"; Current method name; $data.infos.session.id+" (voir infos dans logs)")
			This.trace.EnvoyerMessages([msgk_log]; "Ré ouverture de la session"; Current method name; JSON Stringify($data.infos))
			
		: (Session.isGuest())
			// ouverture par invité Web ; ses données
			// ici session.userName vaut "", Session.isGuest()=vrai
			cs.$session.me.InitUserData()
			
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Session Guest"; Current method name; "Session id "+Session.id+" - idleTimeout "+String(Session.idleTimeout)+" - expirationDate "+String(Session.expirationDate))
			
		Else 
			// what else?
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Authentification KO"; Current method name; "Session id "+Session.id)
			$result:=False
	End case 
	
	// mettre à jour le contexte
	$pageWeb.InitContexte()
	// initialiser les variables process
	// dont vs4D : prévenir le navigateur que le serveur est présent (à faire tout de suite)
	$pageWeb.RestaurerHTTPvars("HTTPvars")
	// attention : ici, v7.4.5, un ASSERT(DEBUG ALV (Storage.System;"REQUEST:TRACE_VARIABLE"... empêche celui de "sur connexion Web"
	//TracerSession(Créer objet("méthode"; Nom méthode courante; "url"; $url; "$0"; Chaîne($0)))
	
	
Function surConnexionWeb($url : Text; $entete : Text; $adresseIPnavigateur : Text; $adresseIPserveur : Text; $LogIn : Text; $motDePasse : Text)
	// init du process Web (le process de la session peut avoir changé)
	cs._CommunFunctions.me.InitProcess()
	
	// essayer une url ALV
	// Compiler_Web n’a pas été appelé, toutes les variables et sélections existent
	Case of 
			// essayer de traiter toutes les url envoyées par le client HTTP
		: (cs.$authentificationEmail.new().ActiverSession($url))
			// ok le mobile User est authentifié
			
		: (This.TraiterURLpage($url))
			// commande traitée
			
		: (This.TraiterURLaction($url))
			// commande traitée
			
			//  // essayer les commandes des composants :
			// essayer les commandes des albums
		: (cs.xALB.$serveurWeb.new().ConnexionWeb($url; $entete; $adresseIPnavigateur; $adresseIPserveur; $LogIn; $motDePasse))
			// commande traitée
			
			// filtrer certaines erreurs
		: ($url="/ressources@")
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Serveur Web"; Current method name; "L'url '"+$url+"' n'est pas traitée")
			
		: (Session=Null)
			// on n'est pas sur le serveur Web
			
		Else 
			// commande inconnue, retour à la case départ
			cs._Trace.me.DebugerMethode($url; Current method name; $url+" : commande 4DCGI inconnue")
	End case 
	
	cs._Trace.me.Session(Current method name; $url)
	
	
	// ----------------------
	// MARK:Traitement des url
	// ----------------------
	
Function TraiterURLpage($url : Text)->$result : Boolean
	// traiter toutes les url envoyées par un formulaire en construction
	// remarque : ces url adressent la BDD
	// transmettre l'url à la bonne classe $page pour traitement
	
	This.result:=This.InitResult(0; ""; False)
	This.result.nomMethode:=Current method name
	This.result.resultat:=""
	
	// analyser l'url
	This.LireParametresUrl($url)
	
	Case of 
		: (This.balisesUrl=Null)
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incomplète - pas de balises à traiter"
			
		: (This.balisesUrl.length<2)
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incomplète - le contexte de $page, balisesUrl[2], est absent"
			
		: (Not(OB Is defined(cs; "$page"+This.balisesUrl[1])))
			// url non traitée ici
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incomplète - la classe $page"+This.balisesUrl[1]+" est inconnue"
			
		Else 
			// créer la classe et traiter l'url
			// le résultat, s'il y a, est dans $result.resultat
			// en cas d'erreur elle peut renseigner $result
			This.result:=cs["$page"+This.balisesUrl[1]].new().TraiterURL($url)
	End case 
	
	This.result.success:=(This.result.Error=0)
	$result:=This.result.success
	
	
Function TraiterURLaction($url : Text)->$result : Boolean
	// traiter toutes les url liées à des actions
	// remarque : ces url concernent les albums, carto, arbres et notes
	// transmettre l'url à la bonne classe pour traitement
	$result:=True
	
	Case of 
		: (cs.$albums.new($url).Traiter())
			// url traité par l'album
			
		: (cs.$action.new().TraiterURL($url))
			// url traité par le serveur
			
		Else 
			// pas concerné !
			$result:=False
	End case 
	
	
Function LireParametresUrl($url : Text)
	// $url est de la forme /4Dxxx/yyy/nomFunction {?param1 {+?param2...}}, avec xxx = CGI ou ACTION ou SCRIPT, yyy le contexte WEB, APP ...
	var $corps : Text
	
	// lire les balises
	This.balisesUrl:=Split string($url; "/"; sk ignore empty strings)
	This.paramsUrl:=Null
	This.paramsBody:=Null
	
	If (This.balisesUrl.length>0)
		// analyser la function
		This.paramsUrl:=Split string(This.balisesUrl[This.balisesUrl.length-1]; "?")
		// virer function+params
		This.balisesUrl:=This.balisesUrl.slice(0; This.balisesUrl.length-1)
		// ajouter la function seule
		This.balisesUrl.push(This.paramsUrl[0])
		// lister les paramètres
		This.paramsUrl.shift()
		
		// nettoyer les paramètres
		Case of 
			: (This.paramsUrl.length<2)
				// rien à faire
			: (This.paramsUrl[0]="@+")
				// nettoyer les '+'
				This.paramsUrl:=Split string(This.paramsUrl.join(""); "+")
				ALERT(Current method name+".  +")
			: (This.paramsUrl[0]="@&")
				// l'url vient de javascript, le séparateur de params est '&' ('+' ne passe pas)
				This.paramsUrl:=Split string(This.paramsUrl.join(""); "&")
				ALERT(Current method name+".  &")
		End case 
		
		
		ARRAY TEXT($clésEntête; 0)
		ARRAY TEXT($valeursEntête; 0)
		WEB GET HTTP HEADER($clésEntête; $valeursEntête)
		
		// lister les paramètres
		Case of 
				// cas méthode GET
			: (Size of array($clésEntête)=0)
			: ($valeursEntête{Find in array($clésEntête; "X-METHOD")}="GET")
				// les paramètres viennent de l'URL : ils sont dans This.paramsUrl
				// rien à faire
				
				// cas méthode POST
			: ($valeursEntête{Find in array($clésEntête; "X-METHOD")}#"POST")
			: (Find in array($clésEntête; "Content-Type")=-1)
			: ($valeursEntête{Find in array($clésEntête; "Content-Type")}#"application/json")
			Else 
				// les paramètres sont dans le body
				// ok on a reçu un objet
				WEB GET HTTP BODY($corps)
				This.paramsBody:=JSON Parse($corps)
		End case 
		
	Else 
		This.balisesUrl:=Null
	End if 
	
	
	//----------------------------------
	// MARK:Utilitaires
	//----------------------------------
	
Function InitResult($Error : Integer; $ErrorDescription : Text; $success : Boolean)->$result : Object
	
	If (Count parameters=0)
		$Error:=0
		$ErrorDescription:=""
		$success:=True
	End if 
	$result:=New object("Error"; $Error; "ErrorDescription"; $ErrorDescription; "success"; $success)
	
	$result.nomMethode:=Current method name
	
	
Function InitSession($portée : Text)
	// paramètres communs à tous les contextes
	
	Case of 
		: (Session=Null)
			// en test par exemple
		: (Not(OB Is defined(Session; "storage")))
			// pourquoi ?
		Else 
			If (Not(OB Is defined(Session.storage; $portée+"_HTTPvars")))
				Use (Session.storage)
					// tous les attributs de $portée_HTTPvars sont des noms de variables process
					Session.storage[$portée+"_HTTPvars"]:=New shared object
					// les autres données
					Session.storage[$portée+"_Data"]:=New shared object
				End use 
			End if 
	End case 
	
    

[class]Personnes - 30/10/2025 18:17:52

      property lesEvents : cs.EventsSelect
// attributs de la classe
property nom; prenom; autres_prenoms; metier; commentaire : Text
property sexe : Boolean

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	
	Super("Personnes"; $IDentité)
	
	This.lesEvents:=Null
	
	
Function FixerParametresListe()->$result : Object
	$result:=New object
	$result.DataClassNom:="Personnes"
	$result.Requete:="id>0"
	$result.nomFunction:="AfficherFORMdetail"
	$result.nomFORMlist:="detailSimple"
	$result.TriParNom:="nom asc, prenom asc"
	$result.TriParPrenom:="prenom asc, nom asc"
	
	
Function CreerBDDentity()->$result : Object
	$result:=New object
	$result.IDunique:=This.IDunique
	$result.nom:=This.nom
	$result.prenom:=This.prenom
	
	$result.Elementliste:=This.Elementliste()
	// les champs calculés
	Super.CreerBDDentity($result; ["Champ_1"; "Champ_2"; "Champ_3"; "Champ_4"; "Champ_boutonLibelle_1"; "Champ_boutonLibelle_2"; "Champ_titreIconed_1"; "Champ_titreIconed_2"])
	
	
Function labelDataClass()->$result : Text
	$result:=Super.labelDataClass("la ")
	
	
Function RédigerCommentaire($formats : Object)->$result : Text
	var $c : Collection
	
	$result:=""
	$c:=New collection
	Case of 
		: (Not($formats.params ?? 0))
		: (Not(OB Is defined($formats; "séparateurComments")))
		: (Not(OB Is defined($formats; "débutComment")))
		: (Not(OB Is defined($formats; "finComment")))
		Else 
			$c.push(This.metier; This.commentaire)
			$result:=$c.join($formats.séparateurComments; ck ignore null or empty)
	End case 
	$result:=($formats.débutComment+$result+$formats.finComment)*Num(Length($result)#0)
	
	
	// ----------------------
	//MARK:Sélection
	// ----------------------
	
Function Naissance()->$result : Object
	// renvoie un objet _EvenementPersonnel
	$result:=This.EvenementPersonnel(22000)
	
	
Function Baptême()->$result : Object
	// renvoie un objet _EvenementPersonnel
	$result:=This.EvenementPersonnel(22300)
	
	
Function Décès()->$result : Object
	// renvoie un objet _EvenementPersonnel
	$result:=This.EvenementPersonnel(22100)
	
	
Function EvenementPersonnel($type : Integer)->$result : Object
	// renvoie l'entité events perso de type $1
	var $c : Collection
	
	If (This.lesEvents=Null)
		This.cFct.AjouterObjetA(This.LesEvents(); This; "lesEvents")
	End if 
	
	$result:=Null
	$c:=This.lesEvents.selection.query("type = :1"; $type)
	If ($c.length>0)
		$result:=$c[0]
	End if 
	
	
Function LesMediasPerso()->$result : cs.MediasSelect
	// créer la collection des medias perso (type = 1)
	$result:=cs.MediasSelect.new(This.dataClass.LesMediasDeTypeZone(1))
	$result.Créer()
	
	
Function LesMedias()->$result : cs.MediasSelect
	// créer la collection de tous les medias où apparait this
	$result:=cs.MediasSelect.new(This.dataClass.LesMediasDeTypeZone(-1))
	$result.Créer()
	
	// supprimer les flux et les medias privées, et trier par date
	$result.selection:=$result.selection.query("type != :1 AND private = 0"; 3).orderBy("dateNum asc")
	
	
	// attention, les liens généalogiques (Parents et conjoint) ne peuvent pas être traités ici en direct
	// la création des personnes ramènerait ici, d'où un bouclage infernale
	// Parents et conjoints sont accessibles par les fonctions suivantes
	
Function LesEvents()->$result : cs.EventsSelect
	// créer la collection des events
	$result:=cs.EventsSelect.new(This.dataClass.LesEvents())
	$result.Créer()
	
	
Function LesUnions()->$result : cs.UnionsSelect
	// créer la collection des unions triées par date
	$result:=cs.UnionsSelect.new(This.dataClass.LesUnions())
	$result.Créer()
	
	// trier les unions par date
	$result.selection:=$result.selection.orderBy("leEvent.dateNum asc")
	
	
	// ----------------------
	//MARK: FORM Liste
	// ----------------------
	
Function Elementliste()->$result : Text
	$result+="<article id='"+This.IDunique+"' class='element-listeEntity' nom='"+This.nom+"' prenom='"+This.prenom+"'>"
	
	$result+="<article>"
	$result+=This.LibelléLié(This._tagUrl+"WebDATA/<!--#4DEVAL $1.nomFunction-->?<!--#4DEVAL $1.nomFORMlist-->?Personnes?"+This.IDunique; New object("Options"; 3))
	$result+="</article>"
	
	$result+="<article><span>"
	$result+=This.DatesDeVie()
	$result+="</span></article>"
	
	$result+="</article>"
	
	
Function DatesDeVie()->$result : Text
	var $naissance; $décès : Object
	
	$naissance:=This.Naissance()
	Case of 
		: ($naissance=Null)
		: ($naissance.dateChaine="")
		Else 
			// on a une date, pas forcément valide
			$result:=$naissance.dateChaine
	End case 
	
	$result:=$result+" - "
	
	$décès:=This.Décès()
	Case of 
		: ($décès=Null)
		: ($décès.dateChaine="")
		Else 
			// on a une date, pas forcément valide
			$result:=$result+$décès.dateChaine
	End case 
	
	
	// ----------------------
	//MARK: FORM Detail
	// ----------------------
	
Function Champ_1()->$result : Text
	// photo d'identité
	var $selection : Object
	
	$selection:=This.LesMediasPerso()
	
	Case of 
		: ($selection.selection.length>0)
			$selection.selection:=$selection.selection.orderBy("dateNum desc")
			$result:=$selection.selection[0].getVignetteUrl($selection.selection[0].ID)
			
		: (This.sexe)
			// icone femelle
			$result:="../../ressources/Images/16811.png"
			
		Else 
			// icone male
			$result:="../../ressources/Images/16812.png"
	End case 
	
	
Function Champ_2()->$result : Text
	$result:=This.nom
	
	
Function Champ_3()->$result : Text
	$result:=This.prenom+" "+This.autres_prenoms
	
	
Function Champ_4()->$result : Text
	var $data : Object
	
	OB SET($data; "params"; 0x000D)
	This.cFct.AjouterObjetA(This; $data; "entité")
	
	$result:=This.InformationEntité($data)
	
	
Function Champ_boutonLibelle_1()->$result : Text
	$result:="16829.svg"
	
	
Function Champ_boutonLibelle_2()->$result : Text
	$result:=Localized string("145")
	
	
Function Champ_titreIconed_1()->$result : Text
	$result:="16830.svg"
	
	
Function Champ_titreIconed_2()->$result : Text
	$result:=Localized string("146")
	
	
Function NomComplet()->$result : Text
	
	Case of 
		: ((This.nom=Null) & (This.prenom=Null))
			$result:="Inconnu"
			
		: (This.nom=Null)
			$result:=This.prenom
			
		: (This.prenom=Null)
			$result:=This.nom
			
		Else 
			$result:=This.nom+" "+This.prenom
	End case 
	
	
    

[class]$pageAppLOGIN - 04/07/2026 09:58:01

      property check : cs.$authentificationEmail
property sessionUser; Utilisateur : cs.UtilisateursALV

Class extends $pageWeb

Class constructor()
	
	Super()
	
	This.check:=cs.$authentificationEmail.new()
	
	
	//----------------------------------
	// MARK:Traitement url
	//----------------------------------
	
Function seConnecter()
	// tout commence ici
	
	TEXT TO DOCUMENT(cs.xSDK.Traces.new().GetGarbageDossier().folder("_WEBMOdebug/"+Current method name).file(Timestamp+"1.json").platformPath; "mobileWebInfo est null : "+String(Session.storage.mobileWebInfo=Null)+Char(Carriage return)+JSON Stringify(This.paramsBody; *)+Char(Carriage return)+JSON Stringify(This.paramsBody; *))
	
	Case of 
		: (This.paramsBody=Null)
			
		: (Not(OB Is defined(This.paramsBody; "sessionID")))
			
		Else 
			This.Authentifier(This.paramsBody)
	End case 
	
	
Function Authentifier($request : Object)  //->$result : Object
	var $response; $data : Object
	
	If (This._finaliser($request))
		// le processus d'authentication est terminé
		// ou ré-ouverture d'une session existante
		
		If (This.RestaureSessionMOB($request))
			
			// fixer les privilèges
			$data:=New object
			$data.userName:=Session.storage.userInfo.userName
			// ne PAS utiliser les roles (pb dans fichier .json
			$data.privileges:="ReadRecords"  // tout le monde pareil, sinon lire les userInfo (attribut de UtilisateursALV) 
			Session.setPrivileges($data)
		End if 
		
		// ici on envoie .waiting = false et .succcess = true
		$response:=This.InitReponseRequete()
		WEB SEND TEXT(JSON Stringify($response))
		
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Session MOB en cours "+$request.sessionID; Current method name; "ID session Web "+Session.id+"  "+JSON Stringify($response))
		
	Else 
		// initialiser l'authentification
		// versionner
		This.getAppInfo($request)
		
		// renseigner les infos du device
		This.getDeviceInfo($request)
		
		// préciser qu'on vient d'un navigateur WEB
		$request.client:="mobileAPP"
		// le status du processus d'authentification est initialisé 'en attente'
		$request.status:="pending"
		
		// vérifier si l'adresse eMail est un identifiant connu et envoyer l'eMail
		$response:=This.check.Authentification($request)
		$response.message:=$response.statusText
		$response.waiting:=True & ($response.success)
		// si OK, un eMail a été envoyé
		
		// ici on envoie .waiting= true et .succcess function d'état du processus
		WEB SEND TEXT(JSON Stringify($response))
		
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "New session MOB "+$request.session.id; Current method name; "ID session Web "+Session.id+"  "+JSON Stringify($response))
		
	End if 
	
	
Function getDeviceInfo($request : Object)
	// les données du device dans l'entete et le body de la requête
	var $dataTexteB64; $dataTexte : Text
	
	$request.device:=New object
	
	ARRAY TEXT($clésEntête; 0)
	ARRAY TEXT($valeursEntête; 0)
	WEB GET HTTP HEADER($clésEntête; $valeursEntête)
	
	If (Find in array($clésEntête; "X-Device-Info")>0)
		$dataTexteB64:=$valeursEntête{Find in array($clésEntête; "X-Device-Info")}
		BASE64 DECODE($dataTexteB64; $dataTexte)
		$request.device:=JSON Parse($dataTexte; Is object)
		
	Else 
		This.trace.Créer(-16802; Current method name; "L'entête de la requête ne contient pas d'élément 'X-Device-Info'")
	End if 
	
	If (Find in array($clésEntête; "Accept-Language")>0)
		$request.language:=New object
		$dataTexte:=$valeursEntête{Find in array($clésEntête; "Accept-Language")}
		$request.language.id:=Substring($dataTexte; 1; Position(","; $dataTexte)-1)
	End if 
	
	// lire sessionID courant de l'APP du device
	// le ID session est dans le body de la requête
	var $corps : Text
	var $data : Object
	
	$request.session:=New object
	// ici un objet mobileAPP a pour ID de session l'ID envoyé par le device
	
	WEB GET HTTP BODY($corps)
	$data:=JSON Parse($corps)
	
	If (OB Is defined($data; "sessionID"))
		$request.session.id:=$data.sessionID
		$request.session.ip:=$valeursEntête{Find in array($clésEntête; "Host")}
		
	Else 
		This.trace.Créer(-16803; Current method name; "'sessionID' est absent du corps de la requête")
	End if 
	
	
Function seConnecterWEB()
	// se connecter au serveur Web
	
	Case of 
		: (This.paramsUrl.length=0)
		: (Not(This.RestaureSessionMOB(New object("sessionID"; This.paramsUrl[0]))))
			// session WEB initialisée
		Else 
			// envoyer la première page
			This.Envoyer("listeSimple.shtml"; Session.storage.UserParams.EtatNavigation)
	End case 
	
	
	//----------------------------------
	// MARK:Etat process
	//----------------------------------
	
Function RestaureSessionMOB($request : Object)->$result : Boolean
	// si une session $request.sessionID existe dans le dossier mobileWeb, la restaurer
	var $data : Object
	
	$result:=False
	$data:=New object
	
	Case of 
		: (Not(OB Is defined($request; "sessionID")))
		: (Not(cs.$session.me.getSessionDeToken($request.sessionID; $data)))
		Else 
			$result:=True
			
			cs.$session.me.InitialiserSessionInfos($data.infos)
			
			cs.$cookie.me.Ecrire("mobileWebID"; Session.storage.mobileWebInfo.session.id)
			cs.$cookie.me.Ecrire("Expires"; "Thu, 01 Jan "+String(Year of(Current date)+2)+" 00:00:01 GMT")
			
			// init de la navigation
			Use (Session.storage.UserParams)
				Session.storage.UserParams.EtatNavigation:="Personnes"
			End use 
	End case 
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Restauration "+Choose($result; "OK"; "KO"); Current method name; "requete : "+JSON Stringify($request))
	
	
Function _finaliser($request : Object)->$result : Boolean
	// vérifier que la session MOB $request.sessionID a été activée
	var $data : Object
	
	$result:=False
	
	If (OB Is defined($request; "sessionID"))
		$data:=cs.$session.me.getSessionObjet($request.sessionID)
		
		Case of 
			: ($data=Null)
			: ($data.status#"accepted")
			Else 
				
				$result:=True
		End case 
	End if 
	
	
	//----------------------------------
	// MARK:Log APP
	//----------------------------------
	
Function RetourAPP()
	// on a reçu un log de l'APP
	var $corps : Text
	var $c : Collection
	var $log : Object
	
	WEB GET HTTP BODY($corps)
	
	Try
		$c:=JSON Parse($corps; Is collection)
	Catch
		// on a reçu autre chose qu'une collection
		$c:=New collection
	End try
	
	// ici on utilise la trace cible pour avoir accès au parametre 'origine'
	If ($c.length>0)
		For each ($log; $c)
			cs._Trace.me.cible.EnvoyerMessages([msgk_log]; "APPMO"; "Nouveau log"; Current method name; JSON Stringify($log))
		End for each 
	End if 
	
	WEB SEND TEXT("ok"; "text/html")
	
    

[class]$pageWebITEM - 25/06/2026 09:25:10

      Class extends $pageWeb

Class constructor()
	
	Super()
	
	
Function LireSTR()
	var $params : Object
	
	This.result.resultat:="#err LireSTR"
	If (This.paramsUrl.length>0)
		$params:=New object
		
		If (This.paramsUrl.length>1)
			$params.genre:=(Position("féminin"; This.paramsUrl[1]; *)>0)
			$params.plur:=(Position("pluriel"; This.paramsUrl[1]; *)>0)
		End if 
		
		// le nom de l'apps peut être utilisé dans les chaines
		This.rsc.SetObjet(Est Ressource APP; "Ressources_Communes/Nom_Application"; Is text; $params; "param_1")
		This.paramsUrl.push("")
		This.result.resultat:=This.cFct.LireLocatedSTR(Num(This.paramsUrl[0]); $params)
	End if 
	This.result.resultat:=Char(1)+This.result.resultat
	
	
Function urlServeurWeb()
	var $texte : Text:=""
	This.rsc.SetVariable(Est Ressource APP; "Serveurs_ALV/IP_Serveur"; Is text; ->$texte)
	This.result.resultat:=Char(1)+$texte
	
	
Function adressServeurWeb()
	var $texte : Text:=""
	This.rsc.SetVariable(Est Ressource APP; "Serveurs_ALV/Adresse_serveur"; Is text; ->$texte)
	This.result.resultat:=Char(1)+$texte
	
	
Function userIdentifiant()
	var $texte : Text:=""
	
	Case of 
		: (Not(OB Is defined(Session.storage; "mobileWebInfo")))
		: (Not(OB Is defined(Session.storage.mobileWebInfo; "email")))
		Else 
			$texte:=Session.storage.mobileWebInfo.email
	End case 
	This.result.resultat:=Char(1)+$texte
	
    

[class]_filtrageDonnees - 30/07/2026 11:21:05

      property filtreEvents; filtrePersonnes; filtreCommunes; filtreLieux; filtreMedias : Collection

singleton Class constructor()
	
	This.filtreEvents:=Null
	This.filtrePersonnes:=Null
	This.filtreCommunes:=Null
	This.filtreLieux:=Null
	This.filtreMedias:=Null
	
	
	
Function CréerLesFiltres($isPublic : Boolean)
	
	If ($isPublic)
		// filtre Events
		This.CréerFiltreEventsPublic()
		
		// autres filtres
		This.CréerFiltrePersonnes()
		This.CréerFiltreCommunes()
		This.CréerFiltreMedias()
		
	Else 
		// pas de filtrage
		This.filtreEvents:=Null
		This.filtrePersonnes:=Null
		This.filtreCommunes:=Null
		This.filtreLieux:=Null
		This.filtreMedias:=Null
	End if 
	
	
Function CréerFiltreEventsPublic()
	// pour les invités
	var $année : Integer:=0
	var $dateMin; $dateMax : Date
	
	//  Les events antérieurs à l'année pivot
	cs.xSDK.ResourceALV.me.SetVariable(Est Ressource WEBMO; "Site_Web/Annee_pivot"; Is longint; ->$année)
	$dateMin:=Date("01/01/1000")
	$dateMax:=Date("01/01/"+String($année))
	ARRAY LONGINT($tabID; 0)
	Begin SQL
		SELECT ID FROM Events WHERE Events.dateNum > :$dateMin AND Events.dateNum < :$dateMax INTO :$tabID;
	End SQL
	This.filtreEvents:=New collection
	ARRAY TO COLLECTION(This.filtreEvents; $tabID)
	
	
Function CréerFiltrePersonnes()
	// * Les personnes des events filtrés
	var $c : Collection
	
	// chercher les personnes des événements familiaux
	// puis ajouter les personnes des événements perso
	ARRAY LONGINT(tabID; 0)
	ARRAY LONGINT($tabID; 0)
	Begin SQL
		SELECT DISTINCT Relations.membre
		FROM EventsFam
		LEFT OUTER JOIN (Unions LEFT OUTER JOIN Relations ON Unions.couple = Relations.groupe)
		ON EventsFam.famille = Unions.ID
		WHERE  {fn EstDansCollection('filtreEvents',EventsFam.evenement) AS NUMERIC} > -1
		INTO :tabID;
		
		SELECT DISTINCT personne
		FROM EventsPerso
		WHERE {fn EstDansCollection('filtreEvents',EventsPerso.evenement) AS NUMERIC} > -1
		INTO :$tabID;
	End SQL
	
	// on merge
	This.filtrePersonnes:=New collection
	ARRAY TO COLLECTION(This.filtrePersonnes; tabID)
	$c:=New collection
	ARRAY TO COLLECTION($c; $tabID)
	This.filtrePersonnes:=This.filtrePersonnes.concat($c).distinct()
	
	
Function CréerFiltreCommunes()
	// * communes
	ARRAY LONGINT($tabID; 0)
	Begin SQL
		SELECT DISTINCT Sites.commune
		FROM Sites
		INNER JOIN Lieux ON Lieux.site = Sites.ID
		WHERE Lieux.ID IN
		(SELECT DISTINCT Lieux.ID
		FROM Lieux
		INNER JOIN Events ON Events.lieu = Lieux.ID
		WHERE {fn EstDansCollection('filtreEvents',Events.ID) AS NUMERIC} > -1)
		INTO :$tabID;
	End SQL
	This.filtreCommunes:=New collection
	ARRAY TO COLLECTION(This.filtreCommunes; $tabID)
	
	// * les lieux localisés
	ARRAY LONGINT(tabID; 0)
	ARRAY LONGINT($tabID; 0)
	Begin SQL
		SELECT DISTINCT Lieux.ID
		FROM Lieux
		INNER JOIN Sites ON Lieux.site = Sites.ID
		WHERE Sites.ID IN
		(SELECT DISTINCT Sites.ID
		FROM Sites
		INNER JOIN Communes ON Sites.commune = Communes.ID
		WHERE {fn EstDansCollection('filtreCommunes',Communes.ID) AS NUMERIC} > -1)
		INTO :tabID;
		
		SELECT DISTINCT Lieux.ID
		FROM Lieux
		WHERE {fn EstDansTableauNumeric('tabID',Lieux.ID) AS NUMERIC} > -1 AND Lieux.latitude <> 0 AND Lieux.longitude <> 0
		INTO :$tabID;
	End SQL
	This.filtreLieux:=New collection
	ARRAY TO COLLECTION(This.filtreLieux; $tabID)
	
	
Function CréerFiltreMedias()
	// * medias des events webables
	var $dateMin; $dateMax : Date
	var $c : Collection
	
	// remarque : cette sélection contient les documents PDF ET les photos associées aux évents
	$dateMin:=Date("01/01/1000")
	$dateMax:=Date("01/01/"+String(Year of(Current date)+1-30))
	ARRAY LONGINT(tabID; 0)
	ARRAY LONGINT($tabID; 0)
	Begin SQL
		SELECT DISTINCT Zones.media
		FROM Events
		LEFT OUTER JOIN (Instantanes LEFT OUTER JOIN Zones ON Instantanes.zone = Zones.ID)
		ON Events.ID = Instantanes.event
		WHERE  {fn EstDansCollection('filtreEvents',Events.ID) AS NUMERIC} > -1
		INTO :tabID;
		
		SELECT DISTINCT Medias.ID
		FROM Medias
		WHERE  {fn EstDansTableauNumeric('tabID',Medias.ID) AS NUMERIC} > -1 AND Medias.dateNum > :$dateMin AND Medias.dateNum < :$dateMax AND private = 0
		INTO :$tabID;
	End SQL
	This.filtreMedias:=New collection
	ARRAY TO COLLECTION(This.filtreMedias; $tabID)
	
	// * medias des lieux webables, non privés
	Begin SQL
		SELECT DISTINCT Zones.media
		FROM Lieux
		LEFT OUTER JOIN (Paysages LEFT OUTER JOIN Zones ON Paysages.zone = Zones.ID)
		ON Lieux.ID = Paysages.lieu
		WHERE  {fn EstDansCollection('filtreLieux',Lieux.ID) AS NUMERIC} > -1
		INTO :tabID;
		
		SELECT DISTINCT Medias.ID
		FROM Medias
		WHERE  {fn EstDansTableauNumeric('tabID',Medias.ID) AS NUMERIC} > -1 AND private = 0
		INTO :$tabID;
	End SQL
	// on merge
	$c:=New collection
	ARRAY TO COLLECTION($c; $tabID)
	This.filtreMedias:=This.filtreMedias.concat($c).distinct()
	
    

[class]UtilisateursALV - 04/10/2025 09:35:22

      // attributs de la classe
property Password; AdresseEmail : Text

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	// initialiser l'objet avec les données de l'entité $IDentité de la BDD
	
	Super("UtilisateursALV"; $IDentité)
	
	
    

[class]DicoDesNomsSelect - 04/10/2025 09:35:46

      Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("DicoDesNomsSelect"; $requête)
	
	
	
	// ----------------------
	//MARK:HTML
	// ----------------------
	
Function Ecrire()->$result : Text
	var $c : Collection
	var $ID : Integer
	
	// créer la sélection, ici on fait simple, pas de table [DicoDesNoms]
	$c:=New collection
	For each ($ID; This.collection)
		$c.push(cs.xSQL.DicoDesNoms.new($ID).patronyme)
	End for each 
	
	$c:=$c.distinct().orderBy("asc")
	
	$result:="<p>"
	$result+=Localized string("154")
	$result+="</p></br>"
	
	$result+="<p>"
	$result+=$c.join(", ")
	$result+="</p>"
	
    

[class]_maintenance - 10/08/2026 09:35:37

      property nomTache : Text:="MaintenanceWEBMO"
property tache : cs.xSDK.Tache
property Taches : Collection

Class extends _composant

Class constructor()
	
	Super()
	
	
	// -----------------------------
	// MARK:Maintenance
	// -----------------------------
	
Function FixerListeTâches()
	// lister les tâches à exécuter
	var $data : Object
	
	This.Taches:=New collection
	
	$data:=New object
	$data.functionID:="MettreAjourBDDmobile"
	// prochaine nuit à 4:30
	$data.dateTache:=String(Add to date(Current date; 0; 0; 1); ISO date GMT; ?04:30:00?)
	// tous les jours
	$data.période:=New object("jour"; 1; "seconde"; 0)
	$data.initialiser:=True
	This.Taches.push(OB Copy($data))
	This.trace.EnvoyerMessages([msgk_event]; "Maintenance"; Current method name; "tâche 'MettreAjourBDDmobile' programmée (voir détails dans Logs)")
	This.trace.EnvoyerMessages([msgk_log]; "Maintenance ChargerBDDmobile"; Current method name; JSON Stringify($data))
	
	
	$data:=New object
	$data.functionID:="CréerMediaMobile"
	// prochaine nuit à 4:30
	$data.dateTache:=String(Add to date(Current date; 0; 0; 1); ISO date GMT; ?04:35:00?)
	// tous les jours
	$data.période:=New object("jour"; 1; "seconde"; 0)
	$data.initialiser:=True
	This.Taches.push(OB Copy($data))
	This.trace.EnvoyerMessages([msgk_event]; "Maintenance"; Current method name; "tâche 'CréerMediaMobile' programmée (voir détails dans Logs)")
	This.trace.EnvoyerMessages([msgk_log]; "Maintenance CréerMediaMobile"; Current method name; JSON Stringify($data))
	
	
	$data:=New object
	$data.functionID:="InstallerAlbums"
	// prochaine nuit à 5:00
	$data.dateTache:=String(Add to date(Current date; 0; 0; 1); ISO date GMT; ?05:00:00?)
	// tous les jours
	$data.période:=New object("jour"; 1; "seconde"; 0)
	$data.initialiser:=True
	This.Taches.push(OB Copy($data))
	This.trace.EnvoyerMessages([msgk_event]; "Maintenance"; Current method name; "tâche 'InstallerAlbums' programmée (voir détails dans Logs)")
	This.trace.EnvoyerMessages([msgk_log]; "Maintenance InstallerAlbums"; Current method name; JSON Stringify($data))
	
	
Function Démarrer()
	// exécuter dans un process externe
	var $params : Object
	var $numProc : Integer
	
	Case of 
		: ((Storage.System.typeApplication#4D Serveur APP)) & (Not(Storage.Host.estDebugAPP()))
			// serveur ALV seul uniquement ou debug
		Else 
			$params:=New object
			$params.nomTache:=This.nomTache
			$params.initProcess:=Formula(InitProcessComposantThreadSafe)
			
			$numProc:=Storage.Host.processUser.call(Null).NouveauProcessComposant("xWEBMO"; "_maintenance"; "SuperviserLesTaches"; $params)
	End case 
	
	
Function Arrêter()
	cs.xSDK.RegistreTaches.new().Tuer(This.nomTache)
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Arrêt du monitoring")
	
	
Function SuperviserLesTaches($data : Object)
	// gérer les tâches de la maintenance des sessions et données mobiles
	var $tâche : Object
	var $jours : Integer
	var $heure : Integer
	
	This.tache:=$data.tache
	// tempo de 1 mn, que l'installation du serveur soit terminée
	Waiting(60*60)
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Démarrage du monitoring")
	
	This.FixerListeTâches()
	
	
	// lancer le monitoring des tâches
	Repeat 
		For each ($tâche; This.Taches)
			Case of 
				: (Not(OB Is defined($tâche; "functionID")))
				: (Not(OB Is defined($tâche; "dateTache")))
				: (Not(OB Is defined($tâche; "initialiser")))
				: (Not(($tâche.initialiser) | (String(Current date; ISO date GMT; Current time)>$tâche.dateTache)))
					// attendre
				Else 
					// ok, on a tout pour cette tâche
					
					This[$tâche.functionID]()
					
					// date de la prochaine maintenance
					// nombre de jours
					// on peut avoir rater des périodes ; combien?
					$jours:=Current date-Date($tâche.dateTache)
					// ajouter la période
					$jours:=$jours+$tâche.période.jour+((Time($tâche.dateTache)+$tâche.période.seconde)\(24*3600))
					// nouvelle heure
					$heure:=Mod(Time($tâche.dateTache)+$tâche.période.seconde; 24*3600)
					// nouvelle date (au cas où, on se resynchronise aussi sur l'heure)
					$tâche.dateTache:=String(Add to date(Date($tâche.dateTache); 0; 0; $jours); ISO date GMT; Time($heure))
					$tâche.initialiser:=False
					
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "La tâche '"+$tâche.functionID+"' sera relancée le '"+$tâche.dateTache+"'")
			End case 
		End for each 
		
		// *** attendre la période suivante
		Waiting(60*10)
	Until (This.tache.Tuer.signaled)
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Arrêt du monitoring")
	
	
	// -----------------------------
	// MARK:DataBase
	// -----------------------------
	
Function ChargerBDD()
	// charger la BDD opérationnelle
	Super.ChargerBDD(cs._document.new().getBDDFolder(); "BDD")
	// charger la BDD invité
	cs._maintenanceGuest.new().ChargerBDD()
	
	
Function MettreAjourBDDmobile()
	var $dossier : 4D.Folder
	var $fichier : 4D.File
	var $data : Object
	var $dateFichier4DD : Text
	
	// initialiser le storage ; on doit avoir un dossier
	$dossier:=cs._document.new().getBDDFolder()
	If (Not($dossier.exists))
		$dossier.create()
	End if 
	
	// date du fichier de données actuel
	$dateFichier4DD:=String(File(Data file; fk platform path).modificationDate; ISO date GMT)
	
	// initialiser la mise à jour ; on doit avoir un fichier
	$fichier:=$dossier.file("config.json")
	If (Not($fichier.exists))
		// créer le fichier
		$data:=New object("dateFichier4DD"; $dateFichier4DD)
		$fichier.setText(JSON Stringify($data; *))
	End if 
	
	// ici on a toujours quelque chose
	$data:=JSON Parse($fichier.getText())
	Case of 
		: ($data=Null)
		: (Not(OB Is defined($data; "dateFichier4DD")))
		: ($data.dateFichier4DD=$dateFichier4DD)
			// OK fichiers à jour
			
		Else 
			// KO, mettre à jour les fichiers
			This.CréerBDDmobile()
	End case 
	
	// mettre à jour la BDD
	This.ChargerBDD()
	
	
Function CréerBDDmobile()
	var $dossier : 4D.Folder
	
	// aucun filtre
	cs._filtrageDonnees.me.CréerLesFiltres(False)
	
	$dossier:=cs._document.new().getBDDFolder()
	This.CréerFichiersBDDmobile($dossier)
	
	
Function CréerFichiersBDDmobile($dossierBDD : 4D.Folder)
	var $dataClassNom : Text
	var $dossierTempo : 4D.Folder
	var $fichier : 4D.File
	
	$dossierTempo:=cs._Trace.me.cible.GetGarbageDossier().folder("tempo_WEBMO")
	$dossierTempo.create()
	
	For each ($dataClassNom; This.listeDataClass)
		// créer le fichier
		$fichier:=$dossierTempo.file($dataClassNom+".json")
		This.CréerFichierBDDmobile($dataClassNom; $fichier)
		// recopier dans le dossier serveurAPP
		$fichier.copyTo($dossierBDD; fk overwrite)
	End for each 
	
	$dossierTempo.delete(Delete with contents)
	
	
Function CréerFichierBDDmobile($dataClassNom : Text; $fichier : 4D.File)
	// créer la base de données locale sur disque local
	var $c : Collection
	var $dataClass; $selection : Object
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Création du fichier de la DataClass "+$dataClassNom+" : "+$fichier.platformPath)
	
	$dataClass:=cs[$dataClassNom].new().FixerParametresListe()
	
	// créer la sélection d'entités (collection d'ID)
	$selection:=cs[$dataClassNom+"Select"].new($dataClass.Requete)
	// ici on a une collection d'ID filtrée
	$c:=$selection.CreerBDDmobile(This.tache)
	
	// enregistrer
	$fichier.setText(JSON Stringify($c))
	
	
	// -----------------------------
	// MARK:Medias
	// -----------------------------
	
Function CréerMediaMobile()
	var $sélection : cs.MediasSelect
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Medias"; Current method name; "Démarrage du traitement Medias")
	
	// récupérer la sélection de medias
	$sélection:=cs.MediasSelect.new("private = 0 AND (type = 1 OR type = 2)")
	$sélection.Créer()
	
	// créer les fichiers pour Mobile
	$sélection.CréerMediaMobile(This.tache)
	
	// créer les vignettes pour Mobile
	This.CréerVignettesMobile()
	
	// créer les blasons
	This.CréerBlasonsMobile()
	
	// instaler le tout
	cs._composant.new().InstallerRessourcesMEDIA(cs._document.new().getRacineHTMLFolder())
	
	
Function CréerVignettesMobile()
	// stocker dans $folder les vignettes des media
	var $dossierVignettes; $dossierMedias : 4D.Folder
	var $file; $fichier : 4D.File
	var $pict; $vignette : Picture
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Medias"; Current method name; "Démarrage du traitement Vignettes")
	
	$dossierMedias:=This.document.getMediasFolder()
	$dossierVignettes:=This.document.getVignettesFolder()
	
	For each ($file; $dossierMedias.files()) While (Not(This.tache.Tuer.signaled))
		READ PICTURE FILE($file.platformPath; $pict)
		
		// redimensionner l'image
		CREATE THUMBNAIL($pict; $vignette; WebMob Icon largeur; WebMob Icon hauteur)
		// passer en JPEG
		CONVERT PICTURE($vignette; WebMob Icon format)
		// enregistrer
		$fichier:=$dossierVignettes.file(This.document.getVignetteNom(Num($file.fullName)))
		WRITE PICTURE FILE($fichier.platformPath; $vignette; WebMob Icon format)
	End for each 
	
	
Function CréerBlasonsMobile()
	var $sélection : cs.CommunesSelect
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Medias"; Current method name; "Démarrage du traitement Blasons")
	
	// récupérer la sélection de communnes
	$sélection:=cs.CommunesSelect.new("ID > 0")
	$sélection.Créer()
	
	// créer les fichiers pour Mobile
	$sélection.CréerBlasonsMobile(This.tache)
	
	
	// -----------------------------
	// MARK:Albums
	// -----------------------------
	
Function InstallerAlbums()
	var $DossierDestination : 4D.Folder
	
	// ici le rootFolder est fixé : demander l'installation des albums
	$DossierDestination:=This.document.getRacineHTMLFolder()
	CALL WORKER("WK_Composant_WEBMO"; Formula(cs.xALB._maintenance.new().InstallerAlbums($DossierDestination)))
	
    

[class]EventsSelect - 04/10/2025 09:36:00

      Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("EventsSelect"; $requête)
	
	This.selection:=Null
	
	
	
Function LesProtagonistes()->$result : cs.PersonnesSelect
	var $event : Object
	var $c : Collection
	
	$c:=New collection
	For each ($event; This.selection)
		$c:=$c.combine($event.LesProtagonistes().selection.extract("ID"))
	End for each 
	// créer la liste
	$result:=cs.PersonnesSelect.new($c)
	
	
Function LesMembres()->$result : Text
	var $event : Object
	var $c : Collection
	
	$c:=New collection
	For each ($event; This.selection)
		$c:=$c.push($event.LesProtagonistes().selection.extract("nom").join("-"))
	End for each 
	$c:=$c.distinct().orderBy(ck ascending)
	// créer la liste
	$result:=$c.join(", ")
	
	
Function EtatCivil()->$result : Text
	// lister les events 'date' 'libellé protagonistes'
	var $formats : Object
	var $event : cs.Events
	
	$result:=""
	// on veut le symbole event, 'le'
	$formats:=New object("Options"; 0x00109100; "FormatDate"; Internal date short; "FormatHeure"; HH MM)
	
	For each ($event; This.selection)
		$result+="<p>"+($event.FormaterDate($formats)+" "+$event.FormaterHeure($formats)+" "+$event.Libellé($formats))+"</p>"
	End for each 
	
    

[class]_SF_Sessions - 10/08/2026 09:35:48

      property wndRef : Integer
property nomOBJ : Text:=""
property functionID : Text
property trace : cs._Trace
property description : Text

singleton Class constructor()
	
	This.trace:=cs._Trace.me
	
	
	// ----------------------
	//MARK:Traitement
	// ----------------------
	
Function TraiterFORMevent()
	
	If (OB Is defined(FORM Event; "objectName"))
		// event d'un objet formulaire
		This.functionID:="_FORM_"+FORM Event.objectName
		This.nomOBJ:=FORM Event.objectName
		
	Else 
		// event du formulaire
		This.functionID:="_FORM_Formulaire"
		This.nomOBJ:=""
	End if 
	
	If (OB Is defined(This; This.functionID))
		This[This.functionID]()
	End if 
	
	
	// ----------------------
	//MARK:Formulaire
	// ----------------------
	
Function _FORM_Formulaire()
	
	Case of 
		: (FORM Event.code=On Load)
			
			// charger les objets
			This.onEndLoad()
			
	End case 
	
	
Function onEndLoad()
	// en DUR pour l'instant
	var $c : Collection
	var $nomOBJ : Text
	
	$c:=New collection("listeSessions")
	// appel, après le onload du formulaire, du onload des objets de $c
	
	// lancer l'event 'onLoad'
	For each ($nomOBJ; $c)
		If (OB Is defined(This; "_FORM_"+$nomOBJ))
			This.nomOBJ:=$nomOBJ
			This["_FORM_"+$nomOBJ]()
		End if 
	End for each 
	
	
Function _FORM_listeSessions()
	var $popup; $choice : Text
	var $ligne : Integer
	var $data : Object
	var $params : Object:=New object
	
	Case of 
		: (FORM Event.code=On Load)
			
			Form[This.nomOBJ]:=New collection
			
			$data:=This.RequeterServeurAPP("GetSessionsInfos"; $params)
			Case of 
				: ($data=Null)
				: (Not(OB Is defined($data; "sessions")))
				Else 
					
					Form[This.nomOBJ]:=$data.sessions
					LISTBOX SELECT ROW(*; This.nomOBJ; 0; lk remove from selection)
			End case 
			
			
		: (FORM Event.code=On Clicked)
			
			Form.description:=This.FormaterDescription()
			
			If (Contextual click)
				
				$popup:=Create menu
				$ligne:=Form.listeSessionsPositionElementCourant
				
				If ($ligne>0)
					APPEND MENU ITEM($popup; "Delete")
					SET MENU ITEM PARAMETER($popup; -1; "delete")
				End if 
				
				$choice:=Dynamic pop up menu($popup)
				RELEASE MENU($popup)
				
				Case of 
					: ($choice="delete")  // Delete the session file
						
						$params:=Form.listeSessionsElementCourant
						This.RequeterServeurAPP("SupprimerSession"; $params)
						Form[This.nomOBJ].remove($ligne-1)
						
				End case 
			End if 
			
		Else 
			
			ASSERT(False; "Form event activated unnecessarily ("+String(FORM Event.description)+")")
			
			//______________________________________________________
			
	End case 
	
	
Function FormaterDescription()->$result : Text
	// mettre en forme les textes JSON
	
	$result:=""
	If (Form.listeSessionsElementCourant#Null)
		$result:=JSON Stringify(Form.listeSessionsElementCourant; *)
		$result:=Replace string($result; "{"; "")
		$result:=Replace string($result; "}"; "")
		$result:=Replace string($result; ","; "")
		$result:=Replace string($result; Char(Double quote); "")
		$result:=Replace string($result; ":"; " :")
	End if 
	
	
	// ----------------------
	//MARK:Fonctions
	// ----------------------
	
Function RequeterServeurAPP($nomFonction : Text; $params : Object)->$result : Object
	var $url : Text
	var $status : Integer
	
	$result:=Null
	
	Case of 
		: (Not(OB Is defined(This; $nomFonction)))
			This.trace.Créer(-16868; Current method name; "la fonction '"+$nomFonction+"' n'est pas définie dans la classe "+OB Class(This).name).LeverException([msgk_event; msgk_log])
			
		: (Not(Storage.System.estExécutéDansHôte))
			// exécution en local (composant)
			$result:=This[$nomFonction]($params)
			
		: (Storage.Host.estDebugAPP())
			// mode debug BDDmère
			$result:=This[$nomFonction]($params)
			
		Else 
			// cas normal ; interroger le serveur courant
			$url:=New collection("/4DHTTP"; "xWEBMO"; OB Class(This).name; $nomFonction).join("/")
			$status:=Storage.Host.RequeteHTTP.call().Requeter($url; "Get"; $params; "blob")
			
			Case of 
				: ($status#200)
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "[KO] Erreur HTTP "+String($status); Current method name; "url "+$url)
					
				: (Not(OB Is defined($params; "reqRetour")))
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "[KO] reqRetour"; Current method name; "Est vide, url "+$url)
					
				Else 
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "[OK] Sessions reçues"; Current method name; "url "+$url)
					// rappel le résultat est dans $params
					
					$result:=$params.reqRetour
			End case 
	End case 
	
	
Function GetSessionsInfos($params)->$result : Object
	// ici on est sur le serveurAPP ou la BDDmère
	// Description:
	// Returns an object containing detailed information on mobile applications
	// & associated devices' sessions
	// ----------------------------------------------------
	var $text : Text
	var $session : Object
	var $file : 4D.File
	var $dossier : 4D.Folder
	
	$result:=New object("sessions"; New collection)
	
	$dossier:=cs._document.new().getMobileWebFolder()
	
	// Each file, without extension, should correspond to a session
	For each ($file; $dossier.files().query("extension =''"))
		
		// Check that the file name is composed of hexadecimal characters only
		If (Match regex("(?mi-s)^[[:xdigit:]]+$"; $file.name; 1; *))
			
			// Make sure it's a json
			$text:=$file.getText()
			
			If (Match regex("(?msi)^\\{.*\\}$"; $text; 1; *))
				
				$session:=JSON Parse($text)
				$session.nomFichier:=$file.name
				$result.sessions.push($session)
				
			End if 
		End if 
	End for each 
	
	$text:=Choose($result.sessions.length>1; "s"; "")
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; String($result.sessions.length)+" session"+$text+" trouvée"+$text; Current method name; "dans le dossier "+$dossier.platformPath)
	
	
Function SupprimerSession($params : Object)->$result : Object
	// ici on est sur le serveurAPP ou la BDDmère
	var $dossier : 4D.Folder
	
	$dossier:=cs._document.new().getMobileWebFolder()
	Case of 
		: (Not(OB Is defined($params; "nomFichier")))
		: (Not($dossier.file($params.nomFichier).exists))
		Else 
			$dossier.file($params.nomFichier).delete()
	End case 
	
    

[class]CommunesSelect - 04/06/2026 12:56:14

      Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("CommunesSelect"; $requête)
	
	This.selection:=Null
	This.length:=0
	
	
	
	// ----------------------
	//MARK:PageHTML
	// ----------------------
	
Function CreerBDDmobile($tâche : cs.xSDK.Tache)->$result : Collection
	var $entité : cs.Communes
	
	// créer la sélection d'entités
	This.Créer()
	
	// créer les entités de la BDD mobile
	$result:=New collection
	If (This.selection.length>0)
		// ordonner
		This.selection:=This.selection.orderBy("nom asc")
		
		For each ($entité; This.selection) While (Not($tâche.Tuer.signaled))
			$result.push($entité.CreerBDDentity())
		End for each 
	End if 
	
	
	// -----------------------------
	// MARK:Maintenance
	// -----------------------------s
	
Function CréerBlasonsMobile($tâche : cs.xSDK.Tache)
	var $dossier : 4D.Folder
	var $entité : cs.Communes
	
	$dossier:=This.document.getBlasonsFolder()
	
	For each ($entité; This.selection) While (Not($tâche.Tuer.signaled))
		$entité.CréerBlasonMobile($dossier)
	End for each 
	
	
    

[class]_maintenanceGuest - 08/08/2026 14:04:24

      // gestion de la BDD guest
property nomTache : Text:="MaintenanceWEBMOguest"
property tache : cs.xSDK.Tache

Class extends _composant

Class constructor()
	
	Super()
	
	
Function CréerBDDmobileGuest($data : Object)
	// fonction de maintenance, non automatique, en principe appelée une fois
	var $dossier : 4D.Folder
	var $numProc : Integer:=-1
	var $class : cs._maintenance
	
	If (Count parameters=0)
		$data:=New object
		$data.nomTache:=This.nomTache
		$data.initProcess:=Formula(InitProcessComposantThreadSafe)
		
		$numProc:=Storage.Host.processUser.call(Null).NouveauProcessComposant("xWEBMO"; "_maintenanceGuest"; "CréerBDDmobilePublic"; $data)
		
	Else 
		$class:=cs._maintenance.new()
		$class.tache:=$data.tache
		
		// filtrage public
		cs._filtrageDonnees.me.CréerLesFiltres(True)
		
		// créer les fichiers BDD publiques
		$dossier:=This.getBDDguestFolder()
		$class.CréerFichiersBDDmobile($dossier)
	End if 
	
	
Function CréerFiltresGuest()
	var $dossier : 4D.Folder
	
	// fonction de maintenance, non automatique, en principe appelée une fois
	cs._filtrageDonnees.me.CréerLesFiltres(True)
	
	// créer les fichiers Filtres publiques
	$dossier:=This.getBDDguestFolder()
	$dossier.file("filtrePersonnes.json").setText(JSON Stringify(cs._filtrageDonnees.me.filtrePersonnes))
	$dossier.file("filtreEvents.json").setText(JSON Stringify(cs._filtrageDonnees.me.filtreEvents))
	
	
Function ChargerBDD()
	Use (Storage)
		Storage["BDDguest"]:=New shared object
	End use 
	
	// pour la navigation
	Use (Storage.BDDguest)
		Storage.BDDguest.navAll:=New shared object
	End use 
	
	Super.ChargerBDD(This.getBDDguestFolder(); "BDDguest")
	This.ChargerFiltres()
	
	
Function ChargerFiltres()
	var $c : Collection
	
	// enregistrer les filtres (utilisés par les Arbres Généalogiques)
	Use (Storage)
		Storage.EntitésWebables:=New shared object("filtrePersonnes"; New shared collection; "filtreEvents"; New shared collection)
	End use 
	
	$c:=JSON Parse(This.getBDDguestFolder().file("filtrePersonnes.json").getText(); Is collection)
	sharedCollection($c; Storage.EntitésWebables.filtrePersonnes)
	$c:=JSON Parse(This.getBDDguestFolder().file("filtreEvents.json").getText(); Is collection)
	sharedCollection($c; Storage.EntitésWebables.filtreEvents)
	
	
Function getBDDguestFolder()->$result : 4D.Folder
	$result:=Folder(fk resources folder).folder("BDDpublic")
	$result.create()
	
	
    

[class]LieuxSelect - 04/10/2025 09:34:35

      Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("LieuxSelect"; $requête)
	
	This.selection:=Null
	
	
    

[class]$authentificationEmail - 31/07/2026 09:49:49

      property request; userInfo; resultActiverSession; resultEmailChecker : Object
property urlActiverSession : Text

Class extends _composant

Class constructor
	
	Super()
	
	This.request:=Null
	This.userInfo:=Null
	// url nécessitant un traitement
	This.urlActiverSession:="activation"
	This.resultActiverSession:=Null
	
	
	
	
Function getAdresseServeur()->$result : Text
	$result:=cs.$serveur.new().getAdresseServeur()
	
	
Function getPortServeur()->$result : Text
	var $serveur : 4D.WebServer
	
	$serveur:=WEB Server(Web server database)
	$result:=String($serveur.HTTPSPort)
	
	
	// -----------------------------
	// MARK:Authentification par eMail
	// -----------------------------
	
Function Authentification($request : Object)->$response : Object
	// traiter la demande d'authentification (équivalent de l'évènement 'On Mobile App Authentification')
	// si ok, envoyer un eMail d'authentification
	
	This.request:=$request
	This.trace.EnvoyerMessages([msgk_log]; "$request :"; Current method name; JSON Stringify(OB Copy($request)))
	
	$response:=This.InitReponseRequete()
	$response.function:=Current method name
	Case of 
		: (Not(This.estUtilisateurIdentifié($request)))
			// user inconnu
			$response.statusText:=Localized string("7")
			
		: (Not(This.FixerParamètresAuthentification()))
			$response.statusText:="Erreur paramètres Authentification"
			
		: (This.ConfirmerLogInParEmail())
			$response:=This.resultEmailChecker
			
			// initialiser les données de session
			This._initDonneesDeSession()
			
		Else 
			$response:=This.resultEmailChecker
			This.trace.EnvoyerMessages([msgk_event]; "$response :"; Current method name; "Erreur construction / envoi de l'eMail")
	End case 
	
	
Function _initDonneesDeSession()
	// ici un user a été identifié ; ses données sont dans This.userInfo
	
	cs.$session.me.InitialiserSession()
	Use (Session.storage)
		Use (Session.storage.userInfo)
			// info serveur
			Session.storage.userInfo.email:=This.request.email
			Session.storage.userInfo.IDunique:=This.userInfo.IDunique
			// info ALV
			Session.storage.userInfo.userName:=This.userInfo.First_Name
			Session.storage.userInfo.IDfamille:=This.userInfo.IDfamille
			Session.storage.userInfo.estGuest:=This.userInfo.estGuest
		End use 
	End use 
	
	// initialiser le fichier session mobileWeb
	cs.$session.me.setSessionFile(This.request.session.id; This.request; Session.storage.userInfo)
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "session MOB "+This.request.session.id; Current method name; "[OK] (voir détails .userInfo dans Logs)")
	This.trace.EnvoyerMessages([msgk_log]; "userInfo :"; Current method name; JSON Stringify(This.userInfo))
	
	
Function ConfirmerLogInParEmail()->$result : Boolean
	// envoyer un eMail de confirmation
	var $data : Object
	
	This.resultEmailChecker:=cs.$session.me.WEBmobileEmailChecker(This.request)
	$result:=This.resultEmailChecker.success
	
	This.trace.EnvoyerMessages([msgk_event]; "resultEmailChecker"; Current method name; Choose($result; "[OK]"; "[KO]")+". Confirmation - message EmailChecker "+This.resultEmailChecker.statusText)
	This.trace.EnvoyerMessages([msgk_log]; "resultEmailChecker"; Current method name; JSON Stringify(This.resultEmailChecker))
	This.trace.EnvoyerMessages([msgk_log]; "sessionGetPrivileges"; Current method name; JSON Stringify(Session.getPrivileges()))
	$data:=OB Copy(Session.storage)
	OB REMOVE($data; "UserParams")
	This.trace.EnvoyerMessages([msgk_log]; "sessionStorage"; Current method name; "isGuest() "+String(Session.isGuest())+" SessionStorage "+JSON Stringify($data))
	
	
Function FixerParamètresAuthentification()->$result : Boolean
	// renseigner les infos du template utilisé par 'Mobile App Email Checker'
	var $fichier; $params : Object
	
	$fichier:=Folder(fk resources folder).folder("WEB mobile Server").file("settings.json")
	
	If ($fichier.exists)
		$params:=JSON Parse($fichier.getText())
	Else 
		$params:=New object
	End if 
	
	// renseigner les paramètres, objet .smtp (cf doc de Mobile App Email Checker)
	// qui envoie le mail :
	cs.xMAIL.$eMail.new().LireParametresSMTP($params)
	This.rsc.SetObjet(Est ressource MAIL; "mail/authentificationUtilisateur"; Is text; $params.smtp; "from")
	
	// le mail envoyé
	$params.emailSubject:=This.cFct.LireLocatedSTR(2)
	$params.template:=New object
	$params.template.emailToSend:=This.CréerPageHTMLConfirmation()
	// emailConfirmActivation : utilise la page par défaut (cf doc)
	
	$params.activation:=New object
	$params.activation.scheme:="https"
	$params.activation.hostname:=This.getAdresseServeur()
	$params.activation.port:=This.getPortServeur()
	$params.activation.path:=This.urlActiverSession
	
	// les messages utilisateur
	$params.message:=New object
	$params.message.successConfirmationMailMessage:=This.cFct.LireLocatedSTR(3)  // "Verify your email address"
	$params.message.waitSendMailConfirmationMessage:=This.cFct.LireLocatedSTR(6)  // "The mail is already sent thank you to wait before sending again"
	$params.message.successActiveSessionsMessage:=This.cFct.LireLocatedSTR(4)  // "You are successfully authenticated"
	$params.message.expireActiveSessionsMessage:=This.cFct.LireLocatedSTR(5)  // "This email confirmation link has expired!"
	
	// enregistrer
	TEXT TO DOCUMENT($fichier.platformPath; JSON Stringify($params))
	$result:=True
	
	
Function estUtilisateurIdentifié($data : Object)->$result : Boolean
	// chercher le user
	// normalement il n'y a qu'un user, si plusieurs on prend le premier
	var $ID; $groupe : Integer
	var $eMail; $LogIn; $Name; $First_Name; $UUID : Text
	
	$result:=False
	
	Case of 
		: ($data=Null)
		: ($data.email="")
		Else 
			// v9.5.4 : le login d'un user est son adresse Mail
			$eMail:=$data.email
			$ID:=-1
			$LogIn:=""
			$Name:=""
			$First_Name:=""
			$groupe:=-1
			$UUID:=""
			Begin SQL
				SELECT  ID, LogIn, Name, First_Name, Groupe, IDunique FROM UtilisateursALV WHERE Adresse_eMail = :$eMail INTO :$ID, :$LogIn, :$Name, :$First_Name, :$groupe, :$UUID;
			End SQL
			
			This.userInfo:=New object
			If ($ID>0)
				// on a trouvé quelqu'un
				$result:=True
				
				// ses infos de log
				This.userInfo.LogIn:=$LogIn
				This.userInfo.Name:=$Name
				This.userInfo.First_Name:=$First_Name
				This.userInfo.IDunique:=$UUID
				This.userInfo.privileges:="ReadRecords"
				
				// trouver son groupe familial
				Begin SQL
					SELECT  IDfamille FROM wwwGroupes WHERE ID = :$groupe INTO :$ID;
				End SQL
				This.userInfo.IDfamille:=$ID
				This.userInfo.estGuest:=(This.userInfo.IDfamille=-1008)  // famille des Invités
				
			Else 
				This.userInfo.nom:="cher Inconnu"
			End if 
	End case 
	
	
Function CréerPageHTMLConfirmation()->$result : Text
	// créer dans les ressources le contenu du eMail de confirmation d'authentification
	// remarque : le template fournit par le composant 4D est en anglais et anonyme. Ici le texte est en français et personalisé
	var $dataTexte : Text
	
	// chemin du template de la page
	$result:="ConfirmMailTemplate.html"
	
	// créer le document
	$dataTexte:=Document to text(Folder(fk resources folder).folder("TemplatesMobileWeb").file($result).platformPath; "UTF-8")
	PROCESS 4D TAGS($dataTexte; $dataTexte; This.userInfo.First_Name)
	
	// enregistrer dans le serveur
	TEXT TO DOCUMENT(Folder(fk resources folder).folder("WEB mobile Server").file($result).platformPath; $dataTexte)
	
	
	// -----------------------------
	// MARK:Traitement des URL session
	// -----------------------------
	
Function ActiverSession($url : Text)->$result : Boolean
	// analyser l'URL envoyé depuis l'eMail
	var $token : Text
	// ici on reçoit aussi les url Albums !
	
	// requête refusée par défaut
	$result:=False
	
	If ($url=("@"+This.urlActiverSession+"@"))
		// accepter la requête
		
		This.resultActiverSession:=cs.$session.me.WebMobileActiveSession($url)
		$result:=This.resultActiverSession.success
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Mobile Web Active Session"+Choose($result; " [OK]"; " [KO]"); Current method name; $url+" - message "+This.resultActiverSession.message+" - erreur "+This.resultActiverSession.error)
		
		// lire l'ID
		$token:=cs.$session.me.getTokenFromURL($url)
		
		// lire user session
		Case of 
			: (Not($result))
			: ($token="")
				// *** bof
				
				//: (Not(OB Is empty(Session.storage.mobileWebInfo)))
				//// *** ré activation d'une session existante
				//This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Session Active"; Current method name; "$token "+$token+" - isGuest() "+String(Session.isGuest())+" (voir détails dans logs)")
				//This.trace.EnvoyerMessages([msgk_log]; "Session Active"; Current method name; JSON Stringify(Session.storage))
				
			Else 
				This._nouvelleSession($token)
		End case 
	End if 
	
	
Function _nouvelleSession($token : Text)
	// *** nouvelle session
	var $data : Object
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Nouvelle Session MOB"; Current method name; "$token "+$token)
	
	// récupérer les infos de session mobileWeb
	$data:=cs.$session.me.getSessionObjet($token)
	cs.$session.me.InitialiserSessionInfos($data)
	
	cs.$cookie.me.Ecrire("4DSID_ALVserveurWebMobile"; Session.storage.mobileWebInfo.session.id)
	cs.$cookie.me.Ecrire("Expires"; "Thu, 01 Jan "+String(Year of(Current date)+2)+" 00:00:01 GMT")
	
    

[class]$authentificationGuest - 31/07/2026 09:50:28

      Class extends $authentificationEmail

Class constructor()
	
	Super()
	
	
Function Authentification($request : Object)->$result : Boolean
	// traiter la demande d'authentification d'unutilisateur invité
	// équivalent de super.Authentification() et super.ActiverSession() mais sans envoi d'un eMail d'authentification
	
	This.request:=$request
	$result:=False
	
	Case of 
		: (Not(This.estUtilisateurIdentifié($request)))
			// user inconnu
			
		: (This.estGuest())
			$result:=True
			
			This._initDonneesDeSession()
			This._nouvelleSession($request.session.id)
			This._finaliser()
	End case 
	
	
Function estGuest()->$result : Boolean
	$result:=False
	Case of 
		: (This.userInfo=Null)
		: (Not(OB Is defined(This.userInfo; "estGuest")))
		Else 
			$result:=This.userInfo.estGuest
	End case 
	
	
Function _finaliser()
	// le processus d'authentication est terminé, fixer les privilèges (on n'est plus Guest)
	var $data : Object
	
	Use (Session.storage.UserParams)
		Session.storage.UserParams.EtatNavigation:="Personnes"
	End use 
	
	// fixer les privilèges
	$data:=New object
	$data.userName:=Session.storage.userInfo.userName
	// ne PAS utiliser les roles (pb dans fichier .json
	$data.privileges:=New collection("ReadRecords")  // tout le monde pareil, sinon lire les userInfo (attribut de UtilisateursALV) 
	Session.setPrivileges($data)
	
	
    

[class]$action - 02/08/2026 12:32:29

      property carto : cs.xCarto.$carte
property entity : Object
Class extends $pageWeb

Class constructor()
	
	Super()
	
	This.carto:=cs.xCarto.$carte.new()
	This.entity:=Null
	
	
	
Function TraiterURL($url : Text)->$result : Boolean
	// traitement des url internes au serveur (appel des classes $pageXXX par les templates)
	// remarque : dans cette version, ces url ont le même format que celles des requêtes client HTTP
	$result:=False
	This.result.nomMethode:=Current method name
	
	// récupérer les paramètres
	This.LireParametresUrl($url)
	
	// normalement .balise[2] est le nom d'une function d'ici
	Case of 
		: (This.balisesUrl=Null)
		: (This.balisesUrl.length<3)
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incorrecte - il manque la paramètres .balise[2]"
			
		: (Not(OB Is defined(This; This.balisesUrl[2])))
			This.result.Error:=-15068
			This.result.ErrorDescription:="url incorrecte - .balise[2] '"+This.balisesUrl[2]+"' n'est pas une function de this "+OB Class(This).name
			
		Else 
			// on accepte la demande
			$result:=True
			
			// le résultat, s'il y a, est dans this.result.resultat
			// exécuter la function, en cas d'erreur elle peut renseigner This.result
			This[This.balisesUrl[2]]()
	End case 
	
	
	// -----------------------------
	// MARK:Notes
	// -----------------------------
	
	// pour les notes, on utilise le woker pour générer du contenu HTML contextuel et, en particulier hors du composant ALB (qui ne reconnait pas les méthodes WEBMO ???)
Function AfficherNotes()
	var $texte : Text
	var $data : Object
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Création"; Current method name; "Notes de "+"?"+Session.storage.UserParams.EtatNavigation)
	
	$texte:="<h2>"+Localized string("1108")+"</h2>"
	
	// interroger la BDD_NOTE
	$data:=New object("DataClassNom"; Session.storage.UserParams.EtatNavigation)
	If (cs.$notes.new(New object).LireNotes($data))
		$texte:=$data.notes
	End if 
	
	// envoyer le contenu
	WEB SEND TEXT(JSON Stringify([This.paramsUrl[0]; $texte]))
	
	
Function SaisirNote()
	var $racineXML; $ElémentXML; $texte; $label : Text
	
	This.InitStructureXML("article"; "")
	
	//attention : la balise "textarea" ne doit pas être autofermante => il faut initialiser une valeur
	// s'il existe une valeur, le placeholder ne fonctionne plus => supprimer la valeur après création du texte
	$racineXML:=DOM Create XML element(This.RacineXML; "article"; "class"; "noteLibelle")
	$ElémentXML:=DOM Create XML element($racineXML; "p")
	DOM SET XML ELEMENT VALUE($ElémentXML; Localized string("218"))
	$ElémentXML:=DOM Create XML element($racineXML; "article")
	$ElémentXML:=DOM Create XML element($ElémentXML; "textarea"; "class"; "noteInput"; "id"; "noteObjet"; "name"; "noteObjet"; "placeholder"; "Une remarque, une correction..."; "type"; "text"; "value"; "")
	DOM SET XML ELEMENT VALUE($ElémentXML; "toto")
	
	// calculer la balise 219
	Try
		$label:=cs[Session.storage.UserParams.EtatNavigation].new().labelDataClass()
		$label:=This.cFct.LireLocatedSTR(219; New object("param_1"; $label))
	Catch
		$label:=This.cFct.LireLocatedSTR(219; New object("param_1"; "L'ALBUM"))
	End try
	
	$racineXML:=DOM Create XML element(This.RacineXML; "article"; "class"; "noteLibelle")
	$ElémentXML:=DOM Create XML element($racineXML; "p")
	DOM SET XML ELEMENT VALUE($ElémentXML; $label)
	$ElémentXML:=DOM Create XML element($racineXML; "article")
	$ElémentXML:=DOM Create XML element($ElémentXML; "textarea"; "class"; "noteInput"; "id"; "noteContenu"; "name"; "noteContenu"; "placeholder"; "Description..."; "type"; "text"; "value"; "")
	DOM SET XML ELEMENT VALUE($ElémentXML; "toto")
	
	$texte:=This.ExporterStructureXML()
	$texte:=Replace string($texte; "toto"; "")
	
	// envoyer le contenu
	WEB SEND TEXT($texte; "text/html")
	
	
Function AjouterNoteDeBDD()
	// ajouter l'entité de BDD concernée
	var $requete : Object
	
	$requete:=JSON Parse(This.paramsUrl[0])
	
	$requete.EntitéRéduite:=New object
	$requete.EntitéRéduite.DataClassNom:=Session.storage.UserParams.EtatNavigation
	$requete.EntitéRéduite.IDunique:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].currentEntity.IDunique
	
	This._AjouterNote($requete)
	
	
Function AjouterNoteDeALB()
	// ajouter l'entité de l'image concernée
	var $requete : Object
	
	$requete:=JSON Parse(This.paramsUrl[0])
	
	$requete.EntitéRéduite:=New object
	$requete.EntitéRéduite.DataClassNom:=Session.storage.UserParams.EtatNavigation
	$requete.EntitéRéduite.IDunique:=Session.storage.ALB_UserParams.Album.Medias[Num($requete.indexMedia)].MED_IDunique
	
	This._AjouterNote($requete)
	
	
Function _AjouterNote($requete : Object)
	// traiter la requete
	var $response : Object
	
	$requete.success:=False
	
	Case of 
		: (Not(OB Is defined($requete; "EntitéRéduite")))
		: (Not(OB Is defined($requete.EntitéRéduite; "DataClassNom")))
		: (Not(OB Is defined($requete.EntitéRéduite; "IDunique")))
		: (Not(OB Is defined($requete; "note")))
		: (Not(OB Is defined($requete.note; "objet")))
		: (Not(OB Is defined($requete.note; "contenu")))
		Else 
			
			$requete.success:=True
			cs.$notes.new($requete).EcrireNote()
	End case 
	
	$response:=New object
	$response.success:=$requete.success
	$response.message:=Localized string(String(1020+Num($response.success)))
	WEB SEND TEXT(JSON Stringify($response))
	
	
	// -----------------------------
	// MARK:Arbre
	// -----------------------------
	
Function menuAfficherArbre()
	// ouvrir l'arbre
	
	cs.$serveur.new().InitSession("ARB")
	
	// renseigner deQui
	Use (Session.storage.UserParams)
		// par defaut la personne sélectionnée
		Session.storage.UserParams.UUIDretourNavigation:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].currentEntity.IDunique
		
		// voir si on a cliquer sur un lien d'arbre
		Case of 
			: (Not(OB Is defined(This; "paramsUrl")))
			: (This.paramsUrl.length<2)
			Else 
				// utiliser cette personne
				Session.storage.UserParams.UUIDretourNavigation:=This.paramsUrl[1]
		End case 
	End use 
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation"; Current method name; "Arbre de ?Personnes?"+String(Session.storage.ARB_HTTPvars.UUIDpersonne))
	
	This.Envoyer("mobileArbreAfficher.shtml"; "Personnes"; "ARB_HTTPvars")
	
	
Function AfficherArbre()
	// une page Web est affichée et en attente de contenu
	// créer l'arbre de  Session.storage.UserParams.ARB_IDdeCujus
	var $params; $EtatProcessus : Object
	var $arbreXML; $RacineXML; $texte : Text
	var $ID : Integer
	
	// message en cas d'erreur
	$texte:="<h2>"+This.cFct.LireLocatedSTR(11)+"</h2>"
	Case of 
		: (Not(OB Is defined(Session.storage.UserParams; "UUIDretourNavigation")))
		Else 
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Création"; Current method name; "Arbre de "+"?Personnes?"+Session.storage.UserParams.UUIDretourNavigation)
			
			// c'est ok
			$ID:=cs.Personnes.new(Session.storage.UserParams.UUIDretourNavigation).ID
			
			$params:=New object
			$params.IDarbre:=$ID
			$params.IDpersonne:=CodeEnreg($ID; [1])
			$params.NmaxAscendance:=3
			$params.NmaxDescendance:=1
			$params.CheminDossierBDD_AG:=This.document.getSessionFolder().platformPath
			
			$params.EntitésWebables:=New object  // impératif en non guest
			If (Session.storage.userInfo.estGuest)
				$params.PersonnesAffichables:="filtrePersonnes"
				$params.EntitésWebables[$params.PersonnesAffichables]:=Storage.EntitésWebables.filtrePersonnes
				$params.EventsAffichables:="filtreEvents"
				$params.EntitésWebables[$params.EventsAffichables]:=Storage.EntitésWebables.filtreEvents
			End if 
			
			$params.nomBDD:="ARB"
			$params.Modele:="MobileArbre.xml"
			$params.functionID:="Construire_AG"
			
			$params.Session_Etat:=Storage.Host.Session_Etat
			$params.optionsMsg:=[msgk_event]
			// options : on veut les cadres des personnes absentes (=> largeur image SVG fixe)
			$params.Options:=0x0023  // liens dynamiques"
			$params.SéparateurParamsURL:="?"  // séparateur des paramètres dans les URL (4DACTION...)"
			// initialiser l'état
			OB SET($EtatProcessus; "Params"; 0x0000)
			//OB FIXER($EtatProcessus;"Params";0x0000 ?? 25)  // afficher viewer
			OB SET($EtatProcessus; "SaisieAutorisée"; False)
			$params.EtatProcessus:=$EtatProcessus
			
			// on lance (le résultat sera reçu dans arbreXML)
			cs.xARB.$arbre.new().Imager_AG($params)
			If ($params.arbreXML#"")
				$arbreXML:=$params.arbreXML
				// récupérer l'arbre
				$RacineXML:=DOM Parse XML variable($arbreXML)
				DOM EXPORT TO VAR($RacineXML; $texte)
				// pour des tests
				//DOM EXPORTER VERS FICHIER($RacineXML; Dossier 4D(Dossier Logs; *)+"Test Arbre.xml")
				DOM CLOSE XML($RacineXML)
				// supprimer la balise <?xml  >
				$texte:=Substring($texte; Position("<"; $texte; 2; *))
				
			Else 
				$texte:=JSON Stringify($params.EtatProcessus; *)
			End if 
	End case 
	
	// envoyer le contenu
	WEB SEND TEXT($texte)
	
	
	// -----------------------------
	// MARK:Carte
	// -----------------------------
	
Function menuAfficherCarte()
	// ouvrir la carte
	// variables process d'ici
	var wwwCarto_Opacity_OSM; wwwCarto_Opacity_GP; wwwCarto_Opacity_Cible; wwwZoneWeb_ZoomMin; wwwZoneWeb_ZoomMax : Text
	var wwwCarto_Install_GP : Text
	// variables fixées par le composant CARTO
	var wwwCarto_LatitudeCentre; wwwCarto_LongitudeCentre; wwwZoneWeb_Zoom; ZoneWeb_Largeur; ZoneWeb_Hauteur; wwwMediaPath : Text
	
	This.entity:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].currentEntity
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation"; Current method name; "Carte de "+"?"+This.entity.DataClassNom+"?"+This.entity.IDunique)
	
	vs4D:=""
	
	cs.$serveur.new().InitSession("CARTO")
	wwwCarto_LatitudeCentre:=""
	wwwCarto_LongitudeCentre:=""
	wwwZoneWeb_Zoom:=""
	ZoneWeb_Largeur:=""
	ZoneWeb_Hauteur:=""
	wwwMediaPath:=""
	
	// renseigner deQui
	// l'entité sélectionnée
	Use (Session.storage.UserParams)
		Session.storage.UserParams.UUIDretourNavigation:=This.entity.IDunique
	End use 
	
	Use (Session.storage.CARTO_HTTPvars)
		// nécessaires pour .getCarteData()
		Session.storage.CARTO_HTTPvars.wwwRacineRessources:="../../"
		Session.storage.CARTO_HTTPvars.wwwContexteWeb:="ALVmobile"
		// initialiser les variables process contenus dans la carte
		Session.storage.CARTO_HTTPvars.wwwCarto_Install_GP:="false"
		Session.storage.CARTO_HTTPvars.wwwCarto_Opacity_Cible:="0.0"
		This.rsc.SetObjet(Est Ressource WEBMO; "Carte/Carto_Opacity_OSM"; Is text; Session.storage.CARTO_HTTPvars; "wwwCarto_Opacity_OSM")
		This.rsc.SetObjet(Est Ressource WEBMO; "Carte/Carto_Opacity_GP"; Is text; Session.storage.CARTO_HTTPvars; "wwwCarto_Opacity_GP")
		// zoom
		This.rsc.SetObjet(Est Ressource WEBMO; "Carte/Carto_ZoomMin_OL"; Is text; Session.storage.CARTO_HTTPvars; "wwwZoneWeb_ZoomMin")
		This.rsc.SetObjet(Est Ressource WEBMO; "Carte/Carto_ZoomMax_OL"; Is text; Session.storage.CARTO_HTTPvars; "wwwZoneWeb_ZoomMax")
	End use 
	
	// créer les data de la carte
	This.AfficherCarte()
	
	// envoyer la page
	This.Envoyer(Choose(OB Is defined(Session.storage.CARTO_HTTPvars; "wwwCarto_LatitudeCentre"); "mobileCarteAfficher.shtml"; "mobileCarteVide.shtml"); Session.storage.UserParams.EtatNavigation; "CARTO_HTTPvars")
	
	
Function AfficherCarte()
	// créer les données de la carte
	var $params : Object
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Création"; Current method name; "Carte de "+"?"+This.entity.DataClassNom+"?"+This.entity.IDunique)
	
	// init de la sélection de lieux et des paramètres associés de la zone à afficher
	$params:=New object
	// récepteur des variables process
	$params.HTTPvars:=OB Copy(Session.storage.CARTO_HTTPvars)
	
	// l'entité à cartographier (entité réduite , ici pas de DataStore)
	$params.sélection:=New collection(New object("DataClassNom"; This.entity.DataClassNom; "IDs"; New collection(This.entity.ID)))
	// créer le dossier où mettre les fichiers
	$params.cheminRacineHTML:=This.document.getRacineHTMLFolder().platformPath
	// dossier de sessions
	$params.SessionID:=Session.id
	$params.cheminSessionFolder:=This.document.getSessionFolder().platformPath
	$params.sousDossierImages:="images"
	// urls sur le serveur Web
	$params.nomFichierMarkers:="dataGP_"+This.entity.IDunique+".js"
	// pour la page Web, icones d'instance vues au niveau de la session
	$params.dossierCartoURL:=$params.HTTPvars.wwwRacineRessources+Localized string("129")+"/"+Session.id
	
	// formats pour mobile
	$params.Apparence:=New object("CodeLangue"; "fr"; "SymbolConjoints"; " & "; "SymbolDateLieu"; " - "; "FormatDate"; 5; "FormatHeure"; 2; "FormatLieu"; 1; "FormatGeoLoc"; 1)
	$params.PopUpOptions:=New object("Lieux"; 0x00040000; "Communes"; 0x00C80000; "Departements"; 0x00C80000; "Regions"; 0x00880000; "Pays"; 0x0000)
	$params.PopUpLiens:=True
	
	// demander la carte et générer ses données de session
	This.carto.getCarteData($params)
	
	// installer les ressources carto
	This.carto.InstallerRessources($params.cheminRacineHTML)
	// créer les fichiers
	This.carto.InstallerDonnées($params)
	// récupérer les MarkersData
	Use (Session.storage.CARTO_Data)
		Session.storage.CARTO_Data.MarkersData:=$params.data.MarkersData
	End use 
	
	// récupérer les variables process
	sharedObject($params.HTTPvars; Session.storage.CARTO_HTTPvars)
	
	
	// -----------------------------
	// MARK:Album
	// -----------------------------
	
Function AfficherMediaALB()
	// bouchonner l'appel
	var $texte : Text
	
	$texte:=JSON Stringify({vide: "vide"}; *)
	// envoyer le contenu
	WEB SEND TEXT($texte)
	
    

[class]$pageWebNAV - 31/07/2026 17:51:24

      property prefixSelectionApp; prefixSelection; nomSelection; nomBDD : Text

Class extends $pageWeb

Class constructor()
	
	Super()
	
	This.prefixSelectionApp:="selectionNavAll"
	This.prefixSelection:="selectionNav"
	
	
	
Function set NomSelectionNavAll($dataClassNom : Text)
	This.nomSelection:=This.prefixSelectionApp+$dataClassNom
	
	
Function set NomSelectionNav($dataClassNom : Text)
	This.nomSelection:=This.prefixSelection+$dataClassNom
	
	
Function get nomBDD()->$result : Text
	$result:="BDDguest"
	Case of 
		: (Not(OB Is defined(Session.storage; "userInfo")))
		: (Not(OB Is defined(Session.storage.userInfo; "estGuest")))
		Else 
			$result:=Choose(Session.storage.userInfo.estGuest; "BDDguest"; "BDD")
	End case 
	
	
	// -----------------------------
	// MARK:Création
	// -----------------------------
	
Function CréerSelectionNavSession()
	// créer les sélections All pour la session
	var $dataClassNom : Text
	
	Use (Session.storage.UserParams)
		
		For each ($dataClassNom; This.listeDataClass)
			This.NomSelectionNavAll:=$dataClassNom  // attribut calculé
			Session.storage.UserParams[This.nomSelection]:=New shared object
			
			Use (Session.storage.UserParams[This.nomSelection])
				// paramètrage des listes
				Session.storage.UserParams[This.nomSelection].filtre:=""
				Session.storage.UserParams[This.nomSelection].tri:=""
				// sélection d'entités
				Session.storage.UserParams[This.nomSelection].selection:=New shared collection
				// suivant le user on charge la BDD opérationnelle ou la BDD guest 
				sharedCollection(Storage[This.nomBDD][$dataClassNom]; Session.storage.UserParams[This.nomSelection].selection)
			End use 
		End for each 
	End use 
	
	
Function CréerListeHTML($selection : Collection; $params : Object)->$result : Text
	var $texte : Text
	
	$texte:=$selection.extract("Elementliste").join("")
	PROCESS 4D TAGS($texte; $result; $params)
	
	
Function CréerSelectionNavAutre($dataClassNom : Text)
	var $selection : Object
	var $nomForm : Text
	var $c : Collection:=[]
	
	// créer la sélection à afficher
	Case of 
		: ($dataClassNom="Medias")
			$selection:=This.getCurrentEntityALL().LesMedias()
			$nomForm:="mediaSimple"
			
		: ($dataClassNom="Sites")
			$selection:=This.getCurrentEntityALL().LesSites()
			$nomForm:="detailSimple"
			
		Else 
			$selection:=Null
	End case 
	
	If (Not($selection=Null))
		// créer la BDD de cette sélection à partir de la BDD
		$c:=Storage[This.nomBDD][$dataClassNom].query("IDunique IN :1"; $selection.selection.extract("IDunique"))
		
		// ajouter à session
		This.NomSelectionNav:=$selection.DataClassNom
		Use (Session.storage.UserParams)
			Session.storage.UserParams[This.nomSelection]:=New shared object
		End use 
		
		Use (Session.storage.UserParams[This.nomSelection])
			Session.storage.UserParams[This.nomSelection].tri:=""
			Session.storage.UserParams[This.nomSelection].selection:=New shared collection
		End use 
		sharedCollection($c; Session.storage.UserParams[This.nomSelection].selection)
		
		// récupérer et enregistrer les UUID
		$c:=$selection.selection.extract("IDunique")
		
		Use (Session.storage.UserParams[This.nomSelection])
			Session.storage.UserParams[This.nomSelection].collection:=New shared collection
		End use 
		sharedCollection($c; Session.storage.UserParams[This.nomSelection].collection)
	End if 
	
	
Function SelectionNavAllvide()->$result : Text
	// créer une sélection vide, utilisées par défaut (par exemple si la maintenance n'a pas été lancée)
	
	// il faut styler correctement le texte
	$result:="<article id='0' class='element-listeEntity'>"
	$result+=Localized string("1023")
	$result+="</article>"
	
	
	// -----------------------------
	// MARK:Selection
	// -----------------------------
	
Function getListeHTML()->$result : Text
	var $data : Object
	var $c : Collection
	
	If (OB Is defined(Session.storage; "UserParams"))
		If (Not(OB Is defined(Session.storage.UserParams; Session.storage.UserParams.currentSelectionNavNom)))
			$result:=This.SelectionNavAllvide()
			
		Else 
			$data:=OB Copy(Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom])
			
			If ($data.tri="")
				$c:=$data.selection
			Else 
				$c:=$data.selection.orderBy($data.tri)
			End if 
			
			$data:=cs[Session.storage.UserParams.EtatNavigation].new().FixerParametresListe()
			$result:=This.CréerListeHTML($c; $data)
		End if 
	End if 
	
	
Function setSelectionNavAll($dataClassNom : Text)
	// fixer le nom de la sélection pour la navigation dans l'App
	This.NomSelectionNavAll:=$dataClassNom
	
	// la stocker pour la navigation dans App et pour le retour d'une navigation annexe
	Use (Session.storage.UserParams)
		Session.storage.UserParams.Contexte:="ALL"
		Session.storage.UserParams.currentSelectionNavNom:=This.nomSelection
		Session.storage.UserParams.currentSelectionNavAllNom:=This.nomSelection
	End use 
	
	
Function setSelectionNavSelect($dataClassNom : Text)
	// initialiser la navigation dans une sélection
	This.CréerSelectionNavAutre($dataClassNom)
	
	Use (Session.storage.UserParams)
		Session.storage.UserParams.currentSelectionNavNom:=This.nomSelection
	End use 
	
	
Function setCurrentEntity($dataClassNom : Text; $UUID : Text)
	// créer l'entité sélectionnée
	var $entité : Object
	
	$entité:=cs[$dataClassNom].new($UUID)
	// la stocker
	cs._CommunFunctions.me.AjouterObjetA($entité; Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom]; "currentEntity")
	
	
Function getCurrentEntity()->$result : Object
	$result:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].currentEntity
	
	
Function getCurrentEntityALL()->$result : Object
	$result:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavAllNom].currentEntity
	
	
Function getNextEntityUUID()->$result : Text
	var $c : Collection
	var $index : Integer
	
	$c:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].collection
	
	$result:=This.getCurrentEntity().IDunique
	
	$index:=$c.indexOf($result)
	If ($index<($c.length-1))
		$result:=$c[$index+1]
	End if 
	
	
Function getPreviousEntityUUID()->$result : Text
	var $c : Collection
	var $index : Integer
	
	$c:=Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].collection
	
	$result:=This.getCurrentEntity().IDunique
	
	$index:=$c.indexOf($result)
	If ($index>0)
		$result:=$c[$index-1]
	End if 
	
	
	// -----------------------------
	// MARK:Actions
	// -----------------------------
	
Function navGoToPrevious()
	// navigation dans une page Detail
	var $UUID; $url : Text
	
	$UUID:=This.getPreviousEntityUUID()
	$url:="/4DCGI/WebDATA/"+This.paramsUrl[0]+"?"+This.paramsUrl[1]+"?"+This.getCurrentEntity().DataClassNom+"?"+$UUID
	WEB SEND HTTP REDIRECT($url)
	
	
Function navGoToNext($nomPage : Text)
	// navigation dans une page Detail
	var $UUID; $url : Text
	
	$UUID:=This.getNextEntityUUID()
	$url:="/4DCGI/WebDATA/"+This.paramsUrl[0]+"?"+This.paramsUrl[1]+"?"+This.getCurrentEntity().DataClassNom+"?"+$UUID
	WEB SEND HTTP REDIRECT($url)
	
	
Function getFilterData()
	// enregistrer la valeur du filtre et la collection d'entités résultante, calculés par l'agent
	var $corps; $statusText : Text
	var $data : Object
	
	$statusText:="KO"
	
	ARRAY TEXT($clésEntête; 0)
	ARRAY TEXT($valeursEntête; 0)
	WEB GET HTTP HEADER($clésEntête; $valeursEntête)
	
	Case of 
		: (Not($valeursEntête{Find in array($clésEntête; "X-METHOD")}="POST"))
		: (Not($valeursEntête{Find in array($clésEntête; "Content-Type")}="Application/alv-data"))
		Else 
			
			WEB GET HTTP BODY($corps)
			$data:=JSON Parse($corps)
			
			Case of 
				: (Not(OB Is defined($data; "filtre")))
				: (Not(OB Is defined($data; "collection")))
				: (Session.storage.UserParams.currentSelectionNavNom="")
					// pas initialisé (en debug par exemple)
				Else 
					
					Use (Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom])
						Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].filtre:=$data.filtre
						Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].collection:=New shared collection
					End use 
					sharedCollection($data.collection; Session.storage.UserParams[Session.storage.UserParams.currentSelectionNavNom].collection)
					
					$statusText:="OK"
			End case 
	End case 
	
	WEB SEND TEXT($statusText)
	
	
	// -----------------------------
	// MARK:Navigation
	// -----------------------------
	
Function Initialiser()
	var $data : Object
	// démarrage sur la page "Personnes"
	
	$data:=Session.storage.UserParams
	Use ($data)
		$data.selectionNavMedias:=New shared object
		$data.EtatNavigation:="Personnes"
	End use 
	This.setSelectionNavAll("Personnes")
	
	// initialiser les sélections User
	This.CréerSelectionNavSession()
	
	
Function retourNavAll()
	var $entité : Object
	var $url : Text
	
	$entité:=This.getCurrentEntityALL()
	
	$url:="/4DCGI/WebDATA/AfficherFORMliste?detailSimple?"+$entité.DataClassNom+"?"+$entité.IDunique
	WEB SEND HTTP REDIRECT($url)
	
	
Function retourNavSelect()
	var $url : Text
	
	$url:="/4DCGI/WebDATA/RafraichirFORMselection?collectionSelection?"+Session.storage.UserParams.EtatNavigation
	WEB SEND HTTP REDIRECT($url)
	
	
Function retourNavExtension()
	var $url : Text
	
	$url:="/4DCGI/WebDATA/AfficherFORMdetail?detailSimple?"+Session.storage.UserParams.EtatNavigation+"?"+Session.storage.UserParams.UUIDretourNavigation
	WEB SEND HTTP REDIRECT($url)
	
	
	// wrapper (tout ce qui est extension est gérer par cs.$action
Function menuAfficherArbre()
	cs.$action.new().menuAfficherArbre()
	
	
Function menuAfficherCarte()
	cs.$action.new().menuAfficherCarte()
	
    

[class]_Trace - 15/11/2025 16:09:14

      property cible : cs.xSDK.Traces
property success : Boolean
property origine : Text

singleton Class constructor()
	
	This.cible:=cs.xSDK.Traces.new()
	This.success:=False
	This.origine:="WEBMO"
	
	
	// ----------------------
	//MARK:Wrappers
	// ----------------------
	
Function set Error($numError : Integer)
	This.cible.Error:=$numError
	
Function get Error()->$result : Integer
	$result:=This.cible.Error
	
	
Function set ErrorDescription($description : Text)
	This.cible.ErrorDescription:=$description
	
Function get ErrorDescription()->$result : Text
	$result:=This.cible.ErrorDescription
	
	
Function Intercepter()
	ErrorNum:=This.cible.Intercepter(This.origine; Error; Error method; Error line; Error formula)
	
	
Function Initialiser($nomMethode : Text)->$result : Object
	This.cible.CréerErreur(This.origine; 0; $nomMethode; "")
	$result:=This
	
	
Function Créer($Error : Integer; $nomMethode : Text; $ErrorDescription : Text)->$result : Object
	This.cible.CréerErreur(This.origine; $Error; $nomMethode; $ErrorDescription)
	$result:=This
	
	
Function FixerSuccess()
	This.cible.FixerSuccess()
	This.success:=This.cible.success
	
	
Function LeverException($options : Collection)
	// renseigner le label de l'erreur
	This.cible.ErrorLabel:=Localized string(String(This.cible.Error))
	// lancer le traitement de l'erreur
	This.cible.LeverException($options)
	
	
Function EnvoyerMessages($options : Collection; $libellé : Text; $source : Text; $description : Text)
	This.cible.EnvoyerMessages($options; This.origine; $libellé; $source; $description)
	
	
Function DebugerMethode($libellé : Text; $source : Text; $description : Text)->$result : Boolean
	// mettre à jour l'état (activation / désactivation des ASSERT)
	cs._composant.new().InstallerDonnéesHote()
	
	$result:=This.cible.DebugerMethode(Storage.Host; This.origine; $libellé; $source; $description)
	
	
Function DebugerVariables($libellé : Text; $source : Text; $data : Object)->$result : Boolean
	// toujours appelé par un ASSERT (Storage.Host ne peut être réactualisé)
	$result:=This.cible.DebugerVariables(Storage.Host; This.origine; $libellé; $source; $data)
	
	
	// ----------------------
	//MARK:Session
	// ----------------------
	
Function Session($nomMethode : Text; $url : Text)
	var $data : Object
	var $c : Collection
	var $attribut : Text
	
	$data:=New object
	$data.methode:=$nomMethode
	$data.url:=$url
	
	$data.numProc:=Current process
	$data.nomProc:=Current process name
	$data.storage:=Null
	
	Case of 
		: (Not(Get assert enabled))
		: (Session=Null)
			$data.id:="pas de session ouverte"
			
		Else 
			$data.id:=Session.id
			$data.userName:=Session.userName
			$data.isGuest:=Session.isGuest()
			$data.idleTimeout:=Session.idleTimeout
			$data.expirationDate:=Session.expirationDate
			
			$data.HTTPvars:=Null
			If (OB Is defined(Session.storage; "HTTPvars"))
				$data.HTTPvars:=OB Copy(Session.storage.HTTPvars)
			End if 
			
			// les variables process courantes
			$c:=OB Keys(Session.storage.HTTPvars)
			$data.HTTPvarsCourantes:=New object
			For each ($attribut; $c)
				$data.HTTPvarsCourantes[$attribut]:=Get pointer($attribut)->
			End for each 
			
			$data.UserParams:=Null
			If (OB Is defined(Session.storage; "UserParams"))
				$data.UserParams:=OB Copy(Session.storage.UserParams)
			End if 
			
			// pour finir
			//$data.storage:=OB Copier(Session.storage)
	End case 
	
	ASSERT(cs._Trace.me.DebugerVariables(String(Milliseconds)+"_SessionStorage"; Current method name; $data))  //  "Options"; 0x0080
	
	
    

[class]UnionsSelect - 04/10/2025 09:34:53

      Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("UnionsSelect"; $requête)
	
	This.selection:=Null
	This.length:=0
	
    

[class]Events - 04/10/2025 09:35:41

      // attributs de la classe
property heure : Time
property dateNumValid : Boolean
//property dateNum : Date
property dateChaine; commentaire : Text
//property genre : Boolean

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	// initialiser l'objet avec les données de l'entité $IDentité de la BDD
	// rappel : l'event est genré dynamiquement (voir le getEvent() de la classe Personne)
	
	Super("Events"; $IDentité)
	
	
	
Function RédigerCommentaire($formats : Object)->$result : Text
	$result:=""
	Case of 
		: (Not($formats.params ?? 0))
		: (Not(OB Is defined($formats; "débutComment")))
		: (Not(OB Is defined($formats; "finComment")))
		Else 
			$result:=This.commentaire
	End case 
	$result:=($formats.débutComment+$result+$formats.finComment)*Num(Length($result)#0)
	
	
	// ----------------------
	//MARK:Wrappers
	// ----------------------
	
Function Libellé($userFormats : Object)->$libellé : Text
	var $formats : Object
	
	// on va les modifier
	$formats:=OB Copy($userFormats)
	// ajouter les formats manquants
	This._FixerParamètresLibellé($formats)
	
	$libellé:=This.dataClass.Libellé($formats)
	
	
	// ----------------------
	//MARK:Selection
	// ----------------------
	
Function LesProtagonistes()->$result : cs.PersonnesSelect
	$result:=cs.PersonnesSelect.new(This.dataClass.LesProtagonistes())
	$result.Créer()
	
	
	// ----------------------
	//MARK:Form Detail
	// ----------------------
	
Function FormaterDate($formats : Object)->$result : Text
	// code dateNum dans le format $Formats avec les options $3.Options
	// $formats :
	// .FormatDate
	// .FormatHeure
	// .Options:
	//.  bit 8 = entête
	var $data : Object
	var $options : Integer
	
	$result:=""
	
	$options:=$Formats.Options
	
	$data:=New object("dateChaine"; This.dateChaine)
	cs.xSDK.Outils.me.getDateNum($data)
	
	// mettre au format demandé
	Case of 
		: ($Formats.FormatDate=0)  // la date de la BDD au format numérique
			$formats.dateValide:=$data.dateNumValid
			$formats.dateNum:=Add to date(!00-00-00!; Year of($data.dateNum); Month of($data.dateNum); Day of($data.dateNum))
			
		: ($Formats.FormatDate=Internal date long)  // la date mémorisée en la BDD
			$result:=Choose(($options ?? 8) & $data.dateNumValid; Localized string("1008"); " ")+This.dateChaine
			
		: ($Formats.FormatDate=Internal date short)  // la date de la BDD si possible au format numérique 7 = "jj-mm-aaaa"
			Case of 
				: ($data.dateNumValid)
					$result:=(Localized string("1008")*Num($options ?? 8))+String(Day of($data.dateNum))+"-"+String(Month of($data.dateNum))+"-"+String(Year of($data.dateNum))
					
				: (This.dateChaine="")
					$result:=This.cFct.LireLocatedSTR(1004)  // inconnu
					
				Else 
					$result:=(" "*Num($options ?? 8))+This.dateChaine
			End case 
			
		: ($Formats.FormatDate=11)  // la date de la BDD au format numérique = "aaaa"
			$result:=String(Year of($data.dateNum))
	End case 
	
	
Function FormaterHeure($formats : Object)->$result : Text
	// code heure dans le format $Formats avec les options $3.Options
	// $formats :
	// .FormatHeure
	// .Options:
	//   bit 8 = entête
	var $options : Integer
	var $texte : Text
	
	$options:=$Formats.Options
	
	$texte:=String(Time(This.heure); Num($Formats.FormatHeure))
	$texte:=Replace string($texte; ":"; "h"; *)
	$texte:=(Localized string("33")*Num($options ?? 8))+$texte
	$result:=$texte*Num(Not(Time(This.heure)=?00:00:00?))
	
	
    

[class]PersonnesSelect - 30/07/2026 16:35:32

      Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("PersonnesSelect"; $requête)
	
	This.selection:=Null
	This.length:=0
	
	
	
Function LesPatronymes()->$result : cs.DicoDesNomsSelect
	$result:=cs.DicoDesNomsSelect.new(This.dataClass.LesPatronymes())
	
	
	// ----------------------
	//MARK:PageHTML
	// ----------------------
	
Function CreerBDDmobile($tâche : cs.xSDK.Tache)->$result : Collection
	var $entité : cs.Personnes
	
	// créer la sélection d'entités
	This.Créer()
	
	// créer les entités de la BDD mobile
	$result:=New collection
	If (This.selection.length>0)
		// ordonner
		This.selection:=This.selection.orderBy("nom asc, prenom asc")
		
		For each ($entité; This.selection) While (Not($tâche.Tuer.signaled))
			$result.push($entité.CreerBDDentity())
		End for each 
	End if 
	
    

[class]Medias - 30/11/2025 10:58:14

      property fct : cs.xSDK.Outils
property lesPersonnages : Object
property lesInstantanes; lesPaysages : Collection
// attributs de la classe
property nom; dateChaine; titre; commentaire; nomFichier : Text
property type : Integer
property dateNum : Date

Class extends _WEBMO_DataStore

Class constructor($IDentité : Variant)
	
	Super("Medias"; $IDentité)
	
	This.fct:=cs.xSDK.Outils.me
	
	
	
	
Function FixerParametresListe()->$result : Object
	$result:=New object
	$result.DataClassNom:="Medias"
	$result.Requete:="private = 0 AND (type = 1 OR type = 2)"
	$result.nomFunction:="AfficherFORMdetail"
	$result.nomFORMlist:="mediaSimple"
	$result.TriParTitre:="titre asc, dateNum asc"
	$result.TriParDate:="dateNum asc, titre asc"
	
	
Function CreerBDDentity()->$result : Object
	$result:=New object
	$result.IDunique:=This.IDunique
	$result.dateNum:=This.dateNum
	$result.titre:=This.titre
	
	$result.Elementliste:=This.Elementliste()
	// les champs calculés
	Super.CreerBDDentity($result; ["Champ_1"; "Champ_2"; "Champ_3"; "Champ_ParagrapheTitre_11"; "Champ_ParagrapheTitre_12"; "Champ_ParagrapheTitre_21"; "Champ_ParagrapheTitre_22"])
	
	
Function labelDataClass()->$result : Text
	$result:=Super.labelDataClass("le ")
	
	
	// ----------------------
	//MARK:Sélection
	// ----------------------
	
Function getLesLiens()
	This._LesPersonnages()
	This._LesInstantanes()
	This._LesPaysages()
	
	
Function _LesPersonnages()
	var $selection : cs.PersonnesSelect
	
	$selection:=cs.PersonnesSelect.new(This.dataClass.LesPersonnes())
	// créer .selection
	$selection.Créer()
	Use (This)
		This.lesPersonnages:=New shared object
	End use 
	This.cFct.AjouterObjetA($selection; This; "lesPersonnages")
	
	
Function _LesInstantanes()
	var $selection : Object
	
	$selection:=cs.EventsSelect.new(This.dataClass.LesEvents())
	// créer .selection
	$selection.Créer()
	Use (This)
		This.lesInstantanes:=New shared collection
	End use 
	This.cFct.AjouterObjetA($selection; This; "lesInstantanes")
	
	
Function _LesPaysages()
	var $selection : Object
	
	$selection:=cs.EventsSelect.new(This.dataClass.LesLieux())
	// créer .selection
	$selection.Créer()
	Use (This)
		This.lesPaysages:=New shared collection
	End use 
	This.cFct.AjouterObjetA($selection; This; "lesPaysages")
	
	
	// ----------------------
	//MARK: FORM Liste
	// ----------------------
	
Function Elementliste()->$result : Text
	$result:="<article id='"+This.IDunique+"' class='element-collection' nom='"+This.titre+"' date='"+String(This.dateNum)+"'>"
	
	$result+=This.ImageLié()
	
	$result+="<article class='date-Collection'><span>"
	$result+=This.dateChaine
	$result+="</span></article>"
	
	$result+="<article class='titre-Collection'><span>"
	$result+=This.titre
	$result+="</span></article>"
	
	$result+="</article>"
	
	
Function ImageLié()->$result : Text
	$result:="<a href="+Char(Double quote)
	$result+=This._tagUrl+"WebDATA/<!--#4DEVAL $1.nomFunction-->?<!--#4DEVAL $1.nomFORMlist-->?Medias?"+This.IDunique
	$result+=Char(Double quote)+"><img class="+Char(Double quote)+"image-Collection"+Char(Double quote)+" src="
	$result+=This.getVignetteUrl(This.ID)
	$result+=" alt="+Char(Double quote)+String(This.ID)+Char(Double quote)+"/></a>"
	
	
	// ----------------------
	//MARK: FORM Detail
	// ----------------------
	
Function getVignetteUrl($ID : Integer)->$result : Text
	$result:=This.document.getVignetteURL($ID)
	
	
Function Champ_1()->$result : Text
	$result:="<img src="
	$result+=This.getMediasURL()
	$result+=" alt="+Char(Double quote)+String(This.ID)+Char(Double quote)+"</>"
	
	
	//Function Champ_1New()->$result : Text
	//Case of 
	//: (This.type=1)
	//$result:="<img src="
	//$result+=This.getMediasURL()
	//$result+=" alt="+Char(Double quote)+String(This.ID)+Char(Double quote)+"</>"
	
	//: (This.type=2)
	//$result:="<embed type='application/pdf' src="
	//$result+=This.getMediasURL()
	//$result+=" alt="+Char(Double quote)+String(This.ID)+Char(Double quote)+"</>"
	//End case 
	
	
Function Champ_2()->$result : Text
	$result:=This.dateChaine
	
	
Function Champ_3()->$result : Text
	$result:=This.titre
	
	
Function Champ_ParagrapheTitre_11()->$result : Text
	$result:=Localized string("153")
	
	
Function Champ_ParagrapheTitre_12()->$result : Text
	// décrire les zones accrochées à this
	var $entité : Object
	var $texte : Text
	
	This.getLesLiens()
	
	Case of 
		: (This.lesInstantanes.selection.length=1)
			// lié à un event
			$texte:=This.lesInstantanes.selection[0].Libellé(New object("Options"; 0x0103DD00))
			$result:="Photo prise lors "+$texte
			
		: (This.lesPaysages.selection.length=1)
			// un lieu
			$texte:=This.lesPaysages.selection[0].Libellé(New object("Options"; 0x00CA0000))
			$result:="Photo prise à "+$texte
	End case 
	
	// récupérer les zones personnages
	If (This.lesPersonnages.selection.length>0)
		// attention : éviter de mettre le bazar dans les sélections utilisées
		// ici, on n'utilise pas les liens pour remonter aux personnes ; cela perturbe les autres fonctions (tri par la date en particulier)
		$texte:=""
		For each ($entité; This.lesPersonnages.selection)
			$texte+=", "+$entité.Libellé(New object("Options"; 0x0003))
		End for each 
		// nettoyer
		$texte:=Substring($texte; 3)
		
		$texte:=Choose(This.lesPersonnages.selection.length=1; "Personne présente : "; "Personnes présentes (de gauche à droite) : ")+$texte
		$result+=(". "*Num(Length($result)>0))+$texte
	End if 
	
	
Function Champ_ParagrapheTitre_21()->$result : Text
	$result:=Localized string("151")
	
	
Function Champ_ParagrapheTitre_22()->$result : Text
	$result:=This.commentaire
	
	
	// -----------------------------
	// MARK:Maintenance
	// -----------------------------
	
Function getMediaNom()->$result : Text
	$result:="media_"+String(This.ID; "00000")+WebMob Image format
	
	
Function getMediaNomNew()->$result : Text
	Case of 
		: (This.type=1)
			$result:="media_"+String(This.ID; "00000")+WebMob Image format
			
		: (This.type=2)
			$result:="media_"+This.nomFichier
			
	End case 
	
	
Function getMediasURL()->$result : Text
	If (Storage.System.estExécutéDansHôte)
		$result:=This.document.getDataURL()
		
	Else 
		$result:="../../ressources/Images/mobileData/"
	End if 
	$result+=Localized string("147")+"/"+This.getMediaNom()
	
    

[class]MediasSelect - 04/06/2026 14:26:14

      property fct : cs.xSDK.Outils

Class extends _WEBMO_DataStore

Class constructor($requête : Variant)
	
	Super("MediasSelect"; $requête)
	
	This.fct:=cs.xSDK.Outils.me
	This.selection:=Null
	This.length:=0
	
	
	// ----------------------
	//MARK:PageHTML
	// ----------------------
	
Function CreerBDDmobile($tâche : cs.xSDK.Tache)->$result : Collection
	var $entité : cs.Medias
	
	// créer la sélection d'entités
	This.Créer()
	
	// créer les entités de la BDD mobile
	$result:=New collection
	If (This.selection.length>0)
		// ordonner
		This.selection:=This.selection.orderBy("dateNum asc")
		
		For each ($entité; This.selection) While (Not($tâche.Tuer.signaled))
			$result.push($entité.CreerBDDentity())
		End for each 
	End if 
	
	
	// -----------------------------
	// MARK:Maintenance
	// -----------------------------s
	
Function CréerMediaMobile($tâche : cs.xSDK.Tache)
	// télécharger les media de la sélection et les mettre dans $dossier au format mobile
	var $dossier : 4D.Folder
	var $entité; $data; $result; $fichier; $application : Object
	var $pict : Picture
	var $gauche; $haut; $droite; $bas; $largeur; $hauteur : Integer
	var $zoom : Real
	
	$dossier:=This.document.getMediasFolder()
	$application:=Storage.Host.$application.call()
	
	For each ($entité; This.selection) While (Not($tâche.Tuer.signaled))
		$fichier:=$dossier.file($entité.getMediaNom())
		
		Case of 
			: ($fichier.exists)
				// le media mobile existe déjà, pas besoin de recharger
				
			Else 
				// ok on y va
				$data:=New object("IDnomFichier"; String($entité.ID); "cheminDestination"; $dossier.platformPath; "Options"; 0)
				// dans ce process ; avec -1 pas de mise à jour FORM
				$result:=$application.TelechargerFichierMedia($data)
				// le fichier est dans $resultat.fichier
				
				If ($result.success)
					// recadrer l'image ; taille image max 1200 (hauteur et largeur)
					READ PICTURE FILE($result.fichier.platformPath; $pict)
					// nettoyer
					$result.fichier.delete()
					PICTURE PROPERTIES($pict; $largeur; $hauteur)
					$zoom:=-Scaled to fit prop centered
					$gauche:=0
					$haut:=0
					$droite:=WebMob Image largeur
					$bas:=WebMob Image hauteur
					This.fct.CalculerRectangleMedia(->$gauche; ->$haut; ->$droite; ->$bas; $largeur; $hauteur; ->$zoom)
					$pict:=$pict*$zoom
					
					// passer en JPEG
					CONVERT PICTURE($pict; WebMob Image format; 0.5)
					WRITE PICTURE FILE($fichier.platformPath; $pict; WebMob Image format)
					
					Waiting(1)
				End if 
		End case 
	End for each 
	
	
Function CréerMediaMobileNew($tâche : cs.xSDK.Tache)
	// télécharger les media de la sélection et les mettre dans $dossier au format mobile
	var $dossier : 4D.Folder
	var $entité; $data; $result; $fichier; $application : Object
	var $pict : Picture
	var $gauche; $haut; $droite; $bas; $largeur; $hauteur : Integer
	var $zoom : Real
	
	$dossier:=This.document.getMediasFolder()
	$application:=Storage.Host.$application.call()
	
	For each ($entité; This.selection) While (Not($tâche.Tuer.signaled))
		$fichier:=$dossier.file($entité.getMediaNom())
		
		Case of 
			: ($fichier.exists)
				// le media mobile existe déjà, pas besoin de recharger
				
			Else 
				// ok on y va
				$data:=New object("IDnomFichier"; String($entité.ID); "cheminDestination"; $dossier.platformPath; "Options"; 0)
				// dans ce process ; avec -1 pas de mise à jour FORM
				$result:=$application.TelechargerFichierMedia($data)
				// le fichier est dans $resultat.fichier
				
				Case of 
					: (Not($result.success))
						
					: ($entité.type=1)
						// recadrer l'image ; taille image max 1200 (hauteur et largeur)
						READ PICTURE FILE($result.fichier.platformPath; $pict)
						// nettoyer
						$result.fichier.delete()
						PICTURE PROPERTIES($pict; $largeur; $hauteur)
						$zoom:=-Scaled to fit prop centered
						$gauche:=0
						$haut:=0
						$droite:=WebMob Image largeur
						$bas:=WebMob Image hauteur
						This.fct.CalculerRectangleMedia(->$gauche; ->$haut; ->$droite; ->$bas; $largeur; $hauteur; ->$zoom)
						$pict:=$pict*$zoom
						
						// passer en JPEG
						CONVERT PICTURE($pict; WebMob Image format; 0.5)
						WRITE PICTURE FILE($fichier.platformPath; $pict; WebMob Image format)
						
					: ($entité.type=2)
						// pour l'instant recopie du PDF
						$result.fichier.rename($fichier.name+".pdf")
						
				End case 
				Waiting(1)
		End case 
	End for each 
	
	
	//Function CompresserPDF($fichier : 4D.File)
	
	//If (This.existsPlugIn())
	
	//End if 
	
	
	//Function existsPlugIn()->$result : Boolean
	//ARRAY LONGINT($tabNum; 0)
	//ARRAY TEXT($Elements; 0)
	//PLUGIN LIST($tabNum; $Elements)
	
	//// ID "AinsiLaVie Pack" = 15003, défini dans "4D Plugin Wizard"
	//If (Find in array($tabNum; 15003)=-1)
	//$result:=False
	//Else 
	//$result:=True
	//End if 
	
    

[class]$cookie - 25/06/2026 15:44:41

      property liste : Collection

singleton Class constructor()
	
	
	
	// -----------------------------
	// MARK:Traitement des cookies
	// -----------------------------
	
Function Lire()->$result : Collection
	// lire le contenu du cookie
	// rappel : de base 4D crée un cookie avec le nom "4DSID_ALVserveurWebMobile"
	var $cookie : Text
	var $i : Integer
	var $c; $cookies : Collection
	
	This.liste:=New collection
	
	$cookie:=This.getCookie()
	
	$cookies:=Split string($cookie; ";")
	For ($i; 0; $cookies.length-1)
		$c:=Split string($cookies[$i]; "="; sk trim spaces)
		This.liste.push(New object("nom"; $c[0]; "valeur"; $c[1]))
	End for 
	
	$result:=This.liste
	
	
Function Ecrire($attribut : Text; $valeur : Text)
	var $cookie : Text
	var $timeOut : Integer
	
	$cookie:=$attribut+"="+$valeur
	This.setCookie($cookie)
	
	WEB GET OPTION(Web inactive session timeout; $timeOut)
	cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Nouveau cookie"; Current method name; $attribut+" = "+$valeur+" - timeOut session Web "+String($timeOut)+" mn - ID session "+Session.id)
	
	
Function Supprimer()
	// on ne peut pas supprimer le fichier cookie. Pour invalider l'authentification, on change le date d'expiration du cookie
	This.Ecrire("Expires"; "Thu, 01 Jan 1970 00:00:01 GMT")
	This.Ecrire("path"; "/")
	
	
	// -----------------------------
	// MARK:Traitement des URL session
	// -----------------------------
	
Function getMobileWebID()->$result : Text
	// renvoie le ID de la session mobile
	
	$result:=""
	
	This.Lire()
	Case of 
		: (Not(OB Is defined(This; "liste")))
		: (This.liste.query("nom = :1"; "4DSID_ALVserveurWebMobile").length=0)
		Else 
			$result:=This.liste.query("nom = :1"; "4DSID_ALVserveurWebMobile")[0].valeur
	End case 
	
	
Function getCookie()->$result : Text
	var $rang : Integer
	
	ARRAY TEXT($clésEntête; 0)
	ARRAY TEXT($valeursEntête; 0)
	WEB GET HTTP HEADER($clésEntête; $valeursEntête)
	
	$rang:=Find in array($clésEntête; "Cookie")
	If ($rang#-1)
		$result:=$valeursEntête{$rang}
	End if 
	
	
Function setCookie($valeur : Text)
	var $cookie : Text
	
	$cookie:="Set-Cookie: "+$valeur
	WEB SET HTTP HEADER($cookie)
	
    

[class]_CommunFunctions - 30/10/2025 11:54:23

      singleton Class constructor()
	
	
Function InitProcess()
	var ErrorNum : Integer
	ErrorNum:=0
	ON ERR CALL(Formula(traceHandler).source; ek local)  // gestion des erreurs
	
	
Function LireLocatedSTR($ID : Integer; $options : Object)->$result : Text
	var $newTexte : Text:=""
	var $début; $fin : Integer
	var $libellé; $texte; $balise : Text
	var $UserPrefs : Object
	var $c; $cEL : Collection
	
	$texte:=Localized string(String($ID))
	
	// options
	Case of 
		: (Count parameters=1)
			// pas d'options
			
		: (($ID>=1) & ($ID<=70999))
			// traiter les chaines complexes dans un texte multistyle. Les balises d'éléments HTML sont traitées ailleurs (mécanisme différent)
			// v7.4.5 Remarque : traiter toutes les chaines. Les balises ALV sont différentes du mécanisme de 4D : "Utiliser des références dans les textes statiques"
			$UserPrefs:=Storage.UserPrefs
			
			// rechercher dans $result une balise d'un type de $c à partir de $début
			// une balise est la forme :typeBalise?xxxx:
			
			// d'abord chercher les balises de type xCode, référant un élément de langages
			// puis traiter les balises de type xliff dans un texte multistyle
			// puis traiter les balises de type uri dans un texte multistyle
			$c:=New collection(":xCode"; ":xliff"; ":uri")
			
			For each ($libellé; $c)
				$début:=1
				While ($début>0)
					// chercher la balise
					$fin:=Position($libellé; $texte; $début; *)
					
					If ($fin=0)
						// c'est fini : arrêter la recherche
						$début:=-1
						
					Else 
						// récupérer la balise
						$début:=$fin
						$fin:=Position(":"; $texte; $début+1; *)
						$balise:=Substring($texte; $début; $fin-$début+1)  // balise à remplacer
						
						// récupérer les paramètres
						$newTexte:=Substring($balise; 2; Length($balise)-2)  // virer les marqueurs (plus propre)
						$cEl:=Split string($newTexte; "?"; sk ignore empty strings)
						
						// plusieurs cas
						Case of 
								// il faut avoir trouvé la fin de la balise
							: ($fin=0)
								$début:=0
								$newTexte:=""
								cs._Trace.me.Créer(-15085; Current method name; "balise xliff incomplète dans la chaine "+String($ID)).LeverException([msgk_son; msgk_log])
								
								// il faut au moins 1 élément
							: ($cEl.length=0)
								// effacer la balise
								$texte:=Replace string($texte; $balise; "")
								
								// calculer le texte remplaçant la balise
							: ($libellé=":xCode")
								Case of 
										// traiter les balises dont la valeur est contextuelle (paramètres de la méthode)
									: ($cEl[1]="genre")
										$newTexte:="?"
										Case of 
											: (Not(OB Is defined($options; "genre")))
											: (Not(OB Is defined($UserPrefs; "Apparence")))
												// la langue n'est pas toujours définie (exemple : à l'ouverture de l'application, et avant l'ouverture de session)
												// imposer le français
												$newTexte:="e"*Num($options.genre)
												
											: ($UserPrefs.Apparence.Formulaire.CodeLangue="fr")
												$newTexte:="e"*Num($options.genre)
										End case 
										
									: ($cEl[1]="plur")
										$newTexte:="?"
										Case of 
											: (Not(OB Is defined($options; "plur")))
											: (Not(OB Is defined($UserPrefs; "Apparence")))
												// la langue n'est pas toujours définie (exemple : à l'ouverture de l'application, et avant l'ouverture de session)
												// imposer le français
												$newTexte:="s"*Num($options.plur)
												
											: ($UserPrefs.Apparence.Formulaire.CodeLangue="fr")
												$newTexte:="s"*Num($options.plur)
												
											: ($UserPrefs.Apparence.Formulaire.CodeLangue="en")
												$newTexte:="s"*Num($options.plur)
										End case 
										
									: ($cEl[1]="param_@")
										$newTexte:="?"
										Case of 
											: (Not(OB Is defined($options; $cEl[1])))
											Else 
												$newTexte:=$options[$cEl[1]]
										End case 
										
										// pour le reste, il faut au moins 2 éléments
									: ($cEl.length<3)
										// effacer la balise
										$newTexte:=""
										
										// une variable, sa valeur est renseignée dans Storage.STR
									: ($cEl[1]="variable")
										$newTexte:="err :xCode?variable"
										Case of 
											: (Not(OB Is defined(Storage; "STR")))
											: (Not(OB Is defined(Storage.STR; $cEl[2])))
												// pas créée
												$newTexte:="err "+$cEl[2]
											Else 
												// c'est ok
												$newTexte:=Storage.STR[$cEl[2]]
										End case 
								End case 
								
							: ($libellé=":xliff")
								Case of 
										// une chaine localisée
									: ($cEl.length=2)
										// une balise xliff peut contenir des éléments calculés => on boucle
										$newTexte:=This.LireLocatedSTR(Num($cEl[1]); $options)
										
										// une chaine en ressource
									: ($cEl.length=3)
										Case of 
											: ($cEl[1]="4D")
												// chaine ressource de 4D
												$newTexte:=Localized string($cEl[2])
												
											: ($cEl[1]="ALV")
												// chaine ressource de ALV (APP, WEB ...)
												$newTexte:=""
												If (Not(cs.xSDK.ResourceALV.me.SetVariable(Est Ressource APP; $cEl[2]; Is text; ->$newTexte)))
													$newTexte:=$balise
												End if 
												
											: ($cEl[1]="WEB")
												// chaine ressource de ALV (APP, WEB ...)
												$newTexte:=""
												If (Not(cs.xSDK.ResourceALV.me.SetVariable(Est Ressource WEBMO; $cEl[2]; Is text; ->$newTexte)))
													$newTexte:=$balise
												End if 
												
											Else 
												// v6.7.1 : "Lire chaîne dans liste" : commande obsolète
												$newTexte:=$cEl[2]
										End case 
								End case 
								
								
							: ($libellé=":uri")
								// v8.4.5 : suppression de ST FIXER TEXTE. Ce traitement doit se faire après tous les autres
								// plusieurs cas
								Case of 
										// il faut au moins 3 éléments
									: ($cEl.length<4)
										// effacer la balise
										$result:=Replace string($result; $balise; "")
										
									Else 
										// un lien vers un objet ALV
										$newTexte:=String(CodeEnreg(Num($cEl[2]); [Num($cEl[3])]))
										$newTexte:="<span style=\"-d4-ref-user:'"+$newTexte+"'\">"+$cEl[1]+"</span>"
								End case 
								//// continuer depuis le début (on ne connait pas la longueur de ce qui a été inséré)
								//$début:=1
								//// pour passer les tests suivants
								//$balise:=""
								//$newTexte:=""
								
							Else 
								// on ne sait pas
								$newTexte:="err locatedSTR "+String($ID)
						End case 
						// traduire la balise (il peut y en avoir plusieurs)
						$texte:=Replace string($texte; $balise; $newTexte)
						
						// continuer
						$début:=$début+Length($newTexte)
					End if 
				End while 
				
				// balise suivante
			End for each 
	End case 
	
	$result:=$texte
	
	
Function AjouterObjetA($entité : Object; $data : Object; $attribut : Text)
	// stocker l'instance de classe $entité à l'attribut $attribut de $data
	var $entitéCopy : Object
	
	Use ($data)
		$data[$attribut]:=New shared object
	End use 
	// $data[$attribut] appartient à un autre groupe partagé
	
	Use ($data[$attribut])
		// $entité appartient à un groupe partagé
		// en faire une copie 
		$entitéCopy:=OB Copy($entité; ck shared; $data[$attribut])
		// $entité est maintenant dans le même groupe partagé que $data[$attribut]
		
		// on peut la mettre dans $data via sa copie
		$data[$attribut]:=$entitéCopy
	End use 
	
	
Function RetaillerImage($pict : Pointer; $largeur : Integer; $hauteur : Integer)
	// formater l'image $pict aux $largeur / $hauteur max
	var $zoom : Real
	var $width; $height; $gauche; $haut : Integer
	
	$zoom:=-Scaled to fit prop centered  // format d'affichage de l'image
	$gauche:=0
	$haut:=0
	PICTURE PROPERTIES($pict->; $width; $height)
	cs.xSDK.Outils.me.CalculerRectangleMedia(->$gauche; ->$haut; ->$largeur; ->$hauteur; $width; $height; ->$zoom)
	$pict->:=$pict->*$zoom
	
	
	
    

[ ]SF_Sessions - 07/08/2026 09:16:22

      cs._SF_Sessions.me.TraiterFORMevent()
    

onStartup - 05/11/2025 19:47:30

      // ne s'exécute pas dans une base hôte

ON ERR CALL(Formula(traceHandler).source; ek global)

// pour les tests hors base hôte
cs._composant.new().InitVariablesWEB()
CALL WORKER("WK_Composant_WEBMO"; Formula(cs._CommunFunctions.me.InitProcess()))

// installer le dossier racine Webserveur
cs._composant.new().InstallerServeur()

    

onExit - 26/10/2025 11:35:52

      // ne s'exécute pas dans une base hôte

// exporter le code du composant si pas compilé
ON ERR CALL(Formula(traceHandler).source; ek local)

cs.xSDK.ExportCode4D.new().DémarrerComposant()

    

onWebConnection - 28/10/2025 18:35:48

      #DECLARE($url : Text; $entete : Text; $IPnavigateur : Text; $IPserveur : Text; $LogIn : Text; $motDePasse : Text)

cs.$serveur.new().surConnexionWeb($url; $entete; $IPnavigateur; $IPserveur; $LogIn; $motDePasse)

    

onWebAuthentication - 28/10/2025 18:35:57

      #DECLARE($url : Text; $entete : Text; $IPnavigateur : Text; $IPserveur : Text; $LogIn : Text; $motDePasse : Text)->$result : Boolean

$result:=cs.$serveur.new().surAuthentificationWeb($url; $entete; $IPnavigateur; $IPserveur; $LogIn; $motDePasse)

    

onHostDatabaseEvent - 05/11/2025 19:47:39

      #DECLARE($numEvent : Integer)

Case of 
	: ($numEvent=On before host database startup)
		
		// ici pas d'interaction avce les autres composants et la BDDmère
		ON ERR CALL(Formula(ErrorHandler).source; ek local)
		
		cs._composant.new().InitVariablesWEB()
		
		Use (Storage.System)
			Storage.System.estExécutéDansHôte:=True
			Storage.System.estExécutéDansAPP:=cs.xSDK.EnvironnementALV.new().estExecuteDansAPP()
		End use 
		
		// déactiver les ASSERT si le composant est compilé (réactivable par les options d'appel du composant)
		SET ASSERT ENABLED(Not(Is compiled mode))
		
		CALL WORKER("WK_Composant_WEBMO"; Formula(cs._CommunFunctions.me.InitProcess()))
		
		
	: ($numEvent=On after host database startup)
		// à faire ici tout est initialisé, en particulier le monde extérieur
		
		ON ERR CALL(Formula(traceHandler).source; ek global)
		
		cs._composant.new().Installer()
		
		
	: ($numEvent=On before host database exit)
		// placer ici le code à exécuter avant le "Sur fermeture" de la base hôte
		cs._maintenance.new().Arrêter()
		
		cs.$serveur.new().Arreter()
		
		
	: ($numEvent=On after host database exit)
		// placer ici le code à exécuter après le "Sur fermeture" de la base hôte
End case