⇧
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#ALV 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
⇧
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