⇧
Exécuter Function Coopérative - 10/03/2025 14:17:55
#DECLARE($classe : Object; $params : Object)->$nunProc : Integer
var $nomProcess; $nomTache : Text
var $data : Object
$nunProc:=0
Case of
: (Not(OB Is defined($params; "functionID")))
cs._Trace.me.Créer(-15068; Current method name; "'functionID' n'est pas défini dans $params").LeverException([msgk_event; msgk_log])
: (Value type($params.functionID)#Is text)
cs._Trace.me.Créer(-15068; Current method name; "'$params.functionID' n'est pas un texte").LeverException([msgk_event; msgk_log])
: (Not(OB Is defined($params; "numProcessAppelant")))
cs._Trace.me.Créer(-15068; Current method name; "'numProcessAppelant' n'est pas défini dans $params").LeverException([msgk_event; msgk_log])
: ($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
$nunProc:=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)
// appeler un callBack
If (OB Is defined($params; "CallBack"))
$data:=$params
// ici on doit être thread-safe
//CALL WORKER("WK_Services"; Appeler Le Formulaire; $params.numProcessAppelant; OB Get($params; "CallBack"; Is text); $data)
End if
Else
cs._Trace.me.Créer(-15081; Current method name; "La classe "+$classe.name+" n'est pas de function "+$params.functionID).LeverException([msgk_event; msgk_log])
End if
$nunProc:=Current process
End case
⇧
InitProcess - 29/03/2025 17:14:17
Capable de process préemptif
ProcInProgressCmd:=""
AsynchroProgress:=0
ProcInProgressTime:=0
ON ERR CALL(Formula(traceHandler).source; ek local)
Use (Storage.Processes)
Storage.Processes[Current process name]:=New shared object("Commande"; "")
End use
⇧
Bac à sable ALB - 04/06/2026 10:44:26
Partagée entre composants et base hôte
var $commande; $i : Integer
var $fichier; $Xpath; $texte; $titre : Text
var $xml; $racineXML : Text
var $heure : Time
var $o; $oo; $ooP : Object
var $image; $pict : Picture
var $c : Collection
$o:=New object
$oo:=New object
InitProcess
$c:=New collection(0)
$c.push(6)
For each ($i; $c)
$commande:=$commande ?+ $i
End for each
//$oo:=$o.serveur.ListerLesDocuments($x; ->$c)
// MARK:00 Console
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 application ALV"
cs.xSDK.ResourceALV.me.SetObjet(Est Ressource APP; "Ressources_Communes/nbrMaxLogs"; Is longint; $params; "nbrMaxLogs")
cs.xSDK.EvenementsALV.me.AfficherEditeur($params)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "WK libellé 1"; Current method name; "test ALB : WK description 1")
End if
// MARK:01 Photos
If ($commande ?? 1)
cs.$photos.new().MettreAjour()
End if
// MARK:02 Test traces
If ($commande ?? 2)
var $erreurSDK : cs.xSDK.Traces
$erreurSDK:=cs.xSDK.Traces.new().CréerErreur("SDK"; -15068; Current method name; "test erreur -15068")
$erreurSDK.ErrorLabel:=Localized string(String($erreur.Error))
$erreurSDK.LeverException([msgk_event; msgk_log])
Waiting(1)
var $erreur : cs._Trace
$erreur:=cs._Trace.new().Créer(0; Current method name; "")
$erreur.Error:=-16212
$erreur.ErrorDescription:="test erreur -16212"
$erreur.LeverException([msgk_event; msgk_log])
Waiting(1)
var $trace : cs._Trace
$trace:=cs._Trace.me
$trace.EnvoyerMessages([msgk_event; msgk_log]; "WK libellé 1"; Current method name; "singleton ALB : WK description 1")
$heure:=Create document(cs.xSDK.Traces.new().GetGarbageDossier().file("test").platformPath)
$trace.Créer(-16212; Current method name; "singleton ALB : test erreur -16212")
$trace.LeverException([msgk_event; msgk_log])
Waiting(1)
Use (Storage.Host)
Storage.Host.Session_Etat:=(Storage.Host.Session_Etat ?+ 6) ?+ 8
End use
$trace.DebugerMethode("Catalogue"; Current method name; "Mise à jour de l'album ")
End if
// MARK:03 Edit / Visu Album
If ($commande ?? 3)
// appel pour test local du composant
var $data : Object
$data:=New object
// créer les paramètres
$data.Informations:=New object
$data.params:=New object
$data.params.groupesFamiliaux:=New collection
$data.params.CheminDossierFTP:=cs._document.new().getDossierTravail().folder("ALB_FTP").platformPath
Case of
: (Macintosh control down)
// afficher la consultation
$data.Commande:="$albumsVisualisateur"
$data.Informations.Contexte:="_album_"
$data.params.userIDfamille:=-1006
Else
// afficher l'édition
$data.Commande:="$albumsEditeur"
$data.Informations.Contexte:="Albums"
$data.params.userIDfamille:=0
End case
cs.$albums.new().ModifierVisualisations($data)
End if
// MARK:04 requete
If ($commande ?? 4)
// test $pageXXX
$o:=Folder(fk home folder).folder("Sites/WebFolder/albums/18E07508C1294065966C1E7BB93374DD").file("Data_ALB.json")
$texte:=Document to text($o.platformPath; "UTF-8"; Document with LF)
$oo:=JSON Parse($texte; Is object)
$ooP:=New shared object("Album"; New shared object)
//Utiliser ($ooP)
//$ooP.Album:=Créer objet partagé
//Fin utiliser
$oo:=JSON Parse($texte; Is object)
sharedObject($oo; $ooP.Album)
$oo:=cs.$pageWebREQUETE.new("/4DCGI/WebREQETE/LireMediaDate?MED_HoroDate?0")
$texte:=$oo.LireMediaDate()
End if
// MARK:05
If ($commande ?? 5)
$titre:="005_modif"
$fichier:="/Users/philippe/Documents/"+$titre+".jpg"
$o:=File($fichier)
READ PICTURE FILE($o.platformPath; $image)
$racineXML:=DOM Create XML Ref("metaData")
GET PICTURE METADATA($image; ""; $racineXML)
//DOM EXPORTER VERS VARIABLE($racineXML;$texte)
DOM EXPORT TO FILE($racineXML; System folder(Documents folder)+$titre+"_XML.xml")
DOM GET XML ELEMENT NAME($RacineXML; $Xpath)
$xml:=DOM Find XML element($racineXML; $Xpath+"/TIFF")
DOM GET XML ATTRIBUTE BY NAME($xml; "ImageDescription"; $texte)
DOM SET XML ATTRIBUTE($xml; "ImageDescription"; $texte+" modif")
DOM REMOVE XML ATTRIBUTE($xml; "XResolution")
DOM REMOVE XML ATTRIBUTE($xml; "YResolution")
DOM EXPORT TO FILE($xml; System folder(Documents folder)+$titre+"_$xml_XML.xml")
SET PICTURE METADATA($image; IPTC caption abstract; "toto")
DOM CLOSE XML($racineXML)
$fichier:="/Users/philippe/Documents/"+$titre+"_modif.jpg"
$o:=File($fichier)
WRITE PICTURE FILE($o.platformPath; $image)
READ PICTURE FILE($o.platformPath; $pict)
$racineXML:=DOM Create XML Ref("metaData")
GET PICTURE METADATA($pict; ""; $racineXML)
DOM EXPORT TO FILE($racineXML; System folder(Documents folder)+$titre+"_modif_XML.xml")
DOM CLOSE XML($racineXML)
//DOM EXPORTER VERS VARIABLE($racineXML;$texte)
//$oo:=Créer objet("propriété";EXIF date time digitized;"valeur";Chaîne(Date du jour;ISO date GMT;Heure courante))
//FIXER MÉTADONNÉES IMAGE($image;$oo.propriété;$oo.valeur)
//$fichier:="/Users/philippe/Documents/004_modif.jpg"
//$o:=Fichier($fichier)
//ÉCRIRE FICHIER IMAGE($o.platformPath;$image;"JPEG")
End if
// MARK:06 maintenance
If ($commande ?? 6)
// lancer la mise à jour des albums
cs._composant.new().InitVariablesALB()
cs._maintenance.new().Démarrer()
End if
// MARK:31 traduction
If ($commande ?? 31)
cs._composant.new().ModifierTraduction()
End if
⇧
Avancement Process - 18/11/2024 17:44:44
#DECLARE($numProc : Integer; $ProcInProgressStartTime : Integer; $ProcInProgressDuration : Integer; $numProgress : Integer)
// calcule l'avancement (variable process "ProcInProgressTime") de la tâche réalisée par le process "$1"
// dont le début est $2 et la durée est $3 (hypothèse : la tâche entretient son avancement entre 0 et 10000)
// met jour les variables process courant de l'état de l'avancement du process "$1"
// met a jour l'état d'un PROGRESS, si un ID existe
var $ProcInProgressTime : Integer
Case of
: ($numProc>0)
// surveiller le process $1
If (Count parameters=3)
$numProgress:=0
End if
While (Process state($numProc)#Aborted)
// espionner le process $numProc
GET PROCESS VARIABLE($numProc; ProcInProgressEtat; ProcInProgressEtat; ProcInProgressTime; $ProcInProgressTime; ProcInProgressState; ProcInProgressState)
// mettre à jour l'avancement du thermomètre
ProcInProgressTime:=$ProcInProgressStartTime+($ProcInProgressDuration*$ProcInProgressTime/10000) // normalisation (/10000) * durée de la tâche
// objet PROGRESS
If ($numProgress>0)
// mettre à jour le libellé de l'état d'avancement
Progress SET PROGRESS($numProgress; ProcInProgressTime/10000; ProcInProgressEtat; False)
// tuer le process à la demande de l'utilisateur
Case of
// il n'y a peut-être pas de btn
: (Not(Progress Get Button Enabled($numProgress)))
// action?
: (Not(Progress Stopped($numProgress)))
Else
// purger le process
SET PROCESS VARIABLE($numProc; ProcInProgressCmd; "Tuer process")
End case
End if
Waiting(10)
End while
End case
⇧
Exécuter Function Préemptive - 04/06/2026 10:34:00
Capable de process préemptif
#DECLARE($classe : Object; $params : Object)->$nunProc : Integer
var $nomTache : Text
var $data : Object
var $trace : cs._Trace
$trace:=cs._Trace.me
$nunProc:=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.me.Tuer($nomTache)
// créer le nouveau process
$params.numProcessAppelant:=Current process
$nunProc:=New process(Current method name; 0; "$ALB_process_"+$nomTache; $classe; $params; *)
Else
// c'est ok
cs._composant.new().InitProcess()
// 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
$nunProc:=Current process
End case
⇧
traceHandler - 23/12/2024 17:59:05
Capable de process préemptif
cs._Trace.me.Intercepter()
⇧
COMPILER_WEB - 23/11/2024 10:55:28
// initialiser les variables process gérés par cs._composant.InitHTTPvars()
// *** session
var vs4D; wwwRacineRessources; wwwEtatNavigation; wwwURLalbum; wwwIndexMedia; wwwTitre; wwwComment : Text
var wwwNbrMedias : Integer
vs4D:=""
wwwRacineRessources:="../../"
wwwEtatNavigation:=""
wwwURLalbum:=""
wwwIndexMedia:=""
wwwNbrMedias:=0
wwwTitre:=""
wwwComment:=""
var wwwEtatNavigationALB : Text
⇧
ALB_EcrireElementHTML - 22/01/2025 11:50:00
Partagée entre composants et base hôte
Disponible via les balises HTML et les URLs 4D (4DACTION...)
Capable de process préemptif
// traiter une requete de worker
#DECLARE($url : Text)->$retour : Text
var $serveurWeb : cs.$serveurWeb
var $result : Object
$serveurWeb:=cs.$serveurWeb.new()
$result:=$serveurWeb.TraiterURL($url)
// renvoyer le resultat
$retour:=$result.resultat
⇧
COMPILER - 26/02/2025 10:27:37
//••••• Général •••••
var ProcInProgressTime; ProcInProgressState : Integer
var ProcInProgressCmd; ProcInProgressEtat : Text
var AsynchroProgress : Integer
var ErrorNum : Integer
⇧
[class]_albums - 19/02/2026 19:10:24
property FTP : cs._FTP
property params; Album; mediaInformation; requête : Object
property listeAlbums; pathsAlbums; nomsAlbums : Collection
property indexAlbum : Integer
property indexMedia : Integer
// données standard pour tout formulaire
property grandEcran : Boolean:=False
property mémoTaille : Boolean:=True
Class extends _composant
Class constructor()
Super()
This.params:=New object // peut servir
// données d'album
This.listeAlbums:=Null
This.pathsAlbums:=Null
This.Album:=New object
// données de medias
This.mediaInformation:=New object
// données de requete
This.requête:=New object
// FTP
This.FTP:=cs._FTP.new()
// ----------------------
// MARK:Sélection
// -----------------------
Function ListerAlbums()->$result : cs._Trace
// lire sur le serveur la liste des albums
var $data : Object
$result:=cs._Trace.me.Initialiser(Current method name)
// pour gérer la connexion FTP
$data:=This.requête
// le dossier est créé dans le process appelant
This.CréerDossierDeTravail("ALBs_FTP")
$data.dossierTravail:=This.dossierTravail
// lister les albums
// on ne veut que les dossiers de la racine
$data.Options:=0x0002
$data.pathsDocuments:=New collection
$data.listeDocuments:=New collection
Case of
: (Not(This.FTP.ListerLesDocuments($data).success))
$result.Error:=-16217
$result.ErrorDescription:="FTP : connexion impossible à "+This.FTP.serveur.params.hébergement
: (Not(OB Is defined($data; "listeDocuments")))
$result.Error:=-15068
$result.ErrorDescription:="propriété 'listeDocuments' non définie"
: ($data.listeDocuments.length=0)
$result.Error:=-16209
$result.ErrorDescription:="FTP : pas d'albums trouvés à "+This.FTP.racineFTP
Else
// c'est ok
This.FiltrerSurFamille($data)
// s'approprier la liste des albums
This.listeAlbums:=$data.listeDocuments
This.pathsAlbums:=$data.pathsDocuments
This.nomsAlbums:=$data.nomsDocuments
End case
Function FiltrerSurFamille($data : Object)
var $result : cs._Trace
var $path : Text
var $c1; $c2; $c3 : Collection
// remarque: le IDfamille existe, sinon on ne serait pas là
$c1:=New collection
$c2:=New collection
$c3:=New collection
For each ($path; $data.pathsDocuments)
$result:=This.getDonnéesAlbum($path)
Case of
: ($result.Error#0)
// nouvel album
$c1.push($data.listeDocuments[$data.pathsDocuments.indexOf($path)])
$c2.push($path)
$c3.push(Localized string("1"))
: ($result.Album=Null)
// les droits sont un objet avec NomGroupe, IDgroupe (familial) et Droits (vrai si la famille y a accès)
: (($result.Album.Acces_Droits.query("IDgroupe = :1"; This.params.params.userIDfamille).length=0) & (Storage.System.estExecuteDansHote) & (This.environnement.typeApplication()#ALV BDD mère))
Else
// le user a accès à cet album
$c1.push($data.listeDocuments[$data.pathsDocuments.indexOf($path)])
$c2.push($path)
$c3.push($result.Album.Nom)
End case
End for each
$data.listeDocuments:=$c1.copy()
$data.pathsDocuments:=$c2.copy()
$data.nomsDocuments:=$c3.copy()
Function getDonnéesAlbum($path : Text)->$result : cs._Trace
// lire le fichier xml des données de l'album
var $data : Object
var $dataTexte : Text
$result:=cs._Trace.me.Initialiser(Current method name)
// nouveau dossier de travail
This.requête.dossierTravail.create()
// contexte
$data:=This.requête
// demander le fichier à l'hébergeur, créer la requête :
$data.pathFichier:=$path+"Data_ALB.json" // ne pas détruire .cheminDossier
$data.cheminFichier:=$data.dossierTravail.platformPath+"Data_ALB.json"
$result:=This.FTP.TéléchargerFichier($data)
// plusieurs cas gérés
If ($result.success)
// c'est bon
$dataTexte:=Document to text($result.fichier.platformPath; "UTF-8"; Document with LF)
$result.Album:=JSON Parse($dataTexte; Is object)
Else
$result.Album:=Null
End if
Function OuvrirAlbum()->$result : cs._Trace
// lire les informations de l'album courant
var $path; $nom : Text
$result:=cs._Trace.me.Initialiser(Current method name)
// chemin FTP de l'album courant
$path:=This.pathsAlbums[This.indexAlbum]
$result:=This.getDonnéesAlbum($path)
// plusieurs cas gérés
Case of
: (This.pathsAlbums=Null)
: ($result.Error=0)
// c'est bon
This.Album:=$result.Album
: (This.ImporterDonnées($path).Error=0)
// ok (en particulier import des anciennes structures)
// This.Album est renseigné
This.Album.pathDossier:=$path
Else
// ici on a un nouvel album, initialiser ses données
This.Album:=New object
This.Album.Identification_ID:=Generate UUID
This.Album.Acces_Droits:=New collection
This.Album.Medias:=New collection
This.Album.pathDossier:=$path
// fixer son ID_nom
$nom:=Split string($path; "/"; sk ignore empty strings)[1]
This.Album.Identification_Nom:=$nom
This.Album.Nom:=Localized string("1")
// créer le dossier des media
$result.Error:=This.FTP.serveur.CréerRépertoire(This.document.getAlbumMediasURL($path)).Error
End case
// les erreurs sont traitées au dessus
$result.LeverException([msgk_event; msgk_log])
Function ImporterDonnées($path : Text)->$result : cs._Trace
// lire les données disponibles au chemin $1
var $fichier : 4D.File
var $data : Object
var $structureDeDonnées; $varName; $dataTexte : Text
var $i : Integer
var $c : Collection
var $valid : Boolean
var $xml : cs.xSDK.XML
$xml:=cs.xSDK.XML.me
$data:=This.requête
$data.pathFichier:=$path+"Informations.xml" // ne pas détruire .cheminDossier
$data.cheminFichier:=$data.dossierTravail.platformPath+"Informations.xml"
$result:=This.FTP.TéléchargerFichier($data)
This.Album:=New object
If ($result.Error=0)
// c'est ok, importer les données
$fichier:=$result.fichier
// Lire et éditer les infos de l'album
$structureDeDonnées:=""
$result:=$xml.LireFichier($fichier; ->$structureDeDonnées)
// renseigner les objets du formulaire dont le nom = "xml " & xPath de $path
ARRAY TEXT($Objets; 0)
ARRAY POINTER($Variables; 0)
FORM GET OBJECTS($Objets; $Variables; *)
For ($i; 1; Size of array($Objets))
Case of
: ($Objets{$i}="ALB_@")
$varName:=Replace string($Objets{$i}; "ALB_"; "")
//xPath XML
$path:=Replace string($varName; "_"; "/")
$dataTexte:="" // re-init !
$xml.LireLeChemin(->$structureDeDonnées; $path; ->$dataTexte)
This.Album[$varName]:=$dataTexte
End case
End for
ARRAY LONGINT($IDfamille; 0)
// lire les droits : c'est un tableau d'ID familiaux ayant droit sur cet album
$xml.LireLeChemin(->$structureDeDonnées; "Acces/Droits"; ->$IDfamille)
$c:=New collection
ARRAY TO COLLECTION($c; $IDfamille)
// récupérer dans la BDD la liste des ID de groupes familiaux
ARRAY TEXT($Noms; 0)
ARRAY LONGINT($IDs; 0)
// lire les groupes familiaux
If (Storage.System.estExecuteDansHote)
Begin SQL
SELECT Nom, IDfamille FROM wwwGroupes INTO :$Noms, :$IDs;
End SQL
End if
This.Album.Acces_Droits:=New collection
For ($i; 1; Size of array($Noms))
$valid:=($c.indexOf($IDs{$i})>-1)
This.Album.Acces_Droits.push(New object("Droits"; $valid; "NomGroupe"; $Noms{$i}; "IDgroupe"; $IDs{$i}))
End for
// initialiser le catalogue de medias
This.Album.Medias:=New collection
End if
Function TrierMedias()
// trier les medias de l'album par date d'origine
This.Album.Medias:=This.Album.Medias.orderBy("MED_HoroDate")
Function FermerAlbum()
// enregistrer les données de l'album
var $data; $fichier : Object
var $dataTexte; $path : Text
var $result : cs._Trace
$result:=cs._Trace.me.Créer(-15068; Current method name; "Err params")
$data:=This.requête
Case of
// possible (au chargement en particulier)
: (This.Album=Null)
: (Not(OB Is defined(This.Album; "pathDossier")))
: (This.Album.pathDossier="")
Else
// enregistrer les données album en local
$fichier:=$data.dossierTravail.file("Data_ALB.json")
$dataTexte:=JSON Stringify(This.Album)
TEXT TO DOCUMENT($fichier.platformPath; $dataTexte; "UTF-8"; Document with LF)
// mettre à jour sur l'hébergeur
$path:=This.Album.pathDossier+"Data_ALB.json"
$result.Error:=This.FTP.serveur.EnvoyerFichier($fichier.platformPath; $path).Error
End case
$result.LeverException([msgk_event; msgk_log])
⇧
[class]$albumsEditeur - 04/06/2026 10:56:21
property saisie : Object
property fichier : 4D.File
property catalogueMedias : Collection
property saisieEnCours; modifié : Boolean
property ProcInProgressNum : Integer:=0
property avancement : Integer:=0
property rangMedia : Integer:=0
property listeMedias : Integer:=0
Class extends _albums
Class constructor()
Super()
This.saisie:=New object
This.listeMedias:=0
// ----------------------
//Mark:Sélection
// -----------------------
Function ListerAlbums()
// alimenter le sélecteur d'albums
// collectionner les albums
Super.ListerAlbums()
// créer la collection des albums avec cette collection
Form.selecteur.values:=This.nomsAlbums
Function FixerNavigationClavier()
If (This.saisieEnCours)
// invalider la navigation fléchée
OBJECT SET SHORTCUT(*; "grpBtnNav@"; "")
Else
// retablir la navigation fléchée
OBJECT SET SHORTCUT(*; "grpBtnNavPreviousRecord"; Shortcut with Left arrow)
OBJECT SET SHORTCUT(*; "grpBtnNavNextRecord"; Shortcut with Right arrow)
End if
Function MajGroupesFamiliaux()
// vérifier la complétude de la liste les groupes familiaux
var $groupe : Object
Case of
: (This.params.params.groupesFamiliaux.length=0)
Else
For each ($groupe; This.params.params.groupesFamiliaux)
If (This.Album.Acces_Droits.query("NomGroupe = :1"; $groupe.Nom).length=0)
// nouveau groupe
This.Album.Acces_Droits.push(New object("Droits"; False; "NomGroupe"; $groupe.Nom; "IDgroupe"; $groupe.IDfamille))
End if
End for each
End case
// ----------------------
// MARK:Formulaire
// -----------------------
Function OuvrirAlbum()->$result : Object
// ouvrir l'album
Super.OuvrirAlbum()
// récupérer les groupes familiaux
This.MajGroupesFamiliaux()
Form.modifié:=False
Function OuvrirFormulaire()
// méthode du process U_Nav Albums
var $wndNum : Integer
var $nomForm : Text
NO DEFAULT TABLE
// la fenêtre est ouverte en taille normale. Le user ne peut pas modifier la taille
$nomForm:="U_Nav Albums"
$wndNum:=Open form window($nomForm; -Palette form window; Horizontally centered; Vertically centered; *)
// initialiser la valeur
DIALOG($nomForm; This)
CLOSE WINDOW
CLEAR VARIABLE($wndNum)
Function InitFormulaire()
// lire / mettre à jour les medias
var $data : Object
Form.AsynchroProgress:=True
// fermer un précédent album
Case of
: (Form.indexAlbum=-1)
// pas d'album sélectionné
: (Form.ProcInProgressNum>0)
// un process en cours, attendre
Else
// mettre à jour le catalogue de l'album sélectionné (il y a peut-être de nouveaux media)
$data:=New object("params"; New object)
$data.params.Album:=Form.Album
$data.params.numFenetreAppelante:=Current form window
$data.params.function:="CallBack"
$data.params.dossierTravail:=Form.requête.dossierTravail
$data.params.commande:="Démarrer"
// construire l'appel au worker
$data.execute:=Formula(cs.$albumsEditeur.new().MettreAjourCatalogue(This.params))
// lancer le traitement
CALL WORKER("WK_Composant_ALB"; Formula($data.execute().source))
End case
Function FermerAlbum()->$result : Object
// enregistrer les données de l'album
var $fichier : Object
If (Form.modifié)
$result:=Super.FermerAlbum()
End if
// purger
$fichier:=This.requête.dossierTravail.file("Data_ALB.json")
CLEAR VARIABLE($fichier)
OB REMOVE(This.requête; "fichier")
// purger le dossier de travail
This.requête.dossierTravail.delete(Delete with contents)
// RAZ données de l'album courant
Form.Album:=New object
// RAZ récipient des données affichées de l'album courant
Form.saisie:=New object
// RAZ LH
Form.listeMedias:=New list
// ----------------------
// MARK:Import
// -----------------------
Function MettreAjourCatalogue($params : Object)
// passer en revue les medias et mettre à jour le catalogue si des ajouts / suppressions sont détectés
var $result; $data; $mediaInformation : Object
var $i; $width; $height : Integer
var $catalogueMedias; $c; $poubelle : Collection
var $image : Picture
// en principe s'exécute dans un process externe
ON ERR CALL(Formula(traceHandler).source; ek local)
// initialiser la classe
This.Album:=$params.Album
This.dossierTravail:=$params.dossierTravail
// lister les medias de l'album sélectionné
$data:=New object
$data.pathDossier:=This.document.getFTPalbumMediasPath(This.Album.Identification_Nom)
$data.Options:=0x0005
$data.pathsDocuments:=New collection
$data.listeDocuments:=New collection
$data.listeInfos:=New collection
$result:=This.FTP.ListerLesDocuments($data)
$result:=cs._Trace.me.Initialiser(Current method name)
If ($data.pathsDocuments.length=0)
// ici on est mal !
$result.ErrorDescription:="L'album "+This.Album.Identification_Nom+" est vide"
Else
// tout est ok pour créer / mettre à jour le catalogue
$catalogueMedias:=This.Album.Medias
This.modifié:=False
For ($i; 1; $data.pathsDocuments.length)
// état du media
$c:=$catalogueMedias.query("MED_IDunique = :1"; File($data.listeDocuments[$i-1]; fk platform path).name)
Case of
: ($c.length=0)
// les données du media ne sont pas connues, ajouter au catalogue
// récupérer les chemins d'origine du fichier et de son dossier
This.fichier:=File($data.pathsDocuments[$i-1])
$data.pathDossier:=This.fichier.parent.path
// télécharger le fichier
$data.pathFichier:=$data.pathsDocuments[$i-1]
$data.cheminFichier:=This.dossierTravail.platformPath+$data.listeDocuments[$i-1]
$result:=This.FTP.serveur.RecevoirFichier($data.pathFichier; $data.cheminFichier)
// récupérer le fichier téléchargé
This.fichier:=File($data.cheminFichier; fk platform path)
// récupérer les infos et metadata du fichier
This.mediaInformation:=New object
// path du fichier sur l'hébergeur
This.mediaInformation.nomFichier:=This.fichier.fullName
This.mediaInformation.pathFichier:=$data.pathsDocuments[$i-1]
This.mediaInformation.taille:=$data.listeInfos[$i-1].taille
// horodatage du fichier
This.mediaInformation.dateModification:=$data.listeInfos[$i-1].date
This.mediaInformation.heureModification:=$data.listeInfos[$i-1].heure
// metadata du fichier
$result:=This.ImporterMétaDonnéesImage()
If ($result.success)
// on a reconnu un media
// deux cas : un nouveau media, ou un media existant qui a été modifié
If ($catalogueMedias.query("MED_IDunique = :1"; This.mediaInformation.MED_IDunique).length=0)
// un nouveau media, l'ajouter au catalogue
$catalogueMedias.push(This.mediaInformation)
End if
// si on est ici, c'est que le fichier a été ajouté ou modifié
$data.fichier:=This.fichier
// This.fichier est en local, mettre à jour le fichier dans l'album
// consiste à supprimer l'ancien fichier sur l'hébergeur, puis téléverser le fichier local (baptisé UUID)
$result:=This.FTP.serveur.SupprimerFichier($data.pathFichier)
$data.pathFichier:=$data.pathDossier+This.fichier.fullName
$result:=This.FTP.serveur.EnvoyerFichier(This.fichier.platformPath; $data.pathFichier)
// mettre à jour le chemin du media
This.mediaInformation.nomFichier:=This.fichier.fullName
This.mediaInformation.pathFichier:=$data.pathFichier
$data.pathsDocuments[$i-1]:=$data.pathFichier
End if
This.modifié:=True
// voir si le media a changé de dossier
: ($data.pathsDocuments[$i-1]#$c[0].pathFichier)
This.mediaInformation:=$c[0]
This.mediaInformation.pathFichier:=$data.pathsDocuments[$i-1]
// voir si le media a été modifié
: (($data.listeInfos[$i-1].date=$c[0].dateModification) & ($data.listeInfos[$i-1].taille=$c[0].taille) & ($data.listeInfos[$i-1].heure=$c[0].heureModification))
// rappel : ici les heures sont des entierLong
Else
// télécharger le fichier
$data.commande:="TéléchargerFichier"
$data.pathFichier:=$data.pathsDocuments[$i-1]
$data.cheminFichier:=This.dossierTravail.platformPath+$data.listeDocuments[$i-1]
$result:=This.FTP.serveur.RecevoirFichier($data.pathFichier; $data.cheminFichier)
This.fichier:=File($data.cheminFichier; fk platform path)
// mettre à jour les infos
This.mediaInformation:=$c[0]
// nom du fichier (l'extension a peut-être changé)
This.mediaInformation.nomFichier:=This.fichier.fullName
This.mediaInformation.pathFichier:=$data.pathsDocuments[$i-1]
// horodatage du fichier
This.mediaInformation.dateModification:=$data.listeInfos[$i-1].date
This.mediaInformation.heureModification:=$data.listeInfos[$i-1].heure
// image
This.mediaInformation.taille:=$data.listeInfos[$i-1].taille
READ PICTURE FILE(This.fichier.platformPath; $image)
If (Picture size($image)>0)
// écrire la largeur / hauteur de l'image
PICTURE PROPERTIES($image; $width; $height)
OB SET(This.mediaInformation; "MED_DimensionX"; $width)
OB SET(This.mediaInformation; "MED_DimensionY"; $height)
End if
This.modifié:=True
End case
ProcInProgressEtat:=$data.pathsDocuments[$i-1]
ProcInProgressTime:=$i/$data.pathsDocuments.length*10000
//If (This.tache.Tuer.signaled)
//$i:=1+$data.pathsDocuments.length
//End if
End for
// y a t-il des articles du catalogue qui ne sont plus des fichiers?
$poubelle:=New collection
$c:=$data.pathsDocuments
For each ($mediaInformation; $catalogueMedias)
If ($c.indexOf($mediaInformation.pathFichier)=-1)
$poubelle.push($catalogueMedias.indexOf($mediaInformation))
End if
End for each
// supprimer les $mediaInformation non retrouvés
If ($poubelle.length>0)
For ($i; $poubelle.length; 1; -1)
$catalogueMedias.remove($poubelle[$i-1])
End for
This.modifié:=True
End if
ProcInProgressEtat:=""
ProcInProgressTime:=10000
Waiting(30)
// trier par date d'origine
This.catalogueMedias:=$catalogueMedias.orderBy("MED_HoroDate")
End if
// ici on a un catalogueMedias
$params.catalogueMedias:=This.catalogueMedias
$params.modifié:=This.modifié
// renvoyer le resultat
Case of
: (Not(OB Is defined($params; "numFenetreAppelante")))
Else
// envoyer pour la suite
$data:=New object
$data.params:=OB Copy($params)
// il n'y a que le texte qui semble passer dans les .params
// construire l'appel au requêteur
$data.execute:=Formula(cs.$albumsEditeur.new()["CallBack"](This.params))
CALL FORM($params.numFenetreAppelante; Formula($data.execute().source))
End case
Function ImporterMétaDonnéesImage()->$result : cs._Trace
// autopsier les infos de l'image, et compléter This.mediaInformation
var $image : Picture
var $texte; $dateSaisie; $titreSaisi; $commentaireSaisi; $racineXML; $ElementXML; $attribut; $valeur : Text
var $width; $height; $nbAttributs; $i : Integer
var $GPS : Object
$result:=cs._Trace.me.Initialiser(Current method name)
READ PICTURE FILE(This.fichier.platformPath; $image)
If (Picture size($image)>0)
// initialiser les données (au cas où elles manqueraient)
$dateSaisie:=""
$commentaireSaisi:=""
// écrire la largeur / hauteur de l'image
PICTURE PROPERTIES($image; $width; $height)
OB SET(This.mediaInformation; "MED_DimensionX"; $width)
OB SET(This.mediaInformation; "MED_DimensionY"; $height)
//v8.1.17 après modification la date est dans 'IEXIF user comment'
GET PICTURE METADATA($image; EXIF user comment; $dateSaisie)
If (($dateSaisie="") | (Date($dateSaisie)=!00-00-00!))
// lire un horodatage disponible de création de l'image
GET PICTURE METADATA($image; EXIF date time original; $dateSaisie)
If (($dateSaisie="") | (Date($dateSaisie)=!00-00-00!))
GET PICTURE METADATA($image; EXIF date time digitized; $dateSaisie)
If (($dateSaisie="") | (Date($dateSaisie)=!00-00-00!))
GET PICTURE METADATA($image; IPTC digital creation date time; $dateSaisie)
If (($dateSaisie="") | (Date($dateSaisie)=!00-00-00!))
GET PICTURE METADATA($image; IPTC date time created; $dateSaisie)
If (($dateSaisie="") | (Date($dateSaisie)=!00-00-00!))
// on prend les données fichier
$dateSaisie:=String(This.fichier.modificationDate; ISO date GMT; This.fichier.modificationTime)
End if
End if
End if
End if
End if
// écrire l'horodatage de création de l'image
OB SET(This.mediaInformation; "MED_HoroDate"; $dateSaisie)
// l'UUID du fichier peut ne pas exister (metadata non initialisées)
GET PICTURE METADATA($image; EXIF image unique ID; $texte)
Case of
: (Match regex("[0-9ABCDEF]{32}"; $texte))
// media déjà initialisé
GET PICTURE METADATA($image; TIFF document name; $titreSaisi)
Else
// le titre de l'image est le nom d'origine du fichier
$titreSaisi:=This.fichier.fullName
$texte:=Generate UUID
// mémoriser dans l'image : le nom original du fichier, l'initialisation du titre et du UUID,
SET PICTURE METADATA($image; EXIF image unique ID; $texte)
WRITE PICTURE FILE(This.fichier.platformPath; $image)
End case
// renommer le fichier
This.fichier:=This.fichier.rename($texte+This.fichier.extension)
//mettre le nouveau nom
OB SET(This.mediaInformation; "nomFichier"; This.fichier.fullName)
// finalement
OB SET(This.mediaInformation; "MED_IDunique"; $texte)
OB SET(This.mediaInformation; "MED_Nom"; $titreSaisi)
// lire le nom d'origine
OB SET(This.mediaInformation; "MED_NomOrigine"; $titreSaisi)
// lire le commentaire de l'image
GET PICTURE METADATA($image; IPTC caption abstract; $commentaireSaisi)
OB SET(This.mediaInformation; "MED_Commentaire"; $commentaireSaisi)
// lire les meta données GPS
$racineXML:=DOM Create XML Ref("Root")
$ElementXML:=DOM Create XML element($racineXML; "/Root/GPS")
GET PICTURE METADATA($image; "GPS"; $ElementXML)
$nbAttributs:=DOM Count XML attributes($ElementXML)
$GPS:=New object
For ($i; 1; $nbAttributs)
DOM GET XML ATTRIBUTE BY INDEX($ElementXML; $i; $attribut; $valeur)
$GPS[$attribut]:=$valeur
End for
DOM CLOSE XML($racineXML)
OB SET(This.mediaInformation; "GPS"; $GPS)
Else
$result.Error:=-15068
$result.ErrorDescription:="image non disponible, "+This.fichier.platformPath+" non reconnu"
// $2 est vide
End if
$result.FixerSuccess()
// les erreurs sont traitées au dessus
$result.LeverException([msgk_event; msgk_log])
Function CallBack($data : Object)
// le catalogue de l'album sélectionné a été créé dans $1, on continue
// mémoriser dans Form la liste (chronologique) des medias
Form.Album.Medias:=$data.catalogueMedias
Form.modifié:=$data.modifié
This.ListerMedias()
Function ListerMedias()
var $metaData : Object
var $i : Integer
This.Album.Medias:=Form.Album.Medias.orderBy("MED_HoroDate")
// créer la LH des medias
Form.listeMedias:=New list
For ($i; 1; Form.Album.Medias.length)
$metaData:=Form.Album.Medias[$i-1]
// itemref est la position dans le LH (cad index+1 du media dans le catalogue de medias)
APPEND TO LIST(Form.listeMedias; $metaData["MED_Nom"]; $i)
SET LIST ITEM PARAMETER(Form.listeMedias; 0; "UUID"; $metaData["MED_IDunique"])
SET LIST ITEM PROPERTIES(Form.listeMedias; 0; False; Italic; 0; -1)
SET LIST ITEM PARAMETER(Form.listeMedias; 0; Additional text; $metaData["MED_NomOrigine"])
End for
SET LIST PROPERTIES(Form.listeMedias; 0; 0; 17)
Form.AsynchroProgress:=False
// afficher la première photo
Form.rangMedia:=1
This.AfficherMedia()
// ----------------------
// MARK:Saisie
// -----------------------
Function AfficherMedia()
// afficher les données du media rangMedia dans les champs EXIF_
var $mediaInformation; $data; $result : Object
var $image : Picture
var $dataTexte : Text
$data:=Form.requête
Form.saisie.photo:=0
Form.IndexCatalogue:=""
// données affichées saisissables
Form.saisie.Date:=!00-00-00!
Form.saisie.Heure:=?00:00:00?
Form.saisie.MED_Nom:=""
Form.saisie.MED_Commentaire:=""
Case of
: (Not(OB Is defined(Form.Album; "Medias")))
: (Form.Album.Medias.length=0)
Else
// sélectionner le média (nécessaire quand on navigue par les flèches)
SELECT LIST ITEMS BY POSITION(Form.listeMedias; Form.rangMedia)
Form.IndexCatalogue:=String(Form.rangMedia)+" / "+String(Form.Album.Medias.length)
// récupérer les infos dans le catalogue
$mediaInformation:=Form.Album.Medias[Form.rangMedia-1]
// fixer pathFichier (affiché dans le formulaire, non saisissable)
Form.pathFichier:=$mediaInformation.pathFichier
// afficher la photo
$data.pathFichier:=$mediaInformation.pathFichier
$data.nomFichier:=$mediaInformation.nomFichier
$result:=This.FTP.TéléchargerFichierMedia($data)
If ($result.success)
READ PICTURE FILE($result.fichier.platformPath; $image)
Form.saisie.photo:=$image
End if
// afficher les infos MED, saisissables
$dataTexte:=$mediaInformation["MED_HoroDate"]
Form.saisie.Date:=Date($dataTexte)
Form.saisie.Heure:=Time($dataTexte)
Form.saisie.MED_Nom:=$mediaInformation["MED_Nom"]
Form.saisie.MED_Commentaire:=$mediaInformation["MED_Commentaire"]
End case
Function ModifierDonnée()
var $varName; $dataTexte : Text
var $mediaInformation : Object
// retrouver le nom de l'objet modifié
$varName:=OBJECT Get name(Object with focus)
Case of
// filtrer les modifications d'album
: ($varName="Albums")
: ($varName="ALB_@")
// les modifications de données d'album sont faites directement dans l'objet Album
Form.modifié:=True
: (Not(OB Is defined(Form; "Album")))
// pas encore d'album disponible
: (Not(OB Is defined(Form.Album; "Medias")))
: (Form.Album.Medias.length=0)
// pas encore de medias disponibles
: (Form.rangMedia<1)
// rien d'initialisé
: (Form.rangMedia>Form.Album.Medias.length)
// pb
Else
// ici on a une modification d'un media
// récupérer les infos dans le catalogue
$mediaInformation:=Form.Album.Medias[Form.rangMedia-1]
Case of
: ($varName="selection")
// filtrer
: (($varName="Date") | ($varName="Heure"))
$dataTexte:=String(OB Get(Form.saisie; "Date"; Is date); ISO date GMT; OB Get(Form.saisie; "Heure"; Is time))
$mediaInformation.MED_HoroDate:=$dataTexte
Form.modifié:=True
Else
// cas génériques
$dataTexte:=OB Get(Form.saisie; $varName; Is text)
OB SET($mediaInformation; $varName; $dataTexte)
Form.modifié:=True
End case
// mettre à jour la LH
If ($varName="MED_Nom")
GET LIST ITEM(Form.listeMedias; *; $i; $dataTexte)
$dataTexte:=OB Get(Form.saisie; $varName; Is text)
SET LIST ITEM(Form.listeMedias; $i; $dataTexte; $i)
End if
End case
Function EditerRessourceComposant($path : Text)
// Lire / écrire une ressource application
// par principe la donnée est éditée dans un formulaire
// $1 = objet où lire / écrire, $2 = nom propriété de $1, $3 = chemin dans la ressource du composant {, $4 type valeur}
var $erreur : cs._Trace
var $nomObjet : Text
var $dataTexte : Text
$erreur:=cs._Trace.me.Initialiser(Current method name)
$nomObjet:=OBJECT Get name(Object current)
Case of
: (Not(This.rsc.SetVariable(Est Ressource ALB; $path; Is text; ->$dataTexte)))
$erreur.Error:=-15058
$erreur.ErrorDescription:=$path+" est une donnée inconnue"
: (Form event code=On Load)
Form[$nomObjet]:=$dataTexte
: (Form event code=On Data Change)
$dataTexte:=Form[$nomObjet]
$erreur.Error:=-16003*Num(Not(This.rsc.SetResourceALV(Est Ressource ALB; $path; ->$dataTexte)))
End case
$erreur.LeverException([msgk_event; msgk_log])
⇧
[class]_document - 27/02/2026 10:56:44
property environnement : cs.xSDK.EnvironnementALV
property rsc : cs.xSDK.ResourceALV
// fixer par les classes héritantes
property nomServeurWeb : Text
Class constructor
This.environnement:=cs.xSDK.EnvironnementALV.new()
This.rsc:=cs.xSDK.ResourceALV.me
Function getDataServeurFolder()->$result : Object
$result:=Storage.Host.$document.call(Null).getDataServeurFolder()
//------------------
//mark:Installation
//------------------
Function getDataFolder()->$result : 4D.Folder
// installation : dossier des albums pour le composant
Case of
: (Storage.System.typeApplication=ALV Serveur APP)
// cas opérationnel
$result:=This.getDataServeurFolder().folder("WEBalbums")
$result.create()
: (Storage.System.typeApplication=ALV Serveur HTTP)
// utilisation de 4D Server pour debug
$result:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_ALBdebug").folder("WEBalbums")
$result.create()
: (Storage.System.typeApplication=ALV BDD mère)
// pour test local
$result:=cs.xSDK.Traces.new().GetGarbageDossier().folder("_ALBdebug").folder("WEBalbums")
$result.create()
Else
// rien
$result:=Null
End case
Function getRacineHTMLFolder($nomServeurWeb : Text)->$result : Object
// renvoyer le chemin du dossier racine du serveur $nomServeurWeb
var $c : Collection
$result:=Null
Case of
: (Application type=4D Remote mode)
// filtrer (pas concerné pour l'instant)
Else
$c:=WEB Server list.query("name = :1"; $nomServeurWeb)
Case of
: ($c.length#1)
: ($c[0].isRunning=False)
Else
$result:=Folder($c[0].rootFolder)
End case
End case
Function getInstallationMobileFolder()->$result : Object
var $serveur : Object
$result:=Null
$serveur:=WEB Server(Web server host database)
Case of
: (Application type=4D Remote mode)
: (Not(Storage.System.estExecuteDansHote))
// filtrer (pas concerné pour l'instant)
: (Not(This.environnement.estServeur()))
// pour les tests (BDDmère uniquement)
$result:=This.getDossierTravail().folder("ALB_debugMobile")
: ($serveur=Null)
// pas de serveur hôte
Else
// on se met dans le dossier racine du site Web hôte
$result:=Folder($serveur.rootFolder)
End case
Function getDossierTravail()->$result : Object
$result:=cs.xSDK.Traces.new().GetGarbageDossier().folder("tempo_ALB")
$result.create()
//------------------
//MARK:Hébergeur
//------------------
Function getAlbumMediasURL($IDnom : Text)->$result : Text
$result:=$IDnom+"Medias/"
Function getFTPalbumMediasPath($IDnom : Text)->$result : Text
$result:=This.getFTPalbumPath($IDnom)
$result+="Medias/"
Function getFTPalbumPath($IDnom : Text)->$result : Text
This.rsc.SetVariable(Est Ressource ALB; "chemins/FTP/Path"; Is text; ->$result)
$result+=$IDnom+"/"
//------------------
//MARK:Requete
//------------------
Function getWEBalbumsFolder_Courant()->$result : Object
var $nomDossier : Text
$result:=Null
Case of
: (Not(This.rsc.SetVariable(Est Ressource ALB; "chemins/albums/URL"; Is text; ->$nomDossier)))
Else
$result:=This.getRacineHTMLFolder_Courant().folder($nomDossier)
End case
Function getRacineHTMLFolder_Courant()->$result : Object
// renvoyer le chemin du dossier racine du serveur courant
// attention le composant peut être utilisé par 2 serveurs Web
var $serveur : 4D.WebServer
// trouver le serveur requeteur
$serveur:=WEB Server(Web server receiving request)
$result:=Null
Case of
: (Application type=4D Remote mode)
// filtrer (pas concerné pour l'instant)
: ($serveur=Null)
Else
// cas serveur APP ou BDD mère (pour test)
$result:=Folder($serveur.rootFolder)
End case
//------------------
//MARK:Site Web
//------------------
Function getWEBalbumMediasFolder($IDabum : Text)->$result : Object
$result:=This.getWEBalbumFolder($IDabum).folder("Medias")
$result.create()
Function getWEBalbumVignettesFolder($IDabum : Text)->$result : Object
$result:=This.getWEBalbumFolder($IDabum).folder("Vignettes")
$result.create()
Function getWEBalbumFolder($IDabum : Text)->$result : Object
$result:=This.getWEBalbumsFolder().folder($IDabum)
Function getWEBalbumsFolder()->$result : 4D.Folder
// les albums sont installés dans les données du serveur APP
var $nomDossier : Text
$result:=Null
Case of
: (Not(This.rsc.SetVariable(Est Ressource ALB; "chemins/albums/URL"; Is text; ->$nomDossier)))
Else
$result:=This.getDataFolder().folder($nomDossier)
$result.create() // au cas ou
End case
Function getWEBalbumMediasURL()->$result : Text
$result:=Session.storage.ALB_HTTPvars.wwwURLalbum+"Medias/"
Function getWEBalbumVignettesURL()->$result : Text
$result:=Session.storage.ALB_HTTPvars.wwwURLalbum+"Vignettes/"
⇧
[class]_composant - 18/02/2026 16:49:20
property environnement : cs.xSDK.EnvironnementALV
property document : cs._document
property fct : cs.xSDK.Outils
property rsc : cs.xSDK.ResourceALV
property dossierTravail : Object
Class constructor()
// environnement
This.environnement:=cs.xSDK.EnvironnementALV.new()
This.document:=cs._document.new()
This.fct:=cs.xSDK.Outils.me
This.rsc:=cs.xSDK.ResourceALV.me
This.dossierTravail:=Null
Function InitProcess()
ProcInProgressCmd:=""
AsynchroProgress:=0
ProcInProgressTime:=0
ON ERR CALL(Formula(traceHandler).source; ek local)
Use (Storage.Processes)
Storage.Processes[Current process name]:=New shared object("Commande"; "")
End use
//---------------------
// MARK:Installation
//---------------------
Function InitVariablesALB()
var $data : Object
Use (Storage)
Storage["System"]:=New shared object
Storage["Host"]:=New shared object
Storage["Processes"]:=New shared object
End use
Use (Storage.System)
Storage.System.estExecuteDansHote:=False
Storage.System.estExecuteDansAPP:=False
// tout le monde n'est pas initialisé ; on ne sait pas si on est serveur Web
Storage.System.estServeur:=False
Storage.System.AlertesHôte:=6 // gère les messages d'alerte
// obsolete
Storage.System.Session_Etat:=0 // équivalent des UserPrefs
Storage.System.Status:=0
$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)+"DataALB.xml"
// accès au worker du composant
$data.nomWorker:="WK_Composant_ALB"
$data.méthodeWorker:="Installer Albums"
// mémoriser
cs.xSDK.ResourceALV.me.Inscrire(Est Ressource ALB; $data)
Function Installer()
Use (Storage.System)
Storage.System.estExecuteDansAPP:=This.environnement.estExecuteDansAPP()
End use
cs.xSDK.$composant.new().InstallerRessourcesAPP()
This.InstallerDonnéesHote()
This.IntegerDansHote()
// lancer la maintenance
cs._maintenance.new().Démarrer()
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
Use (Storage)
Storage.Host:=New shared object
End use
: (Storage.System.typeApplication=ALV Client APP)
: (Storage.System.typeApplication=4D Remote mode)
// filtrer
Else
$data:=New object
// une erreur est générée si l'appel vient d'un composant en test
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 IntegerDansHote()
// BDDmère ou APP ou client
// intégrer le composant dans la base hôte, pour les interfaces :
If (Storage.System.estExecuteDansHote)
// dans cette version rien à faire
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; "Installer Albums"; "Intégration dans base hôte")
End if
//---------------------
// MARK:Travail
//---------------------
Function CréerDossierDeTravail($nomDossier : Text)
// créer le dossier de travail
This.dossierTravail:=This.document.getDossierTravail().folder($nomDossier)
This.dossierTravail.delete(Delete with contents)
This.dossierTravail.create()
Function SupprimerDossierDeTravail()
This.dossierTravail.delete(Delete with contents)
// ----------------------
// MARK:Modification
// ----------------------
Function ModifierTraduction()
// ouvrir l'éditeur des traductions
cs.xSDK.TraductionsEditeur.new().ModifierTraductions()
⇧
[class]_FTP - 22/02/2026 09:18:57
property fct : cs.xSDK.Outils
property rsc : cs.xSDK.ResourceALV
property serveur : cs.xSDK.ServicesFTP
property racineFTP : Text
Class constructor()
var $data : Object
This.fct:=cs.xSDK.Outils.me
This.rsc:=cs.xSDK.ResourceALV.me
// paramètres de connexion FTP
$data:=This.FixerParametresFTP()
This.racineFTP:=$data.racine
This.serveur:=cs.xSDK.ServicesFTP.new($data.params)
Function ListerLesDocuments($requête : Object)->$result : cs._Trace
var $path : Text
var $c : Collection
var $élément : Object
$result:=cs._Trace.me.Initialiser(Current method name)
// options :
// - bit 0 = les fichiers
// - bit 1 = les dossiers
// - bit 2 = chemin récursif
If (OB Is defined($requête; "pathDossier"))
// cas normal
$path:=$requête.pathDossier
Else
// le demandeur ne connait rien du contexte
$path:=This.racineFTP
End if
$c:=New collection
$result.Error:=This.serveur.ListerLesDocuments($path; ->$c).Error
If (OB Is defined($requête; "Options"))
// on renvoie le path absolu des fichiers dans .pathsDocuments, nom dans .listeDocuments, date, heure et taille dans .listeInfos
// Attention : les collections doivent avoir été créées à l'appel pour être reçues en retour
If (Not(OB Is defined($requête; "pathsDocuments")))
// pour que la suite fonctionne ! rappel : les collections créées ici ne sont pas reçues par l'appelant
$requête.pathsDocuments:=New collection
$requête.listeDocuments:=New collection
$requête.listeInfos:=New collection
End if
// ajouter les documents trouvés
// pas d'options
Case of
: ($c.length=0)
: ($requête.Options ?? 0)
// ajouter les fichiers à la collection
For each ($élément; $c)
Case of
: ($élément.nom=".@")
: ($élément.type#Is a document)
Else
// ajouter le chemin du fichier
$requête.pathsDocuments.push($path+$élément.nom)
// ajouter le nom du fichier
$requête.listeDocuments.push($élément.nom)
If (OB Is defined($requête; "listeInfos"))
$requête.listeInfos.push(OB Copy($élément))
End if
End case
End for each
: ($requête.Options ?? 1)
// ne garder que les dossiers
For each ($élément; $c)
Case of
: ($élément.nom=".@")
: ($élément.type#Is a folder)
Else
// ajouter le chemin du dossier
$requête.pathsDocuments.push($path+$élément.nom+"/")
// ajouter le nom du fichier
$requête.listeDocuments.push($élément.nom)
End case
End for each
End case
// récursivité des dossiers
If ($requête.Options ?? 2)
// mémoriser le niveau courant
$path:=$requête.pathDossier
For each ($élément; $c)
If ($élément.type=Is a folder)
// relancer avec ce dossier
$requête.pathDossier:=$path+$élément.nom+"/"
$result:=This.ListerLesDocuments($requête)
End if
End for each
End if
End if
$result.FixerSuccess()
Function TéléchargerFichier($data : Object)->$result : cs._Trace
var $info : Object
$result:=cs._Trace.me.Initialiser(Current method name)
$result.Error:=-15068
Case of
: (Not(OB Is defined($data; "pathFichier")))
$result.ErrorDescription:="le paramètre 'pathFichier' n'est pas renseigné"
: (Not(OB Is defined($data; "cheminFichier")))
$result.ErrorDescription:="le paramètre 'cheminFichier' n'est pas renseigné"
: (Not(This.serveur.getFileInfo($data.pathFichier; ->$info).success))
$result.Error:=10060
$result.ErrorDescription:="err 'getFileInfo'"
Else
// ok, on a tout
// télécharger
$result.Error:=This.serveur.RecevoirFichier($data.pathFichier; $data.cheminFichier).Error
$result.ErrorDescription:=("err 'RecevoirFichier'")*Num($result.Error#0)
// restaurer l'horodatage original du fichier
If ($result.Error=0)
SET DOCUMENT PROPERTIES($data.cheminFichier; False; False; OB Get($info; "date"; Is date); OB Get($info; "heure"; Is time); OB Get($info; "date"; Is date); OB Get($info; "heure"; Is time))
End if
// retourner le fichier téléchargé
$result.fichier:=File($data.cheminFichier; fk platform path)
End case
$result.FixerSuccess()
Function TéléchargerFichierMedia($data : Object)->$result : cs._Trace
var $fichier : Object
$result:=cs._Trace.me.Initialiser(Current method name)
$fichier:=File($data.dossierTravail.platformPath+$data.nomFichier; fk platform path)
If ($fichier.exists)
$result.fichier:=$fichier
$result.success:=True
Else
// télécharger
$result:=This.TéléchargerFichier($data)
End if
Function FixerParametresFTP()->$result : cs._Trace
// fixer les noms d'hébergement, identif et mdp pour la connexion à l'host des albums ALV
// en principe uniquement la BDD mère
// lire les données dans le fichier en ressource
var $hébergement; $identifiant; $motDePasse; $path : Text
$result:=cs._Trace.me.Initialiser(Current method name)
$hébergement:=""
$identifiant:=""
$motDePasse:=""
$result.Error:=-15012
Case of
: (Not(This.rsc.SetVariable(Est Ressource ALB; "ConnexionFTP/NomHost"; Is text; ->$hébergement)))
$result.ErrorDescription:="Connexion/NomHost"
: (Not(This.rsc.SetVariable(Est Ressource ALB; "ConnexionFTP/Identifiant"; Is text; ->$identifiant)))
$result.ErrorDescription:="Connexion/Identifiant"
: (Not(This.rsc.SetVariable(Est Ressource ALB; "ConnexionFTP/MotDePasse"; Is text; ->$motDePasse)))
$result.ErrorDescription:="Connexion/MotDePasse"
// lire le chemin du dossier des albums
: (Not(This.rsc.SetVariable(Est Ressource ALB; "chemins/FTP/Path"; Is text; ->$path)))
$result.ErrorDescription:="Connexion/NomHost"
Else
// c'est ok
$result.Error:=0
$result.ErrorDescription:=""
$result.params:=New object
$result.params.hébergement:=$hébergement
$result.params.user:=New object
$result.params.identifiant:=$identifiant
$result.params.motDePasse:=$motDePasse
// serveur un peu lent à se réveiller !
$result.params.timeOut:=30
$result.racine:=$path
End case
$result.LeverException([msgk_event])
⇧
[class]$serveurWeb - 30/05/2025 18:39:35
property balisesUrl; paramsUrl : Collection
Class extends _composant
Class constructor()
Super()
//---------------------
// MARK:Requete HTTP
//---------------------
Function AuthentificationWeb($url : Text; $header : Text; $BrowserIP : Text; $ServerIP : Text; $user : Text; $password : Text)->$success : Boolean
// renvoie vrai
// si l'url $1 est concerné par ce serveur.
// et si l'url $1 est accepté.
// en cas serveur Web, depuis v8.2 les albums sont intégrés à la session webUser => l'authentification est donc faite avec $3
// pour les tests, ou l'anti piratage, faire une authentification locale
This.InitProcess()
// par défaut, refus accès ou pas traité ici
$success:=False
Case of
: ($url="/4DCGI/WebAlbums/seConnecter")
Case of
: (Count parameters>1)
// appel depuis le serveur WEB, $3 = un user
Case of
: (Not(OB Is defined(Session)))
: (Not(OB Is defined(Session.storage; "WebUser")))
: (Not(OB Is defined(Session.storage.WebUser; "droits")))
// pas normal !
Else
// ok on a un user
// vérifier son droit d'accès à l'espace privé (en principe déjà fait)
// on pourrait tester un bit propre à ALB
$success:=(Session.storage.WebUser.droits ?? 0)
// ici session.userName est renseigné, Session.isGuest()=faux
This.InitDataSession()
End case
Else
// appel local (test), ou piratage de l'url ...
// même logique que le serveur Web
Case of
// Pour des raisons de sécurité, refuser les noms qui contiennent @
: (($user="") | cs.xSDK.Outils.me.ContientJoker($user))
// $utilisateur="" dans 2 cas : init de la saisie LogIn/pwd et annulation de la saisie
If (wwwEtatNavigationALB#"authentification")
// ici on vient de cliquer sur "espace privé"
wwwEtatNavigationALB:="authentification"
// début du processus (demande d'identif par le navigateur)
Else
// Ici : annulation de l'authentification par user
cs.$pageWeb.new("").Envoyer("AlbumsAccueil.shtml"; "accueil")
End if
// refus demander au navigateur un LogIn / MdP
Else
// identifiant et MdP ont été saisis par le webUser : reconnaître l'utilisateur
$success:=WEB Validate digest($user; $password)
End case
End case
//: ($url="/4DCGI/Album/consulter@")
: ($url="/4DCGI/Album/editer@")
// est un albums user ?
//$3->:=ALB_Traiter Action HTML ($url;->$utilisateur)
$success:=True
: ($url="/4DCGI/WebAlbum@")
// les autres url sont autorisées si une session existe
$success:=True
: (($url="@4DSCRIPT/ALB_@") | ($url="@ressourcesAlbum@"))
// les commandes / ressources sont autorisées si une session existe
$success:=True
Else
// pas concerné, passer la main
$success:=False
End case
// initialiser les variables process
This.RestaurerHTTPvars()
Function ConnexionWeb($url : Text; $header : Text; $BrowserIP : Text; $ServerIP : Text; $user : Text; $password : Text)->$success : Boolean
// appel du composant ; ici on est thread-safe
var $result : Object
This.InitProcess()
$result:=This.TraiterURL($url)
$success:=($result.Error=0)
Function TraiterURL($url : Text)->$result : Object
// traiter toutes les url envoyées par le formulaire le client HTML (construction formulaire ou action) ou une requete HTTP javascript
// traiter toutes les url envoyées par le client HTTP ou par un formulaire en construction
// remarque : dans les 2 cas, les url ont le même format :
// /4Dxxx/yyy/nomFunction {?param1 {?param2...}}, avec xxx = CGI ou ACTION ou SCRIPT, yyy le contexte d'une classe ($pageWebyyy)
// transmettre l'url à la bonne classe $page pour traitement
$result:=cs._Trace.me.Initialiser(Current method name)
$result.resultat:=""
// analyser l'url
This.LireParametresUrl($url)
Case of
: (This.balisesUrl=Null)
$result.Error:=-15068
$result.ErrorDescription:="url incomplète - pas de balise à traiter"
: (This.balisesUrl.length<2)
$result.Error:=-15068
$result.ErrorDescription:="url incomplète - le contexte de $page, balisesUrl[1], est absent"
: (Not(OB Is defined(cs; "$page"+This.balisesUrl[1])))
// url non traitée ici
$result.Error:=-15068
$result.ErrorDescription:="url incomplète - la classe $page"+This.balisesUrl[1]+" est inconnue"
Else
// créer la classe et traiter l'url
// le résultat, s'il y a, est dans $result.resultat
// en cas d'erreur elle peut renseigner $result
$result:=cs["$page"+This.balisesUrl[1]].new($url).TraiterURL()
End case
// on ne lève pas d'exception (l'historique des requêtes Web est dans le journal WEB)
Function LireParametresUrl($url : Text)
// séparer $url en :
// une liste de balises 4Dxxx, yyy, nomFunction
// une liste de paramètres param1 param2...
// lire les balises
This.balisesUrl:=Split string($url; "/"; sk ignore empty strings)
This.paramsUrl:=New collection
Case of
: ($url="")
: (This.balisesUrl.length=0)
Else
// analyser la function
This.paramsUrl:=Split string(This.balisesUrl[This.balisesUrl.length-1]; "?")
// virer function+params
This.balisesUrl:=This.balisesUrl.slice(0; This.balisesUrl.length-1)
// ajouter la function seule
This.balisesUrl.push(This.paramsUrl[0])
End case
//---------------------
// MARK:Serveur Web
//---------------------
Function InitDataSession()
// initialiser les variables session
This.InitData()
This.InitUserParams()
This.InitHTTPvars()
Function InitData()
var $data : Object
// variables par défaut pour la construction du site web ; surchargeables
Use (Session.storage)
Session.storage.ALB_Data:=New shared object
End use
$data:=Session.storage.ALB_Data
Use ($data)
$data.urlOuvrirALB:="/WebAlbums/OuvrirAlbum"
$data.urlVisualiserALB:="/WebAlbums/VisualiserAlbum"
$data.pageAccueilALB:="AlbumsAccueil.shtml"
$data.pageVisuALB:="AlbumVisualiser.shtml"
$data.avecContact:=True
End use
Function InitUserParams()
var $data : Object
// variables utilisées dans les sélections courantes
// au gré de la navigation de l'utilisateur
Use (Session.storage)
Session.storage.ALB_UserParams:=New shared object
End use
$data:=Session.storage.ALB_UserParams
Use ($data)
$data.Albums:=New shared collection
$data.Album:=New shared object
$data.Medias:=New shared collection
End use
Function InitHTTPvars()
var $data : Object
// variables utilisées dans les templates de page Web
// en cours de recherche ou de saisie utilisateur
// RAPPEL : cette liste sert à initialiser TOUTES les variables proces ; toutes ces variables doivent être dans cette liste
Use (Session.storage)
Session.storage.ALB_HTTPvars:=New shared object
End use
$data:=Session.storage.ALB_HTTPvars
Use ($data)
// init accueil
$data.wwwURLalbum:=""
$data.wwwTitre:=""
$data.wwwComment:=""
$data.wwwRacineRessources:="../../" // depuis v5.2 les 4DURL sont de la forme /4Dxxx/xxx/yyy
End use
Function InitContexte($url : Text)
var $vs4D : Text
$vs4D:="4D4D"
Case of
: ($url="@albums@")
$vs4D:="4D4Dalbum"
End case
// stocker
Use (Session.storage.ALB_HTTPvars)
Session.storage.ALB_HTTPvars.vs4D:=$vs4D
End use
Function RestaurerHTTPvars()
// 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 variables process (utile en mode interprété)
COMPILER_WEB
If (OB Is defined(Session.storage; "ALB_HTTPvars"))
$data:=Session.storage.ALB_HTTPvars
For each ($attribut; $data)
$ptr:=Get pointer($attribut)
Case of
: (Undefined($ptr->))
// pas normal
cs._Trace.me.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
⇧
[class]$albumsVisualisateur - 06/02/2026 17:48:52
property IndexCatalogue; urlMenuNav : Text
property nomZoneWeb : Text:="ZoneNav"
Class extends _albums
Class constructor()
Super()
This.grandEcran:=True
This.mémoTaille:=False
// ----------------------
// sélection
// -----------------------
Function ListerAlbums()
// alimenter le sélecteur d'albums
// collectionner les albums
Super.ListerAlbums()
// créer la collection des albums avec cette collection
Form.selecteur.values:=This.nomsAlbums
// ----------------------
// formulaire
// -----------------------
Function OuvrirFormulaire()
// méthode du process U_Nav_Album_
var $wndNum : Integer
var $nomForm : Text
// créer la fenêtre
If (Is Windows)
Else
$nomForm:="Visualiser Album"
// ici on ne veut pas de palettes
// ??? This.$formulaire.FixerVisibilitéPalettes(False)
HIDE MENU BAR
$wndNum:=Open form window($nomForm; Plain form window; *)
Case of
: (Not(Storage.System.estExecuteDansHote))
SHOW MENU BAR
: (Not(This.grandEcran))
Else
SET WINDOW RECT(0; 0; Screen width; Screen height; $wndNum)
End case
SET WINDOW TITLE(""; $wndNum)
DIALOG($nomForm; This)
CLOSE WINDOW
CLEAR VARIABLE($wndNum)
End if
Function InitFormulaire()
// un album doit exister ; préparer l'affichage
Case of
: (OB Is empty(This.Album))
: (Not(OB Is defined(This.Album; "Medias")))
: (This.Album.Medias.length=0)
Else
This.TrierMedias()
This.CréerBarreNavigation()
WA OPEN URL(*; This.nomZoneWeb; This.urlMenuNav) // Ouvrir la page Web
// afficher le premier media
This.indexMedia:=0
This.AfficherMedia()
End case
Function CréerBarreNavigation()
// créer une page html avec la liste des icones de l'album
var $fichier; $template : Text
var $texteBlobé : Blob
$fichier:=Folder(Get 4D folder(Current resources folder); fk platform path).folder("TemplatesPagesWeb").file("AlbumNavigation.shtml").platformPath
DOCUMENT TO BLOB($fichier; $texteBlobé)
$template:=BLOB to text($texteBlobé; UTF8 text without length)
// ici on n'a pas les données du serveur Web => le domaine est en DUR dans le template
PROCESS 4D TAGS($template; $template; This.Album.Medias; This.Album.Identification_ID)
SET BLOB SIZE($texteBlobé; 0)
TEXT TO BLOB($template; $texteBlobé; UTF8 text without length)
$fichier:=This.requête.dossierTravail.platformPath+"ALV_navALB.htm"
BLOB TO DOCUMENT($fichier; $texteBlobé)
This.urlMenuNav:=$fichier
Function AfficherMedia()
// afficher le media courant
var $data; $result : Object
var $image : Picture
This.mediaInformation:=This.Album.Medias[This.indexMedia]
// convertir la date
Form.HoroDate:=Date(This.mediaInformation.MED_HoroDate)
// afficher la photo
$data:=This.requête
$data.pathFichier:=This.mediaInformation.pathFichier
$data.nomFichier:=This.mediaInformation.nomFichier
$result:=This.FTP.TéléchargerFichierMedia($data)
If ($result.success)
READ PICTURE FILE($result.fichier.platformPath; $image)
Form.photo:=$image
End if
This.IndexCatalogue:=String(This.indexMedia+1)+" / "+String(This.Album.Medias.length)
// ----------------------
// MARK: navigation
// -----------------------
Function PremierMedia()
This.indexMedia:=0
This.AfficherMedia()
Function MediaPrécédent()
If (This.indexMedia=0)
BEEP
Else
This.indexMedia:=This.indexMedia-1
This.AfficherMedia()
End if
Function MediaSuivant()
If (This.indexMedia=(This.Album.Medias.length-1))
BEEP
Else
This.indexMedia:=This.indexMedia+1
This.AfficherMedia()
End if
Function DernierMedia()
This.indexMedia:=This.Album.Medias.length-1
This.AfficherMedia()
Function AllerAMedia($index : Integer)
This.indexMedia:=$index
This.AfficherMedia()
⇧
[class]$pageWebAlbums - 06/02/2026 17:48:59
Class extends $pageWeb
Class constructor($url : Text)
Super($url)
// les paramètres sont récupérés
Function TraiterURL()->$result : cs._Trace
// traitement des url de la page albums
This.result.Initialiser(Current method name)
// normalement .balise[2] est le nom d'une function de $pageXXX
Case of
: (This.balisesUrl.length<3)
This.result.Error:=-15068
This.result.ErrorDescription:="url incorrecte - il manque la paramètres .balise[2]"
: (Not(OB Is defined(This; This.balisesUrl[2])))
This.result.Error:=-15068
This.result.ErrorDescription:="url incorrecte - .balise[2] '"+This.balisesUrl[2]+"' n'est pas une function de this "+OB Class(This).name
Else
// on accepte la demande
// le résultat, s'il y a, est dans this.result.resultat
// exécuter la function, en cas d'erreur elle peut renseigner This.result
This[This.balisesUrl[2]]()
End case
$result:=This.result
Function seConnecter()
// l'authentification est ok
This.Envoyer(Session.storage.ALB_Data.pageAccueilALB; "accueilAlbums")
Function ListerAlbums()
var $dossiers : Collection
// dossier des albums (v6.4.6 dans le dossier Web)
$dossiers:=This.document.getWEBalbumsFolder_Courant().folders()
This.Lister($dossiers)
Function Lister($dossiers : Collection)
var $result; $path : Text
var $album : Object
This.getListe($dossiers)
Case of
: (Session.storage.ALB_UserParams.Albums=Null)
// pas normal
$result:="<h2>"+Localized string("1010")+"</h2>"
: (Session.storage.ALB_UserParams.Albums.length=0)
// pb installation, ou utilisateur invité
$result:="<h2>"+Localized string("1009")+"</h2>"
Else
$result:=""
For each ($album; Session.storage.ALB_UserParams.Albums)
$path:=Session.storage.ALB_Data.urlOuvrirALB+"?"+$album.Identification_ID
$result:=$result+"<h2>"+"<a href="+Char(Double quote)+"/4DCGI"+$path+Char(Double quote)+">"+$album.Nom+"</a>"+"</h2>"
// contact
If (Session.storage.ALB_Data.avecContact)
$result:=$result+"<h3><spam>"+This.getContact($album)+"</spam></h3>"
End if
End for each
End case
This.result.resultat:=Char(1)+$result
Function getListe($dossiers : Collection)
// lister les albums disponibles sur le serveur Web et filtrer ceux autorisés par le WebUser
// peut être appeler par l'extérieur
var $dataTexte : Text
var $IDfamille : Integer
var $c : Collection
var $dossier; $fichier; $metaData : Object
// lire l'ID du groupe familial du WebUser
Case of
: (Not(Storage.System.estExecuteDansHote))
$IDfamille:=-1001
: (Storage.System.typeApplication=ALV Serveur HTTP)
// test avec 4D Serveur
$IDfamille:=-1001
: (Storage.System.typeApplication=ALV BDD mère)
// pour test
$IDfamille:=-1001
: (Session.isGuest())
$IDfamille:=-1
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Accueil albums [KO]"; Current method name; "Session invité, groupe familial "+String($IDfamille))
: (OB Is defined(Session.storage; "userInfo"))
// ok session mobile
$IDfamille:=Session.storage.userInfo.IDfamille
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Accueil albums [OK]"; Current method name; "Session Mobile, groupe familial "+String($IDfamille))
: (OB Is defined(Session.storage; "WebUser"))
// ok session Web
$IDfamille:=Session.storage.WebUser.leGroupe.IDfamille
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Accueil albums [OK]"; Current method name; "Session Web, groupe familial "+String($IDfamille))
Else
// c'est ko
$IDfamille:=0
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Accueil albums [KO]"; Current method name; "Aucune session, groupe familial "+String($IDfamille))
End case
$c:=New collection
Case of
: ($dossiers.length=0)
: ($IDfamille=0)
$c:=Null
Else
For each ($dossier; $dossiers)
// lire les droits de l'album $dossier
$fichier:=$dossier.file("Data_ALB.json")
// c'est bon
$dataTexte:=Document to text($fichier.platformPath; "UTF-8"; Document with LF)
$metaData:=JSON Parse($dataTexte; Is object)
Case of
: ($fichier=Null)
// les droits sont un objet avec NomGroupe, IDgroupe (familial) et Droits (vrai si la famille y a accès)
: ($metaData.Acces_Droits.query("IDgroupe = :1 AND Droits = :2"; $IDfamille; True).length=0)
Else
// le WebUser a acces à cet album
$c.push($metaData)
End case
End for each
$c:=$c.orderBy("Identification_Nom")
// mémoriser
sharedCollection($c; Session.storage.ALB_UserParams.Albums)
End case
Function OuvrirAlbum()
// ici on est authentifié; on ouvre un album
var $dataTexte : Text
var $sélection : Collection
// .paramsUrl[0] est l'ID de l'album
If (This.paramsUrl.length>0)
// lire les informations de l'album sélectionné
// adresse des données de l'album sur le site
This.rsc.SetVariable(Est Ressource ALB; "chemins/albums/URL"; Is text; ->$dataTexte)
Use (Session.storage.ALB_HTTPvars)
Session.storage.ALB_HTTPvars.wwwURLalbum:="/"+$dataTexte+"/"+This.paramsUrl[1]+"/"
End use
// récupérer les données de l'album
This.result.ErrorDescription:="ouverture de "+This.paramsUrl[0]+" : absence de catalogue"
$sélection:=Session.storage.ALB_UserParams.Albums.query("Identification_ID = :1"; This.paramsUrl[1])
Case of
: ($sélection=Null)
: ($sélection.length=0)
Else
Use (Session.storage.ALB_UserParams)
Session.storage.ALB_UserParams.Album:=$sélection[0]
End use
Use (Session.storage.ALB_HTTPvars)
// mémoriser le nombre de medias (sert dans la navigation)
Session.storage.ALB_HTTPvars.wwwNbrMedias:=$sélection[0].Medias.length
// initialiser la variable (mais on ne décide rien ici)
Session.storage.ALB_HTTPvars.wwwIndexMedia:="0"
End use
End case
// remarque : l'erreur est envoyée à l'hôte
// ouvrir l'album
This.Rediriger("/4DCGI"+Session.storage.ALB_Data.urlVisualiserALB)
Else
// retour à l'accueil
This.Rediriger("/4DCGI/Albums/seConnecter")
End if
Function VisualiserAlbum()
// ouvrir l'album initialisé par ailleurs
This.Envoyer(Session.storage.ALB_Data.pageVisuALB; "visualisationAlbum")
⇧
[class]_Trace - 29/05/2025 09:46:09
property cible : cs.xSDK.Traces
property success : Boolean
// propriétés spécifiques des functions appelantes
property Album : Object
property fichier : 4D.File
property resultat : Text
property params : Object
property racine : Text
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("ALB"; Error; Error method; Error line; Error formula)
Function Initialiser($nomMethode : Text)->$result : Object
This.cible.CréerErreur("ALB"; 0; $nomMethode; "")
$result:=This
Function Créer($Error : Integer; $nomMethode : Text; $ErrorDescription : Text)->$result : Object
This.cible.CréerErreur("ALB"; $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; "ALB"; $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; "ALB"; $libellé; $source; $description)
⇧
[class]$pageWebREQUETE - 20/01/2025 10:45:33
Class extends $pageWeb
Class constructor($url : Text)
Super($url)
// les paramètres sont récupérés
Function LireMedia()
var $path; $texte; $titre : Text
var $index; $largeur; $hauteur : Integer
// il faudrait tester le Type de media (image / video)
$index:=This.getIndexMedia()
// chemin du fichier media
$path:=This.getMediaData("nomFichier"; $index)
$path:=This.document.getWEBalbumMediasURL()+$path
$largeur:=Num(This.getMediaData("MED_DimensionX"; $index))
$hauteur:=Num(This.getMediaData("MED_DimensionY"; $index))
$titre:=This.getMediaData("MED_Nom"; $index)
$texte:="<a>"
$texte:=$texte+"<img src="+Char(Double quote)+$path+Char(Double quote)+" title="+Char(Double quote)+$titre+Char(Double quote)
// terminer la balise (format flex)
Case of
: ($largeur>=$hauteur)
$texte:=$texte+" width="+Char(Double quote)+"100%"+Char(Double quote)+"/>"
: ($largeur<$hauteur)
$texte:=$texte+" height="+Char(Double quote)+"100%"+Char(Double quote)+"/>"
End case
$texte:=$texte+"</a>"
WEB SEND TEXT($texte; "texte/html")
Function LireMediaData()
// renvoyer la donnée du media
var $index : Integer
var $texte : Text
$index:=This.getIndexMedia()
$texte:=This.getMediaData(This.paramsUrl[1]; $index)
WEB SEND TEXT($texte; "texte/html")
Function LireMediaDate()
// renvoyer la date du media
var $index : Integer
var $texte : Text
$index:=This.getIndexMedia()
$texte:=This.getMediaData(This.paramsUrl[1]; $index)
$texte:=String(Date($texte); Internal date long)
WEB SEND TEXT($texte; "texte/html")
Function LireNumerotation()
var $texte : Text
$texte:=String(Num(This.paramsUrl[1])+1)+" / "+String(Session.storage.ALB_UserParams.Album.Medias.length)
WEB SEND TEXT($texte; "texte/html")
Function getMediaData($attribut : Text; $index : Integer)->$result : Text
// renvoyer la donnée du media
// attention on renvoie un texte ou un integer
var $variant : Variant
$result:="errMediaData"
Case of
: ($index<0)
: ($attribut="")
// il faut un nom de data
: (Not(OB Is defined(Session.storage.ALB_UserParams.Album.Medias[$index]; $attribut)))
Else
// c'est bon
$variant:=Session.storage.ALB_UserParams.Album.Medias[$index][$attribut]
If ((Value type($variant)=Is real) | (Value type($variant)=Is longint))
$result:=String($variant)
Else
$result:=$variant
End if
End case
Function getIndexMedia()->$result : Integer
// l'index est le dernier paramètre de l'url
$result:=-1
Case of
: (This.paramsUrl.length<1)
// il faut au moins 2 params
: (Num(This.paramsUrl[This.paramsUrl.length-1])>Session.storage.ALB_UserParams.Album.Medias.length)
Else
$result:=Num(This.paramsUrl[This.paramsUrl.length-1])
End case
⇧
[class]_sauvegarde - 22/10/2025 11:04:05
Class extends _albums
Class constructor()
Super()
Function Démarrer($params : Object)
// sauvegarder toutes les albums sur les disques désignés dans les préférences
var $numProgress; $i : Integer
var $pict : Picture
This.params.userIDfamille:=-1
// lister les albums
This.ListerAlbums()
// c'est parti pour une sauvegarde
$numProgress:=Progress New
Progress SET WINDOW VISIBLE(False; 40; Screen height-200)
READ PICTURE FILE(Folder(fk resources folder).folder("images").file("16005.gif").platformPath; $pict)
Progress SET ICON($numProgress; $pict)
Progress SET WINDOW VISIBLE(True; -1; -1; True)
Case of
: (Not(OB Is defined($params; "chemin")))
// il faut un chemin de disque !
: (Not(Folder($params.chemin; fk platform path).exists))
// disque de sauvegarde non monté, la BDD mère gère les erreurs
Progress SET TITLE($numProgress; Localized string("5115")+" '"+$params.chemin+"'"; 0; ""; False)
Waiting(60*10)
Else
// c'est ok, on a un dossier accessible
Progress SET TITLE($numProgress; Localized string("5116")+" '"+$params.chemin+"'"; 0; ""; False)
For ($i; 0; This.pathsAlbums.length-1)
Progress SET PROGRESS($numProgress; ($i)/This.pathsAlbums.length; Localized string("5132")+" '"+This.listeAlbums[$i]+"'"; True)
// créer le dossier de travail
This.CréerDossierDeTravail("ALB_sauvegarde_FTP")
This.SauvegarderAlbum(This.pathsAlbums[$i]; $params.chemin)
This.SupprimerDossierDeTravail()
End for
End case
Progress SET PROGRESS($numProgress; 1)
Waiting(60)
Progress QUIT($numProgress)
Function SauvegarderAlbum($url : Text; $cheminDossier : Text)
var $result; $data; $params : Object
var $i : Integer
// lister tous les documents de l'album
$data:=New object
$data.pathDossier:=$url
$data.pathsDocuments:=New collection
$data.listeDocuments:=New collection
$data.listeInfos:=New collection
// tous les documents de tous les dossiers
$data.Options:=0x0005
$result:=This.FTP.ListerLesDocuments($data)
Case of
: (Not($result.success))
: ($data.pathsDocuments.length=0)
: (Test path name($cheminDossier)#Is a folder)
Else
// on y va
$params:=New object
$params.dossier:=Folder($cheminDossier; fk platform path)
// au cas ou
$params.dossier.create()
For ($i; 0; $data.pathsDocuments.length-1)
$params.pathFichier:=$data.pathsDocuments[$i]
This.SauvegarderFichier($params; $data.listeInfos[$i])
End for
End case
Function SauvegarderFichier($params; $info : Object)
// créer / remplacer le fichier $url dans le dossier $dossier
// on remplace si le fichier a changé
var $fichier; $result : Object
var $c : Collection
var $nom : Text
$result:=New object
// nom / chemin du fichier
$c:=Split string($params.pathFichier; "/"; sk ignore empty strings)
// nom du fichier
$nom:=$c.pop()
// chemin du dossier du fichier
$fichier:=$params.dossier.folder($c.join("/"))
// au cas ou
$fichier.create()
// chemin du fichier
$fichier:=$fichier.file($nom)
$result.success:=True
Case of
// on n'a pas de dossier
: ($params.pathFichier="@ @")
// ici on n'aime pas les blancs (génère une erreur FTP)
$result.success:=False
: (Not($fichier.exists))
: ($fichier.modificationDate<$info.modificationDate)
: ($fichier.modificationDate>$info.modificationDate)
: ($fichier.modificationTime<$info.modificationTime)
Else
// fichier à jour
$result.success:=False
End case
If ($result.success)
// v10.6.12 : toujours télécharger en local puis copier sur le support externe
$params.cheminFichier:=This.dossierTravail.file("tempo_"+String(Random)).platformPath
$result:=This.FTP.TéléchargerFichier($params)
COPY DOCUMENT($params.cheminFichier; $fichier.platformPath)
End if
⇧
[class]$pageWeb - 22/10/2025 11:04:13
property result : cs._Trace
Class extends $serveurWeb
Class constructor($url : Text)
Super()
This.LireParametresUrl($url)
This.result:=cs._Trace.me
//----------------------------------
// MARK:Element HTML
//----------------------------------
Function LireSTR()
This.result.resultat:=""
If (This.paramsUrl.length>0)
This.result.resultat:=Localized string(This.paramsUrl[1])
End if
This.result.resultat:=Char(1)+This.result.resultat
Function Date()
var $result : Text
var $date : Date
$result:=""
This.rsc.SetVariable(Est Ressource APP; "Ressources_Communes/Nom_Application"; Is text; ->$result)
$date:=!00-00-00!
This.rsc.SetVariable(Est Ressource ALB; "Site_Web/Date_Mise_a_jour"; Is date; ->$date)
$result:=$result+" - Le "+String($date; Internal date long)
This.result.resultat:=Char(1)+$result
Function getContact($album : Object)->$result : Text
$result:="Pas de contact"
Case of
: (Not(OB Is defined($album; "Contact_eMail")))
: (Not(OB Is defined($album; "Contact_Nom")))
Else
$result:="Contact <a href=mailto:"+$album.Contact_eMail+">"+$album.Contact_Nom+"</a>"
End case
//----------------------------------
// MARK:Requetes HTTP
//----------------------------------
Function TraiterURL()->$result : cs._Trace
// traitement des url
This.result.Initialiser(Current method name)
// normalement .balise[2] est le nom d'une function de $pageXXX
Case of
: (This.balisesUrl.length<3)
This.result.Error:=-15068
This.result.ErrorDescription:="url incorrecte - il manque la paramètres .balise[2]"
: (Not(OB Is defined(This; This.balisesUrl[2])))
This.result.Error:=-15068
This.result.ErrorDescription:="url incorrecte - .balise[2] '"+This.balisesUrl[2]+"' n'est pas une function de this "+OB Class(This).name
Else
// on accepte la demande
// le résultat, s'il y a, est dans this.result.resultat
// exécuter la function, en cas d'erreur elle peut renseigner This.result
This[This.balisesUrl[2]]()
End case
$result:=This.result
//----------------------------------
// MARK:Gestion des pages
//----------------------------------
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 de construction de la page .shtml
This.RestaurerHTTPvars()
WEB SEND FILE($nomPageWeb)
Function Rediriger($url : Text)
WEB SEND HTTP REDIRECT($url)
⇧
[class]$photos - 04/03/2026 11:33:27
property dossierPhotos : 4D.Folder
property catalogueMedias; catalogueMediasHost : Object
Class extends _maintenance
Class constructor()
Super()
//---------------------
// MARK:Traitement Photos
//---------------------
Function MettreAjour()
// copie des albums locaux vers le dossier de l'hébergeur
// attention, telle que conçue cette function n'est appelable que sur la machine hôte du serveur APP (lecture directe des dossiers)
var $c : Collection
var $dossier : 4D.Folder
If ((Storage.System.typeApplication=ALV Serveur APP) | (Not(Storage.System.estExecuteDansHote)))
// c'est parti
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; Current method name; "Mise à jour des albums PhotosSyno")
// créer le dossier de travail
This.CréerDossierDeTravail("ALB_FTP")
// initialiser FTP
This.FixerParametresFTPphotos()
// liste des albums locaux
$c:=This.document.getWEBalbumsFolder().folders(fk ignore invisible)
Case of
: (This.FTP.racineFTP="")
: ($c.length=0)
Else
For each ($dossier; $c)
This.MettreAjourDossierPhotos($dossier)
End for each
End case
This.SupprimerDossierDeTravail()
End if
Function MettreAjourDossierPhotos($dossier : 4D.Folder)
// $dossier est un dossier album sur le disque dur local
var $dataTexte : Text
var $fichiers : Collection
var $fichier : 4D.File
// lire le catalogue des albums
$fichier:=$dossier.file("Data_ALB.json")
$dataTexte:=$fichier.getText("UTF-8"; Document with LF)
This.catalogueMedias:=JSON Parse($dataTexte; Is object)
ASSERT(cs._Trace.me.DebugerMethode("Catalogue"; Current method name; "Mise à jour de l'album '"+This.catalogueMedias.Identification_Nom+"'"))
// lister les medias de $dossier (dans disque dur local) et ses sous dossiers
$fichiers:=This.document.getWEBalbumMediasFolder(This.catalogueMedias.Identification_ID).files(fk recursive+fk ignore invisible)
Case of
: (Not(This.ListerDocumentsDeDossierPhotos(This.catalogueMedias.Identification_Nom).success))
// la liste des données de l'album Photos sur l'hébergeur est vide
: ($fichiers.length=0)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Traitement"; Current method name; "L'album '"+This.catalogueMedias.Identification_Nom+"' local est vide")
Else
// dossier des photos à envoyer
This.dossierPhotos:=This.dossierTravail.folder(This.catalogueMedias.Identification_Nom)
This.dossierPhotos.create()
For each ($fichier; $fichiers)
If (This.estPhotoAncienne($fichier))
// créer le document à ajouter à $listeSyno
This.CréerPhoto($fichier)
End if
End for each
If (This.dossierPhotos.files(fk ignore invisible).length>0)
// il y a des modifications
// mettre à jour le catalogue des medias de l'album au dossier
$fichier:=$dossier.file("Data_ALB.json")
$fichier.copyTo(This.dossierPhotos)
// transférer le tout vers l'hébergeur
This.Téléverser()
Else
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Traitement"; Current method name; "pas de mise à jour de l'album '"+This.catalogueMedias.Identification_Nom+"'")
End if
This.dossierPhotos.delete(Delete with contents)
End case
Function CréerPhoto($fichier : 4D.File)
var $catalogueMedia; $params : Object
var $image : Picture
var $fichierSVG : 4D.File
// récupérer les données du fichier
Try
$catalogueMedia:=This.catalogueMedias.Medias.query("nomFichier = :1"; $fichier.fullName)[0]
Catch
$catalogueMedia:=Null
End try
If (Not($catalogueMedia=Null))
$params:=New object
$params.width:=$catalogueMedia.MED_DimensionX
$params.height:=$catalogueMedia.MED_DimensionY
$params.url:="file://"+cs.xSDK.Outils.me.ConvertirPathVersURL($fichier.platformPath; False; True)
$params.titre:=$catalogueMedia.MED_Nom
$params.taillePoliceTitre:=96
$params.taillePoliceComment:=64
$params.date:=String(Date($catalogueMedia.MED_HoroDate); Internal date long)
$params.comment:=This.traiterCaractèresSpéciaux($catalogueMedia.MED_Commentaire)
$params.length:=2*Int($params.width/$params.taillePoliceComment)
$params.nbrMaxLignes:=10
This.MultiLignerTexte($catalogueMedia.MED_Commentaire; $params)
$params.viewBoxWidth:=$catalogueMedia.MED_DimensionX
$params.viewBoxHeight:=$catalogueMedia.MED_DimensionY+(2*$params.taillePoliceTitre)+100+(($params.lignes.length-1)*$params.taillePoliceComment)
$params.Format:=Copy XML data source
This.TraiterTemplateSVG("ImageDeMedia.xml"; ->$image; $params)
$fichierSVG:=This.dossierPhotos.file(Replace string($catalogueMedia.MED_HoroDate; ":"; "_")+$catalogueMedia.nomFichier)
WRITE PICTURE FILE($fichierSVG.platformPath; $image)
End if
//---------------------
// MARK:Hébergeur
//---------------------
Function ListerDocumentsDeDossierPhotos($nomDossier : Text)->$result : cs._Trace
// fixer la liste des photos et leur catalogue sur l'hébergeur
var $data : Object
var $documents : Collection:=Null
var $dataTexte : Text
// tester la présence du dossier $nomDossier
$data:=New object
$data.pathDossier:=This.FTP.racineFTP
$data.Options:=0x0002 // les dossiers
$data.pathsDocuments:=New collection
$data.listeDocuments:=New collection
$result:=This.FTP.ListerLesDocuments($data)
Case of
: (Not($result.success))
$result.Error:=-16217
$result.ErrorDescription:="Connexion FTP impossible : "+$result.ErrorDescription
: ($data.listeDocuments.indexOf($nomDossier)=-1)
// rappel : cet album est lié à des règles de 'Photos Synology' ; on ne peut pas le créer ici"
$result.Error:=-16209
$result.ErrorDescription:="L'album "+$nomDossier+" n'existe pas. Créer dans 'Photos Synology' un album intelligent à ce nom"
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Traitement"; Current method name; "L'album '"+$nomDossier+"' n'existe pas. Créer dans 'Photos Synology' un album intelligent à ce nom")
Else
$result.Error:=0
// lister les medias de l'album sur l'hébergeur
$data:=New object
$data.pathDossier:=This.FTP.racineFTP+$nomDossier+"/"
$data.Options:=0x0001 // les documents
$data.pathsDocuments:=New collection
$data.listeDocuments:=New collection
$result:=This.FTP.ListerLesDocuments($data)
Case of
: (Not($result.success))
$result.Error:=-16217
$result.ErrorDescription:="Connexion FTP impossible : "+$result.ErrorDescription
Else
$result.Error:=0
$documents:=$data.listeDocuments
End case
Case of
: ($documents=Null)
: ($documents.indexOf("Data_ALB.json")=-1)
// catalogue absent
Else
// le télécharger
$data.pathFichier:=$data.pathDossier+"Data_ALB.json"
$data.cheminFichier:=This.dossierTravail.platformPath+"Data_ALB.json"
$result:=This.FTP.TéléchargerFichier($data)
If ($result.success)
// c'est bon
$dataTexte:=Document to text($result.fichier.platformPath; "UTF-8"; Document with LF)
This.catalogueMediasHost:=JSON Parse($dataTexte; Is object)
End if
End case
End case
$result.FixerSuccess()
Function estPhotoAncienne($fichier)->$result : Boolean
// $fichier est un fichier du dossier local ; on nom est
// comparer les dates de modification de la photos du catalogue local et du catalogue Syno
var $catalogueMedia; $catalogueMediaHost : Object
Try
$catalogueMedia:=This.catalogueMedias.Medias.query("nomFichier = :1"; $fichier.fullName)[0]
Catch
$catalogueMedia:=Null
End try
Try
$catalogueMediaHost:=This.catalogueMediasHost.Medias.query("nomFichier = :1"; $fichier.fullName)[0]
Catch
$catalogueMediaHost:=Null
End try
$result:=True
Case of
: ($catalogueMedia=Null)
// pas normal, on abandonne
$result:=False
: ($catalogueMediaHost=Null)
// photo non présente, on l'ajoute
: ($catalogueMediaHost.dateModification<$catalogueMedia.dateModification)
// on ajoute la plus récente
Else
// on passe
$result:=False
End case
//$result:=True // pour test !
Function Téléverser()
var $data : Object
Case of
// dossier à transférer
: (This.dossierPhotos=Null)
// chemin sur l'hébergeur
: (This.FTP=Null)
// normalement déjà initialisé
Else
// on a tout, on continue
// transférer avec les options :
$data:=New object
$data.dossier:=This.dossierPhotos
// options : forcer la mise à jour et supprimer les orphelins
$data.Options:=(0x0001 ?+ 2)
// versionner
$data.cheminFTP:=This.FTP.racineFTP+This.catalogueMedias.Identification_Nom+"/"
$data.tache:=cs.xSDK.RegistreTaches.new().Inscrire(New object("nomProcess"; Current process name; "nomTache"; "MiseAjourSynoPhotos"; "numProcessAppelant"; Current process))
// lancer la tâche
$data.result:=This.FTP.serveur.MettreAjourDossier($data)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Téléversement "+Choose($data.result.success; "[OK]"; "[KO]"); Current method name; "Mise à jour de "+String(This.dossierPhotos.files(fk ignore invisible).length)+" photos dans l'album "+This.catalogueMedias.Identification_Nom)
End case
//---------------------
// MARK:Utilitaires
//---------------------
Function traiterCaractèresSpéciaux($texte : Text)->$result : Text
$result:=$texte
$result:=Replace string($result; "&"; "&")
Function MultiLignerTexte($texte : Text; $params : Object)
// spliter dans le tableau $ptrTab le texte $texte en N lignes de $params.length max caractères
// N ne doit pas dépasser $params.nbrMaxLignes
var $c : Collection
var $itemText; $mot : Text
var $Error : Integer
$Error:=-15068
$params.lignes:=New collection
Case of
: (Not(OB Is defined($params; "length")))
: (Not(OB Is defined($params; "nbrMaxLignes")))
Else
// c'est ok
$Error:=0
$c:=Split string($texte; " "; sk ignore empty strings)
ARRAY TEXT($tabElements; 0)
COLLECTION TO ARRAY($c; $tabElements)
$itemText:=""
For each ($mot; $c)
If (Length($itemText+" "+$mot)<$params.length)
$itemText:=$itemText+$mot+" "
Else
$params.lignes.push($itemText)
$itemText:=$mot+" "
End if
End for each
// ajouter le petit dernier
$params.lignes.push($itemText)
// on ne veut pas plus de "nbrMaxLignes" lignes
If ($params.lignes.length>$params.nbrMaxLignes)
$params.lignes:=$params.lignes.resize($params.nbrMaxLignes)
// finaliser le dernier élément
$itemText:=$params.lignes[$params.lignes.length-1]
$params.lignes[$params.lignes.length-1]:=Choose(Length($itemText+"...")>$params.length; Substring($itemText; 1; Length($itemText)-3)+"..."; $itemText+"...")
End if
End case
cs._Trace.me.Créer($Error; Current method name; "paramètres incorrects").LeverException([msgk_event; msgk_log])
Function TraiterTemplateSVG($nomTemplate : Text; $ptrImage : Pointer; $params : Object)->$result : Integer
// $nomTemplate : nom du fichier template en ressources, $ptrImage image destination, $params paramètres de construction
var $format : Integer
var $path : 4D.File
var $dossier : 4D.Folder
var $racineXML : Text
var $pict : Picture
var $texteBlobé : Blob
var $dataText : Text
$path:=Folder(fk resources folder).folder("TemplatesALB").file($nomTemplate)
$dossier:=This.dossierTravail
$result:=-15068
Case of
: (Not($path.exists))
: (Type($ptrImage->)#Is picture)
// créer le fichier
: (This.TraiterBalisesFichier($path; $dossier; $params)#0)
Else
$result:=0
// résultat dans $dossier+$nomTemplate
$path:=$dossier.file($nomTemplate)
End case
Case of
// ok avant
: ($result#0)
// on a un fichier
: (Not($path.exists))
: (Not(OB Is defined($params; "Format")))
Else
DOCUMENT TO BLOB($path.platformPath; $texteBlobé)
$dataText:=BLOB to text($texteBlobé; UTF8 text without length)
$racineXML:=DOM Parse XML source($path.platformPath)
// on a une structure XML
$result:=Erreur de lecture du fichier*Num(ok=0)
If ($result=0)
// convertir en image
$format:=$params.Format
SVG EXPORT TO PICTURE($racineXML; $pict; $format)
CONVERT PICTURE($pict; ".JPG"; 0.9)
$path.delete()
$path:=Folder(fk home folder).folder("tempo_ALV/_ALBdebug").file("Test_SVG_SynoPhotos.xml")
DOM EXPORT TO FILE($racineXML; $path.platformPath) //pour des tests"
DOM CLOSE XML($racineXML)
$ptrImage->:=$pict
Else
cs._Trace.me.Créer(-15075; Current method name; "Erreur lecture du fichier de la photo '"+$params.titre+"' (voir détails dans logs)").LeverException([msgk_event; msgk_log])
cs._Trace.me.EnvoyerMessages([msgk_log]; "Erreur lecture de fichier"; Current method name; JSON Stringify($params))
End if
End case
Function TraiterBalisesFichier($fichier : 4D.File; $dossier : 4D.Folder; $params : Object)->$result : Integer
// appliquer le traitement des balises 4D au contenu d'un fichier texte
// $1 est un fichier : traiter le texte du fichier
// $2 dossier des fichiers traités
// $3 : options
var $dataTexte : Text
var $chemin : 4D.File
var $texteBlobé : Blob
$result:=-15068
Case of
: (Not($fichier.exists))
Else
$result:=0
// lire le contenu à traiter
$dataTexte:=$fichier.getText()
// créer le chemin de destination
$chemin:=$dossier.file($fichier.fullName)
// on y va
PROCESS 4D TAGS($dataTexte; $dataTexte; $params)
// on enregistre
SET BLOB SIZE($texteBlobé; 0)
TEXT TO BLOB($dataTexte; $texteBlobé; UTF8 text without length)
$chemin.setContent($texteBlobé)
End case
Function FixerParametresFTPphotos()
var $path : Text:=""
var $result : cs._Trace
This.FTP.racineFTP:="" // erreur
$result:=cs._Trace.me.Initialiser(Current method name)
$result.Error:=-15012
Case of
: (Not(This.rsc.SetVariable(Est Ressource ALB; "chemins/FTP/PathSynologyPhotos"; Is text; ->$path)))
$result.ErrorDescription:="Connexion/NomHost"
Else
$result.Error:=0
// initialiser
This.FTP.racineFTP:=$path
End case
$result.LeverException([msgk_event; msgk_log])
⇧
[class]$pageWebAlbum - 16/04/2025 19:11:59
Class extends $pageWebAlbums
Class constructor($url : Text)
Super($url)
// les paramètres sont récupérés
Function TraiterURL()->$result : cs._Trace
// traitement des url de la page albums
This.result.Initialiser(Current method name)
// normalement .balise[2] est le nom d'une function de $pageXXX
Case of
: (This.balisesUrl.length<3)
This.result.Error:=-15068
This.result.ErrorDescription:="url incorrecte - il manque la paramètres .balise[2]"
: (Not(OB Is defined(This; This.balisesUrl[2])))
This.result.Error:=-15068
This.result.ErrorDescription:="url incorrecte - .balise[2] '"+This.balisesUrl[2]+"' n'est pas une function de this "+OB Class(This).name
Else
// on accepte la demande
// le résultat, s'il y a, est dans this.result.resultat
// exécuter la function, en cas d'erreur elle peut renseigner This.result
This[This.balisesUrl[2]]()
End case
$result:=This.result
//----------------------------------
// MARK:Album
//----------------------------------
Function getInformation($attribut : Text)->$result : Text
// l'info peut ne pas exister !
$result:=""
Case of
: (Not(OB Is defined(Session.storage.ALB_UserParams; "Album")))
: (Not(OB Is defined(Session.storage.ALB_UserParams.Album; $attribut)))
Else
// renvoyer l'info
$result:=Session.storage.ALB_UserParams.Album[$attribut]
End case
//----------------------------------
// MARK:Element HTML
//----------------------------------
Function Information()
This.result.resultat:=""
Case of
: (This.paramsUrl.length<1)
Else
// renvoyer l'info
This.result.resultat:=This.getInformation(This.paramsUrl[1])
End case
This.result.resultat:=Char(1)+"<spam>"+This.result.resultat+"</spam>"
Function Contact()
This.result.resultat:=Char(1)+This.getContact(Session.storage.ALB_UserParams.Album)
Function Vignettes()
var $result; $url; $fichier; $texte : Text
var $mediaInformation : Object
var $i : Integer
// fixer l'URL du dossier des vignettes
$url:=This.document.getWEBalbumVignettesURL()
$result:=""
// // créer la page à partir du tableau NomsFichier
If (Session.storage.ALB_UserParams.Album.Medias.length>0)
// mettre les vignettes dans le flux HTML
For ($i; 1; Session.storage.ALB_UserParams.Album.Medias.length)
$mediaInformation:=Session.storage.ALB_UserParams.Album.Medias[$i-1]
$fichier:=$mediaInformation.nomFichier
$texte:=$mediaInformation["MED_Nom"]
// appel de la function javascript
// rappel : width et height sont fixés par CSS (nécessaire pour l'alignement des images)
$result:=$result+"<a onclick="+Char(Double quote)+"Visualiser('#openDiaporama','IndexMedia','"+String($i-1)+"')"+Char(Double quote)+">"
$result:=$result+"<img alt="+Char(Double quote)+$fichier+Char(Double quote)+" title="+Char(Double quote)+$texte+Char(Double quote)+" src="+Char(Double quote)+$url+This.fct.ConvertirPathVersURL($fichier; False; False; True)+Char(Double quote)
// terminer la balise (pour le format flex)
Case of
: ($mediaInformation["MED_DimensionX"]>=$mediaInformation["MED_DimensionY"])
$result:=$result+" width="+Char(Double quote)+"100%"+Char(Double quote)+"/>"
: ($mediaInformation["MED_DimensionX"]<$mediaInformation["MED_DimensionY"])
$result:=$result+" height="+Char(Double quote)+"100%"+Char(Double quote)+"/>"
End case
$result:=$result+"</a>"
End for
End if
This.result.resultat:=Char(1)+$result
Function navDiaporama()
var $result; $dossier; $fichier; $texte : Text
var $mediaInformation : Object
var $i : Integer
// fixer l'URL du dossier des vignettes
$dossier:=This.document.getWEBalbumVignettesURL()
$result:=""
// créer le ruban de navigation
If (Session.storage.ALB_UserParams.Album.Medias.length>0)
$result:="<table><tbody><tr>"
// l'index varie entre 0 et taille - 1 (cf le traitement avascript)
For ($i; 1; Session.storage.ALB_UserParams.Album.Medias.length)
$mediaInformation:=Session.storage.ALB_UserParams.Album.Medias[$i-1]
$fichier:=$mediaInformation.nomFichier
$texte:=$mediaInformation["MED_Nom"]
$result:=$result+"<td><a onClick="+Char(Double quote)+"GotoMedia("+String($i-1)+")"+Char(Double quote)+">"
$result:=$result+"<img alt="+Char(Double quote)+$fichier+Char(Double quote)+" title="+Char(Double quote)+$texte+Char(Double quote)+" src="+Char(Double quote)+$dossier+This.fct.ConvertirPathVersURL($fichier; False; False; True)+Char(Double quote)
// terminer la balise (pour le format flex)
Case of
: ($mediaInformation["MED_DimensionX"]>=$mediaInformation["MED_DimensionY"])
$result:=$result+" width="+Char(Double quote)+"100%"+Char(Double quote)+"/>"
: ($mediaInformation["MED_DimensionX"]<$mediaInformation["MED_DimensionY"])
$result:=$result+" height="+Char(Double quote)+"100%"+Char(Double quote)+"/>"
End case
$result:=$result+"</a>"
$result:=$result+"</td>"
End for
$result:=$result+"</tr></tbody></table>"
End if
This.result.resultat:=Char(1)+$result
⇧
[class]$albums - 20/02/2026 12:19:52
// points d'entrée base hote
Class constructor()
//---------------------
// MARK:Visualisations
//---------------------
Function ModifierVisualisations($params : Object)
var $result : cs._Trace
var $data : Object
var $numProc : Integer
// ici on est dans le process appelant
$result:=cs._Trace.me.Initialiser(Current method name)
$result.Error:=-15068
Case of
: (Not(OB Is defined($params; "Commande")))
$result.ErrorDescription:="Le paramètre 'Commande' est absent de $1"
: (Not(OB Is defined($params; "params")))
$result.ErrorDescription:="Le paramètre 'params' est absent de $1"
: (Not(OB Is defined($params.params; "userIDfamille")))
$result.ErrorDescription:="Le paramètre 'userIDfamille' est absent de $1.params"
: (Not(OB Is defined($params.params; "groupesFamiliaux")))
$result.ErrorDescription:="Le paramètre 'groupesFamiliaux' est absent de $1.params"
: (Not(OB Is defined($params.params; "CheminDossierFTP")))
$result.ErrorDescription:="Le paramètre 'CheminDossierFTP' est absent de $1.params"
Else
// c'est ok
$result.Error:=0
$data:=New object
$data.functionID:="_ModifierVisualisations_process"
$data.nomTache:="Visualisations"
$data.numProcessAppelant:=-1
$data.params:=$params
$numProc:=Exécuter Function Coopérative(cs.$albums; $data)
End case
$result.LeverException([msgk_event; msgk_log])
Function _ModifierVisualisations_process($params)
var $data : Object
// classe du Form :
$data:=cs[$params.params.Commande].new()
// récupérer les paramètres et méthodes hôte
$data.params:=$params.params
$data.$formulaire:=$params.$formulaire
// afficher le formulaire
$data.OuvrirFormulaire()
//---------------------
// MARK:Sauvegarde
//---------------------
Function DémarrerSauvegarde($params : Object)
cs._sauvegarde.new().Démarrer($params)
//---------------------
// MARK:Installation serveur Web
//---------------------
Function InstallerRessourcesSurServeur($dossierDestination : 4D.Folder)
// attention : function toujours appelée par le serveur Web concerné ; on doit donc recevoir le nom du serveur Web concerné
// => asynchrone de l'installation du composant lui-même
// placer les pages HTML et ressources Albums dans le dossier $dossierDestination du serveur $nomServeur
Case of
: (Not(Storage.System.estServeur))
// on n'est pas sur un serveur APP ou HTTP (4D serveur)
// filtrer ces contextes
: ($dossierDestination=Null)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; "Installer Albums"; "InstallerRessourcesSurServeur [KO], le dossier d'installation est null")
: (Not($dossierDestination.exists))
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; "Installer Albums"; "InstallerRessourcesSurServeur [KO], le dossier d'installation '"+$dossierDestination.platformPath+"' n'existe pas")
Else
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Démarrage"; "Installer Albums"; "InstallerRessourcesSurServeur [OK] dans "+$dossierDestination.platformPath)
This.InstallerPagesDynamiques($dossierDestination)
This.InstallerRessources($dossierDestination)
End case
Function InstallerPagesDynamiques($dossierDestination : 4D.Folder)
var $c : Collection
var $fichier : Object
// Ajouter les pages dynamiques
$c:=Folder(fk resources folder).folder("TemplatesPagesWeb").files(fk ignore invisible)
For each ($fichier; $c)
COPY DOCUMENT($fichier.platformPath; $dossierDestination.platformPath; *)
End for each
Function InstallerRessources($dossierDestination : 4D.Folder)
var $dossier : Object
$dossier:=$dossierDestination.folder("ressourcesAlbums")
$dossier.create()
// Ajouter les feuilles de style
COPY DOCUMENT(Folder(Get 4D folder(Current resources folder); fk platform path).folder("css").platformPath; $dossier.platformPath; *)
// Ajouter les ressources images
COPY DOCUMENT(Folder(Get 4D folder(Current resources folder); fk platform path).folder("images").folder("HTML").platformPath; $dossier.platformPath; "images"; *)
// Ajouter les fichiers Java
COPY DOCUMENT(Folder(Get 4D folder(Current resources folder); fk platform path).folder("javascripts").platformPath; $dossier.platformPath; "js"; *)
⇧
[class]_maintenance - 10/08/2026 09:50:46
property nomTache : Text:="MaintenanceALB"
property tache : cs.xSDK.Tache
property FTP : cs._FTP
property Taches : Collection
property pathAlbum : Text
property Album : Object
Class extends _composant
Class constructor($params : Object)
Super()
Case of
: (Count parameters=0)
: (Not(OB Is defined($params; "Session_Etat")))
Else
// en particulier pour test local
Use (Storage.Host)
Storage.Host.Session_Etat:=$params.Session_Etat
End use
End case
This.FTP:=cs._FTP.new()
//---------------------
// MARK:Maintenance Web
//---------------------
Function FixerListeTâches()
// lister les tâches à exécuter
var $data : Object
This.Taches:=New collection
$data:=New object
$data.functionID:="MettreAjourData"
// prochaine nuit à 4:02
$data.dateTache:=String(Add to date(Current date; 0; 0; 1); ISO date GMT; ?04:02:00?)
// tous les jours
$data.période:=New object("jour"; 1; "seconde"; 0)
// lancer au démarrage du serveur APP
$data.initialiser:=True
This.Taches.push(OB Copy($data))
$data:=New object
$data.functionID:="MettreAjourPhotos"
// 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)
// lancer au démarrage du serveur APP
$data.initialiser:=True
This.Taches.push(OB Copy($data))
cs._Trace.me.EnvoyerMessages([msgk_event]; "Maintenance"; Current method name; "tâche <MettreAjourData> programmée (voir détails dans Logs)")
cs._Trace.me.EnvoyerMessages([msgk_log]; "Maintenance - MettreAjourData"; 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.estDebugAPP())))
// serveur ALV seul uniquement ou debug
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()
cs.xSDK.RegistreTaches.me.Tuer(This.nomTache)
cs._Trace.me.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 albums
var $tâche : Object
var $nbre : Integer
This.tache:=$data.tache
This.tache.FixerEtat("init")
// tempo de 3 mn
Waiting(3*60*60)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Démarrage du monitoring")
This.FixerListeTâches()
// lancer le monitoring des tâches
Repeat
For each ($tâche; This.Taches)
Case of
: (Not(OB Is defined($tâche; "functionID")))
: (Not(OB Is defined($tâche; "dateTache")))
: (Not(OB Is defined($tâche; "initialiser")))
: (Not(($tâche.initialiser) | (String(Current date; ISO date GMT; Current time)>$tâche.dateTache)))
// attendre
Else
// ok, on a tout pour cette tâche
This[$tâche.functionID]()
// date de la prochaine maintenance
// nombre de jours
// . on peut avoir rater des périodes ; combien?
$nbre:=Current date-Date($tâche.dateTache)
// . ajouter la période
$nbre:=$nbre+$tâche.période.jour+((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; $nbre); ISO date GMT; Time(Current time+$tâche.période.seconde))
$tâche.initialiser:=False
End case
End for each
// *** attendre la période suivante
Waiting(60*10)
Until (This.tache.Tuer.signaled)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Fin du monitoring")
Function MettreAjourData()
// mettre à jour les albums dans le dossier d'installation du serveur APP, ou serveur HTTP (4D Serveur)
// ce dossier est un dossier de stockage, initialisé puis mis à jour, et à disposition des autres composants
This.tache.FixerEtat("active")
This.MettreAjourWebData()
This.tache.FixerEtat("pending")
//---------------------
// MARK:WEB
//---------------------
Function MettreAjourWebData()
// transfert des albums de l'hébergeur vers le dossier d'installation
var $data : Object
var $i : Integer
var $result : cs._Trace
// créer le dossier de travail
This.CréerDossierDeTravail("ALB_FTP")
// lister les dossiers de la racine
$data:=New object
$data.Options:=0x0002
$data.pathsDocuments:=New collection
$data.listeDocuments:=New collection
$result:=This.FTP.ListerLesDocuments($data)
Case of
: (Not($result.success))
$result.Error:=-16217
$result.ErrorDescription:="Connexion FTP impossible : "+$result.ErrorDescription
: ($data.listeDocuments.length=0)
$result.Error:=-16209
$result.ErrorDescription:="FTP : pas d'albums trouvés à "+This.FTP.racineFTP
Else
// c'est parti
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Mise à jour des albums"; Current method name; "dans '"+This.document.getWEBalbumFolder().platformPath+"'")
For ($i; 0; $data.pathsDocuments.length-1)
This.Album:=$data.listeDocuments[$i]
This.pathAlbum:=$data.pathsDocuments[$i]
This.MettreAjourWebDocuments()
If (This.tache.Tuer.signaled)
// arrêter les frais
$i:=$data.pathsDocuments.length
End if
End for
End case
$result.LeverException([msgk_event; msgk_log])
This.SupprimerDossierDeTravail()
Function MettreAjourWebDocuments()
var $catalogueMedias; $fichier : Object
var $path; $dataTexte : Text
var $result : cs._Trace
$result:=cs._Trace.me.Initialiser(Current method name)
// récupérer les informations de l'album
$fichier:=This.dossierTravail.file("Data_ALB.json")
$result.Error:=This.FTP.serveur.RecevoirFichier(This.pathAlbum+"Data_ALB.json"; $fichier.platformPath).Error
$result.success:=($result.Error=0)
If ($result.success)
// lire le catalogue
$dataTexte:=Document to text($fichier.platformPath; "UTF-8"; Document with LF)
$catalogueMedias:=JSON Parse($dataTexte; Is object)
ASSERT(cs._Trace.me.DebugerMethode("Catalogue"; Current method name; "Mise à jour de l'album '"+$catalogueMedias.Identification_Nom+"'"))
// *** mettre à jour les informations medias
$fichier.copyTo(This.document.getWEBalbumFolder($catalogueMedias.Identification_ID); "Data_ALB.json"; fk overwrite)
// *** transférer les medias modifiés des albums dans les dossiers du site WEB
This.MettreAjourWebDossierMedia($catalogueMedias; 0x0007)
// puis, mettre à jour les vignettes
This.CréerWebVignettes($catalogueMedias; 0x0007)
// *** écrire les ressources
$path:=This.pathAlbum+"ressources/"
$fichier:=This.document.getWEBalbumFolder($catalogueMedias.Identification_ID).folder("ressources")
$result:=This.FTP.serveur.RecevoirDossier($path; $fichier.platformPath; 0x0300)
End if
Function MettreAjourWebDossierMedia($catalogueMedias : Object; $options : Integer)
// créer les medias s'ils n'existent pas
// bit 0 = remplacer si existe,
// bit 1 = remplacer si plus ancien,
// bit 2 = supprimer dans destination les fichiers orphelins,
// bit 3 = copier dans un seul dossier
var $dossier; $fichier; $mediaInformation; $result : Object
var $path; $ProcInProgressEtat : Text
var $mettreAjour : Boolean
var $taille : Integer
$path:=This.document.getAlbumMediasURL(This.pathAlbum)
$dossier:=This.document.getWEBalbumMediasFolder($catalogueMedias.Identification_ID)
This.rsc.SetVariable(Est Ressource ALB; "Site_Web/tailleImage"; Is longint; ->$taille)
ProcInProgressState:=0
// passer en revue chaque media source
For each ($mediaInformation; $catalogueMedias.Medias) While (Not(This.tache.Tuer.signaled))
$mettreAjour:=False
$ProcInProgressEtat:=$mediaInformation.nomFichier
$fichier:=$dossier.file($mediaInformation.nomFichier)
Case of
: (Not($fichier.exists))
// le fichier n'existe pas, le transférer
$mettreAjour:=True
$ProcInProgressEtat:="Ajout de "+$mediaInformation.nomFichier
: ($fichier.exists & ($options ?? 0))
// il existe, mais forcer le transfert
$ProcInProgressEtat:="Remplacement de "+$ProcInProgressEtat
If ($options ?? 1)
// on ne met à jour que si l'existant est plus ancien
// rappel : l'heure des data_ALB est un entier long ; celle des data est un entierLong
$mettreAjour:=(($mediaInformation.dateModification>$fichier.modificationDate) | (($mediaInformation.dateModification=$fichier.modificationDate) & (Time($mediaInformation.heureModification)>$fichier.modificationTime)))
$ProcInProgressEtat:=$ProcInProgressEtat+" du "+String($fichier.modificationDate; System date short)+" à "+String(Time($fichier.modificationTime); HH MM SS)+", par celui du "+String($mediaInformation.dateModification; System date short)+" à "+String(Time($mediaInformation.heureModification); HH MM SS)
Else
// forcer la mise à jour
$mettreAjour:=True
End if
End case
If ($mettreAjour)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Traitement"; Current method name; $ProcInProgressEtat)
// copier le fichier source
$result:=This.FTP.serveur.RecevoirFichier($mediaInformation.pathFichier; $fichier.platformPath)
This.recadrerImageDuFichier($fichier; $fichier; $taille)
SET DOCUMENT PROPERTIES($fichier.platformPath; False; False; $mediaInformation.dateModification; Time($mediaInformation.heureModification); $mediaInformation.dateModification; Time($mediaInformation.heureModification))
//renseigner le nombre de mise à jour
ProcInProgressState:=ProcInProgressState+1
// respirer un peu
Waiting(1)
End if
// renseigner la progression
ProcInProgressTime:=10000*$catalogueMedias.Medias.indexOf($mediaInformation)/$catalogueMedias.Medias.length
End for each
If ($options ?? 2)
// supprimer les fichiers orphelins de destination
// boucle sur les fichiers existants en local
For each ($fichier; $dossier.files(fk ignore invisible)) While (Not(This.tache.Tuer.signaled))
// filtrer les fichiers base64 (plétore de fichiers système...)
If ($catalogueMedias.Medias.query("nomFichier =:1"; $fichier.fullName).length=0)
// ce fichier n'est pas dans ce qui est supposé exister
$fichier.delete()
End if
End for each
End if
Function CréerWebVignettes($catalogueMedias : Object; $options : Integer)
var $fichiers : Collection
var $fichier; $vignette; $dossier; $result : Object
var $taille : Integer
var $mettreAjour : Boolean
var $ProcInProgressEtat : Text
$result:=New object
// lister les fichiers existants
$fichiers:=This.document.getWEBalbumMediasFolder($catalogueMedias.Identification_ID).files(fk ignore invisible)
$result.Error:=-15068
Case of
: ($fichiers=Null)
$result.ErrorDescription:="L'album "+$catalogueMedias.Identification_ID+" n'existe pas"
: ($fichiers.length=0)
$result.ErrorDescription:="L'album "+$catalogueMedias.Identification_ID+" est vide"
Else
$result.Error:=0
ProcInProgressState:=0
// créer le dossier des vignettes de l'album sur le site Web
$dossier:=This.document.getWEBalbumVignettesFolder($catalogueMedias.Identification_ID)
// au cas où ...
$dossier.create()
$taille:=OB Get($catalogueMedias; "Vignettes_TailleVignette"; Is longint)
// passer en revue chaque media source
For each ($fichier; $fichiers) While (Not(This.tache.Tuer.signaled))
$mettreAjour:=False
$ProcInProgressEtat:=$fichier.fullName
$vignette:=$dossier.file($fichier.fullName)
Case of
: (Not($vignette.exists))
// le fichier n'existe pas, le transférer
$mettreAjour:=True
$ProcInProgressEtat:="Ajout de "+$fichier.fullName
: ($vignette.exists & ($options ?? 0))
// il existe, mais forcer le transfert
$ProcInProgressEtat:="Remplacement de "+$ProcInProgressEtat
If ($options ?? 1)
// on ne met à jour que si l'existant est plus ancien
// rappel : l'heure des data_ALB est un entier long ; celle des data est un entierLong
$mettreAjour:=(($fichier.modificationDate>$vignette.modificationDate) | (($fichier.modificationDate=$vignette.modificationDate) & (Time($fichier.modificationTime)>$vignette.modificationTime)))
$ProcInProgressEtat:=$ProcInProgressEtat+" du "+String($vignette.modificationDate; System date short)+" à "+String(Time($vignette.modificationTime); HH MM SS)+", par celui du "+String($fichier.modificationDate; System date short)+" à "+String(Time($fichier.modificationTime); HH MM SS)
Else
// forcer la mise à jour
$mettreAjour:=True
End if
End case
If ($mettreAjour)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Traitement"; Current method name; $ProcInProgressEtat)
// créer la vignette
This.recadrerImageDuFichier($fichier; $vignette; $taille)
SET DOCUMENT PROPERTIES($vignette.platformPath; False; False; $fichier.modificationDate; Time($fichier.modificationTime); $fichier.modificationDate; Time($fichier.modificationTime))
//renseigner le nombre de mise à jour
ProcInProgressState:=ProcInProgressState+1
// respirer un peu
Waiting(1)
End if
// renseigner la progression
ProcInProgressTime:=10000*$fichiers.indexOf($fichier)/$fichiers.length
End for each
End case
Function recadrerImageDuFichier($fichierIN : Object; $fichierOUT : Object; $taille : Integer)
var $image : Picture
var $width; $height; $gauche; $haut : Integer
var $zoom : Real
// lire les données de ce fichier
READ PICTURE FILE($fichierIN.platformPath; $image)
PICTURE PROPERTIES($image; $width; $height)
// calculer la taille de l'image qui rentre dans le cadre $taille x $taille
$zoom:=-Scaled to fit prop centered // format d'affichage de l'image
$gauche:=0
$haut:=0
This.fct.CalculerRectangleMedia(->$gauche; ->$haut; ->$taille; ->$taille; $width; $height; ->$zoom)
$width*=$zoom
$height*=$zoom
If ($fichierIN.extension=".jpg")
// tailler aux multiples de 8
$width-=Mod($width; 8)
$height-=Mod($height; 8)
End if
// créer l'image
CREATE THUMBNAIL($image; $image; $width; $height; Scaled to fit prop centered)
// compresser à 80% , a priori n'affecte pas les format png
CONVERT PICTURE($image; $fichierIN.extension; 0.9)
// enregistrer
WRITE PICTURE FILE($fichierOUT.platformPath; $image; $fichierOUT.extension)
//---------------------
// MARK:Photos Synology
//---------------------
Function MettreAjourPhotos()
// recopie du dossier géré par ce composant ALB dans $destination
var $tache : cs.xSDK.Tache
var $fintraitement : Boolean:=False
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance"; Current method name; "Init de la mise à jour des Albums dans Photos Synologycopie")
Repeat
// lire la présence de la tâche du composant ALB et son état
$tache:=cs.xSDK.RegistreTaches.me.LireTache(This.nomTache)
Case of
: ($tache=Null)
// maintenance non démarrée
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie KO, maintenance ALB non démarrée")
: ($tache.Etat="init")
// tâche maintenance non démarrée
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie KO, tâche maintenance ALB en 'init'")
: ($tache.Etat#"pending")
// maintenance en cours
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie KO, tâche maintenance ALB en 'active'")
Else
// a priori les albums sont disponibles
// mettre à jour
cs.$photos.new().MettreAjour()
$fintraitement:=True
End case
// attendre un peu
Waiting(60*60)
Until ($fintraitement)
//---------------------
// MARK:Installation APP
//---------------------
Function InstallerAlbums($dossierDestination : 4D.Folder)
// recopie du dossier géré par ce composant ALB dans $destination
var $tache : cs.xSDK.Tache
var $fintraitement : Boolean:=False
var $dossierSource : 4D.Folder
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Init copie des albums dans "+$dossierDestination.platformPath)
Repeat
// lire la présence de la tâche du composant ALB et son état
$tache:=cs.xSDK.RegistreTaches.me.LireTache(This.nomTache)
Case of
: ($tache=Null)
// maintenance non démarrée
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie KO, maintenance ALB non démarrée")
: ($tache.Etat="init")
// tâche maintenance non démarrée
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie KO, tâche maintenance ALB en 'init'")
: ($tache.Etat#"pending")
// maintenance en cours
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie KO, tâche maintenance ALB en 'active'")
Else
// a priori les albums sont disponibles
// les recopier
$dossierSource:=cs._document.new().getDataFolder().folders()[0]
$dossierSource.copyTo($dossierDestination; fk overwrite)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie OK, dossier source "+$dossierSource.platformPath)
cs._Trace.me.EnvoyerMessages([msgk_event; msgk_log]; "Maintenance Albums"; Current method name; "Copie OK, dossier destination "+$dossierDestination.platformPath)
$fintraitement:=True
End case
// attendre un peu
Waiting(60*60)
Until ($fintraitement)
⇧
[ ]U_Nav Albums - 01/12/2024 19:22:51
var $avancement : Integer
var $userMessageWnd : Text
Case of
: (Form event code=On Load)
Form.ListerAlbums()
// sélectionner le premier
Form.indexAlbum:=0
Form.OuvrirAlbum()
Form.InitFormulaire()
$avancement:=10
SET TIMER($avancement)
: (Form event code=On Timer) // MaJ des IHM
OBJECT SET VISIBLE(*; "AsynchroProgress"; Form.AsynchroProgress)
OBJECT SET VISIBLE(*; "avancement"; Form.AsynchroProgress)
If (Form.AsynchroProgress)
GOTO OBJECT(*; "EXIF_listeMedias")
GET PROCESS VARIABLE(Form.ProcInProgressNum; ProcInProgressEtat; $userMessageWnd; ProcInProgressTime; $avancement) //;ProcInProgressState;ProcInProgressState)
Form.userMessageWnd:=$userMessageWnd
Form.avancement:=$avancement
Else
Form.ProcInProgressNum:=-1
Form.userMessageWnd:=""
End if
Form.FixerNavigationClavier()
: (Form event code=On Getting Focus)
Form.saisieEnCours:=(OBJECT Get type(*; OBJECT Get name(Object with focus))=Object type text input)
: (Form event code=On Losing Focus)
Form.saisieEnCours:=False
: (Form event code=On Data Change)
Form.ModifierDonnée()
: (Form event code=On Unload)
// mémoriser les données
Form.FermerAlbum()
// on purge
CLEAR LIST(Form.listeMedias)
Else
End case
⇧
[ ]U_Nav Albums - objet grpBtnNavPreviousRecord - 18/11/2024 18:23:33
var $i : Integer
$i:=Selected list items(Form.listeMedias)
Form.rangMedia:=$i-(Num($i>1))
Form.AfficherMedia()
⇧
[ ]U_Nav Albums - objet grpBtnNavNextRecord - 18/11/2024 18:23:09
var $i : Integer
$i:=Selected list items(Form.listeMedias)
Form.rangMedia:=$i+Num($i<Count list items(Form.listeMedias))
Form.AfficherMedia()
⇧
[ ]U_Nav Albums - objet btnNavFirst - 06/05/2023 12:23:08
Form.rangMedia:=1
Form.AfficherMedia()
⇧
[ ]U_Nav Albums - objet btnNavLast - 06/05/2023 12:22:58
Form.rangMedia:=Count list items(Form.listeMedias)
Form.AfficherMedia()
⇧
[ ]U_Nav Albums - objet EXIF_listeMedias - 06/05/2023 12:28:11
Case of
: (Form event code=On Selection Change)
// récupérer le rang du média sélectionné
Form.rangMedia:=Selected list items(Form.listeMedias)
Form.AfficherMedia()
End case
⇧
[ ]U_Nav Albums - objet Sélection - 01/12/2024 19:24:12
Case of
: (Form event code=On Load)
// initialiser la liste
Form.selecteur:=New object
Form.selecteur.values:=New collection(OBJECT Get name(Object current))
Form.selecteur.index:=0
: (Form event code=On Data Change)
// changer d'album
Form.indexAlbum:=Form.selecteur.index
// forcer la mise à jour des medias de l'album ouvert
Form.FermerAlbum()
Form.OuvrirAlbum()
Form.InitFormulaire()
End case
⇧
[ ]U_Nav Albums - objet btnNavTrier - 06/05/2023 12:20:40
Form.ListerMedias()
⇧
[ ]U_Nav Albums - objet Domaine - 01/12/2024 19:51:35
Form.EditerRessourceComposant("ConnexionFTP/NomHost")
⇧
[ ]U_Nav Albums - objet Identifiant - 01/12/2024 19:54:27
Form.EditerRessourceComposant("ConnexionFTP/Identifiant")
⇧
[ ]U_Nav Albums - objet MotDePasse - 01/12/2024 19:54:22
Form.EditerRessourceComposant("ConnexionFTP/MotDePasse")
⇧
[ ]U_Nav Albums - objet hostURL - 01/12/2024 19:54:16
Form.EditerRessourceComposant("Hosting/URL")
⇧
[ ]U_Nav Albums - objet CheminFTP - 01/12/2024 19:54:08
Form.EditerRessourceComposant("chemins/FTP/Path")
⇧
[ ]Visualiser Album - 01/12/2024 19:12:27
// traitements génériques (traités par la base hôte)
Form.$formulaire.surEvenementFormulaire()
Case of
: (Form event code=On Load)
Form.ListerAlbums()
// sélectionner le premier
Form.indexAlbum:=0
: (Form event code=On Data Change)
// changer d'album
Form.indexAlbum:=Form.selecteur.index
End case
If ((Form event code=On Load) | (Form event code=On Data Change))
Form.OuvrirAlbum()
Form.InitFormulaire()
End if
⇧
[ ]Visualiser Album - objet Sélection - 01/12/2024 19:20:04
Case of
: (Form event code=On Load)
// initialiser la liste
Form.selecteur:=New object
Form.selecteur.values:=New collection(OBJECT Get name(Object current))
Form.selecteur.index:=0
End case
⇧
[ ]Visualiser Album - objet btnNavPreviousRecord - 03/05/2023 11:32:04
Form.MediaPrécédent()
⇧
[ ]Visualiser Album - objet btnNavNextRecord - 30/11/2023 14:55:48
Form.MediaSuivant()
⇧
[ ]Visualiser Album - objet btnNavFirst - 03/05/2023 11:31:31
Form.PremierMedia()
⇧
[ ]Visualiser Album - objet btnNavLast - 03/05/2023 11:31:55
Form.DernierMedia()
⇧
onStartup - 29/03/2025 17:14:17
// ne s'exécute pas dans une base hôte
ON ERR CALL(Formula(traceHandler).source; ek global)
cs._composant.new().InitVariablesALB()
// pour les tests hors base hôte, créer le worker de services (utile pour des process thread-safe)
CALL WORKER("WK_Composant_ALB"; Formula(InitProcess).source)
⇧
onExit - 01/04/2025 19:35:06
// 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 - 17/11/2024 12:50:29
Pas de code
⇧
onWebAuthentication - 17/11/2024 12:49:44
Pas de code
⇧
onSystemEvent - 07/12/2021 18:01:34
Pas de code
⇧
onHostDatabaseEvent - 06/02/2026 17:48:06
#DECLARE($numFormEvent : Integer)
// Méthode base sur événement base hôte
Case of
: ($numFormEvent=On before host database startup)
// ici pas d'interaction avce les autres composants et la BDDmère
ON ERR CALL(Formula(ErrorHandler).source; ek local)
cs._composant.new().InitVariablesALB()
Use (Storage.System)
Storage.System.estExecuteDansHote:=True
End use
// déactiver les ASSERT si le composant est compilé (réactivable avec le bit 6 de <>composantStatus)
SET ASSERT ENABLED(Not(Is compiled mode))
CALL WORKER("WK_Composant_ALB"; Formula(InitProcess).source)
// remarque : le worker WK_services existe dans la base hôte
: ($numFormEvent=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()
: ($numFormEvent=On before host database exit)
// placer ici le code à exécuter avant le "Sur fermeture" de la base hôte
If (Storage.System.estServeur)
cs._maintenance.new().Arrêter()
End if
: ($numFormEvent=On after host database exit)
// placer ici le code à exécuter après le "Sur fermeture" de la base hôte
End case