Exécuter Function Coopérative - 30/12/2024 14:02:41

      #DECLARE($classe : Object; $params : Object)->$numProc : Integer
var $nomProcess; $nomTache : Text
var $data : Object
var $trace : cs._Trace

$trace:=cs._Trace.me
$numProc:=0
Case of 
	: (Not(OB Is defined($params; "functionID")))
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'functionID' n'est pas défini dans $params")
		
	: (Value type($params.functionID)#Is text)
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'$params.functionID' n'est pas un texte")
		
	: (Not(OB Is defined($params; "numProcessAppelant")))
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'numProcessAppelant' n'est pas défini dans $params")
		
	: ($params.numProcessAppelant=-1)
		$nomTache:=$params.nomTache
		
		$nomProcess:="$ALV_process_"+$nomTache
		If (OB Is defined($params; "nomProcess"))
			$nomProcess:=$params.nomProcess
		End if 
		
		// créer le nouveau process
		$params.numProcessAppelant:=Current process
		$numProc:=New process(Current method name; 0; $nomProcess; $classe; $params; *)
		
	Else 
		// c'est ok
		cs.$composant.new().InitProcess()
		
		// construire la classe
		$data:=$classe.new()
		
		If ($data[$params.functionID]#Null)
			
			// lancer le traitement demandé
			$data[$params.functionID]($params)
			
			// ici pas de callBack
			
		Else 
			$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "La classe "+$classe.name+" n'est pas de function "+$2.functionID)
		End if 
		
		$numProc:=Current process
End case 

    

Bac a sable MOB - 09/02/2026 10:33:33

Partagée entre composants et base hôte

      var $o : Object
var $url : Text
var $timeOut : Integer
//$o:=Cartographie.$Carte.new()
//$x:=$o.document.LireLocatedSTR(10)

//cs.$notesEditeur.new().Modifier()

//$albums:=cs.$albums.new("")
//$b:=OB Is defined($albums.webALB; "ListerAlbums")

// ouvrir l'éditeur des traductions
cs.xSDK.TraductionsEditeur.new().ModifierTraductions()


//TEXT TO DOCUMENT(Folder(fk documents folder).folder("tempo_ALV/_MOBdebug").file(Timestamp+"_action.json").platformPath; "This.url"+Char(Carriage return)+JSON Stringify(OB Copy(Session)))



    

initProcess - 09/02/2026 10:31:59

Capable de process préemptif

      var ErrorNum : Integer
var ErrorDescription : Text

ErrorNum:=0
ErrorDescription:=""

ON ERR CALL(Formula(traceHandler).source)

    

Exécuter Function Préemptive - 30/12/2024 14:04:27

Capable de process préemptif

      #DECLARE($classe : Object; $params : Object)->$numProc : Integer
var $nomTache : Text
var $data : Object
var $trace : cs._Trace

$trace:=cs._Trace.me
$numProc:=0
Case of 
	: (Not(OB Is defined($params; "functionID")))
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'functionID' n'est pas défini dans $params")
		
	: (Value type($params.functionID)#Is text)
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'$params.functionID' n'est pas un texte")
		
	: (Not(OB Is defined($params; "nomTache")))
		// nécessaire pour tuer la tâche
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'nomTache' n'est pas défini dans $params")
		
	: (Not(OB Is defined($params; "numProcessAppelant")))
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'numProcessAppelant' n'est pas défini dans $params")
		
	: ($params.numProcessAppelant=-1)
		$nomTache:=$params.nomTache
		
		// supprimer la tâche existante
		cs.xSDK.RegistreTaches.new().Tuer($nomTache)
		
		// créer le nouveau process
		$params.numProcessAppelant:=Current process
		$numProc:=New process(Current method name; 0; "$MOB_process_"+$nomTache; $classe; $params; *)
		
	Else 
		// c'est ok
		// construire la classe
		$data:=$classe.new()
		
		If ($data[$params.functionID]#Null)
			// créer la tâche associée
			// numProcessAppelant est nécessaire pour tuer toutes les tâches d'un même process
			$params.tache:=cs.xSDK.RegistreTaches.new().Inscrire(New object("nomProcess"; Current process name; "nomTache"; $params.nomTache; "numProcessAppelant"; $params.numProcessAppelant))
			
			// lancer le traitement demandé
			$data[$params.functionID]($params)
			
			// désinscrire la tâche 'nomTache'
			$params.tache.DésInscrire()
			
		Else 
			$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "La classe "+$classe.name+" n'est pas de function "+$params.functionID)
		End if 
		
		$numProc:=Current process
End case 

    

MOB_EcrireElementHTML - 07/07/2025 17:29:14

Partagée entre composants et base hôte

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

Capable de process préemptif

      #DECLARE($url : Text)->$result : Text
// ici toujours une requête d'album
// attention le nom de la function peut être dans cs.$albums
var $albums : cs.$albums

$albums:=cs.$albums.new($url)

$result:=""
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
		
	: (cs._actionPageWeb.new($url).Traiter())
		// action traitée
	Else 
		
End case 

    

traceHandler - 23/12/2024 10:50:32

      cs._Trace.me.Intercepter()

    

COMPILER_WEB - 30/01/2025 10:28:40

      // initialiser les variables process gérés par $composant.InitHTTPvars()
// *** session
var vs4D; wwwRacineRessources; wwwEtatNavigation; wwwContexteWeb : Text
vs4D:=""
wwwRacineRessources:="../../"
wwwEtatNavigation:=""
wwwContexteWeb:=""

var wwwActionParams : Text
wwwActionParams:=""


//*** albums
var wwwURLalbum; wwwIndexMedia; wwwTitre; wwwComment : Text
var wwwNbrMedias : Integer
wwwURLalbum:=""
wwwIndexMedia:=""
wwwNbrMedias:=0
wwwTitre:=""
wwwComment:=""

var wwwEtatNavigationALB : Text
wwwEtatNavigationALB:=""


// *** cartographie
var wwwCarto_LatitudeCentre; wwwCarto_LongitudeCentre; wwwMediaPath; wwwMarkersData : Text
wwwCarto_LatitudeCentre:=""
wwwCarto_LongitudeCentre:=""
wwwMediaPath:=""
wwwMarkersData:=""

var wwwCarto_Opacity_OSM; wwwCarto_Opacity_GP; wwwCarto_Opacity_Cible; wwwZoneWeb_Zoom; wwwZoneWeb_ZoomMin; wwwZoneWeb_ZoomMax : Text
wwwCarto_Opacity_OSM:=""
wwwCarto_Opacity_GP:=""
wwwCarto_Opacity_Cible:=""
wwwZoneWeb_Zoom:=""
wwwZoneWeb_ZoomMin:=""
wwwZoneWeb_ZoomMax:=""

var MarkersData : Text
MarkersData:=""

// *** ?
var ZoneWeb_Largeur; ZoneWeb_Hauteur : Text
ZoneWeb_Largeur:=""
ZoneWeb_Hauteur:=""

    

[class]$composant - 06/02/2026 18:04:56

      property document : cs._document
property carto : cs.xCarto.$carte
property trace : cs._Trace
property fct : cs.xSDK.Outils
property rsc : cs.xSDK.ResourceALV

// ici on est toujours sur le serveur ALV
Class constructor()
	
	This.document:=cs._document.new()
	This.carto:=cs.xCarto.$carte.new()
	This.trace:=cs._Trace.me
	This.fct:=cs.xSDK.Outils.me
	This.rsc:=cs.xSDK.ResourceALV.me
	
	
	
Function getAdresseServeur()->$result : Text
	EXECUTE METHOD("Installer_LesServeurs"; $result; "getIPserveurAPP")
	
	
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 
	
	
	// -----------------------------
	// MARK:Installation
	// -----------------------------
	
Function InitVariablesMOB()
	var $data : Object
	
	Use (Storage)
		Storage["System"]:=New shared object
		Storage["STR"]:=New shared object
		Storage["Host"]:=New shared object
	End use 
	
	Use (Storage.System)
		// sert, en particulier, pour les process de type monitoring
		Storage.System.ArrêtAPP:=False
		
		Storage.System.estExecuteDansHote:=False
		Storage.System.estExecuteDansAPP:=False
		
		Storage.System.AlertesHôte:=6  // gère les messages d'alerte
		
		$data:=cs.xSDK.EnvironnementALV.new()
		Storage.System.typeApplication:=$data.typeApplication()
		Storage.System.estServeur:=$data.estServeur()
		Storage.System.estClient:=$data.estClient()
	End use 
	
	Use (Storage.Host)
		Storage.Host.Session_Etat:=0
	End use 
	
	// partager des ressources
	$data:=New object
	// fichier de ressources
	$data.chemin:=Get 4D folder(Current resources folder)+"DataMOB.xml"
	// mémoriser
	cs.xSDK.ResourceALV.me.Inscrire(Est Ressource MOB; $data)
	
	
Function Installer()
	
	Use (Storage.System)
		Storage.System.estExecuteDansAPP:=cs.xSDK.EnvironnementALV.new().estExecuteDansAPP()
	End use 
	
	This.InstallerDonnéesHote()
	
	This.InstallerSignatureAPP()
	This.InstallerRessourcesWeb()
	
	
Function InstallerDonnéesHote()
	// lire les données d'installation
	var $attribut : Text
	var $data : Object
	
	Case of 
		: (Not(Storage.System.estExecuteDansHote))
			// exécution du composant en local
		: (Not(Storage.System.estExecuteDansAPP))
			// exécution du composant dans un composant
		: (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 
	
	
Function InstallerSignatureAPP()
	// installer le fichier 'key.mobileapp' près du fichier de données. ce fichier 'appaire' le serveur APP et les mobiles APP
	var $DossierDestination : Object
	
	If (Storage.System.typeApplication=ALV Serveur APP)
		$DossierDestination:=Folder(Data file; fk platform path).parent.folder("MobileApps")
		$DossierDestination.create()  // au cas où
		Folder(Get 4D folder(Current resources folder); fk platform path).folder("MobileApps").file("key.mobileapp").copyTo($DossierDestination; fk overwrite)
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Installer"; Current method name; "'key.mobileapp' recopié dans "+$DossierDestination.platformPath)
	End if 
	
	
Function InstallerRessourcesWeb()
	// installer toutes les ressources du serveur Web dans le dossier racine fourni
	var $DossierDestination : Object
	
	Case of 
		: (Storage.System.typeApplication=ALV Client APP)
		: (Storage.System.typeApplication=ALV BDD mère)
		: (Storage.System.typeApplication=4D Remote mode)
			// filtrer
		Else 
			// dossier d'installation
			$DossierDestination:=This.document.getRacineHTMLFolder()
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Installation [OK]"; Current method name; "Dans "+$DossierDestination.platformPath)
			
			// Ajouter les pages dynamiques
			Dupliquer ContenuDeDossier(Folder(Get 4D folder(Current resources folder); fk platform path).folder("TemplatesPagesWeb"); $DossierDestination)
			
			// Ajouter le Ressources
			$DossierDestination:=$DossierDestination.folder("ressources")
			$DossierDestination.create()
			// Ajouter les feuilles de style
			COPY DOCUMENT(Folder(fk resources folder).folder("css").platformPath; $DossierDestination.platformPath; *)
			// Ajouter les ressources images
			COPY DOCUMENT(Folder(fk resources folder).folder("Images").platformPath; $DossierDestination.platformPath; "images"; *)
			// Ajouter les fichiers Java
			COPY DOCUMENT(Folder(fk resources folder).folder("javascripts").platformPath; $DossierDestination.platformPath; "js"; *)
			
			// installer les ressources carto
			This.carto.InstallerRessources(This.document.getRacineHTMLFolder().platformPath)
			
			// installer les ressources Web albums
			cs.xALB.$albums.new().InstallerRessourcesSurServeur($dossierDestination)
	End case 
	
	
Function InitProcess()
	ON ERR CALL(Formula(traceHandler).source)  // gestion des erreurs
	ErrorNum:=0
	ErrorDescription:=""
	
	
	// -----------------------------
	// MARK:Traitement des url WEB
	// -----------------------------
	
Function Authentification($request : Object)->$response : Object
	// authentification APP
	$response:=cs._authentificationEmail.new().Authentification($request)
	
	
	// -----------------------------
	// MARK:Traitement des actions mobile
	// -----------------------------
	
Function TraiterAction($request : Object)->$response : Object
/*
$request = Informations fournies par l'application mobile
$response = Informations retournées à l'application mobile
*/
	// la réponse doit comprendre
	// .success, toujours, indique que l'action a été prise
	// .dataSynchro = vrai pour rafraichier la sélection après traitement,
	// .statusText message à afficher
	// .close = vrai pour forcer la fermeture de l'éditeur
	$response:=This.InitResponse()
	
	Case of 
		: (cs.$notes.new($request).Traiter($response))
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; $request.action; Current method name; "Action traitée "+Choose($response.success; "[OK] "; "[KO] ")+$response.statusText)
			
		Else 
			// Action inconnue
			$response.success:=False
	End case 
	// renvoyer la réponse
	
	
Function InitResponse()->$result : Object
	$result:=New object("success"; True; "statusText"; ""; "dataSynchro"; False)
	
	
    

[class]$serveurWeb - 05/01/2026 12:39:09

      property url : Text
property paramsUrl; balisesUrl : Collection

Class extends $composant

Class constructor()
	
	Super()
	
	
	// -----------------------------
	// MARK:Traitement des url WEB
	// -----------------------------
	
Function AuthentificationWeb($url : Text; $header : Text; $BrowserIP : Text; $ServerIP : Text; $user : Text; $password : Text)->$result : Boolean
	
	$result:=True
	Case of 
		: ($url="/mobileAlbumsConnecter")
			// initialiser la session web
			Session.setPrivileges("ReadRecords")
			// on n'est plus un invité
			
			// renseigner le IDfamille
			// dans cette version, pas de limite (on ne connait pas le user !!!)
			Use (Session.storage)
				Session.storage.userInfo:=New shared object
			End use 
			Use (Session.storage.userInfo)
				Session.storage.userInfo.IDfamille:=-1001
			End use 
			
		: (($url="@mobile@") | ($url=" /.well-known@"))
			// toujours autorisées
			//  /.well-known est utilisé par cs.WebHandler
			
		Else 
			// pas concerné ici
			$result:=False
	End case 
	
	
Function ConnexionWeb($url : Text; $header : Text; $BrowserIP : Text; $ServerIP : Text; $user : Text; $password : Text)->$result : Boolean
	// traitement des appels de la méthode base "Sur connexion Web" de la base hôte
	
	var $handler : Object
	$handler:=MobileAppServer.WebHandler.new()
	
	$result:=True
	Case of 
		: ($handler.handle($url; $header; $BrowserIP; $ServerIP; $user; $password))
			// Managed by default mobile code (cf doc 4D-Mobile-App-Server)
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "URL traitée par défaut"; Current method name; "url "+$url+" traitée par $handler.handle")
			
		: (cs._authentificationEmail.new().ActiverSession($url))
			// ok le mobile User est authentifié
			
		: (cs.$albums.new($url).Traiter())
			// url traité par l'album
			
		: (cs._actionPageWeb.new($url).Traiter())
			// url traité par le serveur web hôte
			
		Else 
			// pas concerné !
			$result:=False
	End case 
	
	
Function LireParametresUrl($url : Text)
	// errURLvide ou errURLinconnue
	This.url:=""
	This.paramsUrl:=New collection
	
	Case of 
		: ($url="")
		: (This.LireParametresUrlMOB($url))
		: (This.LireParametresUrlWeb($url))
	End case 
	
	
Function LireParametresUrlMOB($url : Text)->$result : Boolean
	// : urls mobile, attention, ici on ne peut pas utiliser ?
	// url est de la forme /nomFunction {_param1 {_param2...}}
	$result:=False
	
	Case of 
		: ($url="@4D@")
			// url de serveur Web
		Else 
			This.paramsUrl:=Split string($url; Séparateur mobURLparams)
			
			If (This.paramsUrl.length>0)
				$result:=True
				
				// url reçue
				This.url:=This.paramsUrl[0]
				// virer les 4Dtags
				This.url:=Replace string(This.url; "/"; "")
				// liste des paramètres
				This.paramsUrl.shift()
			End if 
			
			// si l'url vient d'une mobile action, les paramètres sont dans le contexte
			If (This.paramsUrl.length=0)
				This.LireContextWeb()
			End if 
	End case 
	
	
Function LireParametresUrlWeb($url : Text)->$result : Boolean
	// urls du serveur Web Hôte
	// $url est de la forme /4Dxxx/yyy/nomFunction {?param1 {?param2...}}, avec xxx = CGI ou ACTION ou SCRIPT, yyy le contexte WEB, APP ...
	$result:=False
	
	Case of 
		: ($url#"@4D@")
			// pas url de serveur Web
		Else 
			// lire les balises
			This.balisesUrl:=Split string($url; "/"; sk ignore empty strings)
			
			If (This.balisesUrl.length>0)
				$result:=True
				
				// analyser la function
				This.paramsUrl:=Split string(This.balisesUrl[This.balisesUrl.length-1]; Séparateur webURLparams)
				// virer function+params
				This.balisesUrl:=This.balisesUrl.slice(0; This.balisesUrl.length-1)
				// ajouter la function seule
				This.balisesUrl.push(This.paramsUrl[0])
				// le nom de la function
				This.url:=This.paramsUrl.shift()
				// en final .balise = [4Dtague, nom classe, nonFunction], .params = collection des params de nomFunction
			End if 
	End case 
	
	
Function LireContextWeb()
	var $handler; $context : Object
	
	$handler:=MobileAppServer.WebHandler.new()
	$context:=$handler.getContext()
	If (OB Is defined($context; "entity"))
		This.paramsUrl:=New collection
		This.paramsUrl.push($context.dataClass)
		This.paramsUrl.push(String($context.entity.primaryKey))
		// rappel : la primaryKey d'une entité de la BDD est un ID ; donc le IDunique de entity n'est pas connu ici
	End if 
	
	
Function RestaurerHTTPvars($contexte : Text)
	// copier HTTPvars dans les variables process
	// ici COMPILER_WEB a été appelé (les variables process sont définies)
	var $data : Object
	var $attribut : Text
	var $ptr : Pointer
	
	// définir les constantes process (et en mode interprété les variables process)
	COMPILER_WEB
	
	If (OB Is defined(Session.storage; $contexte))
		$data:=Session.storage[$contexte]
		
		For each ($attribut; $data)
			$ptr:=Get pointer($attribut)
			Case of 
				: (Undefined($ptr->))
					// pas normal ; uniquement utilise en interpreté, renvoie toujours faux en compilé 
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Variable indéfinie"; Current method name; "La variable '"+$attribut+"' n'a pas été initialisée dans COMPILER_WEB")
				Else 
					// une variable
					$ptr->:=$data[$attribut]
			End case 
		End for each 
	End if 
	
	
Function Envoyer($nomPageWeb : Text; $HTTPvars : Text)
	// restaurer les variables process nécessaires à la construction de la page .shtml
	This.RestaurerHTTPvars($HTTPvars)
	WEB SEND FILE($nomPageWeb)
	
	
	// -----------------------------
	// MARK:Traitement des requetes BDD
	// -----------------------------
	
Function TraiterREQUETE()->$result : Object
	// icic on est sur le serveur ou la BDD mère
	var $dataTexte : Text
	var $params : Object
	
	// récupérer les données
	WEB GET HTTP BODY($dataTexte)
	$params:=JSON Parse($dataTexte; Is object)
	
	Case of 
		: ($params=Null)
		: (Not(OB Is defined($params; "functionID")))
			
		: (OB Is defined(This; $params.functionID))
			// la function est ici
			This[$params.functionID]($params)
	End case 
	
	// envoyer la réponse
	$result:=$params.reqRetour
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Commande traitée "+Choose($result=Null; "[KO]"; "[OK]"); Current method name; "")
	
	
Function ListerBDDs($params : Object)
	// lister les paramètres de la BDD notes
	// ici on est sur le serveur
	var $c : Collection
	var $BDD : Object
	
	$c:=New collection
	$BDD:=New object
	$BDD.nomBDD:="BDD notes"
	$BDD.cheminBDD:=This.document.getBDDnotesFolder("BDD_NOTES").platformPath
	$BDD.methodeReq:="RequeterBDDnotes"
	$c.push($BDD)
	
	// retourner la liste
	$params.reqRetour:=New object("ListeBDDs"; $c.orderBy("nomBDD asc"))
	
	
Function ListerTables($params : Object)
	// ici on est sur le serveur (ou la BDD mère en test)
	var $c : Collection
	
	$c:=cs.$notes.new().ListerTables()
	// retourner la liste
	$params.reqRetour:=New object("ListeTables"; $c)
	
	
Function ListerNotes($params : Object)
	// sélectionner les notes de la BDD notes
	// ici on est sur le serveur
	var $c : Collection
	var $note : Object
	
	$c:=cs.$notes.new().LireBDD($params)
	
	// retourner la liste
	$params.reqRetour:=New object("Notes"; $c)
	// créer les champs Date et Heure
	For each ($note; $c)
		$note.Date:=String(Date($note.noteDate); System date abbreviated)
		$note.Heure:=String(Time($note.noteDate); System time short)
	End for each 
	
	// lister les champs de la BDD ; ici on fait simple
	$params.reqRetour.NomsChamps:=New collection
	$params.reqRetour.NomsChamps.push(New object("libelle"; "Date"; "entete"; "Date"))
	$params.reqRetour.NomsChamps.push(New object("libelle"; "Heure"; "entete"; "Heure"))
	$params.reqRetour.NomsChamps.push(New object("libelle"; "auteur"; "entete"; "Auteur"))
	$params.reqRetour.NomsChamps.push(New object("libelle"; "objet"; "entete"; "Objet"))
	$params.reqRetour.NomsChamps.push(New object("libelle"; "contenu"; "entete"; "Contenu"))
	$params.reqRetour.NomsChamps.push(New object("libelle"; "ID"; "entete"; "ID"))
	$params.reqRetour.NomsChamps.push(New object("libelle"; "IDunique"; "entete"; "UUID"))
	
	
Function SupprimerNotes($params : Object)
	cs.$notes.new().SupprimerDansBDD($params)
	
    

[class]_authentificationEmail - 12/12/2025 08:10:38

      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 InitSession()
	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; "mobileInfo")))
		Use (Session.storage)
			Session.storage.mobileInfo:=New shared object
		End use 
	End if 
	
	
Function getSessionObjet($token : Text)->$result : Object
	// renvoie le contenu du fichier $token situé dans l'un des dossiers de "Dossier 4D(Dossier MobileApps)"
	var $apps; $sessions; $c : Collection
	var $app : Object
	
	$result:=Null
	
	// lister toutes les app du dossier  Dossier 4D(Dossier MobileApps;*)
	$apps:=MobileAppServer.App.new().all()
	For each ($app; $apps)
		// $app est une classe App() instanciée par le chemin d'un dossier , de type {teamID}.com.nomEntreprise.nomApplication
		// fixer son ID (bug du composant 4D ?)
		$app.id:=$app.folder.fullName
		// getSessionManager() créer une classe cs.Session de $apps
		// getSessionObjects() est la collection des contenus de tous les fichiers de nom IDsession (sans suffixe)  dans $app
		$sessions:=$app.getSessionManager().getSessionObjects()
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Session demandée"; Current method name; $token+", (info : "+String($sessions.length)+" sessions en cours)")
		// chercher notre session
		$c:=$sessions.query("session.id = :1"; $token)
		If ($c.length=1)
			// trouvé
			// $c[0] est une classe cs.SessionObjet
			$result:=OB Copy($c[0])
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Session demandée "+Choose($c.length=1; "[OK]"; "[KO]"); Current method name; $token+" (voir session dans logs)")
			This.trace.EnvoyerMessages([msgk_log]; "Session demandée"; Current method name; JSON Stringify($result))
		End if 
	End for each 
	
	
	// -----------------------------
	// MARK:Authentification par eMail
	// -----------------------------
	
Function Authentification($request : Object)->$response : Object
	// traiter l'évènement 'On Mobile App Authentification' : envoyer un eMail d'authentification
	// attention la session est disponible uniquement ici
	var $data : Object
	
	This.request:=$request
	This.trace.EnvoyerMessages([msgk_log]; "$request :"; Current method name; JSON Stringify(OB Copy($request)))
	
	$response:=New object
	Case of 
		: (Not(This.estUtilisateurIdentifié($request)))
			// user inconnu
			$response.success:=False
			$response.statusText:=Current method name+" "+This.document.LireLocatedSTR(7)
			
		: (Not(This.FixerParamètresAuthentification()))
			$response.success:=False
			$response.statusText:=Current method name+" : erreur paramètres Authentification"
			
		: (This.ConfirmerLogInParEmail())
			$response:=This.resultEmailChecker
			
			// initialiser les données de session
			// dans la procédure complète, sont écrassées par .ActiverSession()
			Use (Session.storage)
				Session.storage.userInfo:=New shared object
				Use (Session.storage.userInfo)
					// info serveur
					// attention .team n'existe que pour IOS (pas android)
					Session.storage.userInfo.eMail:=$request.email
					Session.storage.userInfo.application:=$request.application.id
					Session.storage.userInfo.deviceOS:=$request.device.os
					// info ALV
					Session.storage.userInfo.IDfamille:=This.userInfo.IDfamille
				End use 
			End use 
			
			// fixer les privilèges
			$data:=New object
			$data.userName:=This.userInfo.LogIn
			//$data.roles:=New collection("UtilisateurMOB") // ne PAS utiliser les roles (pb dans fichier .json)
			$data.privileges:=New collection("ReadRecords")  //(This.userInfo.privileges)  // v11.3.5 role d'un user en consultation
			Session.setPrivileges($data)
			
			This.trace.EnvoyerMessages([msgk_event]; "$response :"; Current method name; "[OK] (voir détails $request et .userInfo dans Logs)")
			This.trace.EnvoyerMessages([msgk_log]; "userInfo :"; Current method name; JSON Stringify(This.userInfo))
			
		Else 
			This.trace.EnvoyerMessages([msgk_event]; "$response :"; Current method name; $response.statusText+" (voir détails $request")
	End case 
	
	
Function ConfirmerLogInParEmail()->$result : Boolean
	// analyser le contenu de l'eMail reçu
	var $data : Object
	
	This.resultEmailChecker:=Mobile App Email Checker(This.request)
	$result:=This.resultEmailChecker.success
	
	If (This.resultEmailChecker.success)
		// initialiser les privilèges et roles pour APP
		//Session.clearPrivileges()
		$data:=New object
		$data.userName:=This.userInfo.LogIn
		// $data.roles:=New collection("UtilisateurMOB")// ne PAS utiliser les roles (pb dans fichier .json)
		$data.privileges:=New collection("ReadRecords")  //(This.userInfo.privileges)  // v11.3.5 role d'un user en consultation
		Session.setPrivileges($data)
	End if 
	
	This.trace.EnvoyerMessages([msgk_event]; "resultEmailChecker"; Current method name; Choose($result; "[OK]"; "[KO]")+". Confirmation "+This.resultEmailChecker.statusText)
	This.trace.EnvoyerMessages([msgk_log]; "resultEmailChecker"; Current method name; JSON Stringify(This.resultEmailChecker))
	This.trace.EnvoyerMessages([msgk_log]; "sessionStorage"; Current method name; "isGuest() "+String(Session.isGuest())+" SessionStorage "+JSON Stringify(Session.storage))
	This.trace.EnvoyerMessages([msgk_log]; "sessionGetPrivileges"; Current method name; JSON Stringify(Session.getPrivileges()))
	
	
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("4D Mobile App 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.document.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:="http"
	$params.activation.hostname:=This.getAdresseServeur()
	$params.activation.port:="80"
	$params.activation.path:=This.urlActiverSession
	
	// les messages utilisateur
	$params.message:=New object
	$params.message.successConfirmationMailMessage:=This.document.LireLocatedSTR(3)  // "Verify your email address"
	$params.message.waitSendMailConfirmationMessage:=This.document.LireLocatedSTR(6)  // "The mail is already sent thank you to wait before sending again"
	$params.message.successActiveSessionsMessage:=This.document.LireLocatedSTR(4)  // "You are successfully authenticated"
	$params.message.expireActiveSessionsMessage:=This.document.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 : Text
	
	$result:=False
	
	If ($data#Null)
		// v9.5.4 : le login d'un user est son adresse Mail
		$eMail:=$data.email
		$ID:=-1
		$LogIn:=""
		$Name:=""
		$First_Name:=""
		$groupe:=-1
		Begin SQL
			SELECT  ID, LogIn, Name, First_Name, Groupe FROM UtilisateursALV WHERE Adresse_eMail = :$eMail INTO :$ID, :$LogIn, :$Name, :$First_Name, :$groupe;
		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.privileges:="ReadRecords"
			
			// trouver son groupe familial
			Begin SQL
				SELECT  IDfamille FROM wwwGroupes WHERE ID = :$groupe INTO :$ID;
			End SQL
			This.userInfo.IDfamille:=$ID
			
		Else 
			This.userInfo.nom:="cher Inconnu"
		End if 
	End if 
	
	
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
	var $dossier : Object
	
	// chemin du template de la page
	$dossier:=Folder(Get 4D folder(Current resources folder); fk platform path)
	$result:="ConfirmMailTemplate.html"
	
	// créer le document
	$dataTexte:=Document to text($dossier.folder("TemplatesMobileApp").file($result).platformPath; "UTF-8")
	PROCESS 4D TAGS($dataTexte; $dataTexte; This.userInfo.prénom)
	
	// enregistrer dans le serveur
	$dossier:=Folder(Get 4D folder(Current resources folder; *); fk platform path)
	TEXT TO DOCUMENT($dossier.folder("4D Mobile App Server").file($result).platformPath; $dataTexte)
	
	
	// -----------------------------
	// MARK:Traitement des URL session
	// -----------------------------
	
Function ActiverSession($url : Text)->$result : Boolean
	var $token : Text
	var $data : Object
	var $i : Integer
	var $isOK : Boolean
	
	// 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
		$result:=True
		This.resultActiverSession:=Mobile App Active Session($url)
		$isOK:=This.resultActiverSession.success
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Mobile App Active Session"+Choose($isOK; " [OK]"; " [KO]"); Current method name; $url+" - message "+This.resultActiverSession.message+" - erreur "+This.resultActiverSession.error)
		
		// lire l'ID
		$token:=""
		ARRAY TEXT($noms; 0)
		ARRAY TEXT($valeurs; 0)
		WEB GET VARIABLES($noms; $valeurs)
		For ($i; 1; Size of array($noms))
			If ($noms{$i}="token")
				$token:=$valeurs{$i}
			End if 
		End for 
		// lire user session
		Case of 
			: (Not(OB Is defined(Session; "storage")))
			: ($token="")
				// *** bof
				
			: (OB Is defined(Session.storage; "userInfo"))
				// *** 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 
				// *** nouvelle session existante
				This.InitSession()
				// lire les infos de session
				$data:=This.getSessionObjet($token)
				// .file pas compatible avec la méthode SharedObjet
				$data.filePath:=$data.file.platformPath
				OB REMOVE($data; "file")
				
				// lire les infos du user
				If (This.estUtilisateurIdentifié($data))
					// renseigner les user infos 
					sharedObject(This.userInfo; Session.storage.userInfo)
					
					// renseigner les mobile session 
					sharedObject($data; Session.storage.mobileInfo)
					
					// fixer les privilèges
					$data:=New object
					$data.userName:=This.userInfo.LogIn
					//$data.roles:=New collection("UtilisateurMOB")// ne PAS utiliser les roles (pb dans fichier .json)
					$data.privileges:=New collection("ReadRecords")  //(This.userInfo.privileges)  // v11.3.5 role d'un user en consultation
					Session.setPrivileges($data)
					
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Nouvelle Session [OK]"; Current method name; "$token "+$token+" - isGuest() "+String(Session.isGuest())+" (voir détails dans logs)")
					This.trace.EnvoyerMessages([msgk_log]; "Nouvelle Session [OK]"; Current method name; JSON Stringify(Session.storage))
					
				Else 
					This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Nouvelle Session [KO]"; Current method name; "$token "+$token+"  -  User inconnu")
				End if 
				
		End case 
	End if 
	
    

[class]$notesEditeur - 06/02/2026 18:04:16

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

Class extends $notes


Class constructor()
	
	Super()
	
	This.serveurWeb:=cs.$serveurWeb.new()
	
	
	
	// -----------------------------
	// MARK:Formulaire
	// -----------------------------
	
Function Modifier()
	// ouvrir l'éditeur de la BDD des journaux
	var $params : Object
	var $numProc : Integer
	
	$params:=New object
	$params.functionID:="OuvrirFormulaire"
	$params.numProcessAppelant:=-1  // dans un process externe
	$params.nomProcess:="U_Nav?NotesMobile"
	$params.nomTache:="Edition"
	$numProc:=Exécuter Function Coopérative(cs.$notesEditeur; $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
	This.InitProcess()
	
	$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.Session_Etat ?? 6))
		// 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
	This.InitProcess()
	
	$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
	
	This.InitProcess()
	
	$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
	
	This.InitProcess()
	
	$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/xMOB/$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]_Trace - 01/01/2025 13:21:45

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

singleton Class constructor()
	
	This.cible:=cs.xSDK.Traces.new()
	This.success:=False
	
	
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("MOB"; Error; Error method; Error line; Error formula)
	
	
Function Initialiser($nomMethode : Text)->$result : Object
	This.cible.CréerErreur("MOB"; 0; $nomMethode; "")
	$result:=This
	
	
Function Créer($Error : Integer; $nomMethode : Text; $ErrorDescription : Text)->$result : Object
	This.cible.CréerErreur("MOB"; $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; "MOB"; $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; "MOB"; $libellé; $source; $description)
	
	
    

[class]$maintenance - 05/01/2026 10:18:49

      property nomTache : Text:="MaintenanceMOB"
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:="CréerMediaMobile"
	// 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 '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))
	
	$data:=New object
	$data.functionID:="GérerLicencesMobile"
	// démarrage dans 5 mn
	$data.dateTache:=String(Current date; ISO date GMT; (Current time+?00:05:00?)%?24:00:00?)
	// toutes les 15 mn
	$data.période:=New object("jour"; 0; "seconde"; 30*60)
	$data.initialiser:=True
	This.Taches.push(OB Copy($data))
	This.trace.EnvoyerMessages([msgk_event]; "Maintenance"; Current method name; "tâche 'GérerLicencesMobile' programmée (voir détails dans Logs)")
	This.trace.EnvoyerMessages([msgk_log]; "Maintenance GérerLicencesMobile"; Current method name; JSON Stringify($data))
	
	
Function Démarrer()
	// exécuter dans un process externe
	var $data : Object
	var $numProc : Integer
	
	Case of 
		: ((Storage.System.typeApplication#4D Serveur APP) & (Not(Storage.Host.Session_Etat ?? 6)))
			// serveur ALV seul uniquement
		Else 
			$data:=New object
			$data.functionID:="SuperviserLesTaches"
			$data.nomTache:=This.nomTache
			$data.numProcessAppelant:=-1
			
			$numProc:=Exécuter Function Préemptive(cs.$maintenance; $data)
	End case 
	
	
Function Arrêter()
	var $nomTache : Text
	
	$nomTache:=This.nomTache
	cs.xSDK.RegistreTaches.new().Tuer($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
	
	// tempo de 5 mn
	Waiting(5*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]($data.tache)
					
					// 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 ($data.tache.partage.Tuer.signaled)
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Arrêt du monitoring")
	
	
	// -----------------------------
	// MARK:Medias
	// -----------------------------
	
Function CréerMediaMobile($tâche : Object)
	var $dossier; $sélection : Object
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Démarrage du traitement")
	
	$dossier:=This.document.getMobileMediaFolder()
	
	// récupérer la sélection de medias
	$sélection:=cs.MediasSelect.new().MediasDeMobile()
	
	// créer les fichiers pour Mobile
	$sélection.CréerMediaMobile($dossier; $tâche)
	
	
	// -----------------------------
	// MARK:Licences
	// -----------------------------
	
Function GérerLicencesMobile()
	var $apps; $sessions; $sessionOS : Collection
	var $app; $session : Object
	var $timeOut : Integer
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Démarrage du traitement")
	
	$apps:=MobileAppServer.App.new().all()
	
	// lister toutes les sessions actives (IOS ou androïd)
	$sessions:=New collection
	For each ($app; $apps)
		// $app est une classe App() instanciée par le chemin d'un dossier , de type {teamID}.com.nomEntreprise.nomApplication
		// fixer son ID (bug du composant 4D ?)
		$app.id:=$app.folder.fullName
		// getSessionManager() créer une classe cs.Session de $apps
		// getSessionObjects() est la collection des contenus de tous les fichiers de nom IDsession (sans suffixe)  dans $app
		$sessionOS:=$app.getSessionManager().getSessionObjects()
		// ajouter le chemin du dossier
		For each ($session; $sessionOS)
			$session.dossier:=$app.folder
		End for each 
		$sessions:=$sessions.combine($sessionOS)
	End for each 
	
	// dans cette version, 2 licences 4D mobile sont disponibles. Un troisième user ne peut donc pas se connecter
	// principe retenu :
	// . si 1 user est connecté, un second peut se connecter : ici on ne fait rien
	// . si 2 users sont connectés, ici on déconnecte celui qui la date de dernier accès la plus ancienne (et supérieure à $timeOut mn)
	
	// trier les user sessions par date de dernier accès
	$sessions:=$sessions.orderBy("lastAccessDate asc")
	
	Case of 
		: ($sessions.length=0)
			//: ($sessions.length<2)
			//// il faut au moins 2 sessions pour en supprimer une
		: (Not(This.rsc.SetVariable(Est Ressource MOB; "Licence/TimeOut"; Is longint; ->$timeOut)))
			// pb , il faut un time-out
		: ($sessions[0].lastAccessDate<($timeOut*60*1000))
			// la session la plus ancienne n'a pas encore vécu plus de "time-out"
		Else 
			// supprimer le fichier de la première session
			$sessions[0].dossier.file($sessions[0].session.id).delete()
			
			MOBILE APP REFRESH SESSIONS  // confirmer 
			
			This.trace.EnvoyerMessages([msgk_event; msgk_log; msgk_mail]; "Maintenance Mobile"; Current method name; "Suppression de la session "+$sessions[0].email)
	End case 
	
	
	// -----------------------------
	// 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]$notes - 05/01/2026 13:55:08

      property request; formats : Object

Class extends $composant

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()->$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.TableNom
	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 
	
	
	// remarque : une action générique pour plusieurs tables n'est pas possible => autant d'EditerNote que de cas !
Function AjouterNotePersonne($response : Object)
	This.AjouterNote($response)
	
Function AjouterNoteCommune($response : Object)
	This.AjouterNote($response)
	
Function AjouterNoteSite($response : Object)
	This.AjouterNote($response)
	
Function AjouterNoteMedia($response : Object)
	This.AjouterNote($response)
	
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.EntitéRéduite.params.objet
	$contenu:=This.request.EntitéRéduite.params.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 : Object
	// lire toutes les notes de la table .DataClassNom 
	var $auteur; $libellé : Text
	var $c; $c1; $c2 : Collection
	var $note : Object
	
	// rappel : ici on arrive via un WebContext : le IDunique de l'entité n'est pas connu
	
	$result:=This.InitResponse()
	
	// lire la BDD notes
	$c:=This.LireBDD($data)
	
	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]$albums - 07/07/2025 16:45:00

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

Class extends $serveurWeb

Class constructor($url : Text)
	
	Super()
	
	This.LireParametresUrl($url)
	
	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 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
	This.Envoyer(Session.storage.ALB_Data.pageAccueilALB; "Accueil")
	$result:=True
	
	
Function OuvrirAlbum()->$result : Boolean
	This.webALB.OuvrirAlbum()
	$result:=True
	
	
Function VisualiserAlbum()->$result : Boolean
	This.webALB.VisualiserAlbum()
	$result:=True
	
	
Function Envoyer($nomPageWeb : Text; $EtatNavigation : Text)
	// fixer les variables process
	Use (Session.storage.ALB_HTTPvars)
		Session.storage.ALB_HTTPvars.wwwEtatNavigation:=$EtatNavigation
	End use 
	
	// restaurer les variables process nécessaires à la construction de la page .shtml
	This.RestaurerHTTPvars("ALB_HTTPvars")
	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:=This.document.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
					
					cs.$notes.new($requete).EcrireNote()
					
			End case 
	End case 
	
	WEB SEND TEXT("traitement "+Choose($requete.success; "OK"; "KO"))
    

[class]_document - 06/02/2026 18:03:54

      property nomDossierAlbums : Text

Class constructor
	
	// nom du serveur Web Hôte
	This.nomDossierAlbums:="albums"
	
	
Function getAppWorkSpaceFolder()->$result : Object
	$result:=Storage.Host.$document.call(Null).GetAppWorkSpace()
	
	
	// -----------------------------
	// MARK:Medias
	// -----------------------------
	
Function getMobileMediaFolder()->$result : Object
	$result:=Storage.Host.$document.call(Null).GetMobileMediaFolder()
	
	
	// -----------------------------
	// MARK:Albums
	// -----------------------------
	
Function getRacineHTMLFolder()->$result : Object
	// utiliser le serveur Web hôte fourni
	$result:=Storage.Host.$document.call(Null).GetRacineHTMLFolder()
	// au besoin, créer le dossier
	If ($result#Null)
		$result.create()
	End if 
	
	
Function getWEBalbumsFolder()->$result : Object
	// (v6.4.6 dans le dossier Web)
	$result:=This.getRacineHTMLFolder().folder(This.nomDossierAlbums)
	
	
Function getWEBalbumVignettesURL()->$result : Text
	$result:=Session.storage.ALB_HTTPvars.wwwURLalbum+"Vignettes/"
	
	
	// -----------------------------
	// MARK:Arbre
	// -----------------------------
	
Function getSessionFolder()->$result : Object
	// le dossier de la session Web
	
	Case of 
		: (Get 4D folder(Current resources folder)=Get 4D folder(Current resources folder; *))
			// test local du composant
			$result:=Folder(System folder(Documents folder); fk platform path).folder("testMobileARB")
			
		: (Session=Null)
			// test depuis la BDDmère sans serveur
			$result:=This.getRacineHTMLFolder().folder(This.LireLocatedSTR(10)).folder("Test")
			
		Else 
			$result:=This.getRacineHTMLFolder().folder(This.LireLocatedSTR(10)).folder(Session.id)
	End case 
	$result.create()
	
	
	// -----------------------------
	// MARK:Notes
	// -----------------------------
	
Function getBDDnotesFolder($nomBDD : Text)->$result : Object
	// nom du fichier .4DB = nom dossier sans le séparateur final
	
	Case of 
		: (Not(Storage.System.estExecuteDansHote))
			// 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(This.LireLocatedSTR(12)).folder($nomBDD+".4dbase")
			$result.create()
			$result:=$result.file($nomBDD)
	End case 
	
	
	// -----------------------------
	// MARK:Utilitaires
	// -----------------------------
	
Function LireLocatedSTR($ID : Integer)->$result : Text
	$result:=Localized string(String($ID))
	
    

[class]MediasSelect - 29/05/2025 15:38:58

      property fct : cs.xSDK.Outils
property DataClassNom : Text
property selection : Collection
property length : Integer

Class constructor
	
	This.fct:=cs.xSDK.Outils.me
	
	
Function MediasDeMobile()->$result : Object
	var $sélection : Object
	
	$sélection:=cs.xSQL.MediasSelect.new("private = 0 AND (type = 1 OR type = 2)").Créer()
	This.DataClassNom:=$sélection.DataClassNom
	This.selection:=$sélection.selection
	This.length:=$sélection.selection.length
	
	$result:=This
	
	
Function CréerMediaMobile($dossier : Object; $tâche : Object)
	// télécharger les media de la sélection et les mettre au format mobile
	var $entité; $data; $result; $fichier; $application : Object
	var $pict : Picture
	var $gauche; $haut; $droite; $bas; $largeur; $hauteur : Integer
	var $Zoom : Real
	
	$application:=Storage.Host.$application.call()
	
	For each ($entité; This.selection) While (Not($tâche.partage.Tuer.signaled))
		$fichier:=$dossier.file("media_"+String($entité.ID)+".jpg")
		
		Case of 
			: ($fichier.exists)
				// le media mobile existe
				
			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:=1200
					$bas:=1200
					This.fct.CalculerRectangleMedia(->$gauche; ->$haut; ->$droite; ->$bas; $largeur; $hauteur; ->$Zoom)
					$pict:=$pict*$Zoom
					
					// passer en JPEG
					CONVERT PICTURE($pict; "image/jpg"; 0.5)
					WRITE PICTURE FILE($fichier.platformPath; $pict; "image/jpg")
				End if 
		End case 
	End for each 
	
    

[class]_actionPageWeb - 09/02/2026 09:16:41

      Class extends $serveurWeb

Class constructor($url : Text)
	
	Super()
	
	// récupérer les paramètres
	This.LireParametresUrl($url)
	
	
	
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 
	
	
	// -----------------------------
	// MARK:Traitement des url de table
	// -----------------------------
	
Function mobileArbreAfficher()
	// ouvrir l'arbre
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation"; Current method name; "Arbre de "+Séparateur webURLparams+This.paramsUrl[0]+Séparateur webURLparams+This.paramsUrl[1])
	
	This.InitSession("ARB")
	
	// renseigner deQui
	Use (Session.storage.ARB_HTTPvars)
		Session.storage.ARB_HTTPvars.wwwActionParams:=Séparateur webURLparams+This.paramsUrl[0]+Séparateur webURLparams+This.paramsUrl[1]
	End use 
	
	This.Envoyer("mobileArbreAfficher.shtml"; "ARB_HTTPvars")
	
	
Function mobileCarteAfficher()
	// ouvrir la carte
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation"; Current method name; "Carte de "+Séparateur mobURLparams+This.paramsUrl[0]+Séparateur mobURLparams+This.paramsUrl[1])
	
	This.InitSession("CARTO")
	vs4D:=""
	
	// renseigner deQui
	Use (Session.storage.CARTO_HTTPvars)
		Session.storage.CARTO_HTTPvars.wwwActionParams:=Séparateur mobURLparams+This.paramsUrl[0]+Séparateur mobURLparams+This.paramsUrl[1]
		// nécessaires pour .getCarteData()
		Session.storage.CARTO_HTTPvars.wwwRacineRessources:="../"
		Session.storage.CARTO_HTTPvars.wwwContexteWeb:="ALVmobile"
		// initialiser les variables process contenus dans la carte
		This.rsc.SetObjet(Est Ressource MOB; "Carte/Carto_Opacity_OSM"; Is text; Session.storage.CARTO_HTTPvars; "wwwCarto_Opacity_OSM")
		This.rsc.SetObjet(Est Ressource MOB; "Carte/Carto_Opacity_GP"; Is text; Session.storage.CARTO_HTTPvars; "wwwCarto_Opacity_GP")
		Session.storage.CARTO_HTTPvars.wwwCarto_Opacity_Cible:="0.0"
		// zoom
		This.rsc.SetObjet(Est Ressource MOB; "Carte/Carto_ZoomMin_OL"; Is text; Session.storage.CARTO_HTTPvars; "wwwZoneWeb_ZoomMin")
		This.rsc.SetObjet(Est Ressource MOB; "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"); "CARTO_HTTPvars")
	
	
Function mobileNotesAfficher()
	// ouvrir la page des notes
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation"; Current method name; "Notes de "+This.paramsUrl[0])
	
	This.InitSession("NOTES")
	
	// renseigner deQui
	Use (Session.storage.NOTES_HTTPvars)
		Session.storage.NOTES_HTTPvars.wwwEtatNavigation:=""
		Session.storage.NOTES_HTTPvars.wwwActionParams:=Séparateur webURLparams+This.paramsUrl[0]+Séparateur webURLparams
	End use 
	
	This.Envoyer("mobileNotesAfficher.shtml"; "NOTES_HTTPvars")
	
	
	// -----------------------------
	// MARK:Traitement des actions d'entity
	// -----------------------------
	
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
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Création"; Current method name; "Arbre de "+Séparateur mobURLparams+This.paramsUrl[0]+Séparateur mobURLparams+This.paramsUrl[1])
	
	// message en cas d'erreur
	$texte:="<style>\n  p {\n    color: white;\n  }\n</style></br></br><p>"+This.document.LireLocatedSTR(11)+"</p>"
	Case of 
		: (This.paramsUrl.length<2)
		: (Num(This.paramsUrl[1])<1)
		Else 
			// c'est ok
			$ID:=Num(This.paramsUrl[1])
			
			OB SET($params; "IDarbre"; $ID)
			OB SET($params; "IDpersonne"; CodeEnreg($ID; [1]))
			OB SET($params; "NmaxAscendance"; 3)
			OB SET($params; "NmaxDescendance"; 1)
			OB SET($params; "CheminDossierBDD_AG"; This.document.getSessionFolder().platformPath)
			
			OB SET($params; "nomBDD"; "ARB")
			OB SET($params; "Modele"; "MobileArbre.xml")
			OB SET($params; "functionID"; "Construire_AG")
			// nom et collections des éléments Webables
			OB SET($params; "EntitésWebables"; New object)
			OB SET($params; "PersonnesAffichables"; "HTTP_Personnes")
			OB SET($params.EntitésWebables; "HTTP_Personnes"; Session.storage["HTTP_Personnes"])
			OB SET($params; "EventsAffichables"; "HTTP_Events")
			OB SET($params.EntitésWebables; "HTTP_Events"; Session.storage["HTTP_Events"])
			
			OB SET($params; "Session_Etat"; Storage.Host.Session_Etat)
			OB SET($params; "optionsMsg"; [msgk_event])
			// options : on veut les cadres des personnes absentes (=> largeur image SVG fixe)
			OB SET($params; "Options"; 0x0013)  // liens dynamiques
			OB SET($params; "SéparateurParamsURL"; Séparateur webURLparams)  // 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)
			OB SET($params; "EtatProcessus"; $EtatProcessus)
			
			// on lance (le résultat sera reçu dans arbreXML)
			cs.xARB.$arbre.new().Imager_AG($params)
			If ($params.success)
				$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)
	
	
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 "+Séparateur mobURLparams+This.paramsUrl[0]+Séparateur mobURLparams+This.paramsUrl[1])
	
	Case of 
		: (This.paramsUrl.length<2)
		: (Num(This.paramsUrl[1])<1)
		Else 
			// 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.paramsUrl[0]; "IDs"; New collection(Num(This.paramsUrl[1]))))
			// 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.paramsUrl[1]+".js"
			// pour la page Web, icones d'instance vues au niveau de la session
			$params.dossierCartoURL:=$params.HTTPvars.wwwRacineRessources+This.document.LireLocatedSTR(10)+"/"+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)
	End case 
	
	
Function AfficherNotes()
	var $texte : Text
	var $data : Object
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Création"; Current method name; "Notes de "+Séparateur mobURLparams+This.paramsUrl[0])
	
	$texte:="<h2>"+Localized string("1108")+"</h2>"
	
	Case of 
		: (This.paramsUrl.length<1)
		Else 
			
			// interroger la BDD_NOTE
			$data:=New object("DataClassNom"; This.paramsUrl[0])
			If (cs.$notes.new(New object).LireNotes($data).success)
				$texte:=$data.notes
			End if 
			
	End case 
	// envoyer le contenu
	WEB SEND TEXT($texte)
	
	
	// -----------------------------
	// MARK:Traitement des actions d'albums
	// -----------------------------
	
Function mobileAlbumNotesAfficher()
	var $IDalbum : Text
	// ouvrir la page des notes de l'album :
	$IDalbum:=Session.storage.ALB_UserParams.Album.Identification_ID
	
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation"; Current method name; "Notes de "+$IDalbum)
	
	This.InitSession("NOTES")
	
	// renseigner deQui
	Use (Session.storage.NOTES_HTTPvars)
		Session.storage.NOTES_HTTPvars.wwwEtatNavigation:="affichageNotesAlbum"
		Session.storage.NOTES_HTTPvars.wwwActionParams:=Séparateur webURLparams+"ALB_"+$IDalbum+Séparateur webURLparams
	End use 
	
	This.Envoyer("mobileNotesAfficher.shtml"; "NOTES_HTTPvars")
	
	
    

[ ]U_Nav NotesMobile - 23/06/2023 11:51:16

      
Case of 
	: (Form event code=On Load)
		SHOW PROCESS(Current process)
		
		// attention : ici le minuteur est toujours actif (la roue doit tourner quand la palette passe en arrière plan)
	: (Form event code=On Timer)
		//Afficher Progression Process
		
	: (Form event code=On Close Box)
		CANCEL
		
End case 

    

[ ]U_Nav NotesMobile - objet grpNotes - 26/02/2025 11:44:50

      var $j : Integer
var $params : Object
var $c; $result : Collection

Case of 
	: (Form event code=On Selection Change)
		// editer la fiche de l'entité de la note 
		If (Form.NoteCourant#Null)
			$result:=Storage.Host.$editeur.call(Null).EditerEntity(Form.NoteCourant.IDunique; Form.NoteCourant.nomTable)
		End if 
		
	: ((Right click) | (Contextual click))  // Clic droit ou Control+clic
		// gestion des modifications
		
		// créer le popMenu
		$c:=New collection(Localized string("3002"))
		$j:=Pop up menu($c.join(";"))
		
		Case of 
			: ($j=1)
				CONFIRM("Supprimer la note "+Form.NoteCourant.nomTable+" - "+Form.NoteCourant.IDunique; "Annuler"; "OK")
				// attention : le btn par défaut est Annuler
				If (ok=0)
					// supprimer dans la BDD :
					$params:=OB Copy(Form.BDD)
					// dans la table
					$params.DataClassNom:=Form.DataClassNom
					// la sélection courante :
					$params.sélection:=Form.NotesSelected.extract("ID")
					
					$params.wndRef:=Current form window
					$params.functionIDcallBack:="AfficherSelection"
					
					CALL WORKER("WK_Composant_MOB"; Formula from string("cs.$notesEditeur.new().SupprimerSelection($1)"); $params)
					Form.estOccupé(True)
					
				End if 
				
		End case 
		
End case 
    

[ ]U_Nav NotesMobile - objet PopupListeBDDs - 08/12/2024 18:45:42

      var $varName : Text
var $params : Object

$varName:=OBJECT Get name(Object current)
Form.listeBDDsVarName:=$varName

Case of 
	: (Form event code=On Load)
		// lister les BDD notes
		$params:=New object
		$params.wndRef:=Current form window
		$params.functionIDcallBack:="AfficherListeBDDs"
		$params.varName:=$varName
		
		CALL WORKER("WK_Composant_MOB"; Formula from string("cs.$notesEditeur.new().ListerBDDs($1)"); $params)
		Form.estOccupé(True)
		
		
	: (Form event code=On Data Change)
		
		If (Form[$varName].index>-1)
			// mémoriser la BDD sélectionnée
			Form.BDD:=Form[$varName]["params"][Form[$varName].index]
			
			// lister les DataClassNoms de la BDD sélectionnée
			$params:=OB Copy(Form.BDD)
			$params.wndRef:=Current form window
			$params.functionIDcallBack:="AfficherListeTables"
			$params.varName:=Form.listeTablesVarName
			
			CALL WORKER("WK_Composant_MOB"; Formula from string("cs.$notesEditeur.new().SelectionnerTables($1)"); $params)
			Form.estOccupé(True)
			
			// RAZ de la liste
			OBJECT SET VISIBLE(*; "grpNotes@"; False)
		End if 
		
End case 
    

[ ]U_Nav NotesMobile - objet PopupListeTables - 09/02/2026 09:15:00

      var $varName : Text

$varName:=OBJECT Get name(Object current)
Form.listeTablesVarName:=$varName

Case of 
	: (Form event code=On Load)
		// lister vide
		
		Form[$varName]:=New object
		Form[$varName].values:=New collection
		Form[$varName].index:=-1
		Form[$varName].currentValue:=Localized string("1107")
		Form[$varName].params:=Null
		
		
	: (Form event code=On Data Change)
		
		If (Form[$varName].index>-1)
			// mémoriser la table sélectionnée
			Form.TableNom:=Form[$varName].currentValue
			
			// demnder la liste des notes de cette table
			Form.CréerSélection()
		End if 
		
End case 
    

onStartup - 29/03/2025 17:22:24

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

cs.$composant.new().InitVariablesMOB()

CALL WORKER("WK_Composant_MOB"; Formula(initProcess).source)

    

onExit - 01/04/2025 20:07:46

      // 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()



    

onWebAuthentication - 24/11/2023 10:49:56

      Pas de code
    

onBackupShutdown - 25/11/2024 15:28:20

      Pas de code
    

onHostDatabaseEvent - 06/02/2026 18:03:46

      #DECLARE($event : Integer)

Case of 
	: ($event=On before host database startup)
		// ici pas d'interaction avec les autres composants et la BDDmère
		ON ERR CALL(Formula(ErrorHandler).source; ek local)
		
		cs.$composant.new().InitVariablesMOB()
		
		Use (Storage.System)
			Storage.System.estExecuteDansHote:=True
		End use 
		
		CALL WORKER("WK_Composant_MOB"; Formula(initProcess).source)
		
		// installer les ressources du composant
		// rien ici
		
		// 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))
		
	: ($event=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()
		
		// lancer la maintenance
		cs.$maintenance.new().Démarrer()
		
	: ($event=On before host database exit)
		// placer ici le code à exécuter avant le "Sur fermeture" de la base hôte
		
		// arrêter la maintenance
		cs.$maintenance.new().Arrêter()
		
	: ($event=On after host database exit)
		// placer ici le code à exécuter après le "Sur fermeture" de la base hôte
End case 

    

onMobileAppAction - 22/12/2023 18:13:47

      Pas de code