Exécuter Function Coopérative - 30/12/2024 13:55:53

      #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 $2")
		
	: (Value type($params.functionID)#Is text)
		$trace.EnvoyerMessages([msgk_event; msgk_log]; "Initialisation :"; Current method name; "'$2.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 $2")
		
	: ($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 "+$params.functionID)
		End if 
		
		$numProc:=Current process
End case 

    

Bac à sable JLOG - 04/01/2026 17:27:58

Partagée entre composants et base hôte

      var $commande; $i : Integer
var $c : Collection
var $o : Object:=New object

//cs.$composant.new().InitProcess()



//cs.$LogsWebImport.new().ExtaireLesChamps($x)

$c:=[0]
//$c.push(1)
$c.push(3)
//$c.push(4)
//$c.push(5)

$commande:=0
For each ($i; $c)
	$commande:=$commande ?+ $i
End for each 


If ($commande ?? 0)
	// afficher la console de l'application
	var $params : Object
	
	$params:=New object
	$params.Commande:="Afficher"
	$params.sourceLogs:=ALV Client APP
	$params.wndTitre:="Logs Composant JLOGS"
	
	$params.nbrMaxLogs:=2000
	cs.xSDK.EvenementsALV.me.AfficherEditeur($params)
	Waiting(60)
End if 

If ($commande ?? 1)
	cs.$LogsWebImport.new().ImporterLogs()
End if 

If ($commande ?? 3)
	// lire la BDD Web
	cs.$LogsWebEditeur.new().OuvrirFormulaire($o)
End if 

If ($commande ?? 4)
	// lire la BDD App
	cs.$LogsAppImport.new().ImporterLogs()
End if 

If ($commande ?? 5)
	
	//$o.execute:=Formula(cs.$LogsAppImport.new().NettoyerBDD())
	//CALL WORKER("WK_Composant_JLOG"; Formula($o.execute()))
	
	// editer la BDD App
	cs.$LogsAppEditeur.new().OuvrirFormulaire($o)
End if 

If ($commande ?? 31)
	cs.$composant.new().ModifierTraduction()
End if 







    

Exécuter Function Préemptive - 03/06/2025 11:20:25

Capable de process préemptif

      #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; "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)
		
		$nomProcess:="$JLOG_"+$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
		
		// construire la classe
		$data:=$classe.new()
		$data.InitProcess()
		
		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 

    

traceHandler - 23/12/2024 10:08:40

Capable de process préemptif

      cs._Trace.me.Intercepter()

    

COMPILER - 09/12/2024 18:55:23

      var ErrorNum : Integer
var ErrorDescription : Text


ARRAY LONGINT(ID; 0)
ARRAY LONGINT(BYTES_SENT; 0)
ARRAY TEXT(ADRESS_IP; 0)
ARRAY TEXT(COOKIE; 0)
ARRAY TEXT(HOST_NAME; 0)
ARRAY TEXT(URL_PAGE; 0)
ARRAY TEXT(USER_AGENT; 0)
ARRAY TEXT(USER_NAME; 0)
ARRAY TEXT(HTTP_REQUEST; 0)
ARRAY TEXT(ADRESS_IP_SERVER; 0)
ARRAY LONGINT(SERVER_STATUS; 0)
ARRAY TEXT(URI; 0)
ARRAY TEXT(URI_QUERY; 0)
ARRAY TEXT(URI_STEM; 0)
ARRAY DATE(DATE_GMT_CONNEXION; 0)
ARRAY TEXT(TIME_GMT; 0)
ARRAY LONGINT(DELAY_SERVER; 0)



    

[class]$composant - 06/02/2026 18:01:58

      property trace : cs._Trace
property nomServeurWeb; FichierRessources; FormatJournal; racineXML; nomFichier : Text
property dossiersLogs; fichiersLogs : Collection

Class constructor()
	
	This.trace:=cs._Trace.me
	
	
	
	// ----------------------
	// MARK:Installation
	// ----------------------
	
Function InitVariablesJLOG()
	var $data : Object
	//•••••  variables globales système  •••••
	
	Use (Storage)
		Storage["System"]:=New shared object
		Storage["Processes"]:=New shared object
		Storage["Host"]:=New shared object
	End use 
	
	Use (Storage.System)
		Storage.System.Status:=0
		Storage.System.estExecuteDansHote:=False
		
		// tout le monde n'est pas initialisé ; on ne sait pas si on est serveur Web
		Storage.System.estServeurWeb:=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 
	
	// partager des ressources
	$data:=New object
	// fichier de ressources
	$data.chemin:=Get 4D folder(Current resources folder)+"DataJLOG.xml"
	// mémoriser
	cs.xSDK.ResourceALV.me.Inscrire(Est Ressource JLog; $data)
	
	
Function Installer()
	
	Use (Storage.System)
		Storage.System.estExecuteDansAPP:=cs.xSDK.EnvironnementALV.new().estExecuteDansAPP()
	End use 
	
	This.InstallerDonnéesHote()
	
	
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 InitProcess()
	ON ERR CALL(Formula(traceHandler).source)  // gestion des erreurs
	ErrorNum:=0
	ErrorDescription:=""
	
	
Function InitResult()->$result : Object
	$result:=New object("Error"; 0; "ErrorDescription"; "")
	
	
Function getDossierLogs($params : Object)->$result : Object
	// renvoyer le dossier des logs en fonction de l'environnement
	
	$result:=New object("dossier"; Null)
	Case of 
		: (Not(OB Is defined($params; "typeApplicationImport")))
			// circuler
			
		: ($params.typeApplicationImport=ALV Serveur APP)
			$result.dossier:=Folder(Data file; fk platform path).parent.folder("Logs")
			
		: ($params.typeApplicationImport=ALV Client APP)
			$result.dossier:=Folder(fk home folder).folder("Library/Caches/AinsiLaVie Client").folders().query("name = :1"; "AinsiLaVie_srv-sourderie_ainsilavie_fr@")
			If ($result.dossier.length>0)
				$result.dossier:=$result.dossier[0].folder("Logs")
			End if 
			
		: ($params.typeApplicationImport=ALV BDD mère)
			$result.dossier:=Folder(fk logs folder; *)
			
		: ($params.typeApplicationImport=100)
			$result.dossier:=Folder(Structure file; fk platform path).parent.parent.parent.folder($params.nomDossierTest).folder("Logs")
	End case 
	
	
	// ----------------------
	// MARK:Editeurs
	// ----------------------
	
Function ModifierTraduction()
	// ouvrir l'éditeur des traductions
	cs.xSDK.TraductionsEditeur.new().ModifierTraductions()
	
	
Function _OuvrirFormulaire_process($params : Object)
	// méthode du process U_Nav LogsWeb
	var $wndNum : Integer
	var $nomForm : Text
	
	// récupérer les paramètres (peuvent avoir changé depuis l'ouverture)
	This.Installer()
	
	NO DEFAULT TABLE
	
	// la fenêtre est ouverte en taille normale
	//$wndNum:=Open form window($nomForm; Movable form dialog box; Horizontally centered; Vertically centered; *)
	$nomForm:=$params.nomFormulaire
	$wndNum:=Open form window($nomForm; -Palette form window; Horizontally centered; Vertically centered; *)
	
	// initialiser la valeur
	DIALOG($params.nomFormulaire; This)
	CLOSE WINDOW
	CLEAR VARIABLE($wndNum)
	
	
Function FixerEtatOccupé($état : Boolean)
	OBJECT SET VISIBLE(*; "AsynchroProgress"; $état)
	Form.AsynchroProgress:=Num($état)
	
	
Function estOccupé()->$result : Boolean
	$result:=(Form.AsynchroProgress=1)
	
	
Function TerminerAction()
	Form.FixerEtatOccupé(False)
	
	
	// ----------------------
	// MARK:Requetes HTTP
	// ----------------------
	
Function RequeterDossierLogsServeur($params : Object)
	// principe : la demande est envoyée au serveur Web Hôte
	// 'sur connexion Web' reçoit l'URL et appelle xJLOG.$composant.RequeteJournaux() ; résultat dans .reqRetour
	// attention, ici il faut requeter les serveur Web avec une function d'une classe qui renvoie un objet
	If (Storage.System.estExecuteDansHote)
		ASSERT(This.trace.DebugerMethode("Post requête"; Current method name; JSON Stringify($params; *)))
		
		// envoyer la requête HTTP
		Storage.Host.RequeteHTTP.call().Requeter("/4DHTTP/xJLOG/$composant/RequeteJournaux"; "GET"; $params; "blob")
		
	Else 
		$params.reqRetour:=Null
	End if 
	
	
Function RequeteJournaux($params : Object)
	// ici on est sur le serveur ; traitement des requetes HTTP de la base hôte
	// on doit renvoyer un objet, que le serveur Web hote enverra
	var $dataTexte : Text
	var $data; $classObjet : Object
	
	// conteneur les données
	$params.reqRetour:=Null
	Case of 
		: ($params=Null)
		: (Not(OB Is defined($params; "functionID")))
			
		: (OB Is defined(This; $params.functionID))
			$data:=This[$params.functionID]($params)
			
		: (OB Is defined(cs.$LogsAppImport.new(); $params.functionID))
			// ok à traiter par cette classe
			$classObjet:=cs.$LogsAppImport.new()
			$classObjet[$params.functionID]($params)
			
		: (OB Is defined(cs.$LogsWebImport.new(); $params.functionID))
			// ok à traiter par cette classe
			$classObjet:=cs.$LogsWebImport.new()
			$classObjet[$params.functionID]($params)
	End case 
	
	
Function LireLogs()->$result : Collection
	// renvoyer le contenu de chaque fichier de la collection This.fichiersLogs
	var $fichierLogs : 4D.File
	var $fhandle : 4D.FileHandle
	var $lines : Collection
	
	$result:=New collection
	
	If (This.fichiersLogs.length>0)
		For each ($fichierLogs; This.fichiersLogs)
			$fhandle:=$fichierLogs.open("read")
			
			$lines:=New collection
			While (Not($fhandle.eof))
				$lines.push($fhandle.readLine())
			End while 
			
			$result.push(New object("nomFichier"; $fichierLogs.fullName; "contenu"; $lines))
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Lignes trouvées "+Choose($lines.length>0; "[OK]"; "[KO]"); Current method name; String($lines.length)+" lignes (entête et logs)")
		End for each 
	End if 
	
	
	// ----------------------
	// MARK:Utilitaires
	// ----------------------
	
Function EncoderBase64($params : Variant)->$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 : Variant
	var $dataBlob : Blob
	
	SET BLOB SIZE($dataBlob; 0)
	BASE64 DECODE($dataTexte; $dataBlob)
	BLOB TO VARIABLE($dataBlob; $result)
	
	
	
    

[class]_BDD - 06/02/2026 17:59:28

      property Logs; Log : Collection
property NomsChamps; ParamsChamps : Collection

Class extends $composant

Class constructor()
	
	ARRAY LONGINT(TABID; 0)
	ARRAY TEXT(TABFICHIER; 0)
	
	Super()
	
	
	// ----------------------
	// MARK:BDD
	// ----------------------
	
Function getDossierBDD_Log($contexte : Text; $nomDossiertest : Text)->$chemin : Text
	var $fichier; $nomDossier : Text
	var $dossier : 4D.Folder
	
	$fichier:=""
	$dossier:=Null
	
	Case of 
		: (Not(cs.xSDK.ResourceALV.me.SetVariable(Est Ressource JLog; $contexte; Is text; ->$fichier)))
			// pas de nom du dossier de la BDD journaux Web
			
		: (Not(Storage.System.estExecuteDansHote))
			// exécution en local ; utiliser une BDD de test
			$dossier:=Folder(Structure file(*); fk platform path)
			$dossier:=$dossier.parent.parent.parent.folder($nomDossiertest).folder($fichier)
			
		: (Storage.System.typeApplication=ALV Serveur APP)
		: (Storage.System.typeApplication=ALV Client APP)
			// hors sujet
			
		: (Not(cs.xSDK.ResourceALV.me.SetVariable(Est Ressource JLog; "DossierJournauxALV"; Is text; ->$nomDossier)))
			
		Else 
			$dossier:=Folder(fk data folder; *).folder($nomDossier).folder($fichier)
	End case 
	
	$chemin:=""
	Case of 
		: ($dossier=Null)
		: ($dossier.exists)
			// c'est bon
			$chemin:=$dossier.platformPath
			
		: ($dossier.create())
			// c'est créé
			$chemin:=$dossier.platformPath
			
		Else 
			$chemin:=""
	End case 
	
	ASSERT(This.trace.DebugerMethode("$LogAppEditeur"; Current method name; "dossier de la BDD des journaux App : "+$chemin))
	This.trace.Créer(-16101*Num($chemin=""); Current method name; "le chemin du dossier de la BDD des journaux App est vide").LeverException([msgk_event; msgk_log])
	
	
Function OuvrirBDD_Externe($cheminBDD : Text)->$chemin : Text
	var $sql_BDDpath; $sql_BDDcourantePath : Text
	
	// SQL ne peut pas utiliser directement $cheminDossier
	$sql_BDDpath:=$cheminBDD
	
	// 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 
	
	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 $nomBDD existante
			Begin SQL
				USE DATABASE DATAFILE :$sql_BDDpath AUTO_CLOSE;
			End SQL
			// attention ici on est thread-safe
			This.trace.EnvoyerMessages([msgk_event]; "Ouverture de la BDD"; Current method name; "<"+$sql_BDDpath+">")
			
		Else 
			// nouvelle BDD externe
			//-- créer les fichiers $fichier.4DB et $fichier.4DD
			// rappel : la structure sera créée par apprentissage des fichiers log
			
			Begin SQL
				CREATE DATABASE IF NOT EXISTS DATAFILE :$sql_BDDpath;
				USE DATABASE DATAFILE :$sql_BDDpath AUTO_CLOSE;
			End SQL
			
			// créer la structure
			This.CreerTablesEnBDD()
			
			This.trace.EnvoyerMessages([msgk_event]; "Création de la BDD"; Current method name; "<"+$sql_BDDpath+">")
	End case 
	$chemin:=$sql_BDDpath
	// Maintenant toutes les requêtes SQL du process sont dirigées vers $sql_BDDpath
	
	
Function CreerTablesEnBDD()
	// créer toutes les table et leurs champs suivant le format 'FormatBDD' 
	var $table; $champ : Object
	var $texte : Text
	var $afaire : Boolean
	
	// peut être dejà fait
	ARRAY TEXT($tables; 0)
	Begin SQL
		SELECT TABLE_NAME FROM _USER_TABLES INTO :$tables;
	End SQL
	
	$afaire:=True
	Case of 
		: (Size of array($tables)=0)
		: (Find in array($tables; This.FormatJournal)=-1)
		Else 
			$afaire:=False
	End case 
	
	If ($afaire)
		// les tables sont à créer
		For each ($table; This.getFormatBDD())
			// 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
					// créer la commande de création d'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 
	End if 
	
	
Function LireLogsEnBDD($typeApplication : Integer; $c : Collection)
	// requête SQL de la BDD ; résultat dans $c
	var $data; $champ : Object
	var $ref_colonne; $expression_arithmétique : Text
	var $sql_Commande : Text
	var $i : Integer
	var $ptr : Pointer
	
	Case of 
		: ($c=Null)
		Else 
			// ok on a tout
			
			// lister les params champs de la BDD
			This.getFormatBDD()
			// ils sont dans This.ParamsChamps
			
			// construire la requête : This.ParamsChamps contient la liste des noms de champ SQL à sélectionner
			// attention : un tableau process de nom This.ParamsChamps.champ doit exister avec le même type que la colonne SQL de ce nom
			
			// initialiser avec l'ID et le nom de fichier
			$ref_colonne:="ID, Fichier"
			$expression_arithmétique:=":TABID, :FICHIER"
			For each ($champ; This.ParamsChamps)
				$ref_colonne:=$ref_colonne+", "+$champ.champ
				$expression_arithmétique:=$expression_arithmétique+", "+":"+$champ.champ
			End for each 
			
			// les données de logs
			$sql_Commande:="SELECT "+$ref_colonne+" FROM "+This.FormatJournal+" WHERE TypeApp = "+String($typeApplication)+" INTO "+$expression_arithmétique+";"
			Begin SQL
				EXECUTE IMMEDIATE : $sql_Commande; 
			End SQL
			
			// passer en objet : créer les données de la liste Box
			For ($i; 1; Size of array(TABID))
				
				$data:=New object("ID"; TABID{$i})
				For each ($champ; This.ParamsChamps)
					$ptr:=Get pointer($champ.champ)
					$data[$champ.libelle]:=$ptr->{$i}
					
					// ajouter l'info Fichier
					$data.nomFichier:=FICHIER{$i}
				End for each 
				$c.push($data)
			End for 
	End case 
	
	
Function FermerBDD_Externe()
	Begin SQL
		USE DATABASE SQL_INTERNAL;
	End SQL
	This.trace.EnvoyerMessages([msgk_event]; "Fermeture de la BDD "+Choose(ok=1; "OK"; "KO"); Current method name; "")
	
	
Function getFormatBDD()->$result : Collection
	var $erreur : Object
	var $fichier : 4D.File
	
	$erreur:=This.InitResult()
	$result:=[]
	
	$fichier:=Folder(fk resources folder).file(This.FichierRessources)
	
	Case of 
		: (Not($fichier.exists))
			$erreur.Error:=-15063
			$erreur.ErrorDescription:="Le fichier ressources du journal "+This.FichierRessources+" n'existe pas"
			
		: (This.FormatJournal="")
			This.trace.EnvoyerMessages([msgk_event]; "Lecture du format [KO]"; Current method name; "Le format du journal n'est pas défini")
			
		Else 
			// c'est ok
			// le format complet de la BDD
			$result:=JSON Parse($fichier.getText(); Is collection)
			
			// les paramètres des champs
			This.ParamsChamps:=$result.query("nomTable = :1"; This.FormatJournal)[0]["champs"].copy()
			// supprimer les champs inutiles pour l'import / édition
			This.ParamsChamps.shift()  // le ID du log
			This.ParamsChamps.shift()  // le lien vers le fichier
	End case 
	
	This.trace.Créer($erreur.Error; Current method name; $erreur.ErrorDescription).LeverException([msgk_event; msgk_log])
	
	
Function LireFormatBDD()->$result : Object
	// lire les données des différents champs possibles de la BDD
	// lecture en ressource du composant
	var $fichier : 4D.File
	var $dataTexte; $ElémentXML; $attribut : Text
	var $params : Object
	var $i : Integer
	var $c : Collection
	TRACE
	$result:=This.InitResult()
	
	// lire le format correspondant des champs de la BDD
	$fichier:=Folder(fk resources folder).file("DataJLOG.xml")
	If ($fichier.exists)
		$dataTexte:=""
		$result:=cs.xSDK.XML.me.LireFichier($fichier; ->$dataTexte)
		This.racineXML:=DOM Parse XML variable($dataTexte)
		$ElémentXML:=DOM Find XML element(This.racineXML; "format_"+This.FormatJournal)
		$result.Error:=-16013*Num(ok=0)
		$result.ErrorDescription:="structure "+"format_"+This.FormatJournal+" non trouvée"
		
	Else 
		$result.Error:=-16103
		$result.ErrorDescription:="fichier "+$fichier.platformPath+" inexistant"
	End if 
	
	// lire dans la structure XML les données des champs (pour SQL, ListBox...)
	This.ParamsChamps:=New collection
	If ($result.Error=0)
		// lire les élément de chaque champ
		ARRAY TEXT($tabXML; 0)
		$ElémentXML:=DOM Find XML element(This.racineXML; "format_"+This.FormatJournal+"/champ"; $tabXML)
		
		// collection des données à récupérer
		$c:=New collection("nomLog"; "colonneSQL"; "typeSQL"; "libelle"; "entete")
		// récupérer les données de chaque champ SQL trouvé dans la BDD
		For ($i; 1; Size of array($tabXML))
			$params:=New object
			
			For each ($attribut; $c)
				$ElémentXML:=DOM Find XML element($tabXML{$i}; $attribut)
				If (ok=1)
					// on a le nom du Log
					DOM GET XML ELEMENT VALUE($ElémentXML; $dataTexte)
					$params[$attribut]:=$dataTexte
				End if 
			End for each 
			
			If (OB Keys($params).length=$c.length)
				// pas d'erreur
				This.ParamsChamps.push($params)
			End if 
		End for 
	End if 
	
	
	// ----------------------
	// MARK:Requetes SQL en BDD
	// ----------------------
	
Function EnregistrerFichier($logs : Object)->$result : Text
	var $sql_Commande; $UUID : Text
	ALERT(Current method name)
	Case of 
		: (Not(OB Is defined($logs; "nomFichier")))
			// oops
		: ($logs.nomFichier="")
		Else 
			$UUID:=Generate UUID
			$sql_Commande:="INSERT INTO FICHIERS (ID_unique, Nom) VALUES ('"+$UUID+"', '"+$logs.nomFichier+"');"
			
			ErrorNum:=0
			Begin SQL
				START TRANSACTION;
				EXECUTE IMMEDIATE :$sql_Commande;
				COMMIT TRANSACTION;
			End SQL
			$result:=Choose(ErrorNum=0; $UUID; "")
	End case 
	
	
Function EnregistrementFichierExiste($logs : Object)->$result : Boolean
	// indique si le log est déjà importé
	var $sql_Commande : Text
	
	// l'enregistrement existe s'il existe des logs avec ce nom de fichier
	If (OB Is defined($logs; "nomFichier"))
		$sql_Commande:="SELECT id FROM "+This.FormatJournal+" WHERE Fichier = '"+$logs.nomFichier+"' INTO :TABID;"
		Begin SQL
			EXECUTE IMMEDIATE :$sql_Commande;
		End SQL
	End if 
	$result:=(Size of array(TABID)>0)
	
	
Function EnregistrerLog()->$result : Boolean
	// enregistrer this.Log dans la BDD
	var $ref_colonne; $expression_arithmétique; $sql_Commande : Text
	var $data : Object
	
	// construire la liste de colonnes et les data associées
	$ref_colonne:=""
	$expression_arithmétique:=""
	
	For each ($data; This.Log)
		$ref_colonne:=$ref_colonne+", "+$data.Champ
		
		// coder la valeur en fonction du type de value BDD
		Case of 
			: ($data.typeValeur="INT")
				$expression_arithmétique:=$expression_arithmétique+", "+String($data.Valeur)
				
			: (($data.typeValeur="VARCHAR@") | ($data.typeValeur="TIMESTAMP"))
				$expression_arithmétique:=$expression_arithmétique+", "+"'"+$data.Valeur+"'"
		End case 
	End for each 
	
	// ajouter le lien avec la table FICHIERS
	$ref_colonne:="Fichier"+$ref_colonne
	$expression_arithmétique:="'"+This.nomFichier+"'"+$expression_arithmétique
	
	$sql_Commande:="INSERT INTO "+This.FormatJournal+" ("+$ref_colonne+") VALUES ("+$expression_arithmétique+");"
	ErrorNum:=0
	Begin SQL
		START TRANSACTION;
		EXECUTE IMMEDIATE :$sql_Commande;
		COMMIT TRANSACTION;
	End SQL
	$result:=(ErrorNum=0)
	
	
Function LireFichiers($dossierLogs : Object)->$result : Collection
	var $sql_Commande : Text
	
	$result:=New collection
	
	// filtrer les dossiers 'associés"
	If (OB Is defined($dossierLogs; "typeApplicationImport"))
		// 
		$sql_Commande:="SELECT DISTINCT Fichier FROM "+This.FormatJournal+" WHERE TypeApp = "+String($dossierLogs.typeApplicationImport)+" INTO :TABFICHIER;"
		Begin SQL
			EXECUTE IMMEDIATE :$sql_Commande;
		End SQL
		
		ARRAY TO COLLECTION($result; TABFICHIER)
	End if 
	
	
Function SupprimerFichier($nomFichier)
	var $sql_Commande : Text
	
	$sql_Commande:="DELETE FROM "+This.FormatJournal+" WHERE Fichier = '"+$nomFichier+"';"
	Begin SQL
		EXECUTE IMMEDIATE :$sql_Commande;
	End SQL
	
    

[class]$LogsApp - 18/02/2026 17:06:52

      Class extends _BDD

Class constructor()
	
	Super()
	
	// fichier des paramètres de la BDD
	This.FichierRessources:="Data_LOG_APP.json"
	// paramètres du journal
	This.FormatJournal:="LOG_APP"
	// lire les paramètres de champs
	This.getFormatBDD()
	
	
Function FixerParametresDossiersLogs()
	// chaque élément de la liste contient les paramètres de lecture d'un dossier Log (Serveur APP, client APP et BDDmère)
	
	This.dossiersLogs:=New collection
	// toujours disponibles
	This.dossiersLogs.push(New object("nomApplication"; "Serveur APP"; "typeApplicationImport"; ALV Serveur APP; "methodeReq"; "RequeterDossierLogsServeur"; "typeApplicationEditeur"; [ALV Serveur APP]; "nomApplicationEditeur"; Localized string("22")))
	This.dossiersLogs.push(New object("nomApplication"; "Client APP"; "typeApplicationImport"; ALV Client APP; "methodeReq"; "RequeterDossierLogsAppLocal"; "typeApplicationEditeur"; [ALV Client APP]; "nomApplicationEditeur"; Localized string("23")))
	// éditeur uniquement
	This.dossiersLogs.push(New object("nomApplication"; "Serveur APP"; "typeApplicationEditeur"; [ALV Serveur APP; ALV Client APP]; "nomApplicationEditeur"; Localized string("24")))
	// non accessible hors base hôte
	If (Storage.System.estExecuteDansHote)
		This.dossiersLogs.push(New object("nomApplication"; "BDD mère"; "typeApplicationImport"; ALV BDD mère; "methodeReq"; "RequeterDossierLogsAppLocal"; "typeApplicationEditeur"; [ALV BDD mère]; "nomApplicationEditeur"; Localized string("25")))
	End if 
	
	// pour les tests, quelque soit l'environnement
	This.AjouterDossierDeTest(This.dossiersLogs)
	
	
Function AjouterDossierDeTest($c : Collection)
	// ajouter le dossier pour les tests
	
	If ((Not(Storage.System.estExecuteDansHote)) | Storage.Host.$application.call(Null).estDebugAPP())
		// pour les tests depuis la BDD mère ou le composant
		$c.push(New object("nomApplication"; "Dossier Test"; "typeApplicationImport"; 100; "methodeReq"; "RequeterDossierLogsAppLocal"; "typeApplicationEditeur"; [100]; "nomApplicationEditeur"; Localized string("26"); "nomDossierTest"; "Test JAPP"))
	End if 
	
	
	// ----------------------
	// MARK:Requêtes SQL
	// ----------------------
	
Function OuvrirBDD_Log()->$chemin : Text
	var $sql_BDDpath : Text
	
	// fixer le nom de la BDD externe ouvrir
	$sql_BDDpath:=This.getDossierBDD_Log("DossierJournalApp"; "Test JAPP")+"LogsApp"
	$chemin:=This.OuvrirBDD_Externe($sql_BDDpath)
	
	
    

[class]$LogsWebImport - 13/11/2025 13:05:00

      property fichiersLogs : Collection
property dossierLogs : Object
property dossierLogsTempo : 4D.Folder

Class extends $LogsWeb

Class constructor()
	
	Super()
	
	
	
	// ----------------------
	// MARK:Import fichiers Logs
	// ----------------------
	
Function ImporterLogs($params : Object)
	// mettre à jour la BDD avec les nouveaux fichiers logs
	var $dossierLogs : Object
	var $logs : Object
	
	This.OuvrirBDD_Log()
	// ici la BDD est ouverte, les tables et champs sont créés et This.ParamsChamps est renseignés 
	
	// Sélectionner les dossiers à importer
	This.FixerParametresDossiersLogs()
	// This.dossiersLogs est renseigné.
	This.dossiersLogs:=This.dossiersLogs.query("typeApplicationImport != :1"; Null)
	
	For each ($dossierLogs; This.dossiersLogs)
		// importer les logs des fichiers du dossier this.dossierLogs
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage import des fichiers"; Current method name; "Fichiers logs de l'application <"+String($dossierLogs.nomApplicationEditeur+">"))
		
		This.dossierLogs:=$dossierLogs
		This.LireFichiersLogs()
		// $logs contient pour chaque fichier son nom et sa collection de logs 
		
		Case of 
			: (This.Logs=Null)
				// pb serveur
			: (This.Logs.length=0)
			Else 
				Begin SQL
					START TRANSACTION;
				End SQL
				
				For each ($logs; This.Logs)
					
					If (Not(This.EnregistrementFichierExiste($logs)))
						// le fichier n'existe pas ; l'importer
						This.nomFichier:=$logs.nomFichier
						
						This.EnregistrerLogs($logs)
						
						This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Import [OK]"; Current method name; "Fichier '"+This.nomFichier+"' importé")
					End if 
				End for each 
				
				Begin SQL
					COMMIT TRANSACTION;
				End SQL
		End case 
		
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Fin import des fichiers"; Current method name; "Fichiers logs de l'application <"+String($dossierLogs.nomApplicationEditeur+">"))
	End for each 
	
	This.FermerBDD_Externe()
	
	// retour au requêteur ; en mode debug $params peut être absent
	If (Count parameters>0)
		$params.execute:=Formula(cs.$LogsWebEditeur.new()[$params.functionIDcallBack]())
		CALL FORM($params.wndRef; Formula($params.execute().source))
	End if 
	
	
Function LireFichiersLogs()
	// lire les logs des fichiers Logs du dossier This.dossierLogs
	var $params : Object
	
	This.Logs:=New collection
	Case of 
		: (Not(OB Is defined(This.dossierLogs; "methodeReq")))
			// erreur initialisation
		: (Not(OB Is defined(This; This.dossierLogs.methodeReq)))
			// erreur méthode de requête
		Else 
			// lancer la lecture de tous ces fichiers
			// il faut toujours un paramètre (compatibilité avec le cas d'un appel par le serveur
			$params:=This.dossierLogs
			$params.functionID:="RequeterDossierLogsWebLocal"
			This[This.dossierLogs.methodeReq]($params)
			// le resultat est dans .reqRetour ; peut être null si pb serveur
			
			This.Logs:=Null
			If ($params.reqRetour#Null)
				This.Logs:=$params.reqRetour.Logs
			End if 
	End case 
	
	
Function EnregistrerLogs($logs : Object)
	// importer dans la BDD les logs de $logs
	var $log : Text
	var $compteur : Integer
	
	$compteur:=0
	Case of 
		: (This.nomFichier="")
			// il y a eu une erreur (est traitée)
			
		: (Not(OB Is defined($logs; "contenu")))
			// oops
		: ($logs.contenu.length=0)
			This.trace.Créer(-15068; Current method name; "Le fichier '"+$logs.nomFichier+"' est vide").LeverException([msgk_event; msgk_log])
		Else 
			
			For each ($log; $logs.contenu)
				// analyser chaque ligne du fichier
				
				Case of 
					: ($log="#Remark: format@")
						// on a le format du journal
						If (("LOG_"+Substring($log; Position("format "; $log; *)+Length("format ")))#This.FormatJournal)
							This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Import [KO]"; Current method name; "Le fichier <"+$logs.nomFichier+"> n'est pas au format attendu ("+This.FormatJournal+")")
							// dans cette version on abandonne
							break
						End if 
						
					: (Substring($log; 1; 7)="#Fields")
					: ($log="#@")  // autres entêtes
						// ne rien faire
						
					Else 
						// ok un bon log
						// récupérer les données de ce log
						This.LireLog($log)
						$compteur:=$compteur+Num(This.EnregistrerLog())
				End case 
			End for each 
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Logs de "+$logs.nomFichier+Choose($compteur>0; " [OK]"; " [KO]"); Current method name; Choose($compteur>0; String($compteur)+" logs traités"; "Aucun log traité")+" sur "+String($logs.length)+" lus")
	End case 
	
	
Function RequeterDossierLogsWebLocal($dossierLogs : Object)
	// renvoyer le contenu des fichiers logs du dossier logs courant
	// attention ici on est sur la BDD mère ou le serveur
	var $data : Object
	var $nomDossier : Text:=""
	
	// lire le dossier des logs
	$data:=This.getDossierLogs($dossierLogs)
	
	This.fichiersLogs:=New collection
	Case of 
		: ($data.dossier=Null)
		: (Not(cs.xSDK.ResourceALV.me.SetVariable(Est Ressource JLog; "DossierLogsWeb"; Is text; ->$nomDossier)))
			// pb quelque part
			
		: (OB Is defined($dossierLogs; "sousDossier"))
			This.fichiersLogs:=$data.dossier.folder($dossierLogs.sousDossier).folder($nomDossier).files(fk ignore invisible)
			
		Else 
			This.fichiersLogs:=$data.dossier.folder($nomDossier).files(fk ignore invisible)
	End case 
	If ($data.dossier=Null)
		$data.dossier:="Dossier inconnu"
	Else 
		$data.dossier:=$data.dossier.platformPath
	End if 
	
	// sélectionner les fichiers logs
	//This.FiltrerFichiers()
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; String(This.fichiersLogs.length)+" fichiers trouvés à traiter "+Choose(This.fichiersLogs.length>0; "[OK]"; "[KO]"); Current method name; "Dans le dossier <"+$data.dossier+">")
	
	// mettre les fichiers au format d'import dans un dossier de travail
	This.FormaterFichiers()
	// récupérer le contenu pour une utilisation locale
	$data.Logs:=This.LireLogs()
	// supprimer le dossier de travail
	This.dossierLogsTempo.delete(Delete with contents)
	// retourner le résultat
	$dossierLogs.reqRetour:=$data
	
	
Function LireLog($log : Text)
	// décoder la ligne $log et créer la collection de champs trouvés dans this.Logd
	var $champs : Collection
	var $champ : Text
	var $indiceChamp : Integer
	
	// trouver tous les $champs (des champs entiers ou des morceaux de champ)
	$champs:=This.ExtaireLesChamps($log)
	
	Case of 
		: ($champs=Null)
			This.trace.Créer(-16108; Current method name; "Erreur de parsing du log "+$log).LeverException([msgk_event; msgk_log])
			
		: ($champs.length=0)
		Else 
			// c'est ok
			This.Log:=New collection
			// indice de progression de lecture des champs
			$indiceChamp:=0
			
			For each ($champ; $champs)
				This.AjouterChamp($champ; ->$indiceChamp)
			End for each 
	End case 
	
	
Function AjouterChamp($mot : Text; $indiceChamp : Pointer)
	var $champ : Object
	
	Case of 
		: ($indiceChamp->>This.ParamsChamps.length)
			// bug dans la trace : TIME apparait 2 fois ; filtrer ce cas
			This.trace.Créer(-16999; Current method name; "trop de champs dans la trace ELF").LeverException([msgk_event; msgk_log])
			
		Else 
			$champ:=New object
			$champ.Champ:=This.ParamsChamps[$indiceChamp->].champ
			$champ.typeValeur:=This.ParamsChamps[$indiceChamp->].type
			$champ.Valeur:=$mot
			
			This.Log.push($champ)
			// pour la suite
			$indiceChamp->:=$indiceChamp->+1
	End case 
	
	
	// ----------------------
	// MARK:Format ELF
	// ----------------------
	
	
Function FormaterFichiers()
	var $c : Collection
	var $fichier; $fichierCopie : 4D.File
	
	This.dossierLogsTempo:=Folder(fk documents folder).folder("tempo_"+Current method name)
	This.dossierLogsTempo.create()
	
	If (This.fichiersLogs.length>0)
		This.fichiersLogs:=This.fichiersLogs.orderBy("fullName asc")
		$c:=New collection
		
		For each ($fichier; This.fichiersLogs)
			$fichierCopie:=$fichier.copyTo(This.dossierLogsTempo; fk overwrite)
			This.FormaterFichier($fichierCopie)
			$c.push($fichierCopie)
		End for each 
		
		This.fichiersLogs:=$c
	End if 
	
	
Function FormaterFichier($fichier : 4D.File)
	var $texte; $oldValeur; $newValeur : Text
	var $indexDebut; $indexFin : Integer
	
	$texte:=$fichier.getText("UTF-8"; Document with CR)
	// remplacer {retourchariot par {
	$texte:=Replace string($texte; "{"+Char(Carriage return); "{")
	$texte:=Replace string($texte; Char(Carriage return)+"}"; "}")
	
	// rappel : le blanc est le caractère de séparation de valeurs de champs
	// => remplacer les blancs dans les valeurs
	$indexDebut:=0
	Repeat 
		$indexDebut:=Position(Char(Double quote); $texte; $indexDebut)
		$indexFin:=Position(Char(Double quote); $texte; $indexDebut+1)
		$oldValeur:=Substring($texte; $indexDebut; $indexFin-$indexDebut+1)
		
		Case of 
			: ($indexDebut=0)
				// c'est fini
				$indexDebut:=-1
				
			: ($indexFin=0)
				// fin de la valeur du champ
				$indexDebut:=-1
				
			: (Split string($oldValeur; " ").length<2)
				// pas de blanc dans la valeur, ne rien faire
				$indexDebut:=$indexFin+1
				
			Else 
				// remplacer
				$newValeur:=Replace string($oldValeur; " "; Char(160))
				$texte:=Replace string($texte; $oldValeur; $newValeur)
				
				$indexDebut:=$indexFin+1
		End case 
	Until ($indexDebut=-1)
	
	// idem avec 
	$indexDebut:=0
	Repeat 
		$indexDebut:=Position(Char(Quote); $texte; $indexDebut)
		$indexFin:=Position(Char(Quote); $texte; $indexDebut+1)
		$oldValeur:=Substring($texte; $indexDebut; $indexFin-$indexDebut+1)
		
		Case of 
			: ($indexDebut=0)
				// c'est fini
				$indexDebut:=-1
				
			: ($indexFin=0)
				// fin de la valeur du champ
				$indexDebut:=-1
				
			: (Split string($oldValeur; " ").length<2)
				// pas de blanc dans la valeur, ne rien faire
				$indexDebut:=$indexFin+1
				
			Else 
				// remplacer
				$newValeur:=Replace string($oldValeur; " "; Char(160))
				$texte:=Replace string($texte; $oldValeur; $newValeur)
				
				$indexDebut:=$indexFin+1
		End case 
	Until ($indexDebut=-1)
	
	$fichier.setText($texte; "UTF-8"; Document with CR)
	
	
Function ExtaireLesChamps($log)->$result : Collection
	// traiter les trucs zarbis
	var $champs : Collection
	
	$result:=Null
	
	// pb avec 4D Mobile
	// remplacer 2 blancs par un blanc
	$log:=Replace string($log; "  "; " ")
	
	// remplacer les champs vides par un null
	$log:=Replace string($log; Char(Double quote)+Char(Double quote); "NULL")
	
	// trouver tous les mots (des champs entiers ou des morceaux de champ)
	$champs:=Split string($log; " "; sk trim spaces)
	// recomposer les champs complexes (qui ont explosé dans le split)
	$champs:=This.TraiterAgentAgent($champs)
	$champs:=This.TraiterURI($champs)
	
	// ici 15 champs ; le champ time est double, en supprimer un
	$result:=$champs.remove($champs.length-2)
	
	If ($result.length#14)
		// autre pb ? 
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Log non standard"; Current method name; $champs.join(" "))
		$result:=Null
	End if 
	// pour être compatibles des functions communes, il faut un TypeApp
	$result.push(String(This.dossierLogs.TypeApp))
	
	
Function TraiterAgentAgent($champs : Collection)->$result : Collection
	// le 'user-agent' démarre au champ 5 jusqu'à la méthode
	var $indice : Integer
	var $c : Collection
	var $champ; $texte : Text
	
	$indice:=-1
	For each ($champ; New collection("GET"; "POST"; "HEAD")) While ($indice=-1)
		$indice:=$champs.indexOf($champ)
	End for each 
	
	Case of 
		: ($champs.length=14)
		: ($champs.length=15)
			// cas nominal ; $indice vaut 7 (non testé)
			
		: ($indice=-1)
			// log sans 'user-agent'
		Else 
			// reconstituer le 'user-agent'
			// il faut concatener entre les indices 5 et $indice -1
			$c:=$champs.slice(5; $indice-1)
			// créer le champ complet
			$texte:=$c.join(" ")
			// purger $champs
			$champs:=$champs.remove(5; $c.length)
			// replacer dans $champs
			$champs:=$champs.insert(5; $texte)
	End case 
	// encoder
	$champs[5]:=This.EncoderBase64($champs[5])
	$result:=$champs.copy()
	
	
Function TraiterURI($champs : Collection)->$result : Collection
	// le 'user-agent' démarre au champ 10 jusqu'au champ .length-4 (= 11 dans le cas normal
	var $c : Collection
	var $texte : Text
	
	Case of 
		: ($champs.length=14)
			// cas nominal ; $indice vaut 7 (non testé)
			
		: ($champs.length<16)
			// quoi?
			
		Else 
			// reconstituer le 'user-agent'
			$c:=$champs.slice(10; $champs.length-4)
			// créer le champ complet
			$texte:=$c.join(" ")
			// purger $champs
			$champs:=$champs.remove(10; $c.length)
			// replacer dans $champs
			$champs:=$champs.insert(10; $texte)
	End case 
	// encoders
	$champs[10]:=This.EncoderBase64($champs[10])
	$result:=$champs.copy()
	
    

[class]$LogsAppEditeur - 04/01/2026 10:59:08

      property EnvALV : cs.xSDK.EnvironnementALV
property functionID; nomOBJ : Text
property LogsApp : Collection

Class extends $LogsApp

Class constructor()
	
	Super()
	
	This.EnvALV:=cs.xSDK.EnvironnementALV.new()
	
	
Function OuvrirFormulaire($params : Object)
	// ouvrir l'éditeur de la BDD des journaux
	//var $params : Object
	var $numProc : Integer
	
	//$params:=New object
	$params.functionID:="_OuvrirFormulaire_process"
	$params.numProcessAppelant:=-1  // dans un process externe
	$params.nomProcess:="U_Nav?LogsApp"
	$params.nomTache:="EditionJAPP"
	$params.nomFormulaire:="U_Nav LogsApp"
	$numProc:=Exécuter Function Coopérative(cs.$LogsAppEditeur; $params)
	
	
	// ----------------------
	//MARK:FORM Events
	// ----------------------
	
Function _TraiterFORMevent()
	
	If (OB Is defined(FORM Event; "objectName"))
		// event d'un objet formulaire
		This.functionID:="_fct_"+FORM Event.objectName
		This.nomOBJ:=FORM Event.objectName
		
	Else 
		// event du formulaire
		This.functionID:="_fct_Formulaire"
		This.nomOBJ:=""
	End if 
	
	If (OB Is defined(This; This.functionID))
		This[This.functionID]()
	End if 
	
	
Function _fct_Formulaire()
	Case of 
		: (Form event code=On Load)
			SHOW PROCESS(Current process)
			
			This.onEndLoad()
			This.MettreAjourImport()
			
		: (Form event code=On Deactivate)
			This.FermerBDD_Externe()
			
		: (Form event code=On Close Box)
			CANCEL
	End case 
	
	
Function onEndLoad()
	var $c : Collection:=["PopupListeApps"]
	var $nomOBJ : Text
	
	For each ($nomOBJ; $c)
		If (OB Is defined(This; "_fct_"+$nomOBJ))
			This.nomOBJ:=$nomOBJ
			This["_fct_"+$nomOBJ]()
		End if 
	End for each 
	
	
Function MettreAjourImport()
	// mettre à jour la BDD avec les fichiers récents (non importés)
	var $data : Object
	
	// dans un worker
	$data:=New object("params"; New object)
	$data.params.wndRef:=Current form window
	$data.params.functionIDcallBack:="TerminerAction"
	
	$data.execute:=Formula(cs.$LogsAppImport.new().MettreAjourImport(This.params))
	CALL WORKER("WK_Composant_JLOG"; Formula($data.execute()))
	
	This.FixerEtatOccupé(True)
	
	
Function _fct_LogsApp()
	Case of 
		: (Form event code=On Load)
			This.FormaterTypeAPP()
			This.FormaterDescription()
	End case 
	
	
Function _fct_PopupListeApps()
	var $data : Object
	
	Case of 
		: (Form event code=On Load)
			// lister les App
			This.FixerParametresDossiersLogs()
			// This.dossiersLogs est renseigné. Sélectionner les dossiers à importer
			This.dossiersLogs:=This.dossiersLogs.query("typeApplicationEditeur != :1"; Null).orderBy("nomApplicationEditeur asc")
			
			Form[This.nomOBJ]:=New object
			Form[This.nomOBJ].values:=This.dossiersLogs.extract("nomApplicationEditeur")
			Form[This.nomOBJ].index:=-1
			Form[This.nomOBJ].currentValue:=Choose(This.dossiersLogs.length=0; Localized string("27"); Localized string("21"))
			
		: (Form event code=On Data Change)
			
			Case of 
				: (Form[This.nomOBJ].index=-1)
					// rien de sélectionner
				: (This.estOccupé())
					// action en cours
				Else 
					// lister les logsApp du serveur sélectionné
					// dans un worker
					$data:=New object("params"; New object)
					$data.params:=OB Copy(This.dossiersLogs[This[This.nomOBJ].index])
					$data.params.wndRef:=Current form window
					$data.params.functionIDcallBack:="AfficherSélection"
					
					$data.execute:=Formula(cs.$LogsAppEditeur.new().ToutSelectionner(This.params))
					CALL WORKER("WK_Composant_JLOG"; Formula($data.execute()))
					
					This.FixerEtatOccupé(True)
			End case 
	End case 
	
	
Function FormaterTypeAPP()->$result : Text
	var $typeAPP : Object
	
	$result:=""
	If (Form.LogCourant#Null)
		$typeAPP:=This.EnvALV.Applications.query("type = :1"; Form.LogCourant.TypeApplication).first()
		$result:=$typeAPP.nomLong
	End if 
	
	
Function FormaterDescription()->$result : Text
	// mettre en forme les textes JSON
	var $i : Integer
	var $objet : Object
	
	$result:=""
	If (Form.LogCourant#Null)
		$i:=Position("{"; Form.LogCourant.Description)
		
		If ($i>0)
			$objet:=JSON Parse(Substring(Form.LogCourant.Description; $i))
			$result:=Substring(Form.LogCourant.Description; 1; $i-1)+Char(Carriage return)+JSON Stringify($objet; *)
			
		Else 
			$result:=Form.LogCourant.Description
		End if 
	End if 
	
	
	// ----------------------
	// MARK:Actions
	// -----------------------
	
Function AfficherSélection($params : Object)
	// retour d'un call à un worker
	// méthode appelée dans le formulaire
	var $data; $log : Object
	
	$data:=This.DécoderBase64($params.result)
	
	// alimenter le formulaire
	// fixer les titres de la LB LogsApp
	Case of 
		: (Not(OB Is defined(Form; "ParamsChamps")))
		: (Form.ParamsChamps=Null)
		Else 
			For each ($params; Form.ParamsChamps)
				OBJECT SET TITLE(*; "LogsApp"+$params.libelle; $params.entete)
			End for each 
	End case 
	
	OBJECT SET VISIBLE(*; "LogsApp"; False)
	Case of 
		: (Not(OB Is defined($data; "LogsApp")))
		: ($data.LogsApp=Null)
		Else 
			// décoder les descriptions
			For each ($log; $data.LogsApp)
				$log.Description:=This.DécoderBase64($log.Description)
			End for each 
			
			// trier par date
			$data.LogsApp:=$data.LogsApp.orderBy("Date asc")
			// afficher
			Form.LogsApp:=$data.LogsApp
			OBJECT SET VISIBLE(*; "LogsApp"; Form.LogsApp.length>0)
	End case 
	This.TerminerAction()
	
	
Function ToutSelectionner($params : Object)
	// afficher tous les logs
	// méthode appelée dans un worker
	
	This.InitProcess()
	
	// initialiser les variables process pour les sélection SQL
	ARRAY TEXT(FICHIER; 0)
	ARRAY LONGINT(TYPEAPP; 0)
	ARRAY TEXT(ORIGINE; 0)
	ARRAY TEXT(DATE_GMT_LOG; 0)
	ARRAY TEXT(NOM_PROCESS; 0)
	ARRAY TEXT(NOM_FUNCTION; 0)
	ARRAY TEXT(LIBELLE; 0)
	ARRAY TEXT(DESCRIPTION; 0)
	
	// les lister
	This.LireLogsEnBDD($params)
	
	// retourner la liste
	This.postCallBack($params)
	
	
Function LireLogsEnBDD($params : Object)
	// requête SQL de la BDD ; résultat dans $params.reqRetour
	var $typeApplication : Integer
	
	This.OuvrirBDD_Log()
	
	This.LogsApp:=New collection
	For each ($typeApplication; $params.typeApplicationEditeur)
		Super.LireLogsEnBDD($typeApplication; This.LogsApp)
	End for each 
	
	This.FermerBDD_Externe()
	
	// renvoyer le résultat
	$params.reqRetour:=New object
	$params.reqRetour.LogsApp:=This.LogsApp
	
	
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.$LogsAppEditeur.new()[$params.functionIDcallBack](This.params))
	CALL FORM($params.wndRef; Formula($data.execute().source))
	
    

[class]_Trace - 03/06/2025 11:20:44

      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("JLOG"; Error; Error method; Error line; Error formula)
	
	
Function Initialiser($nomMethode : Text)->$result : Object
	This.cible.CréerErreur("JLOG"; 0; $nomMethode; "")
	$result:=This
	
	
Function Créer($Error : Integer; $nomMethode : Text; $ErrorDescription : Text)->$result : Object
	This.cible.CréerErreur("JLOG"; $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; "JLOG"; $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; "JLOG"; $libellé; $source; $description)
	
	
    

[class]$LogsWebEditeur - 19/01/2026 11:34:08

      property URL_geoIP : Text:="https://fr.geoipview.com/"
property functionID; nomOBJ : Text
property LogsWeb : Collection

Class extends $LogsWeb

Class constructor()
	
	Super()
	
	This.LogsWeb:=Null
	
	
	// ----------------------
	// MARK:Formulaire
	// -----------------------
	
Function OuvrirFormulaire($params : Object)
	// ouvrir l'éditeur de la BDD des journaux
	var $numProc : Integer
	
	$params.functionID:="_OuvrirFormulaire_process"
	$params.numProcessAppelant:=-1  // dans un process externe
	$params.nomProcess:="U_Nav?LogsWeb"
	$params.nomTache:="EditionJWEB"
	$params.nomFormulaire:="U_Nav LogsWeb"
	$numProc:=Exécuter Function Coopérative(cs.$LogsWebEditeur; $params)
	
	
	// ----------------------
	//MARK:FORM Events
	// ----------------------
	
Function _TraiterFORMevent()
	
	If (OB Is defined(FORM Event; "objectName"))
		// event d'un objet formulaire
		This.functionID:="_fct_"+FORM Event.objectName
		This.nomOBJ:=FORM Event.objectName
		
	Else 
		// event du formulaire
		This.functionID:="_fct_Formulaire"
		This.nomOBJ:=""
	End if 
	
	If (OB Is defined(This; This.functionID))
		This[This.functionID]()
	End if 
	
	
Function _fct_Formulaire()
	Case of 
		: (Form event code=On Load)
			SHOW PROCESS(Current process)
			
			This.onEndLoad()
			This.MettreAjourImport()
			
			// 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 
	
	
Function onEndLoad()
	var $c : Collection:=["PopupListeServeurs"; "ZoneWeb"]
	var $nomOBJ : Text
	
	For each ($nomOBJ; $c)
		If (OB Is defined(This; "_fct_"+$nomOBJ))
			This.nomOBJ:=$nomOBJ
			This["_fct_"+$nomOBJ]()
		End if 
	End for each 
	
	
Function MettreAjourImport()
	// mettre à jour la BDD avec les fichiers récents (non importés)
	var $data : Object
	
	// dans un worker
	$data:=New object("params"; New object)
	$data.params.wndRef:=Current form window
	$data.params.functionIDcallBack:="TerminerAction"
	
	$data.execute:=Formula(cs.$LogsWebImport.new().ImporterLogs(This.params))
	CALL WORKER("WK_Composant_JLOG"; Formula($data.execute()))
	
	This.FixerEtatOccupé(True)
	
	
Function _fct_PopupListeServeurs()
	var $data : Object
	
	Case of 
		: (Form event code=On Load)
			// lister les serveurs Web
			This.FixerParametresDossiersLogs()
			// This.dossiersLogs est renseigné. Sélectionner les dossiers à importer
			This.dossiersLogs:=This.dossiersLogs.query("typeApplicationEditeur != :1"; Null)
			
			Form[This.nomOBJ]:=New object
			Form[This.nomOBJ].values:=This.dossiersLogs.extract("nomApplicationEditeur")
			Form[This.nomOBJ].index:=-1
			Form[This.nomOBJ].currentValue:=Choose(This.dossiersLogs.length=0; Localized string("4"); Localized string("1"))
			
		: (Form event code=On Data Change)
			
			Case of 
				: (Form[This.nomOBJ].index=-1)
					// rien de sélectionner
				: (This.estOccupé())
					// action en cours
				Else 
					// lister les logsWeb du serveur sélectionné
					// dans un worker
					$data:=New object("params"; New object)
					$data.params:=OB Copy(This.dossiersLogs[This[This.nomOBJ].index])
					$data.params.wndRef:=Current form window
					$data.params.functionIDcallBack:="AfficherSélection"
					
					$data.execute:=Formula(cs.$LogsWebEditeur.new().ToutSelectionner(This.params))
					CALL WORKER("WK_Composant_JLOG"; Formula($data.execute().source))
					
					This.FixerEtatOccupé(True)
			End case 
	End case 
	
	
Function _fct_LogsWeb()
	var $i; $j : Integer
	var $attribut : Text
	var $data : Object
	var $c : Collection
	
	Case of 
		: ((Right click) | (Contextual click))  // Clic droit ou Control+clic
			// gestion des modifications
			// log sélectionnée = Form.LogCourant
			
			// créer le popMenu
			$c:=New collection(Localized string("3003"); Localized string("3002"))
			// ajouter les menus contextuels
			If (Form.LogCourant#Null)
				// lire la ligne sélectionnée
				LISTBOX GET CELL POSITION(*; "LogsWeb"; $j; $i)  // colonne, ligne
				
				// colonne sélectionnée -> attribut de Form.LogCourant
				LISTBOX GET OBJECTS(*; "LogsWeb"; $tabObjetsLB)
				$attribut:=Replace string($tabObjetsLB{($j-1)*3+2}; "LogsWeb"; "")
				// menu lié à cette ligne
				$c.unshift(Localized string("3001")+Form.LogCourant[$attribut]+Localized string("3")+Char(Double quote)+$attribut+Char(Double quote))
			End if 
			
			$j:=Pop up menu($c.join(";"))
			// attention, menu dynamique, dans l'ordre :
			Case of 
				: ($j=$c.length)
					// re initiliser la liste
					$data:=New object("params"; New object)
					$data.params:=OB Copy(This.dossiersLogs[Form.PopupListeServeurs.index])
					$data.params.wndRef:=Current form window
					$data.params.functionIDcallBack:="AfficherSélection"
					
					$data.execute:=Formula(cs.$LogsWebEditeur.new().ToutSelectionner(This.params))
					CALL WORKER("WK_Composant_JLOG"; Formula($data.execute().source))
					
					Form.FixerEtatOccupé(True)
					
					
				: ($j=($c.length-1))
					CONFIRM("Supprimer les "+String(This.LogsWeb.length)+" logs de la sélection courante"; "Annuler"; "OK")
					// attention : le btn par defaut est Annuler
					If (ok=0)
						// supprimer de la BDD la sélection courante
						$data:=New object
						$data.sélection:=Form.LogsWeb.extract("ID")
						$data.wndRef:=Current form window
						$data.functionIDcallBack:="AfficherSélection"
						
						$data.execute:=Formula(cs.$LogsWebEditeur.new().SupprimerSelection($data))
						CALL WORKER("WK_Composant_JLOG"; Formula($data.execute().source))
						
						//CALL WORKER("WK_Composant_JLOG"; Formula from string("cs.$LogsWebEditeur.new().SupprimerSélection($1)"); $data)
						Form.FixerEtatOccupé(True)
					End if 
					
				: ($j=1)
					// chercher dans la sélection courante la valeur de la ligne / colonne du clic 
					Form.LogsWeb:=Form["LogsWeb"].query($attribut+" = :1"; Form.LogCourant[$attribut])
			End case 
			
		: (Form event code=On Clicked)
			WA OPEN URL(*; "zoneWeb"; Form.URL_geoIP+"?q="+Form.LogCourant.Adress_IP+"&x=0&y=0")
	End case 
	
	
Function _fct_ZoneWeb()
	Case of 
		: (Form event code=On Load)
			WA OPEN URL(*; Form.nomOBJ; Form.URL_geoIP)
	End case 
	
	
	// ----------------------
	// MARK:Actions
	// -----------------------
	
Function AfficherSélection($params : Object)
	// retour d'un call à un worker
	// méthode appelée dans le formulaire
	var $data; $log : Object
	
	$data:=This.DécoderBase64($params.result)
	
	// alimenter le formulaire
	// fixer les titres de la LB LogsWeb
	Case of 
		: (Not(OB Is defined(Form; "ParamsChamps")))
		: (Form.ParamsChamps=Null)
		Else 
			For each ($params; Form.ParamsChamps)
				OBJECT SET TITLE(*; "LogsWeb"+$params.libelle; $params.entete)
			End for each 
	End case 
	
	OBJECT SET VISIBLE(*; "LogsWeb"; False)
	Case of 
		: (Not(OB Is defined($data; "LogsWeb")))
		: ($data.LogsWeb=Null)
		Else 
			// décoder les champss complexes
			For each ($log; $data.LogsWeb)
				$log.Agent:=This.DécoderBase64($log.Agent)
				$log.URI:=This.DécoderBase64($log.URI)
			End for each 
			
			// trier par date
			$data.LogsWeb:=$data.LogsWeb.orderBy("Date asc")
			// afficher
			Form.LogsWeb:=$data.LogsWeb
			OBJECT SET VISIBLE(*; "LogsWeb"; Form.LogsWeb.length>0)
	End case 
	This.TerminerAction()
	
	
Function ToutSelectionner($params : Object)
	// afficher tous les logs
	// méthode appelée dans un worker
	
	This.InitProcess()
	
	// initialiser les variables process pour les sélection SQL
	ARRAY TEXT(FICHIER; 0)
	ARRAY LONGINT(BYTES_SENT; 0)
	ARRAY TEXT(ADRESS_IP; 0)
	ARRAY TEXT(COOKIE; 0)
	ARRAY TEXT(HOST_NAME; 0)
	ARRAY TEXT(URL_PAGE; 0)
	ARRAY TEXT(USER_AGENT; 0)
	ARRAY TEXT(USER_NAME; 0)
	ARRAY TEXT(HTTP_REQUEST; 0)
	ARRAY TEXT(ADRESS_IP_SERVER; 0)
	ARRAY LONGINT(SERVER_STATUS; 0)
	ARRAY TEXT(URI; 0)
	ARRAY TEXT(URI_QUERY; 0)
	ARRAY TEXT(URI_STEM; 0)
	ARRAY DATE(DATE_GMT_CONNEXION; 0)
	ARRAY TEXT(TIME_GMT; 0)
	ARRAY LONGINT(DELAY_SERVER; 0)
	ARRAY LONGINT(TYPEAPP; 0)
	
	// les lister
	This.LireLogsEnBDD($params)
	
	// retourner la liste
	This.postCallBack($params)
	
	
Function LireLogsEnBDD($params : Object)
	// requête SQL de la BDD ; résultat dans $params.reqRetour
	var $typeApplication : Integer
	
	This.OuvrirBDD_Log()
	
	This.LogsWeb:=New collection
	For each ($typeApplication; $params.typeApplicationEditeur)
		Super.LireLogsEnBDD($typeApplication; This.LogsWeb)
	End for each 
	
	This.FermerBDD_Externe()
	
	// renvoyer le résultat
	$params.reqRetour:=New object
	$params.reqRetour.LogsWeb:=This.LogsWeb
	
	
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.$LogsWebEditeur.new()[$params.functionIDcallBack](This.params))
	CALL FORM($params.wndRef; Formula($data.execute().source))
	
	
Function SupprimerSelection($params : Object)
	// supprimer la sélection courante de log et afficher ce qui reste
	// méthode appelée dans un worker
	ALERT("TODO delete dans BDD (dans cette version, on ne touche pas aux fichiers origine)")
	//This.InitProcess()
	
	//$params.functionID:="SupprimerLogs"
	//This.Exécuter($params)
	//This.trace.Créer($params.reqRetour.Error; $params.functionID; $params.reqRetour.ErrorDescription).LeverException([msgk_event; msgk_log])
	
	// afficher ce qui reste
	This.ToutSelectionner($params)
	
	
Function Exécuter($params : Object)
	// exécuter la requête en local ou sur le serveur sélectionné, 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 
	
    

[class]$LogsWeb - 18/02/2026 17:07:42

      
Class extends _BDD

Class constructor()
	
	Super()
	
	// fichier des paramètres de la BDD
	This.FichierRessources:="Data_LOG_ELF.json"
	// paramètres du journal
	This.FormatJournal:="LOG_ELF"
	// lire les paramètres de champs
	This.getFormatBDD()
	
	
Function FixerParametresDossiersLogs()
	// chaque élément de la liste contient les paramètres de lecture d'un dossier Log (Serveur Web hôte, Serveur Web)
	
	This.dossiersLogs:=New collection
	// toujours disponibles
	This.dossiersLogs.push(New object("TypeApp"; ALV Serveur APP; "typeApplicationImport"; ALV Serveur APP; "methodeReq"; "RequeterDossierLogsServeur"; "typeApplicationEditeur"; [ALV Serveur APP]; "nomApplicationEditeur"; Localized string("38")))
	This.dossiersLogs.push(New object("TypeApp"; ALV Serveur APP; "typeApplicationImport"; ALV Serveur APP; "methodeReq"; "RequeterDossierLogsServeur"; "typeApplicationEditeur"; [ALV Serveur APP]; "nomApplicationEditeur"; Localized string("39"); "sousDossier"; "ALV Serveur Web"))
	// non accessible hors base hôte
	If (Storage.System.estExecuteDansHote)
		This.dossiersLogs.push(New object("TypeApp"; ALV BDD mère; "typeApplicationImport"; ALV BDD mère; "methodeReq"; "RequeterDossierLogsWebLocal"; "typeApplicationEditeur"; [ALV BDD mère]; "nomApplicationEditeur"; Localized string("40")))
	End if 
	
	// pour les tests
	This.AjouterDossierDeTest(This.dossiersLogs)
	
	// ordonner
	This.dossiersLogs:=This.dossiersLogs.orderBy("nomApplicationEditeur asc")
	
	
Function AjouterDossierDeTest($c : Collection)
	// ajouter le dossier pour les tests
	
	If ((Not(Storage.System.estExecuteDansHote)) | Storage.Host.$application.call(Null).estDebugAPP())
		// pour les tests depuis la BDD mère ou le composant
		$c.push(New object("TypeApp"; 100; "typeApplicationImport"; 100; "methodeReq"; "RequeterDossierLogsWebLocal"; "typeApplicationEditeur"; [100]; "nomApplicationEditeur"; Localized string("41"); "nomDossierTest"; "Test JWEB"))
	End if 
	
	
	// ----------------------
	// MARK:Requêtes SQL
	// ----------------------
	
Function OuvrirBDD_Log()->$chemin : Text
	var $sql_BDDpath : Text
	
	// fixer le nom de la BDD externe ouvrir
	$sql_BDDpath:=This.getDossierBDD_Log("DossierJournalWeb"; "test JWEB")+"LogsWeb"
	$chemin:=This.OuvrirBDD_Externe($sql_BDDpath)
	
	
Function SupprimerLogs($params : Object)
	var $ID : Integer
	var $result : Object
	
	This.nomServeurWeb:=$params.nomServeurWeb
	This.OuvrirBDD_Log()
	
	$result:=This.InitResult()
	$result.Error:=-15068
	Case of 
		: (Not(OB Is defined($params; "sélection")))
			$result.ErrorDescription:="'sélection' n'est pas défini dans $params"
		: (Value type($params.sélection)#Is collection)
			$result.ErrorDescription:="'$params.sélection' n'est pas une collection"
		: ($params.sélection.length=0)
			$result.ErrorDescription:="'$params.sélection' est une collection vide"
		Else 
			$result.Error:=0
			$result.ErrorDescription:=String($params.sélection.length)+" enregistrements détruits"
			
			// on fait bourin (pas de DELETE par un $tab)
			For each ($ID; $params.sélection)
				Begin SQL
					DELETE FROM LOGS WHERE ID = :$ID;
				End SQL
				
				If (ok=0)
					$result.Error:=$result.Error+1
					$result.ErrorDescription:=String($result.Error)+" enregistrements non détruits sur "+String($params.sélection.length)
				End if 
			End for each 
	End case 
	$result.success:=($result.Error=0)
	$params.reqRetour:=$result
	
	This.FermerBDD_Externe()
	
    

[class]$LogsAppImport - 04/01/2026 17:27:19

      property dossierLogs : Object

Class extends $LogsApp

Class constructor()
	
	Super()
	
	
	
Function MettreAjourImport($params : Object)
	This.NettoyerBDD()
	This.ImporterLogs($params)
	
	
	// ----------------------
	// MARK:Import fichiers Logs
	// ----------------------
	
Function ImporterLogs($params : Object)
	// mettre à jour la BDD avec les nouveaux fichiers logs
	var $dossierLogs : Object
	var $logs : Object
	
	This.OuvrirBDD_Log()
	// ici la BDD est ouverte, les tables et champs sont créés et This.ParamsChamps est renseignés 
	
	// Sélectionner les dossiers à importer
	This.FixerParametresDossiersLogs()
	// This.dossiersLogs est renseigné.
	This.dossiersLogs:=This.dossiersLogs.query("typeApplicationImport != :1"; Null)
	
	For each ($dossierLogs; This.dossiersLogs)
		// importer les logs des fichiers du dossier this.dossierLogs
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage import des fichiers"; Current method name; "Fichiers logs de l'application "+String($dossierLogs.nomApplication))
		
		This.dossierLogs:=$dossierLogs
		This.LireFichiersLogs()
		// $logs contient pour chaque fichier son nom et sa collection de logs 
		
		Case of 
			: (This.Logs=Null)
				// pb serveur
			: (This.Logs.length=0)
			Else 
				Begin SQL
					START TRANSACTION;
				End SQL
				
				For each ($logs; This.Logs)
					
					If (Not(This.EnregistrementFichierExiste($logs)))
						// le fichier n'existe pas ; l'importer
						This.nomFichier:=$logs.nomFichier
						
						This.EnregistrerLogs($logs)
						
						This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Import [OK]"; Current method name; "Fichier '"+This.nomFichier+"' importé en BDD")
					End if 
				End for each 
				
				Begin SQL
					COMMIT TRANSACTION;
				End SQL
		End case 
		
		This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Fin import des fichiers"; Current method name; "Fichiers logs de l'application "+String($dossierLogs.nomApplication))
	End for each 
	
	This.FermerBDD_Externe()
	
	// retour au requêteur ; en mode debug $params peut être absent
	If (Count parameters>0)
		$params.execute:=Formula(cs.$LogsAppEditeur.new()[$params.functionIDcallBack]())
		CALL FORM($params.wndRef; Formula($params.execute()))
	End if 
	
	
Function LireFichiersLogs()
	// lire les logs des fichiers Logs du dossier $dossierLogs
	var $params : Object
	
	This.Logs:=New collection
	Case of 
		: (Not(OB Is defined(This.dossierLogs; "methodeReq")))
			// erreur initialisation
		: (Not(OB Is defined(This; This.dossierLogs.methodeReq)))
			// erreur méthode de requête
		Else 
			// lancer la lecture de tous ces fichiers
			// il faut toujours un paramètre (compatibilité avec le cas d'un appel par le serveur
			$params:=This.dossierLogs
			$params.functionID:="RequeterDossierLogsAppLocal"
			This[This.dossierLogs.methodeReq]($params)
			// le resultat est dans .reqRetour ; peut être null si pb serveur
			
			This.Logs:=Null
			If ($params.reqRetour#Null)
				This.Logs:=$params.reqRetour.Logs
			End if 
	End case 
	
	
Function EnregistrerLogs($logs : Object)
	// importer dans la BDD les logs de $logs
	var $c : Collection
	var $log : Text
	var $compteur : Integer
	
	$compteur:=0
	Case of 
		: (This.nomFichier="")
			// il y a eu une erreur (est traitée)
			
		: (Not(OB Is defined($logs; "contenu")))
			// oops
		: ($logs.contenu.length=0)
			This.trace.Créer(-15068; Current method name; "Le fichier '"+$logs.nomFichier+"' est vide").LeverException([msgk_event; msgk_log])
		Else 
			
			For each ($log; $logs.contenu)
				// analyser chaque ligne du fichier
				
				$c:=Split string($log; Char(Tab))
				// ajouter le type d'application (en premier)
				$c.unshift(This.dossierLogs.typeApplicationImport)
				// filtrer les entêtes
				Case of 
					: ($log="")
					: ($log="Crée@")
					: ($c.length#This.ParamsChamps.length)
						// trop ou pas assez d'info
						This.trace.Créer(-15068; Current method name; Choose($c.length>This.ParamsChamps.length; "Trop "; "Pas assez ")+"d'informations dans le log '"+$log+"'").LeverException([msgk_event; msgk_log])
					Else 
						// ok un bon log
						// pour faire passer les "caractères spéciaux", transformer la description en blob encodé b64 (ne pas utiliser directement un blob, incompatible de .LireLog() )
						// RAPPEL : pas de "caractères spéciaux" dans le champ libellé (génère une Err SQL)
						$c[$c.length-1]:=This.EncoderBase64($c[$c.length-1])
						// crée les données à enregistrer
						This.LireLog($c)
						$compteur:=$compteur+Num(This.EnregistrerLog())
				End case 
			End for each 
			This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Logs de "+$logs.nomFichier+Choose($compteur>0; " [OK]"; " [KO]"); Current method name; Choose($compteur>0; String($compteur)+" logs lus et traités"; "Aucun log traité")+" sur "+String($logs.contenu.length)+" lus")
	End case 
	
	
Function LireLog($valeurs : Collection)
	// renseigner la valeur de chaque champ
	// attention .ParamsChamps et $valeurs doivent être 2 collections synchrones 
	var $i : Integer
	var $champ : Object
	
	This.Log:=New collection
	For ($i; 0; This.ParamsChamps.length-1)
		$champ:=New object
		$champ.Champ:=This.ParamsChamps[$i].champ
		$champ.typeValeur:=This.ParamsChamps[$i].type
		$champ.Valeur:=$valeurs[$i]
		
		This.Log.push($champ)
	End for 
	
	
Function RequeterDossierLogsAppLocal($dossierLogs : Object)
	// renvoyer le contenu des fichiers logs du dossier logs APP courant
	// attention ici on est sur la BDD mère ou le serveur
	var $data : Object
	
	// lire le dossier des logs
	$data:=This.getDossierLogs($dossierLogs)
	// lister les fichiers logs logs
	If ($data.dossier=Null)
		This.fichiersLogs:=New collection
	Else 
		This.fichiersLogs:=$data.dossier.files(fk ignore invisible)
	End if 
	// renseigner le chemin du dossier
	If ($data.dossier=Null)
		$data.dossier:="Dossier inconnu"
	Else 
		$data.dossier:=$data.dossier.platformPath
	End if 
	
	// sélectionner les fichiers logs
	This.FiltrerFichiers()
	This.trace.EnvoyerMessages([msgk_event; msgk_log]; String(This.fichiersLogs.length)+" fichiers trouvés à traiter "+Choose(This.fichiersLogs.length>0; "[OK]"; "[KO]"); Current method name; "Dans le dossier <"+$data.dossier+">")
	
	// récupérer le contenu pour une utilisation locale
	$data.Logs:=This.LireLogs()
	// retourner le résultat
	$dossierLogs.reqRetour:=$data
	
	
Function FiltrerFichiers()
	var $date : Text
	
	// sélectionner les fichiers logs
	This.fichiersLogs:=This.fichiersLogs.query("name = :1 and extension = :2"; "@.log"; ".txt").orderBy("name asc")
	// supprimer le fichier d'aujourd'hui
	$date:=String(Current date; ISO date)  // attention cf 4D : ce format n'est pas conforme à la norme ISO8601
	$date:=Substring($date; 1; 10)
	Case of 
		: (This.fichiersLogs.length=0)
		: (This.fichiersLogs[This.fichiersLogs.length-1].name#("@"+$date+"@"))
		Else 
			This.fichiersLogs.pop()
	End case 
	
	
	// ----------------------
	// MARK:Suppression Logs
	// ----------------------
	
Function NettoyerBDD()
	// supprimer de la BDD les logs dont les fichiers ne sont plus sur le disque
	var $dossierLogs : Object
	var $c : Collection
	var $nomFichier : Text
	
	This.InitProcess()
	
	This.OuvrirBDD_Log()
	// ici la BDD est ouverte, les tables et champs sont créés et This.ParamsChamps est renseignés 
	
	// Sélectionner les dossiers à en cours
	This.FixerParametresDossiersLogs()
	
	For each ($dossierLogs; This.dossiersLogs)
		
		This.dossierLogs:=$dossierLogs
		This.LireFichiersLogs()
		
		// lire les fichiers en BDD
		$c:=This.LireFichiers($dossierLogs)
		
		Case of 
			: ($c.length=0)
				// BDD vide
			: (This.fichiersLogs.length=0)
				// pas de fichiers logs
			: ($dossierLogs.typeApplicationImport=100)
				// pas toucher aux fichiers test
			Else 
				
				For each ($nomFichier; $c)
					If (This.fichiersLogs.indexOf($nomFichier)=-1)
						// ce fichier n'existe plus ; supprimer son contenu de la BDD
						This.SupprimerFichier($nomFichier)
						This.trace.EnvoyerMessages([msgk_event; msgk_log]; "Suppression fichier"; Current method name; "Suppression des logs du fichier "+$nomFichier)
					End if 
				End for each 
		End case 
	End for each 
	
	This.FermerBDD_Externe()
	Waiting(10)
    

[ ]U_Nav Masque - 08/06/2025 15:27:47

      Form._TraiterFORMevent()

    

[ ]U_Nav LogsApp - 08/06/2025 15:27:47

      Form._TraiterFORMevent()

    

[ ]U_Nav LogsWeb - 08/06/2025 15:27:37

      Form._TraiterFORMevent()

    

onStartup - 12/11/2025 08:23:53

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

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

cs.$composant.new().InitVariablesJLOG()


// pour exécution en locale 
Use (Storage.Host)
	Storage.Host.Session_Etat:=0
	Storage.Host.estDebugAPP:=Formula(Storage.Host.Session_Etat ?? 6)
End use 

// démarrer le worker
CALL WORKER("WK_Composant_JLOG"; Formula from string("cs.$composant.new().InitProcess()"))

    

onExit - 01/04/2025 19:52:03

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

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

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


    

onWebConnection - 04/04/2024 16:59:38

      Pas de code
    

onWebAuthentication - 07/12/2021 18:01:29

      Pas de code
    

onSystemEvent - 07/12/2021 18:01:29

      Pas de code
    

onHostDatabaseEvent - 06/02/2026 17:59:22

      #DECLARE($numEvent : Integer)
// Méthode base sur événement base hôte

Case of 
	: ($numEvent=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().InitVariablesJLOG()
		
		Use (Storage.System)
			Storage.System.estExecuteDansHote:=True
		End use 
		
		// déactiver les ASSERT si le composant est compilé (réactivable par les options d'appel du composant)
		SET ASSERT ENABLED(Not(Is compiled mode))
		
		// démarrer le worker
		CALL WORKER("WK_Composant_JLOGS"; Formula from string("cs.$composant.new().InitProcess()"))
		
		
	: ($numEvent=On after host database startup)
		// à faire ici tout est initialisé, en particulier le monde extérieur
		ON ERR CALL(Formula(traceHandler).source; ek global)
		
		cs.$composant.new().Installer()
		
		
	: ($numEvent=On before host database exit)
		// placer ici le code à exécuter avant le "Sur fermeture" de la base hôte
		
		
	: ($numEvent=On after host database exit)
		// placer ici le code à exécuter après le "Sur fermeture" de la base hôte
End case