From 60b3b776868b4b3d08c46db1b0682db2b5c45529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Carretero?= <frederic.carretero@veremes.com> Date: Fri, 23 Nov 2018 08:58:54 +0100 Subject: [PATCH] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20gtf.engines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gtf.engine/gtf.engines/Traitement.class.inc | 887 ++++++--- gtf.engine/gtf.engines/engine.exe | Bin 75276 -> 75643 bytes gtf.engine/gtf.engines/engine.php | 1661 ++++++++++------- gtf.engine/gtf.engines/engine.sql.inc | 8 +- .../gtf.engines/lang_engines/fr-lang.inc | 6 +- gtf.engine/gtf.engines/licenses/license.txt | 7 - gtf.engine/gtf.engines/php_engine_conf.inc | 4 +- gtf.engine/gtf.engines/string.inc | 20 + gtf.engine/gtf.engines/subscription.php | 4 +- 9 files changed, 1673 insertions(+), 924 deletions(-) delete mode 100755 gtf.engine/gtf.engines/licenses/license.txt diff --git a/gtf.engine/gtf.engines/Traitement.class.inc b/gtf.engine/gtf.engines/Traitement.class.inc index f00fcf53..1d3c1717 100755 --- a/gtf.engine/gtf.engines/Traitement.class.inc +++ b/gtf.engine/gtf.engines/Traitement.class.inc @@ -16,7 +16,10 @@ require_once ("vmlib/phpUtil.inc"); require_once ("vmlib/logUtil.inc"); require_once ("vmlib/stringUtil.inc"); +require_once ("vmlib/cryptUtil.inc"); require_once ("gtf_lib/GtfFmwParser.class.inc"); +require_once ("vmlib/Email.class.inc"); +require_once ("gtf_lib/gtf_object/Order.class.inc"); require_once 'string.inc'; class Traitement { @@ -82,6 +85,37 @@ class Traitement { var $sSource; var $sSourceTemp = ""; + /** + * Paramètres de cryptage du résultat. + */ + var $bDataEncrypt = false; + var $sSecretKey; + + /** + Pramaètre pour modifir la GTF_CONNECTION_STRING + */ + var $bUseExternalDbConnection = false; + + /** + * Formatage des paramètres différent si le traitement est envoyé vers Fme Server. + */ + var $bFmeServer = false; + + /** + * Nom de répertoire du résultat de la demande. + */ + var $sResultDirectoryName = ''; + + /** + * Chemin vers le répertoire du résultat de la demande. + */ + var $sResultDirectoryPath = ''; + + /** + * Tableau contenant la liste des fichiers de destination. + */ + var $aDestinationFiles = array(); + /** * Cette méthode permet d'initialiser les variables. * \param $oBd Connexion à la base de données. @@ -113,15 +147,6 @@ class Traitement { * \param $sFmwFileName Nom du fichier FMW associé à la demande. */ - /** - * Cette méthode permet de générer un nom de fichier unique. - * \return une chaîne de caractères. - */ - function UniqFileName() { - $sUniqFileName = date('YmdHis') . rand(1, 100000); - return $sUniqFileName; - } - /** * Cette méthode permet de générer un tableau de paramètres à partir d'une chaîne dont les délimiteurs sont "|" et "=". * \param $sAllParams Chaine composé de tous les paramètres. @@ -132,7 +157,7 @@ class Traitement { $aLigne = explode("|", $sAllParams); //$aLigne = stringExplode($sAllParams, "|", "\"", "\""); foreach ($aLigne as $sValeur) { - $aValeurs = explode("=", $sValeur); + $aValeurs = explode("=", $sValeur, 2); if ($aValeurs[1] == "") { writeToLog(INFO_NULL_ELEMENT . $aValeurs[0] . ".", $this->sRobotLogFile); } @@ -154,6 +179,8 @@ class Traitement { $sExeName = "FME"; if (!$this->bErreur) { set_time_limit(0); + // Déclaration du tableau de résultat (IMPORTANT!). + $aResult = array(); //$sResultCommande = exec('"'.$this->aProperties["fme_path"].'" APPLY_SETTINGS "Python/Python Interpreter"', $aResult, $iResult); $sResultCommande = exec('"' . $this->aProperties["fme_path"] . '" APPLY_SETTINGS "Python/Use Custom Python" false', $aResult, $iResult); //writeToLog(INFO_INFORM.$sExeName."| ".'"'.$this->aProperties["fme_path"].'" APPLY_SETTINGS "Python/Python Interpreter"', $this->sRobotLogFile); @@ -168,7 +195,7 @@ class Traitement { $aResultCommande = explode("|", $sResultCommande); } //Récupération d'une erreur. - if ($iResult !=0){ + if ($iResult !=0){ //if ($iResult || ($aResultCommande[0] != 0)) { $this->bErreur = true; if (sizeOf($aResultCommande) == 1) @@ -184,17 +211,17 @@ class Traitement { } else { $this->sMessageErreur = "Erreur lors de l exécution de la commande. Consultez le log du robot."; } - $bExistFMELogFile = true; - if (file_exists($sFMELogFile) === false) { - $bExistFMELogFile = false; - } + $bExistFMELogFile = true; + if (file_exists($sFMELogFile) === false) { + $bExistFMELogFile = false; + } foreach ($aResult as $sValeur) { writeToLog(INFO_ERROR . " " . "| " . $sValeur, $this->sRobotLogFile); - if ($bExistFMELogFile === false){ - $monfichier = fopen($sFMELogFile, 'a'); - fputs($monfichier, $sValeur. "\n"); - fclose($monfichier); - } + if ($bExistFMELogFile === false){ + $monfichier = fopen($sFMELogFile, 'a'); + fputs($monfichier, $sValeur. "\n"); + fclose($monfichier); + } } writeToLog(INFO_COMMAND_ERROR . $sCommand . ".", $this->sRobotLogFile); } else { @@ -218,7 +245,7 @@ class Traitement { */ function zipFile($sZipDir, $sZipFileDir, $sZipUrl, $sZipFileName) { writeToLog(str_replace('[sZipDir]', $sZipDir, INFO_FILE_COMPRESSION), $this->sRobotLogFile); - if (createZip($sZipDir, $sZipFileDir . '/' . $sZipFileName . '.zip', 'log')) { + if (createZip($sZipDir, $sZipFileDir . '/' . $sZipFileName . '.zip', 'log', $this->sSecretKey, true)) { $sZipFile = $sZipUrl . "/" . $sZipFileName . ".zip"; writeToLog(INFO_COMPRESSED_FILES . $sZipFile . ".", $this->sRobotLogFile); } else { @@ -234,8 +261,11 @@ class Traitement { function unZipSource($sParam) { $this->sSource = $sParam; // Si la chaine contient un accent, la chaine sera en UTF-8, la fonction file_exists et la fonction de dézippage ne gèrent pas utf-8, il faut donc convertir la chaine en ISO-8859-1 - if (mb_check_encoding($sParam, 'UTF-8')) + $bUtf8Param = false; + if (mb_check_encoding($sParam, 'UTF-8')) { $sParam = utf8_decode($sParam); + $bUtf8Param = true; + } // Si le fichier n'est pas trouvé alors il se trouve dans le répertoire upload if (!file_exists($sParam)) $sParam = $this->aProperties["upload_dir"] . "/" . $sParam; @@ -248,6 +278,9 @@ class Traitement { $sDir = $sParam; $this->sSourceTemp = ""; } + // Si le paramètre passé était encodé en UTF-8 il faut le réencoder sinon problème avec les accents. + if ($bUtf8Param) + $sDir = utf8_encode($sDir); return $sDir; } @@ -261,7 +294,9 @@ class Traitement { writeToLog(INFO_DECOMPRESSING_FILE . $sZipFile . ".", $this->sRobotLogFile); // Les backslashes du chemin de la variable d'environnement TEMP sont remplacés par des slashes $this->aProperties["extract_dir"] = str_replace("\\", "/", $this->aProperties["extract_dir"]); - $sExtractDir = $this->aProperties["extract_dir"] . "/" . $this->UniqFileName() . "/"; + do { + $sExtractDir = $this->aProperties["extract_dir"] . "/" . getUniqRandomId() . "/"; + }while(file_exists($sExtractDir)); $this->sSourceTemp = $sExtractDir; if (unZip($sZipFile, $sExtractDir)) { writeToLog(INFO_FILE_COMPRESSION_DIRECTORY . $sExtractDir . ".", $this->sRobotLogFile); @@ -281,40 +316,51 @@ class Traitement { * $oGtfFmwParser : Objet GtfFmwParser contenant les GUI * $oGui : Objet GUI de la source * $sDirSource : Chaine de caractère contenant le chemin d'accés à la source de données + * \return Tableau des fichiers pour le paramètre "source". */ - function updateSourceParam($oGui, $oGtfFmwParser, $sDirSource) { - $sdelimiterFichier = '""'; - $sdelimiterParam = '""'; - $sSourceMacro = $sdelimiterFichier; + function getSourceParam($oGui, $oGtfFmwParser, $sDirSource, $bUsePattern) { + $aSourceMacro = array(); $i = 0; - + $aSourceFiles = array(); if (is_dir($sDirSource)) { $this->aExtension = $oGui->getExtensionArray($oGui->sFiltreSource); $iExtension = count($this->aExtension); if (($this->aExtension[0] == "*.*" and $oGui->sType == "FILE_OR_URL") or $oGui->sType == "DIRNAME_SRC" or $oGui->sType == "SOURCE_GEODATABASE") { writeToLog(INFO_VALID_DATA_SOURCE_FILE_EXTENSION . $this->aExtension[0], $this->sRobotLogFile); - $sSourceMacro.= $sdelimiterParam . $sDirSource . '/**/' . $sdelimiterParam; + $aSourceFiles[] = $sDirSource . '/**/'; } else { - foreach ($this->aExtension as $sExtension) { - if ($sExtension != "*.*" or ( $sExtension == "*.*" and $iExtension == 1)) { // Si le filtre est différent de *.* (ex : *.mdb) ou si le filtre est seulement *.*. Si le filtre est multiple et que l'un d'eux est *.* (ex : *.*, *.mdb...), il ne sera pas pris en compte - $aExtension = explode(".", $sExtension); - if ($this->countFilesExtension($sDirSource, $aExtension[1]) > 0) { - if ($i != 0) - $sSourceMacro.= "<space>"; - writeToLog(INFO_VALID_DATA_SOURCE_FILE_EXTENSION . $sExtension, $this->sRobotLogFile); - $sSourceMacro.= $sdelimiterParam . $sDirSource . '/**/' . $sExtension . $sdelimiterParam; - $i++; + if ($bUsePattern){ + writeToLog(INFO_USE_PATTERN, $this->sRobotLogFile); + foreach ($this->aExtension as $sExtension) { + if ($sExtension != "*.*" or ( $sExtension == "*.*" and $iExtension == 1)) { // Si le filtre est différent de *.* (ex : *.mdb) ou si le filtre est seulement *.*. Si le filtre est multiple et que l'un d'eux est *.* (ex : *.*, *.mdb...), il ne sera pas pris en compte + $aExtension = explode(".", $sExtension); + if ($this->countFilesExtension($sDirSource, $aExtension[1]) > 0) { + writeToLog(INFO_VALID_DATA_SOURCE_FILE_EXTENSION . $sExtension, $this->sRobotLogFile); + $aSourceFiles[] = $sDirSource . '/**/' . $sExtension; + $i++; + } + } + } + }else{ + writeToLog(INFO_NO_USE_PATTERN, $this->sRobotLogFile); + foreach ($this->aExtension as $sExtension) { + if ($sExtension != "*.*" or ( $sExtension == "*.*" and $iExtension == 1)) { // Si le filtre est différent de *.* (ex : *.mdb) ou si le filtre est seulement *.*. Si le + $aExtension = explode(".", $sExtension); + $aSourceMacroTmp = array_merge($aSourceMacro, $this->returnFileListToString($sDirSource, $aExtension[1])); + $aSourceMacro = array_unique($aSourceMacroTmp); + $i++; + } + } + foreach ($aSourceMacro as $source) { + $aSourceFiles[] = $source; } - } } } - }else { - $sSourceMacro.= $sdelimiterParam . $sDirSource . $sdelimiterParam; - } - $sSourceMacro .= $sdelimiterFichier; - $this->aParametre[$oGui->sDefault_Macro] = $sSourceMacro; - return $sSourceMacro; + }else + $aSourceFiles[] = $sDirSource; + + return $aSourceFiles; } // Compte le nombre de fichier @@ -324,7 +370,7 @@ class Traitement { $num = 0; if (!is_dir($sDirSource)) { $path_parts = pathinfo($sDirSource); - if (is_file($sDirSource) && strtoupper($path_parts['extension']) == strtoupper($sExtension)) + if (is_file($sDirSource) && $path_parts['extension'] == $sExtension) return 1; else return false; @@ -336,6 +382,26 @@ class Traitement { return $num; } + function returnFileListToString($sDirSource, $sExtension) { + $aFileList = array(); + if (!is_dir($sDirSource)) { + $path_parts = pathinfo($sDirSource); + if (is_file($sDirSource) && strtolower($path_parts['extension']) == strtolower($sExtension)){ + array_push($aFileList,$sDirSource); + } + + }else{ + foreach (scandir($sDirSource) AS $entry) { + if (!in_array($entry, array('..', '.'))){ + $aFileListTmp = $aFileList; + $aFileList = array_merge($aFileListTmp,$this->returnFileListToString($sDirSource . "/" . $entry, $sExtension)); + } + + } + } + return $aFileList; + } + /* * Fonction qui se charge de la mise à jour du paramètre "source" s'il existe * $oGtfFmwParser : Objet GtfFmwParser contenant les GUI @@ -343,16 +409,15 @@ class Traitement { * $sDirSource : Chaine de caractère contenant le chemin d'accés à la source de données */ - function updateDestParam($oGui, $oGtfFmwParser, $sNewDir, $sDest) { + function updateDestParam($oGui, $oGtfFmwParser, $sDest) { $this->aExtension = $oGui->getExtensionArray($oGui->sFiltreDest); - if ($sDest == "" && $oGui->sType != "DIRNAME") { - $sDest = $sNewDir; - } + if ($sDest == "" && $oGui->sType != "DIRNAME") + $sDest = $this->sResultDirectoryName; $this->aParametre[$oGui->sDefault_Macro] = ""; $sDest = normalizeString($sDest); $aDest = explode(".", $sDest); if ($oGui->sType == "DIRNAME" or $oGui->sType == "DEST_GEODATABASE") { - $this->aParametre[$oGui->sDefault_Macro] = $this->aProperties["dir_export"] . "/gtf/" . $sNewDir . "/" . $sDest; + $this->aParametre[$oGui->sDefault_Macro] = $this->aProperties["dir_export"] . "/gtf/" . $this->sResultDirectoryName . "/" . $sDest; writeToLog(INFO_OUTPUT_DATA_DIRECTORY . $this->aParametre[$oGui->sDefault_Macro], $this->sRobotLogFile); } if ($oGui->sType == "FILENAME") { @@ -361,7 +426,7 @@ class Traitement { } else { $sResult = str_replace("*", $sDest, $this->aExtension[0]); } - $this->aParametre[$oGui->sDefault_Macro].= $this->aProperties["dir_export"] . "/gtf/" . $sNewDir . '/' . $sResult; + $this->aParametre[$oGui->sDefault_Macro].= $this->aProperties["dir_export"] . "/gtf/" . $this->sResultDirectoryName . '/' . $sResult; $this->sDestination = $sResult; writeToLog(INFO_OUTPUT_DATA_FILE . $this->aParametre[$oGui->sDefault_Macro], $this->sRobotLogFile); @@ -419,7 +484,7 @@ class Traitement { } } - // on encode les balises UTF-8 + // on encode les balises UTF-8 $offset = 0; $iPos = 0; @@ -456,7 +521,7 @@ class Traitement { } } - // on decode les balises UTF-8 + // on decode les balises UTF-8 $matches = array(); preg_match_all("/<u[0-9a-fA-F]{4}>/", $sStr, $matches); @@ -466,9 +531,6 @@ class Traitement { $cChr = $this->unichr($iCode); $sReplaced = str_replace($matches[0][$i], $cChr, $sReplaced); } - if (mb_detect_encoding($sReplaced, "UTF-8, ISO-8859-1") != "ISO-8859-1") { - $sReplaced = iconv("UTF-8", "ISO-8859-1", $sReplaced); - } return $sReplaced; } @@ -484,7 +546,6 @@ class Traitement { function isEncode($sStr) { for ($i = 0; $i < count($this->aReplaceFME); $i++) { if (strpos($sStr, $this->aReplaceFME[$i]) === false && strpos($sStr, $this->aReplaceGTF[$i]) !== false) { - error_log($this->aReplaceFME[$i]); return false; } } @@ -499,89 +560,32 @@ class Traitement { * $oGtfFmwParser : Objet GtfFmwParser contenant les GUI */ - function getTclParams($oGtfFmwParser, $sNewDir) { - $aTGui = array(); + function getTclParams($oGtfFmwParser) { $sChaine = ""; - foreach ($oGtfFmwParser->aGuiObject as $aGui) { - if ($aGui->sDefault_Macro != "") { - array_push($aTGui, $aGui); - } - } - foreach ($aTGui as $oGui) { - //Teste si le paramètre est une source - if ($oGui->bIsSource) { - if ($this->aParametre[$oGui->sDefault_Macro] != "" && substr($this->aParametre[$oGui->sDefault_Macro], 0, 7) != "<quote>") { - $sDirSource = $this->unZipSource($this->aParametre[$oGui->sDefault_Macro]); - $this->updateSourceParam($oGui, $oGtfFmwParser, $sDirSource); - } - } - if ($oGui->bIsDest) { - $sDest = $this->aParametre[$oGui->sDefault_Macro]; - $this->updateDestParam($oGui, $oGtfFmwParser, $sNewDir, $sDest); - } - - if ($this->aParametre[$oGui->sDefault_Macro] != "") { - - if (!$oGui->bIsSource) { - // Protection des guillemets de chaque valeur pour qu'ils soient correctement envoyé à traitement.tcl - // Cette protection s'effectue uniquement pour les paramètre qui ne sont pas sources (un paramètre source est de ce type : """"param1"" ""param2"" ""param3"""" : les guillemets seraient donc protégé et cela fait planter l'exécution) - //$this->aParametre[$oGui->sDefault_Macro] = str_replace('"', '""', $this->aParametre[$oGui->sDefault_Macro]); - } - $aBaliseToReplace = array('<GTF_EQUAL>', '<GTF_PIPE>'); - $aReplacedBalise = array('=', '|'); - $sGTFOutput = str_replace($aBaliseToReplace, $aReplacedBalise, $this->aParametre[$oGui->sDefault_Macro]); - switch ($oGui->sType) { - case "FLOAT": - $sGTFOutput = str_replace(",", ".", $sGTFOutput); - if (!$this->isDecode($sGTFOutput)) - $sGTFOutput = $this->decoderFME($sGTFOutput); - break; - case "LOOKUP_CHOICE": - if (!$this->isEncode($sGTFOutput)) - $sGTFOutput = $this->encoderFME($sGTFOutput); - break; - case "TEXT_EDIT" : - case "TEXT_EDIT_OR_NUM" : - if (!$this->isEncode($sGTFOutput)) { - //$sGTFOutput = $this->decoderFME($sGTFOutput); - $sGTFOutput = $this->encoderFME($sGTFOutput); - } - break; - case "CHOICE": - case "LOOKUP_LISTBOX": - $sGTFOutput = str_replace('"', '""', $sGTFOutput); - if (!$this->isDecode($sGTFOutput)) - $sGTFOutput = $this->decoderFME($sGTFOutput); - break; - default : - $sGTFOutput = str_replace('"', '""', $sGTFOutput); - if (!$this->isDecode($sGTFOutput)) - $sGTFOutput = $this->decoderFME($sGTFOutput); - break; - } - - $sChaine .= " --" . $oGui->sDefault_Macro . " \"" . $sGTFOutput . "\""; - } + $aFmeParameters = $this->getFmeParameters($oGtfFmwParser); + foreach ($aFmeParameters as $sFmeParamKey => $sFmeParamValue) { + // Echappement des doubles quotes pour FME. + $sFmeParamValue = str_replace('"', '\"', $sFmeParamValue); + // Ajout du paramètre en ligne de commande. + $sChaine .= " --" . $sFmeParamKey . " \"" . $sFmeParamValue . "\""; } - // $sChaine = substr($sChaine, 0, -1)."\""; //$sChaine = substr($sChaine, 0, -1); - return $sChaine; } - function listDir($sDossier, $iDemandeId) { - $ouverture = @opendir($sDossier); + function listDir($iDemandeId) { + $ouverture = @opendir($this->sResultDirectoryPath); if (!$ouverture) return false; while ($sFichier = readdir($ouverture)) { if ($sFichier == '.' || $sFichier == '..') continue; - if (is_dir($sDossier . $sFichier)) { + if (is_dir($this->sResultDirectoryPath . $sFichier)) { closedir($ouverture); return true; } else { - $path_parts = pathinfo($sDossier . $sFichier); + $path_parts = pathinfo($this->sResultDirectoryPath . $sFichier); if ($sFichier != "fme_" . $iDemandeId . ".log") { closedir($ouverture); return true; @@ -594,29 +598,27 @@ class Traitement { /* * Fonction qui retourne le nom du fichier résultat qui sera zippé. - * $sDossier correspond au chemin complet dans lequel les fichiers/dossiers résultats seront générés - * $sNewDir correspond au numéro unique du dossier dans lequel les fichiers/dossiers résultats seront générés * $iDemandeId correspond au numéro de la demande * \return Retourne NULL (s'il y a plusieurs fichiers résultat à zipper) ou le fichier résultat qui sera zippé sous la forme 20140324141501876/ventes.xlsx */ - function CheckResult($sDossier, $sNewDir, $iDemandeId) { - $ouverture = @opendir($sDossier); + function CheckResult($iDemandeId) { + $ouverture = @opendir($this->sResultDirectoryPath); $NbFile = 0; while ($sFichier = readdir($ouverture)) { if ($sFichier == '.' || $sFichier == '..') continue; - if (is_dir($sDossier . $sFichier)) { + if (is_dir($this->sResultDirectoryPath . $sFichier)) { $NbFile = $NbFile + 2; } else { - $path_parts = pathinfo($sDossier . $sFichier); + $path_parts = pathinfo($this->sResultDirectoryPath . $sFichier); if ($sFichier != "fme_" . $iDemandeId . ".log") { $NbFile = $NbFile + 1; } } } closedir($ouverture); - $ouverture = @opendir($sDossier); + $ouverture = @opendir($this->sResultDirectoryPath); if ($NbFile != 1) { return ""; } else { @@ -625,8 +627,8 @@ class Traitement { if ($sFichier == '.' || $sFichier == '..') continue; if ($sFichier != "fme_" . $iDemandeId . ".log") { - if (preg_match($pattern, $sFichier)) { - return $sNewDir . "/" . $sFichier; + if (preg_match($pattern, $sFichier) && !$this->bDataEncrypt) { + return $this->sResultDirectoryName . "/" . $sFichier; } else { return ""; } @@ -643,131 +645,142 @@ class Traitement { function Process() { $oGtfFmwParser = new GtfFmwParser($this->aProperties["workspace_dir"] . "/" . $this->sFmwFileName); - // Création du répertoire de sortie des données - $sNewDir = $this->UniqFileName(); - if (!file_exists($this->aProperties["dir_export"] . "/gtf/")){ - mkdir($this->aProperties["dir_export"] . "/gtf/"); - } - $sDestDir = $this->aProperties["dir_export"] . "/gtf/" . $sNewDir; - mkdir($sDestDir); - // Création de l'URL de téléchargement des données - $sDestUrl = $this->aProperties["url_export"] . "/gtf/" . $sNewDir; + // Création du répertoire de résultat. + $this->sResultDirectoryName = $this->createResultDirectory(); //1 Initialisation & calcul du chemin d'accés du fichier TCL $sCommandLine = '"' . $this->aProperties["fme_path"] . '" "' . getenv("GTF_HOME") . "/" . utf8_decode($this->sFmwFileName) . '"'; //3 Ajout des paramètres propres au traitement - $sTclParams = $this->getTclParams($oGtfFmwParser, $sNewDir); + $sTclParams = $this->getTclParams($oGtfFmwParser); if ($sTclParams != "") $sCommandLine .= $sTclParams; - // Ajout de trois paramètres GTF_ORDER_ID, GTF_SHARED_DIR et GTF_CONNECTION_STRING qui seront passés à chaque demande de traitement - $sCommandLine .= " --GTF_ORDER_ID \"" . $this->iDemandeId . "\" --GTF_SHARED_DIR \"" . $this->aProperties["shared_dir"] . "\" --GTF_CONNECTION_STRING \"" . $this->aProperties["server"] . "," . $this->aProperties["port"] . "," . $this->aProperties["database"] . "," . $this->aProperties["login"] . "\" --GTF_REST_URL \"" . $this->aProperties["web_server_name"] . "/" . $this->aProperties["services_alias"] . "/\"" ; + $aTGui = array(); + $sChaine = ""; + + // Ajout des paramètres GTF_ s'ils sont associés à un paramètre publié du traitement + $aGtfParameters = $this->getGtfParameters($oGtfFmwParser); + foreach ($aGtfParameters as $sGtfParamKey => $sGtfParamValue) { + $sCommandLine .= ' --' . $sGtfParamKey . ' "' . $sGtfParamValue . '"'; + } /* * 2 Passage des paramètres généraux * - FMWFILENAME : fichier Fmw * - LOG_ROBOT : fichier log du robot * - LOG_FILENAME : fichier log pour FME */ - $sCommandLine.= " -LOG_FILENAME \"" . $sDestDir . "/fme_" . $this->iDemandeId . ".log\" 2>&1"; + $sCommandLine.= " -LOG_FILENAME \"" . $this->getFmeLogFilePath($this->sResultDirectoryPath) . "\" 2>&1"; writeToLog(INFO_GTF_HOME . getenv("GTF_HOME"), $this->sRobotLogFile); writeToLog(INFO_FME_PATH . getenv("FME_PATH"), $this->sRobotLogFile); writeToLog(INFO_TREATMENT_COMMAND_LINE . $sCommandLine, $this->sRobotLogFile); //4 Lancement de la ligne de commande - $this->sLogFme = $sNewDir . "/fme_" . $this->iDemandeId . ".log"; + $this->sLogFme = $this->getFmeLogFilePath($this->sResultDirectoryName); $sSql = "UPDATE " . $this->aProperties['schema_gtf'] . ".order SET log_url='" . $this->sLogFme . "' WHERE order_id=" . $this->iDemandeId; $this->oBd->execute($sSql); - $this->execute($sCommandLine, $sDestDir . "/fme_" . $this->iDemandeId . ".log"); + $this->execute($sCommandLine, $this->getFmeLogFilePath($this->sResultDirectoryPath)); writeToLog(INFO_TREATMENT_COMMAND_LINE . $sCommandLine, $this->sRobotLogFile); - if (file_exists($sDestDir . "/fme_" . $this->iDemandeId . ".log") == false) { + if (file_exists($this->getFmeLogFilePath($this->sResultDirectoryPath)) == false) { $this->sLogFme = ""; } - $bHasFile = $this->listDir($sDestDir, $this->iDemandeId); - if ($this->sSourceTemp != "" && $this->aProperties["debug_mode"] == false) { - clearDir($this->sSourceTemp); - } - if ($bHasFile) { - $sIsToZip = $this->CheckResult($sDestDir, $sNewDir, $this->iDemandeId); - - if ($sIsToZip == "") { - //Compression en zip des fichiers résultant. Le parametres Array() permet de filtrer les extensions à compresser. Exemples : Array("shp","shx","dbf") ; Array("*") - $sZipFile = $this->zipFile($sDestDir, $sDestDir, $sDestUrl, $this->iDemandeId); - $sZipFile = str_replace($sDestUrl, $sNewDir, $sZipFile); - } else { - $sZipFile = str_replace($sDestUrl, $sNewDir, $sIsToZip); - } - } else { - $sZipFile = ""; - } + // Tous les fichiers de résultat dans une archive .zip. + $sZipFile = $this->zipResultFiles(); return $sZipFile; } /** - * Get the log file of a job. + * Submit a job on Fme Server. * @param {object} $oFmeServer FmeServer class object. * @param {array} $aOrder Order parameters. * @param {array} $aWorkspace Workpace parameters. * @param {array} $aUser User infos. + * @param {object} $oAmazonS3 AmazonS3 class object. + * @param {boolean} $bAsync Asynchronous job. + * @return string */ - function processFmeServerOrder($oFmeServer, $aOrder, $aWorkspace, $aUser, $oAmazonS3 = null) { - $sNewDir = $this->UniqFileName(); - $oGtfFmwParser = new GtfFmwParser($this->aProperties["workspace_dir"] . "/" . $this->sFmwFileName); + function submitFmeServerJob($oFmeServer, $aOrder, $aWorkspace, $aUser, $oAmazonS3 = null, $bAsync = false) { + $this->oFmeServer = $oFmeServer; + $this->oAmazonS3 = $oAmazonS3; + // Création du répertoire de résultat. + $this->sResultDirectoryName = $this->createResultDirectory(); // Fichiers sources en entrée et sortie. + $oGtfFmwParser = new GtfFmwParser($this->aProperties["workspace_dir"] . "/" . $this->sFmwFileName); + $aTGui = array(); $aSourceFiles = array(); - $aDestinationFiles = ''; + $aDestinationFiles = array(); foreach ($oGtfFmwParser->aGuiObject as $oGui) { if ($oGui->sDefault_Macro != "") { + array_push($aTGui, $oGui); //Teste si le paramètre est une source if ($oGui->bIsSource) - $aSourceFiles[] = $oGui->sDefault_Macro; + $aSourceFiles[$oGui->sDefault_Macro] = $this->aParametre[$oGui->sDefault_Macro]; else if ($oGui->bIsDest) - $aDestinationFiles = $oGui->sDefault_Macro; + $aDestinationFiles[] = $oGui->sDefault_Macro; } } + // Sauve la liste des fichiers de destination. + $this->aDestinationFiles = $aDestinationFiles; // Liste des paramètres publiés. - $aWkParams = explode('|', $aOrder['wk_params']); - $aPublishedParams = array(); - foreach($aWkParams as $sWkParam) { - $aWkParam = explode('=', $sWkParam); - $aPublishedParams[$aWkParam[0]] = $aWkParam[1]; - } - // Création d'un dépot pour l'utilisateur si inexistant. - $sRepository = $aUser["login"]; - $oFmeServer->createRepository($sRepository, $aUser["name"]); + $aFmeParameters = $this->getFmeParameters($oGtfFmwParser); + // Ajout des paramètres GTF_ s'ils sont associés à un paramètre publié du traitement. + $aFmeParameters = array_merge($aFmeParameters, $this->getGtfParameters($oGtfFmwParser)); + // Création d'un identifiant pour l'instance de GTF (si inexistant). + if (empty($this->aProperties["gtf_instance_id"])) + $this->aProperties["gtf_instance_id"] = generateGtfInstanceId(); + // Création d'un dépot pour l'instance de GTF si inexistant. + $sRepository = $this->aProperties["gtf_instance_id"]; + if ($oFmeServer->createRepository($sRepository, '') === false) + return false; // Création d'un répertoire dans les ressources si inexistant. $sResource = 'FME_SHAREDRESOURCE_TEMP'; - $sPath = 'gtf'; - $oFmeServer->createResourceConnectionDirectory($sResource, $sPath, $sNewDir); - $sPath .= '/' . $sNewDir; - // Upload des fichiers sources. - foreach($aSourceFiles as $sParamName) { - // Si un objet de la classe AmazonS3 est passé, les ressources seront envoyées sur S3 puis téléchargéss dans FME Server. - if (empty($oAmazonS3)) - $oFmeServer->uploadResourceFile($this->aProperties['upload_dir'] . '/' . $aPublishedParams[$sParamName], $sResource, $sPath, true, true); - else { - // Upload d'un fichier source vers S3. - $sBucket = 'dev-gtf-fmecloud'; - $sKey = $sPath . '/' . $aPublishedParams[$sParamName]; - $oAmazonS3->uploadFile($sBucket, $sKey, $this->aProperties['upload_dir'] . '/' . $aPublishedParams[$sParamName]); - // Upload du fichier S3Downloader.fmw (si inexistant). - $oFmeServer->uploadWorkspaceFile('E:\_Docs\GTF moteur serveur\S3Downloader.fmw', $sRepository); - // Téléchargement depuis FME Server du fichier envoyé sur S3. - $aS3Params = array( - 'access_key_id' => $oAmazonS3->sAwsAccessKeyId, - 'secret_access_key' => $oAmazonS3->sAwsSecretAccessKey, - 'bucket_name' => $sBucket, - 'object_key' => $sKey - ); - $oJobResult = $oFmeServer->uploadResourceFileFromS3($aS3Params, $sRepository, $sResource, $sPath); + $sPath = $this->aProperties["gtf_instance_id"]; + if ($oFmeServer->createResourceConnectionDirectory($sResource, $sPath, $this->sResultDirectoryName) === false) + return false; + $sPath .= '/' . $this->sResultDirectoryName; + // Copie locale du fichier .fmw avec la version. + $sFmwFilePath = $this->aProperties["workspace_dir"] . "/" . $this->sFmwFileName; + $sRepositoryFmwFileName = pathinfo($this->sFmwFileName, PATHINFO_FILENAME) . '_v' . $aWorkspace['version'] . '.' . pathinfo($this->sFmwFileName, PATHINFO_EXTENSION); + $sRepositoryFmwFilePath = $this->aProperties["workspace_dir"] . "/" . pathinfo($this->sFmwFileName, PATHINFO_DIRNAME) . "/" . $sRepositoryFmwFileName; + if (!file_exists($sRepositoryFmwFilePath)) { + copy($sFmwFilePath, $sRepositoryFmwFilePath); + // Supprime le fichier de l'ancienne version du projet (GTF + Fme Server). + if ($aWorkspace['version'] > 0) { + $sRepositoryOldFmwFileName = pathinfo($this->sFmwFileName, PATHINFO_FILENAME) . '_v' . --$aWorkspace['version'] . '.' . pathinfo($this->sFmwFileName, PATHINFO_EXTENSION); + unlink($this->aProperties["workspace_dir"] . "/" . pathinfo($this->sFmwFileName, PATHINFO_DIRNAME) . "/" . $sRepositoryOldFmwFileName); + //$oFmeServer->serviceRequest('repositories/' . $sRepository . '/items/' . $sRepositoryOldFmwFileName, 'delete'); } } // Upload du fichier .fmw. - $sFmwFileName = $this->aProperties["workspace_dir"] . "/" .$this->sFmwFileName; - $oFmeServer->uploadWorkspaceFile($sFmwFileName, $sRepository, true); + if ($oFmeServer->uploadWorkspaceFile($sFmwFilePath, $sRepository, false, $sRepositoryFmwFileName) === false) + return false; + // Upload des fichiers sources. + foreach($aSourceFiles as $sParamName => $sParamValue) { + if ($this->uploadFileToResourceConnection($this->aProperties["upload_dir"] . "/" . $sParamValue, $sRepository, $sResource, $sPath) === false) + return false; + /* + // Upload de plusieurs fichiers sources (ex: contenu d'une archive .zip). + $aFiles = explode('|', $aFmeParameters[$sParamName]); + foreach ($aFiles as $sFilePath) { + if ($this->uploadFileToResourceConnection($sFilePath, $sRepository, $sResource, $sPath) === false) + return false; + } + */ + } + // Upload des ressources complémentaires du projet. + $sResourcesDirectory = $this->aProperties["workspace_dir"] . '/' . pathinfo($this->sFmwFileName, PATHINFO_DIRNAME); + if (is_dir($sResourcesDirectory)) { + foreach (scandir($sResourcesDirectory) as $sFileName) { + if (is_file($sResourcesDirectory . '/' . $sFileName) && array_search(pathinfo($sFileName, PATHINFO_EXTENSION), array('fmw', 'bak')) === false) { + if ($oFmeServer->uploadWorkspaceResourceFile($sResourcesDirectory . '/' . $sFileName, $sRepository, $sRepositoryFmwFileName, false) === false) + return false; + } + } + } // Paramètres publiés dans les données de la requête. - $aJobParams = array( - 'publishedParameters' => array(), - 'NMDirectives' => array( + $aJobParams = array('publishedParameters' => array()); + // Traitement asynchrone. + if ($bAsync) { + $aJobParams['NMDirectives'] = array( 'successTopics' => array( 'TEST_TOPIC_FRED' ), @@ -778,38 +791,438 @@ class Traitement { array('name' => 'order_id', 'value' => $this->iDemandeId) ) //'subsection' => 'REST_SERVICE' - ) - ); - foreach ($aPublishedParams as $sParamName => $sParamValue) { - if (in_array($sParamName, $aSourceFiles)) - $aJobParams['publishedParameters'][] = array("name" => $sParamName, "value" => array('$(' . $sResource . ')/' . $sPath . '/' . pathinfo($sParamValue, PATHINFO_BASENAME))); + ); + } + // Chemins pour les paramètres publiés qui sont des fichiers. + foreach ($aFmeParameters as $sParamName => $sParamValue) { + if (array_key_exists($sParamName, $aSourceFiles)) { + $aFiles = explode('|', $sParamValue); + $aPublishedParametersFiles = array(); + foreach ($aFiles as $sFilePath) + $aPublishedParametersFiles[] = '$(' . $sResource . ')/' . $sPath . '/' . $sFilePath; + $aJobParams['publishedParameters'][] = array("name" => $sParamName, "value" => $aPublishedParametersFiles); + } + else if (in_array($sParamName, $aDestinationFiles)) + $aJobParams['publishedParameters'][] = array("name" => $sParamName, "value" => '$(' . $sResource . ')/' . $sPath . '/' . $this->sResultDirectoryName . '.zip/' . pathinfo($sParamValue, PATHINFO_BASENAME)); else - $aJobParams['publishedParameters'][] = array("name" => $sParamName, "value" => '$(' . $sResource . ')/' . $sPath . '/' . $sNewDir . '.zip/' . $sParamValue); + $aJobParams['publishedParameters'][] = array("name" => $sParamName, "value" => $sParamValue); } // Envoi d'une demande sur un traitement. - $this->oJobResult = $oFmeServer->submitJob($sRepository, $aWorkspace['fmw_file'], $aJobParams); - // Téléchargement du fichier de log du traitement. + $this->oJobResult = $oFmeServer->submitJob($sRepository, $sRepositoryFmwFileName, $aJobParams, $bAsync); + if ($this->oJobResult === false) + return false; + // Demande synchrone -> téléchargement des fichiers de log et de résultat du traitement. + if ($bAsync) { + $this->aJobDestinationFiles = $aDestinationFiles; + $this->sJobResource = $sResource; + $this->sJobPath = $sPath; + } + else { + $sZipFile = ''; + if (!empty($this->oJobResult->id)) + $sZipFile = $this->downloadFmeServerJobResult($this->oJobResult->id, $oFmeServer, $aDestinationFiles, $sResource, $sPath); + return $sZipFile; + } + } + + /** + * Init all the encryption parameters of the user. + */ + function initUserEncryptionParameters() { + // Paramètres de cryptage de l'utilisateur. + $sSql = 'SELECT "order".user_id, workspace.workspace_id, "user".login, "user".dataencrypt AS user_dataencrypt, workspace.dataencrypt AS workspace_dataencrypt, "user".secretkey FROM [sSchemaGtf].order LEFT JOIN [sSchemaFramework].user ON "order".user_id = "user".user_id LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id = workspace.workspace_id WHERE order_id = [iDemandeId]'; + $aParams = array(); + $aParams['sSchemaFramework'] = array('value' => $this->aProperties["schema_framework"], 'type' => 'schema_name'); + $aParams['sSchemaGtf'] = array('value' => $this->aProperties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iDemandeId'] = array('value' => $this->iDemandeId, 'type' => 'number'); + $oPDOResult = $this->oBd->executeWithParams($sSql, $aParams); + if (!$this->oBd->enErreur()) { + $aUserEncryptParams = $this->oBd->ligneSuivante($oPDOResult); + if ($aUserEncryptParams['user_dataencrypt'] === true || $aUserEncryptParams['workspace_dataencrypt'] === true) { + // Le résultat de la demande sera crypté. + $this->bDataEncrypt = true; + // Clé secrète de l'utilisateur (sinon il faut la générer). + if ($aUserEncryptParams['user_dataencrypt'] === true && !empty($aUserEncryptParams['secretkey'])) + // Décryptage de la clé secrète (suppression des caractères "NULL"). + $this->sSecretKey = str_replace(chr(0), '', des($aUserEncryptParams['login'], hexToString($aUserEncryptParams['secretkey']), 0, 0)); + else { + // Génération de la clé secrète. + $this->sSecretKey = getUniqRandomId(); + // Envoi du mail avec la clé secrète générée. + $aObjects = array(); + $aObjects['oOrder'] = new OrderLib($this->oBd, $this->iDemandeId, $this->aProperties, "v_order"); + $aObjects['oOrder']->formatOrderEmail(); + $aObjects['oOrder']->sSecretKey = $this->sSecretKey; + $oEmail = new Email($this->oBd, -1, $this->aProperties, $aObjects); + if (!empty($oEmail->oEmailTemplate->name)) + $oEmail->send(); + } + } + } + else + writeToLog(INFO_USER_ENCRYPT_PARAMETERS . "(SQL : $sSql)", $this->sRobotLogFile); + } + + /** + * Upload a file to a resource connection on Fme Server. + * @param {string} $sFilePath Path of the file to upload. + * @param {string} $sRepository Name of the repository. + * @param {string} $sResource Name of a resource connection. + * @param {string} $sPath Path, relative to the resource connection. + */ + function uploadFileToResourceConnection($sFilePath, $sRepository, $sResource, $sPath) { + // Si un objet de la classe AmazonS3 existe, les ressources seront envoyées sur S3 puis téléchargéss dans FME Server. + if (empty($this->oAmazonS3)) { + if ($this->oFmeServer->uploadResourceFile($sFilePath, $sResource, $sPath, true, true) === false) + return false; + } + else { + // Upload d'un fichier source vers S3. + //$sS3Bucket = $this->aProperties['fme_cloud_s3_bucket']; + $sS3ObjectKey = $sPath . '/' . pathinfo($sFilePath, PATHINFO_BASENAME); + if ($this->oAmazonS3->uploadFile($this->sFmeServerS3Bucket, $sS3ObjectKey, $sFilePath) === false) + return false; + // Upload du fichier S3Downloader.fmw (si inexistant). + if ($this->oFmeServer->uploadWorkspaceFile($this->aProperties['workspace_dir'] . '/S3Downloader.fmw', $sRepository, false) === false) + return false; + // Téléchargement depuis FME Server du fichier envoyé sur S3. + $aS3Params = array( + 'access_key_id' => $this->oAmazonS3->sAwsAccessKeyId, + 'secret_access_key' => $this->oAmazonS3->sAwsSecretAccessKey, + 'bucket_name' => $this->sFmeServerS3Bucket, + 'object_key' => $sS3ObjectKey + ); + $oJobResult = $this->oFmeServer->uploadResourceFileFromS3($aS3Params, $sRepository, $sResource, $sPath); + if ($oJobResult === false) + return false; + } + } + + function setUseExternalDbConnection ($bValue){ + if(is_bool($bValue)){ + $this->bUseExternalDbConnection = $bValue; + } + } + + /** + * get GTF parameters. + * @param {object} $oGtfFmwParser All the GUI. + * @return array + */ + function getGtfParameters($oGtfFmwParser) { + // Ajout des paramètres GTF_ s'ils sont associés à un paramètre publié du traitement. + $aGtfParams = array(); + foreach ($oGtfFmwParser->aGuiObject as $aGui) { + switch($aGui->sDefault_Macro) { + case 'GTF_ORDER_ID': + $aGtfParams['GTF_ORDER_ID'] = $this->iDemandeId; + break; + case 'GTF_SHARED_DIR': + $aGtfParams['GTF_SHARED_DIR'] = $this->aProperties["shared_dir"]; + break; + case 'GTF_CONNECTION_STRING': + if ($aGui->sType == "PASSWORD") { + $sServer = $this->aProperties["server"]; + $iPort = $this->aProperties["port"]; + if ($this->bUseExternalDbConnection){ + $sServer = $this->aProperties["connectionStringExternalIp"]; + $iPort = $this->aProperties["connectionStringExternalPort"]; + } + $aGtfParams['GTF_CONNECTION_STRING'] = $sServer . "," . $iPort . "," . $this->aProperties["database"] . "," . $this->aProperties["login_scheduler"] . "," . $this->aProperties["password_scheduler"]; + } + else + writeToLog(INFO_GTF_CONNECTION_STRING_TYPE_PASSWORD, $this->sRobotLogFile); + break; + case 'GTF_REST_URL': + $aGtfParams['GTF_REST_URL'] = $this->aProperties["web_server_name"] . "/" . $this->aProperties["services_alias"] . '/'; + break; + case 'GTF_USER_RESTRICTION': + // Restriction de l'utilisateur. + $sSql = 'SELECT "user".restriction FROM [sSchemaGtf].order LEFT JOIN [sSchemaFramework].user ON "order".user_id = "user".user_id WHERE order_id = [iDemandeId]'; + $aParams = array(); + $aParams['sSchemaFramework'] = array('value' => $this->aProperties["schema_framework"], 'type' => 'schema_name'); + $aParams['sSchemaGtf'] = array('value' => $this->aProperties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iDemandeId'] = array('value' => $this->iDemandeId, 'type' => 'number'); + $oPDOResult = $this->oBd->executeWithParams($sSql, $aParams); + if (!$this->oBd->enErreur()) { + $aUserRestriction = $this->oBd->ligneSuivante($oPDOResult); + $aGtfParams['GTF_USER_RESTRICTION'] = $aUserRestriction['restriction']; + } + else + writeToLog(INFO_USER_RESTRICTION . "(SQL : $sSql)", $this->sRobotLogFile); + break; + } + } + return $aGtfParams; + } + + /** + * format a GUI occurrence (GtfGui). + * @param {object} $oGui GUI occurrence. + * @return string + */ + function formatGuiOccurrence($oGui) { + $aBaliseToReplace = array('<GTF_EQUAL>', '<GTF_PIPE>'); + $aReplacedBalise = array('=', '|'); + $sGTFOutput = str_replace($aBaliseToReplace, $aReplacedBalise, $this->aParametre[$oGui->sDefault_Macro]); + switch ($oGui->sType) { + case "FLOAT": + $sGTFOutput = str_replace(",", ".", $sGTFOutput); + if (!$this->isDecode($sGTFOutput)) + $sGTFOutput = $this->decoderFME($sGTFOutput); + break; + case "LOOKUP_CHOICE": + if (!$this->isEncode($sGTFOutput)) + $sGTFOutput = $this->encoderFME($sGTFOutput); + break; + case "TEXT_EDIT" : + case "TEXT_EDIT_OR_NUM" : + if (!$this->isEncode($sGTFOutput)) { + //$sGTFOutput = $this->decoderFME($sGTFOutput); + $sGTFOutput = $this->encoderFME($sGTFOutput); + } + break; + case "CHOICE": + case "LOOKUP_LISTBOX": + $sGTFOutput = str_replace('"', '""', $sGTFOutput); + if (!$this->isDecode($sGTFOutput)) + $sGTFOutput = $this->decoderFME($sGTFOutput); + break; + case "LISTBOX": + if ($this->bFmeServer) + $sGTFOutput = explode('<space>', $sGTFOutput); + else if (!$this->isDecode($sGTFOutput)) + $sGTFOutput = $this->decoderFME($sGTFOutput); + break; + default : + //$sGTFOutput = str_replace('"', '""', $sGTFOutput); + if (!$this->isDecode($sGTFOutput)) + $sGTFOutput = $this->decoderFME($sGTFOutput); + break; + } + return $sGTFOutput; + } + + /** + * Compress all the result files to a zip archive. + * @return string + */ + function zipResultFiles() { + // Tous les fichiers de résultat dans une archive .zip. + $bHasFile = $this->listDir($this->iDemandeId); + if ($this->sSourceTemp != "" && $this->aProperties["debug_mode"] == false) + clearDir($this->sSourceTemp); + if ($bHasFile) { + // Paramètres de cryptage de l'utilisateur. + $this->initUserEncryptionParameters(); + // + $sIsToZip = $this->CheckResult($this->iDemandeId); + $sDestUrl = $this->aProperties["url_export"] . "/gtf/" . $this->sResultDirectoryName; + if ($sIsToZip == "") { + //Compression en zip des fichiers résultant. Le parametres Array() permet de filtrer les extensions à compresser. Exemples : Array("shp","shx","dbf") ; Array("*") + $sZipFile = $this->zipFile($this->sResultDirectoryPath, $this->sResultDirectoryPath, $sDestUrl, $this->iDemandeId); + $sZipFile = str_replace($sDestUrl, $this->sResultDirectoryName, $sZipFile); + } + else + $sZipFile = str_replace($sDestUrl, $this->sResultDirectoryName, $sIsToZip); + } + else + $sZipFile = ""; + return $sZipFile; + } + + /** + * Create a directory for the result files. + * @return string + */ + function createResultDirectory() { + // Création du répertoire de sortie des données + if (!file_exists($this->aProperties["dir_export"] . '/gtf/')){ + mkdir($this->aProperties["dir_export"] . '/gtf/'); + } + Do { + $sResultDirectoryName = getUniqRandomId(); + $this->sResultDirectoryPath = $this->aProperties["dir_export"] . '/gtf/' . $sResultDirectoryName; + }while(file_exists($this->sResultDirectoryPath)); + // Création d'un répertoire pour le fichier de log FME. + mkdir($this->sResultDirectoryPath . '/log', 0777, true); + return $sResultDirectoryName; + } + + /** + * get GTF parameters. + * @param {object} $oGtfFmwParser All the GUI. + * @return array + */ + function getFmeParameters($oGtfFmwParser) { + $aTGui = array(); + $aFmeParameters = array(); + $bUsePattern = false; + foreach ($oGtfFmwParser->aGuiObject as $aGui) { + if ($aGui->sDefault_Macro != "") { + array_push($aTGui, $aGui); + } + if ($aGui->sDefault_Macro == "GTF_FORCE_PATTERN") { + if (strtolower($aGui->sDefaultValue) == "true"){ + $bUsePattern = true; + } + } + } + foreach ($aTGui as $oGui) { + //Teste si le paramètre est une source + if ($oGui->bIsSource) { + if ($this->aParametre[$oGui->sDefault_Macro] != "" && substr($this->aParametre[$oGui->sDefault_Macro], 0, 7) != "<quote>") { + $this->aParametre[$oGui->sDefault_Macro] = str_replace('//', '/', $this->aParametre[$oGui->sDefault_Macro]); + $sSourceFilePath = $this->aParametre[$oGui->sDefault_Macro]; + $sDirSource = $this->unZipSource($this->aParametre[$oGui->sDefault_Macro]); + $aSourceFiles = $this->getSourceParam($oGui, $oGtfFmwParser, $sDirSource, $bUsePattern); + // Test si le fichier source est un .zip. + $bZipSource = false; + if (strtolower(pathinfo($sSourceFilePath, PATHINFO_EXTENSION)) == 'zip') { + $bZipSource = true; + // Si le fichier n'est pas trouvé alors il se trouve dans le répertoire upload. + if (!file_exists($sSourceFilePath)) + $sSourceFilePath = $this->aProperties["upload_dir"] . "/" . $sSourceFilePath; + } + // Formatage des fichiers sources. + foreach ($aSourceFiles as $iIndex => $sSourceFile) { + // Si le fichier source est un .zip -> paramètre publié = [chemin de l'archive zip]/[nom du fichier]. + if ($bZipSource) { + if ($this->bFmeServer) { + $sSourceFile = str_replace($sDirSource, '', $sSourceFile); + $aSourceFiles[$iIndex] = pathinfo($sSourceFilePath, PATHINFO_BASENAME) . '/' . $sSourceFile; + } + else + $aSourceFiles[$iIndex] = '""' . str_replace($sDirSource, $sSourceFilePath, $sSourceFile) . '""'; + // Supprime l'archive décompressée. + clearDir($sDirSource); + } + else if ($this->bFmeServer) + $aSourceFiles[$iIndex] = pathinfo($sSourceFile, PATHINFO_BASENAME); + else + $aSourceFiles[$iIndex] = '""' . $sSourceFile . '""'; + // Supprime les doubles slashes. + $aSourceFiles[$iIndex] = str_replace('//', '/', $aSourceFiles[$iIndex]); + } + // Paramètre au format chaine de caractère. + if ($this->bFmeServer) + $this->aParametre[$oGui->sDefault_Macro] = implode('|', $aSourceFiles); + else + $this->aParametre[$oGui->sDefault_Macro] = '"' . implode('<space>', $aSourceFiles) . '"'; + } + } + if ($oGui->bIsDest) { + $sDest = $this->aParametre[$oGui->sDefault_Macro]; + $this->updateDestParam($oGui, $oGtfFmwParser, $sDest); + } + + if ($this->aParametre[$oGui->sDefault_Macro] != "") { + + if (!$oGui->bIsSource) { + // Protection des guillemets de chaque valeur pour qu'ils soient correctement envoyé à traitement.tcl + // Cette protection s'effectue uniquement pour les paramètre qui ne sont pas sources (un paramètre source est de ce type : """"param1"" ""param2"" ""param3"""" : les guillemets seraient donc protégé et cela fait planter l'exécution) + //$this->aParametre[$oGui->sDefault_Macro] = str_replace('"', '""', $this->aParametre[$oGui->sDefault_Macro]); + } + $sGTFOutput = $this->formatGuiOccurrence($oGui); + $aFmeParameters[$oGui->sDefault_Macro] = $sGTFOutput; + } + } + return $aFmeParameters; + } + + /** + * Cryptage d'une archive .zip. + * @param {string} $sZipFilePath Path to the zip archive File. + * @param {string} $sPassword Password for the zip archive. + * @return boolean + */ + function encryptZipArchiveFiles($sZipFilePath, $sPassword) { + $zip = new ZipArchive; + $res = $zip->open($sZipFilePath); + if ($res === true) { + for ($i = 0; $i < $zip->numFiles; $i++) { + $zip->setEncryptionIndex($i, ZipArchive::EM_AES_256, $sPassword); + } + $zip->close(); + return true; + } + else + return false; + } + + /** + * Return the path for the log file. + * @param {string} $sResultDirectoryPath Path to the result directory. + * @return string + */ + function getFmeLogFilePath($sResultDirectoryPath) { + return $sResultDirectoryPath . "/log/fme_" . $this->iDemandeId . ".log"; + } + + /** + * Return the record for a job on Fme Server. + * @param {string} $iJobId The id of the job. + * @param {object} $oFmeServer FmeServer object. + * @return object + */ + function getFmeServerJobRecord($iJobId, $oFmeServer) { + return $oFmeServer->serviceRequest('transformations/jobs/id/' . $iJobId, 'get'); + } + + /** + * Download Result and log files of a job on Fme Server. + * @param {number} $iJobId The od of the job. + * @param {object} $oFmeServer FmeServer object. + * @return object + */ + function downloadFmeServerJobResult($iJobId, $oFmeServer, $aDestinationFiles, $sResource, $sPath) { $sZipFile = ''; - if (!empty($this->oJobResult->id)) { - // Sauve l'url de log fme. - $this->sLogFme = $sNewDir . "/fme_" . $this->iDemandeId . ".log"; - $sSql = "UPDATE " . $this->aProperties['schema_gtf'] . ".order SET log_url='" . $this->sLogFme . "' WHERE order_id=" . $this->iDemandeId; - $this->oBd->execute($sSql); - // Création du répertoire de sortie des données - if (!file_exists($this->aProperties["dir_export"] . "/gtf/")) - mkdir($this->aProperties["dir_export"] . "/gtf/"); - $sDestDir = $this->aProperties["dir_export"] . "/gtf/" . $sNewDir; - mkdir($sDestDir); - // Création de l'URL de téléchargement des données - $sDestUrl = $this->aProperties["url_export"] . "/gtf/" . $sNewDir; - $oFmeServer->getJobLog($this->oJobResult->id, $sDestDir . "/fme_" . $this->iDemandeId . ".log"); - // Télécharge les fichiers de résultat. + // Téléchargement du fichier de log. + if ($oFmeServer->getJobLog($this->oJobResult->id, $this->getFmeLogFilePath($this->sResultDirectoryPath)) === false) + return false; + // Sauve l'url de log fme. + $this->sLogFme = $this->getFmeLogFilePath($this->sResultDirectoryName); + $sSql = "UPDATE " . $this->aProperties['schema_gtf'] . ".order SET log_url='" . $this->sLogFme . "' WHERE order_id=" . $this->iDemandeId; + $this->oBd->execute($sSql); + // Téléchargement des fichiers de résultat (si traitement). + if ($this->oJobResult->status == 'SUCCESS') { + // Télécharge l'archive .zip contenant tous les fichiers de résultat. if (!empty($aDestinationFiles)) { - $oFmeServer->downloadResourceFile($sResource, $sPath . '/' . $sNewDir . '.zip', $sDestDir . '/' . $this->iDemandeId . '.zip'); - $sZipFile = $sNewDir . '/' . $this->iDemandeId . '.zip'; + if ($oFmeServer->downloadResourceFile($sResource, $sPath . '/' . $this->sResultDirectoryName . '.zip', $this->sResultDirectoryPath . '/' . $this->iDemandeId . '.zip') === false) + return false; + $sZipFile = $this->sResultDirectoryName . '/' . $this->iDemandeId . '.zip'; + // Paramètres de cryptage de l'utilisateur. + $this->initUserEncryptionParameters(); + // Cryptage de l'archive .zip de résultat. + if ($this->bDataEncrypt) + $this->encryptZipArchiveFiles($this->sResultDirectoryPath . '/' . $this->iDemandeId . '.zip', $this->sSecretKey); } + // Log. du résultat + if (!empty($this->oJobResult->result)) + $sStatusMessage = $this->oJobResult->result->statusMessage; + else + $sStatusMessage = $this->oJobResult->statusMessage; + writeToLog(INFO_INFORM . 'FME| ' . $sStatusMessage, $this->sRobotLogFile); + writeToLog(INFO_SUCCESSFUL_TREATMENT, $this->sRobotLogFile); + /* + // Télécharge tous les fichiers de résultat. + foreach($aDestinationFiles as $sParamName) { + $sFileName = $aFmeParameters[$sParamName]; + $oFmeServer->downloadResourceFile($sResource, $sPath . '/' . $sFileName, $this->sResultDirectoryPath . '/' . $sFileName); + } + // Tous les fichiers de résultat dans une archive .zip. + $sZipFile = $this->zipResultFiles(); + */ + return $sZipFile; + } + else { + if (!empty($this->oJobResult->result)) + $sStatusMessage = $this->oJobResult->result->statusMessage; + else + $sStatusMessage = $this->oJobResult->statusMessage; + writeToLog(INFO_INFORM . 'FME| ' . $sStatusMessage, $this->sRobotLogFile); + return false; } - return $sZipFile; } } ?> diff --git a/gtf.engine/gtf.engines/engine.exe b/gtf.engine/gtf.engines/engine.exe index 299a2bc1ca56950134511a6694772126a6f3c03c..dda7116dacf3dfdb9bbc35f07da7d6a9ac26d98b 100755 GIT binary patch delta 18297 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�@dBDB|InB^kj5iOH$O4E2mJ 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 delta 17791 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 CPayCB diff --git a/gtf.engine/gtf.engines/engine.php b/gtf.engine/gtf.engines/engine.php index ecd6de0b..e0446897 100755 --- a/gtf.engine/gtf.engines/engine.php +++ b/gtf.engine/gtf.engines/engine.php @@ -10,10 +10,7 @@ */ $properties["id_gtf_engine"] = $_SERVER['argv'][1]; //Identifiant du moteur calculé à partir de la valeur passée en argument $ErrorLicense = $_SERVER['argv'][2]; //Identifiant du moteur calculé à partir de la valeur passée en argument -// Si "$_SERVER["HTTP_HOST"]" n'est pas renseigné -> php notice. -if (!isset($_SERVER["HTTP_HOST"])) - $_SERVER["HTTP_HOST"] = 'localhost'; -// + require_once ("php_engine_conf.inc"); require_once ("vmlib/BD.class.inc"); require_once ("vmlib/logUtil.inc"); @@ -33,7 +30,7 @@ require_once 'string.inc'; require_once 'gtf_lib/FmeCloud.class.inc'; require_once 'gtf_lib/FmeServer.class.inc'; -require_once 'gtf_lib/AmazonS3.class.inc'; +//require_once 'aws_lib/AmazonS3.class.inc'; require_once 'gtf_lib/FmeCloudForGtf.class.inc'; @@ -45,8 +42,7 @@ if ($ErrorLicense == "0") { $sDateFin = new DateTime(date("Y-m-d H:i:s")); //Connexion à la base de données de Données gtf : pas de mot de passe pour le robot - $oBd = new BD($properties["login"], '', $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); - //$oBd = new Vm ('admin', 'admin', $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); + $oBd = new BD($properties['login_scheduler'], $properties['password_scheduler'], $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); if ($oBd->erreurRencontree) { writeToLog(INFO_BASE_CONNECTION_ERROR . $properties["database"], $properties["engine_log_file"]); } else { @@ -114,6 +110,7 @@ if ($ErrorLicense == "0") { if ($properties["nbr_order_max"] < 1) $properties["nbr_order_max"] = 1; $aParams['iNnbrOrderMax'] = array('value' => $properties["nbr_order_max"], 'type' => 'number'); + $aParams['sTimeZone'] = array('value' => str_replace('UTC', '', $properties["server_timezone"]), 'type' => 'string'); $sEnErreur = 2; @@ -122,731 +119,645 @@ if ($ErrorLicense == "0") { } else { // On est PAS en heure creuse $oPDOResult = $oBd->executeWithParams($aSql[$properties["sgbd"]]["boucle_2"], $aParams); } - + $bAsync = true; + $properties['running_jobs_file_path'] = __DIR__ . '/' . 'gtf_engine' . $properties["id_gtf_engine"] . '_jobid.txt'; + // Suppression des traitements asynchrones sur Fme Server dont les demandes ont été supprimées sur GTF. + //removeFmeServerDeletedJobs(); + // Traitement des demandes. while ($aDemande = $oBd->ligneSuivante($oPDOResult)) { - //test pour connaitre les nombres de tentatives de chaque demande - //if ($aDemande['attempt']>=1){ modif par og 26/04/13 - if ($properties["max_attempt"] == '') { - $properties["max_attempt"] = 2; - } - if ($aDemande['attempt'] >= $properties["max_attempt"]) { - writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_TOO_MANY_ORDER_ATTEMPT), $properties["engine_log_file"]); - $iStatut = 4; - - /* - //Mise à jour de la demande. - $sDateTraitement = Date('Y-m-d H:i:s'); - $sSql = $aSql[$properties["sgbd"]]["update_demande"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace("[iStatut]", $iStatut, $sSql); - $sSql = str_replace("[iNbSec]", 0, $sSql); - $sSql = str_replace("[sMessage]", $sMessage, $sSql); - $sSql = str_replace("[sDateTraitement]", $sDateTraitement, $sSql); - $sSql = str_replace("[sResultat]", $sResultat, $sSql); - $sSql = str_replace("[sLogFme]", $sLogFme, $sSql); - - $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ - - //Mise à jour de la demande V2 - // Requete d'update de la demande - $aParams = array(); - $aParams['sSchemaGtf'] = array('value' => $properties['schema_gtf'], 'type' => 'schema_name'); - $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); - $aParams['iNbSec'] = array('value' => 0, 'type' => 'number'); - $aParams['sDateTraitement'] = array('value' => $sDateTraitement, 'type' => 'string'); - $aParams['sResultat'] = array('value' => $sResultat, 'type' => 'string'); - $aParams['sLogFme'] = array('value' => $sLogFme, 'type' => 'string'); - $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande"], $aParams); - - if ($oBd->erreurRencontree) { - writeToLog(INFO_ORDER_UPDATE_ERROR . "(SQL : $sSql)", $properties["engine_log_file"]); - } else { - writeToLog(INFO_ORDER_UPDATE, $properties["engine_log_file"]); - // La demande passe en cours - sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( - 'action' => 'event', - 'service' => 'GtfEvents', - 'data' => array( - 'event' => 'order_started', - 'order' => array( - 'order_id' => $aDemande['order_id'], - 'order_status_id' => $aDemande['order_status_id'], - 'user_id' => $aDemande['user_id'], - 'workspace_id' => $aDemande['workspace_id'] - ) - ) - )); + try { + //test pour connaitre les nombres de tentatives de chaque demande + //if ($aDemande['attempt']>=1){ modif par og 26/04/13 + if ($properties["max_attempt"] == '') { + $properties["max_attempt"] = 2; } - $oPDOResult2 = $oBd->fermeResultat(); - } else { + if ($aDemande['attempt'] >= $properties["max_attempt"] || empty($aDemande['user_id'])) { + if ($aDemande['attempt'] >= $properties["max_attempt"]) + writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_TOO_MANY_ORDER_ATTEMPT), $properties["engine_log_file"]); + else + writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_NO_ASSOCIATED_USER), $properties["engine_log_file"]); + $iStatut = 4; + + //Mise à jour de la demande V2 + $sDateTraitement = gmdate('Y-m-d H:i:s'); + if (updateOrder($aDemande["order_id"], $iStatut, $sDateTraitement, null, 0)) { + writeToLog(INFO_ORDER_UPDATE, $properties["engine_log_file"]); + // Demande non traitable + sendWebsocketOrderStatusMessage('order_started', $iStatut); + } + //$oPDOResult2 = $oBd->fermeResultat(); + } else { - /* - //Vérification des droits de l'utilsateur - $sSql = $aSql[$properties["sgbd"]]["getLogin"]; - $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); - $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ + /* + //Vérification des droits de l'utilsateur + $sSql = $aSql[$properties["sgbd"]]["getLogin"]; + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ - //Vérification des droits de l'utilsateur - // Recuperation des login et droits de l'utilisateur en cour - $aParams = array(); - $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); - $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["getLogin"], $aParams); - - $aUser = $oBd->ligneSuivante($oPDOResult2); - $sGroupListId = getUserGroupsEngines($aUser["login"], $oBd, $properties["mixed_rights_management"]); - // Mise à jour du nombre de tentative dans la base - $iTentative = $aDemande['attempt'] + 1; - $sTable = $properties["schema_gtf"] . ".order"; - $sChamp = "attempt"; - $sWhere = "order_id"; - $sListDemande = $aDemande['order_id']; - $oBd->updateLinkedTable($sTable, $sChamp, $sWhere, $sListDemande, $iTentative); - - // Mise à jour de l'état de la demande de traitement - $oBd->updateLinkedTable($sTable, "order_status_id", $sWhere, $sListDemande, 5); - - writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_ORDER_PROCESSING), $properties["engine_log_file"]); - // OG 27/03/2013 for table job - $sBeginExecutionDate = Date('Y-m-d H:i:s'); - $date = new DateTime(); - $iTimeStampBegin = $date->getTimestamp(); // Pour calcul de la durée - // fin OG 27/03/2013 for job - // $sContenuMail = ""; - $iNbTraitement = $iNbTraitement + 1; - $aUserToRefresh[$iNbTraitement] = $aDemande['user_id']; - $aUserToRefresh[$iNbTraitement] = $aDemande['user_id']; - - /* - //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. - $sSql = $aSql[$properties["sgbd"]]["right_user"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace("[sGroupListId]", $sGroupListId, $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ - - ///* - //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. - $aParams = array(); - $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - $aParams['sGroupListId'] = array('value' => str_replace(',', '|', $sGroupListId), 'type' => 'group'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["right_user"], $aParams); - //*/ - - if ($oBd->erreurRencontree) { - writeToLog(INFO_REQUEST_ERROR . $sSql, $properties["engine_log_file"]); - writeToLog(INFO_PHP_ERROR, $properties["engine_log_file"]); - $sMessage = "Erreur SQL."; - // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; - $iStatut = $sEnErreur; - } else { - if ($sGroupListId == "0") { - $bAutorisationDemande = true; + //Vérification des droits de l'utilsateur + // Recuperation des login et droits de l'utilisateur en cour + $aParams = array(); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["getLogin"], $aParams); + + $aUser = $oBd->ligneSuivante($oPDOResult2); + $sGroupListId = getUserGroupsEngines($aUser["login"], $oBd, $properties["mixed_rights_management"]); + // Mise à jour du nombre de tentative dans la base + $iTentative = $aDemande['attempt'] + 1; + updateOrderAttempt($aDemande['order_id'], $iTentative); + // Mise a jour du message + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $aParams['status'] = array('value' => 2, 'type' => 'number'); + $oPDOResult3 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); + //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } + // Fin - OG 27/03/2013 for table job + $oPDOResult3 = $oBd->fermeResultat(); + + // OG 27/03/2013 for table job + $sBeginExecutionDate = Date('Y-m-d H:i:s'); + $date = new DateTime(); + $iTimeStampBegin = $date->getTimestamp(); // Pour calcul de la durée + // fin OG 27/03/2013 for job + // $sContenuMail = ""; + $iNbTraitement = $iNbTraitement + 1; + $aUserToRefresh[$iNbTraitement] = $aDemande['user_id']; + $aUserToRefresh[$iNbTraitement] = $aDemande['user_id']; + + /* + //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. + $sSql = $aSql[$properties["sgbd"]]["right_user"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[sGroupListId]", $sGroupListId, $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + + ///* + //Vérification si l'utilisateur a les droits pour faire une demande de ce traitement. + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['sGroupListId'] = array('value' => str_replace(',', '|', $sGroupListId), 'type' => 'group'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["right_user"], $aParams); + //*/ + + if ($oBd->erreurRencontree) { + writeToLog(INFO_REQUEST_ERROR . $sSql, $properties["engine_log_file"]); + writeToLog(INFO_PHP_ERROR, $properties["engine_log_file"]); + $sMessage = "Erreur SQL."; + // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; + $iStatut = $sEnErreur; } else { - $bAutorisationDemande = false; - while ($aTraitementsAutorise = $oBd->ligneSuivante($oPDOResult2)) { + if ($sGroupListId == "0") { + $bAutorisationDemande = true; + } else { + $bAutorisationDemande = false; + while ($aTraitementsAutorise = $oBd->ligneSuivante($oPDOResult2)) { - if ($aTraitementsAutorise['workspace_id'] == $aDemande['workspace_id']) { - $bAutorisationDemande = true; + if ($aTraitementsAutorise['workspace_id'] == $aDemande['workspace_id']) { + $bAutorisationDemande = true; + } } } - } - if ($bAutorisationDemande == true) { - if (class_exists("Traitement.class.inc") == false) { - include_once ("Traitement.class.inc"); - } + if ($bAutorisationDemande == true) { + if (class_exists("Traitement.class.inc") == false) { + include_once ("Traitement.class.inc"); + } - /* - // recuperer le nom du fichier fmw a traiter - $sSql = $aSql[$properties["sgbd"]]["select_traitement"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace("[iWorkspaceId]", $aDemande['workspace_id'], $sSql); - $oPDOResult3 = $oBd->execute($sSql); - // */ + /* + // recuperer le nom du fichier fmw a traiter + $sSql = $aSql[$properties["sgbd"]]["select_traitement"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iWorkspaceId]", $aDemande['workspace_id'], $sSql); + $oPDOResult3 = $oBd->execute($sSql); + // */ - ///* - // recuperer le nom du fichier fmw a traiter - $aParams = array(); - $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - $aParams['iWorkspaceId'] = array('value' => $aDemande['workspace_id'], 'type' => 'number'); - $oPDOResult3 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_traitement"], $aParams); - //*/ + ///* + // recuperer le nom du fichier fmw a traiter + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iWorkspaceId'] = array('value' => $aDemande['workspace_id'], 'type' => 'number'); + $oPDOResult3 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_traitement"], $aParams); + //*/ - $aTraitement = $oBd->ligneSuivante($oPDOResult3); - $iEmailTemplateId = $aTraitement['email_template_id']; - $oPDOResult3 = $oBd->fermeResultat(); - // $sSql = $aSql[$properties["sgbd"]]["select_transit_dir"]; - // $oPDOResult4 = $oBd->execute($sSql); - // $aAction = $oBd->ligneSuivante ($oPDOResult4); - // $properties["transit_dir"] = $aAction["value"]; - // $oPDOResult4 = $oBd->fermeResultat(); - - //Lancement du traitement correspondant à la demande. - $bValidFmeLicence = false; - switch($aFmeEngine['fme_engine_type_id']) { - // Traitement exécuté par une occurrence de FME Cloud via l'api rest. - case 'fme_cloud_for_gtf': - $oFmeCloudForGtf = new FmeCloudForGtf($aFmeEngine['iam_access_key_id'], $aFmeEngine['iam_secret_access_key'], $aFmeEngine['s3_region'], 'u0jtg81u4c', 'te', $properties['gtf_instance_id']); - break; - // Traitement exécuté par une occurrence de FME Cloud via l'api rest. - case 'fme_cloud': - $oFmeCloud = new FmeCloud($aFmeEngine['server_url'], $aFmeEngine['fme_cloud_api_token']); - // Liste des occurrences de FME Server. - $aFmeServerInstances = $oFmeCloud->serviceRequest('instances'); - if (!empty($aFmeServerInstances)) { - //print_r($aFmeServerInstances); - $oFmeServerInstance = $aFmeServerInstances[0]; - // Démarrage de l'occurence si elle est en pause. - if ($oFmeServerInstance->state == 'PAUSED') { - $oFmeCloud->startInstance($oFmeServerInstance->id); - // Statut = en cours. - $iStatut = 1; - } - else if ($oFmeServerInstance->state == 'RUNNING') { - //print_r($oFmeServer->getToken()); - $oFmeServer = new FmeServer($oFmeServerInstance->instance_url, $aFmeEngine['login'], $aFmeEngine['password'], 'day', 1); - // Statut de la licence FME --> erreur avec FME Cloud . - //$oLicenseStatus = $oFmeServer->serviceRequest('licensing/license/status'); - //if ($oLicenseStatus->isLicensed === true && $oLicenseStatus->isLicenseExpired === false) { - $bValidFmeLicence = true; - // - $oAmazonS3 = new AmazonS3($aFmeEngine['s3_access_key_id'], $aFmeEngine['s3_secret_access_key'], $aFmeEngine['s3_region']); - // - $oTraitement = new Traitement($oBd, $aDemande['order_id'], $properties["engine_log_file"], $aDemande['wk_params'], $properties, $aDemande['workspace_id'] . "/fme/" . $aTraitement['fmw_file']); - if (!$oTraitement->bErreur) { - $sResultat = $oTraitement->processFmeServerOrder($oFmeServer, $aDemande, $aTraitement, $aUser, $oAmazonS3); - //$sDateFin = new DateTime(date("Y-m-d H:i:s")); - $sLogFme = $oTraitement->sLogFme; + $aTraitement = $oBd->ligneSuivante($oPDOResult3); + $iEmailTemplateId = $aTraitement['email_template_id']; + $oPDOResult3 = $oBd->fermeResultat(); + // $sSql = $aSql[$properties["sgbd"]]["select_transit_dir"]; + // $oPDOResult4 = $oBd->execute($sSql); + // $aAction = $oBd->ligneSuivante ($oPDOResult4); + // $properties["transit_dir"] = $aAction["value"]; + // $oPDOResult4 = $oBd->fermeResultat(); + + //Lancement du traitement correspondant à la demande. + $bValidFmeLicence = false; + switch($aFmeEngine['fme_engine_type_id']) { + // Traitement exécuté par une occurrence de FME Cloud via l'api rest. + case 'fme_cloud_for_gtf': + $oFmeCloudForGtf = new FmeCloudForGtf($aFmeEngine['iam_access_key_id'], $aFmeEngine['iam_secret_access_key'], $aFmeEngine['s3_region'], 'u0jtg81u4c', 'te', $properties['gtf_instance_id']); + break; + // Traitement exécuté par une occurrence de FME Server sur Fme Cloud. + case 'fme_cloud': + $oFmeCloud = new FmeCloud($aFmeEngine['server_url'], $aFmeEngine['fme_cloud_api_token']); + $oFmeCloud->sLogFilePath = $properties["engine_log_file"]; + $sMessage = ''; + // Liste des occurrences de FME Server. + $aFmeServerInstances = $oFmeCloud->serviceRequest('instances'); + if (!empty($aFmeServerInstances)) { + foreach($aFmeServerInstances as $oInstance) { + if ($oInstance->name == $aFmeEngine['fme_server_instance_name']) + $oFmeServerInstance = $oInstance; + } + if (empty($oFmeServerInstance)) { + // Aucune instace active -> mise à jour de la demande (En erreur). + writeToLog(INFO_FME_CLOUD_INSTANCE_NOT_FOUND, $properties["engine_log_file"]); + updateOrder($aDemande["order_id"], 2, gmdate('Y-m-d H:i:s')); + sendWebsocketOrderStatusMessage('order_finished_or_error', 2); + // Envoi un mail de résultat. + sendOrderResultEmail($iEmailTemplateId); + writeToLog(INFO_END_ORDER_PROCESSING, $properties["engine_log_file"]); + // Demande suivante. + continue 2; + } + else { + writeToLog(INFO_FME_CLOUD_INSTANCE_STATUS . $oFmeServerInstance->state, $properties["engine_log_file"]); + // Démarrage de l'occurence si elle est en pause. + if ($oFmeServerInstance->state == 'PAUSED') { + $oFmeCloud->startInstance($oFmeServerInstance->id); + writeToLog(INFO_LAUNCHING_FME_CLOUD_INSTANCE, $properties["engine_log_file"]); + // Nombre de tentative = 0 (instance en pause). + updateOrderAttempt($aDemande["order_id"], 0); + // Demande suivante. + continue 2; } - // http 200 : $oFmeServer->aLastCurlRequestInfo->http_code - if ($oTraitement->oJobResult->status == 'SUCCESS') { - $sMessage = ""; - $iStatut = 3; - // Fichier de résultat. - //if ($sResultat != "") - //$sLienResultat = $sResultat; - $sLienLogFme = $sLogFme; + else if ($oFmeServerInstance->state == 'RUNNING') { + writeToLog(INFO_FME_CLOUD_INSTANCE_RUNNING, $properties["engine_log_file"]); + // Etat de la demande -> en cours. + $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "order_status_id", "order_id", $aDemande['order_id'], 5); + sendWebsocketOrderStatusMessage('order_started', 5); + if (processFmeServerOrder('https://' . $oFmeServerInstance->public_ip, $aFmeEngine['login'], $aFmeEngine['password']) === false) + continue 2; + /* + // Stockage Amazon S3 qui contiendra les fichiers sources pour le traitement FME. + $oAmazonS3 = new AmazonS3($aFmeEngine['s3_access_key_id'], $aFmeEngine['s3_secret_access_key'], $aFmeEngine['s3_region']); + $oAmazonS3->sLogFilePath = $properties["engine_log_file"]; + $oTraitement->sFmeServerS3Bucket = $aFmeEngine['fme_server_s3_bucket']; + */ + // Mise en pause de l'occurence. + //$oFmeCloud->pauseInstance($oFmeServerInstance->id); } else { - $sMessage = ""; - $iStatut = $sEnErreur; + // Nombre de tentative = 0 (instance en cours de démarrage). + updateOrderAttempt($aDemande["order_id"], 0); + // Demande suivante. + continue 2; } - //} - // Mise en pause de l'occurence. - $oFmeCloud->pauseInstance($oFmeServerInstance->id); - } - } - break; - // Traitement exécuté par un serveur fme via l'api rest. - case 'fme_server': - $oFmeServer = new FmeServer($aFmeEngine['server_url'], $aFmeEngine['login'], $aFmeEngine['password'], 'day', 1); - // Statut de la licence FME. - $oLicenseStatus = $oFmeServer->serviceRequest('licensing/license/status'); - if ($oLicenseStatus->isLicensed === true && $oLicenseStatus->isLicenseExpired === false) { - $bValidFmeLicence = true; - $oTraitement = new Traitement($oBd, $aDemande['order_id'], $properties["engine_log_file"], $aDemande['wk_params'], $properties, $aDemande['workspace_id'] . "/fme/" . $aTraitement['fmw_file']); - if (!$oTraitement->bErreur) { - $sResultat = $oTraitement->processFmeServerOrder($oFmeServer, $aDemande, $aTraitement, $aUser); - //$sDateFin = new DateTime(date("Y-m-d H:i:s")); - $sLogFme = $oTraitement->sLogFme; - } - // http 200 : $oFmeServer->aLastCurlRequestInfo->http_code - if ($oTraitement->oJobResult->status == 'SUCCESS') { - $sMessage = ""; - $iStatut = 3; - // Fichier de résultat. - //if ($sResultat != "") - //$sLienResultat = $sResultat; - $sLienLogFme = $sLogFme; + } } else { - $sMessage = ""; - $iStatut = $sEnErreur; + // Aucune instace active -> mise à jour de la demande. + writeToLog(INFO_NO_FME_CLOUD_INSTANCES, $properties["engine_log_file"]); + updateOrder($aDemande["order_id"], 2, gmdate('Y-m-d H:i:s')); + sendWebsocketOrderStatusMessage('order_finished_or_error', 2); + // Envoi un mail de résultat. + sendOrderResultEmail($iEmailTemplateId); + writeToLog(INFO_END_ORDER_PROCESSING, $properties["engine_log_file"]); + // Demande suivante. + continue 2; } - } - break; - // Traitement exécuté par un fme local. - case 'desktop': - $aLicence = getFmeLicence($properties['fme_path']); - $bLicenceIsValid = $aLicence['valid']; - $aLicence['valid'] = 1; - if ($aLicence['valid'] == 1) { - $bValidFmeLicence = true; - $sDateDebut = new DateTime(date("Y-m-d H:i:s")); - $oTraitement = new Traitement($oBd, $aDemande['order_id'], $properties["engine_log_file"], $aDemande['wk_params'], $properties, $aDemande['workspace_id'] . "/fme/" . $aTraitement['fmw_file']); - - if (!$oTraitement->bErreur) { - $sResultat = $oTraitement->Process(); - $sDateFin = new DateTime(date("Y-m-d H:i:s")); - $sLogFme = $oTraitement->sLogFme; - } - //Gestion des erreurs. - - if ($oTraitement->bFmeCrash) - $iStatut = 1; - elseif ($oTraitement->bErreur) { - if ($aTraitement["failed_action_id"] != "" && $oTraitement->sSource != "") { - $aSource = explode("/", $oTraitement->sSource); - $sSourceDirectory = $properties["upload_dir"] . $aSource[0]; - - if ($aTraitement["failed_action_id"] == 1) { - clearDir($sSourceDirectory); - writeToLog(INFO_DELETE_DIRECTORY . $sSourceDirectory . '.', $properties["engine_log_file"]); - } elseif ($aTraitement["failed_action_id"] == 2) { - /* - $sSql = $aSql[$properties["sgbd"]]["select_user"]; - $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); - $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ - ///* - // Selection de l'utilisateur - $aParams = array(); - $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); - $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_user"], $aParams); - //*/ - if ($oBd->erreurRencontree) { - writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); - } else { - $aUser = $oBd->ligneSuivante($oPDOResult2); - - $sUser = userToFolder($aUser["name"]); - if (file_exists($properties["transit_dir"])) { - if (!file_exists($properties["transit_dir"] . "/failed/")) { - mkdir($properties["transit_dir"] . "/failed/"); - } - if (!file_exists($properties["transit_dir"] . "/failed/" . $sUser)) { - mkdir($properties["transit_dir"] . "/failed/" . $sUser); - } - if (!file_exists($properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"])) { - mkdir($properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"]); - } - rename($sSourceDirectory, $properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"]); - writeToLog(str_replace('[sSourceDirectory]', $sSourceDirectory, INFO_MOVING_FILE) . $properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"] . '.', $properties["engine_log_file"]); + break; + // Traitement exécuté par un serveur fme via l'api rest. + case 'fme_server': + // Mise à jour de l'état de la demande de traitement (en cours). + $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "order_status_id", "order_id", $aDemande['order_id'], 5); + sendWebsocketOrderStatusMessage('order_started', 5); + writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_ORDER_PROCESSING), $properties["engine_log_file"]); + // Lancement du traitement. + if (processFmeServerOrder($aFmeEngine['server_url'], $aFmeEngine['login'], $aFmeEngine['password']) === false) + continue 2; + break; + // Traitement exécuté par un fme local. + case 'desktop': + $aLicence = getFmeLicence($properties['fme_path']); + $bLicenceIsValid = $aLicence['valid']; + if ($aLicence['valid'] == 1) { + $bValidFmeLicence = true; + $sDateDebut = new DateTime(date("Y-m-d H:i:s")); + $oTraitement = new Traitement($oBd, $aDemande['order_id'], $properties["engine_log_file"], $aDemande['wk_params'], $properties, $aDemande['workspace_id'] . "/fme/" . $aTraitement['fmw_file']); + if (!$oTraitement->bErreur) { + $oTraitement->setUseExternalDbConnection($aFmeEngine['useexternaldbconnection']); + // Mise à jour de l'état de la demande de traitement (en cours). + $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "order_status_id", "order_id", $aDemande['order_id'], 5); + sendWebsocketOrderStatusMessage('order_started', 5); + writeToLog(str_replace("[aDemande['order_id']]", $aDemande['order_id'], INFO_ORDER_PROCESSING), $properties["engine_log_file"]); + // Maj du pid dans la base. + $sPidFilePath = realpath(dirname($_SERVER['SCRIPT_FILENAME']) . '/pid_' . $properties["id_gtf_engine"] . '.txt'); + if (file_exists($sPidFilePath)) { + $iPid = file_get_contents($sPidFilePath); + if (is_numeric($iPid)) + updateOrderPid($aDemande['order_id'], $iPid); + } + // Lancement du traitement. + $sResultat = $oTraitement->Process(); + $sDateFin = new DateTime(date("Y-m-d H:i:s")); + $sLogFme = $oTraitement->sLogFme; + } + //Gestion des erreurs. + + if ($oTraitement->bFmeCrash) + $iStatut = 1; + elseif ($oTraitement->bErreur) { + if ($aTraitement["failed_action_id"] != "" && $oTraitement->sSource != "") { + $aSource = explode("/", $oTraitement->sSource); + $sSourceDirectory = $properties["upload_dir"] . $aSource[0]; + + if ($aTraitement["failed_action_id"] == 1) { + clearDir($sSourceDirectory); + writeToLog(INFO_DELETE_DIRECTORY . $sSourceDirectory . '.', $properties["engine_log_file"]); + } elseif ($aTraitement["failed_action_id"] == 2) { + /* + $sSql = $aSql[$properties["sgbd"]]["select_user"]; + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + // Selection de l'utilisateur + $aParams = array(); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_user"], $aParams); + //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); } else { - writeToLog(str_replace("[properties['transit_dir']]", $properties['transit_dir'], INFO_DIRECTORY_NOT_FOUND_ENGINE), $properties["engine_log_file"]); - $iStatut = $sEnErreur; + $aUser = $oBd->ligneSuivante($oPDOResult2); + + $sUser = userToFolder($aUser["name"]); + if (file_exists($properties["transit_dir"])) { + if (!file_exists($properties["transit_dir"] . "/failed/")) { + mkdir($properties["transit_dir"] . "/failed/"); + } + if (!file_exists($properties["transit_dir"] . "/failed/" . $sUser)) { + mkdir($properties["transit_dir"] . "/failed/" . $sUser); + } + if (!file_exists($properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"])) { + mkdir($properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"]); + } + rename($sSourceDirectory, $properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"]); + writeToLog(str_replace('[sSourceDirectory]', $sSourceDirectory, INFO_MOVING_FILE) . $properties["transit_dir"] . "/failed/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"] . '.', $properties["engine_log_file"]); + } else { + writeToLog(str_replace("[properties['transit_dir']]", $properties['transit_dir'], INFO_DIRECTORY_NOT_FOUND_ENGINE), $properties["engine_log_file"]); + $iStatut = $sEnErreur; + } } } } - } - $sMessage = $oTraitement->sMessageErreur; - $iStatut = $sEnErreur; - $sResultat = ''; - // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; - // $sContenuMail .= "Contactez l'administrateur pour consulter le fichier de log du robot.<br>"; - $sLienLogFme = $sLogFme; - } else { - if ($aTraitement["success_action_id"] != "" && $oTraitement->sSource != "") { - $aSource = explode("/", $oTraitement->sSource); - $sSourceDirectory = $properties["upload_dir"] . $aSource[0]; - if ($aTraitement["success_action_id"] == 1) { - clearDir($sSourceDirectory); - writeToLog(INFO_DELETE_DIRECTORY . $sSourceDirectory . '.', $properties["engine_log_file"]); - } elseif ($aTraitement["success_action_id"] == 2) { + $sMessage = $oTraitement->sMessageErreur; + $iStatut = $sEnErreur; + $sResultat = ''; + // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; + // $sContenuMail .= "Contactez l'administrateur pour consulter le fichier de log du robot.<br>"; + $sLienLogFme = $sLogFme; + } else { + if ($aTraitement["success_action_id"] != "" && $oTraitement->sSource != "") { + $aSource = explode("/", $oTraitement->sSource); + $sSourceDirectory = $properties["upload_dir"] . $aSource[0]; + if ($aTraitement["success_action_id"] == 1) { + clearDir($sSourceDirectory); + writeToLog(INFO_DELETE_DIRECTORY . $sSourceDirectory . '.', $properties["engine_log_file"]); + } elseif ($aTraitement["success_action_id"] == 2) { - /* - $sSql = $aSql[$properties["sgbd"]]["select_user"]; - $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); - $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ - ///* - //Selection de l'utilisateur - $aParams = array(); - $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); - $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_user"], $aParams); - //*/ - - if ($oBd->erreurRencontree) { - writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); - } else { - $aUser = $oBd->ligneSuivante($oPDOResult2); - - $sUser = userToFolder($aUser["name"]); - if (file_exists($properties["transit_dir"])) { - if (!file_exists($properties["transit_dir"] . "/success/")) { - mkdir($properties["transit_dir"] . "/success/"); - } - if (!file_exists($properties["transit_dir"] . "/success/" . $sUser)) { - mkdir($properties["transit_dir"] . "/success/" . $sUser); - } - if (!file_exists($properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"])) { - mkdir($properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"]); - } - rename($sSourceDirectory, $properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"]); - writeToLog(str_replace('[sSourceDirectory]', $sSourceDirectory, INFO_MOVING_FILE) . $properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"] . '.', $properties["engine_log_file"]); + /* + $sSql = $aSql[$properties["sgbd"]]["select_user"]; + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + //Selection de l'utilisateur + $aParams = array(); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["select_user"], $aParams); + //*/ + + if ($oBd->erreurRencontree) { + writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); } else { - writeToLog(str_replace("[properties['transit_dir']]", $properties['transit_dir'], INFO_DIRECTORY_NOT_FOUND_ENGINE), $properties["engine_log_file"]); - $iStatut = $sEnErreur; + $aUser = $oBd->ligneSuivante($oPDOResult2); + + $sUser = userToFolder($aUser["name"]); + if (file_exists($properties["transit_dir"])) { + if (!file_exists($properties["transit_dir"] . "/success/")) { + mkdir($properties["transit_dir"] . "/success/"); + } + if (!file_exists($properties["transit_dir"] . "/success/" . $sUser)) { + mkdir($properties["transit_dir"] . "/success/" . $sUser); + } + if (!file_exists($properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"])) { + mkdir($properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"]); + } + rename($sSourceDirectory, $properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"]); + writeToLog(str_replace('[sSourceDirectory]', $sSourceDirectory, INFO_MOVING_FILE) . $properties["transit_dir"] . "/success/" . $sUser . "/" . $aTraitement["workspace_id"] . "/" . $aDemande["order_id"] . '.', $properties["engine_log_file"]); + } else { + writeToLog(str_replace("[properties['transit_dir']]", $properties['transit_dir'], INFO_DIRECTORY_NOT_FOUND_ENGINE), $properties["engine_log_file"]); + $iStatut = $sEnErreur; + } } } } + $sMessage = ""; + $iStatut = 3; + $sLienLogFme = $sLogFme; } - $sMessage = ""; - $iStatut = 3; - // $sContenuMail = "Votre demande de traitement '".$aDemande['workspace_nom']."' a réussi.<br>"; - if ($sResultat != "") { - // $sContenuMail .= '<A href="'.$properties["url_export"]."/".$sResultat.'">Télécharger le résultat</A><br>'; - $sLienResultat = $sResultat; - } - $sLienLogFme = $sLogFme; + $bDataEncrypt = $oTraitement->bDataEncrypt; + unset($oTraitement); } - unset($oTraitement); - } - break; - } - // Licence invalide. - if (!$bValidFmeLicence) { - $iTentative = $iTentative - 1; - $sTable = $properties["schema_gtf"] . ".order"; - $sChamp = "attempt"; - $sWhere = "order_id"; - $sListDemande = $aDemande['order_id']; - $oBd->updateLinkedTable($sTable, $sChamp, $sWhere, $sListDemande, $iTentative); - writeToLog(INFO_INVALID_FME_LICENCE_FILE, $properties["engine_log_file"]); + break; + } + // Licence invalide. + if (!$bValidFmeLicence) { + $iTentative = $iTentative - 1; + updateOrderAttempt($aDemande['order_id'], $iTentative); + writeToLog(INFO_INVALID_FME_LICENCE_FILE, $properties["engine_log_file"]); + writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["engine_log_file"]); + $sMessage = "Invalid FME licence file. "; + // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; + $iStatut = 1; + $sLienLogFme = 'Non disponible'; + } + + } else { + writeToLog(INFO_NO_USER_GRANT . $aDemande['workspace_nom'] . ' n\'a pas été défini.', $properties["engine_log_file"]); writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["engine_log_file"]); - $sMessage = "Invalid FME licence file. "; + $sMessage = "Vous n''avez pas les droits nécessaires pour réaliser ce traitement."; // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; - $iStatut = 1; - $sLienLogFme = 'Non disponible'; - } - - } else { - writeToLog(INFO_NO_USER_GRANT . $aDemande['workspace_nom'] . ' n\'a pas été défini.', $properties["engine_log_file"]); - writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["engine_log_file"]); - $sMessage = "Vous n''avez pas les droits nécessaires pour réaliser ce traitement."; - // $sContenuMail = 'Votre demande de traitement "'.$aDemande['workspace_nom'].'" a échoué. Voici le message d\'erreur renvoyé : <br><font color="#FF0000">'.$sMessage.'</font><br>'; - $iStatut = 6; + $iStatut = 6; - if (file_exists($sLogFme)) { - $sLienLogFme = $sLogFme; - } else { - $sLienLogFme = 'Non disponible'; + if (file_exists($sLogFme)) { + $sLienLogFme = $sLogFme; + } else { + $sLienLogFme = 'Non disponible'; + } } } - } - $oPDOResult2 = $oBd->fermeResultat(); + $oPDOResult2 = $oBd->fermeResultat(); - //Mise à jour de la demande. + //Mise à jour de la demande. - if ($iStatut == 3) { - /* - $sSql = $aSql[$properties["sgbd"]]["update_message"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ - ///* - // Mise a jour du message - $aParams = array(); - $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); - //*/ - if ($oBd->erreurRencontree) { - writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + if ($iStatut == 3) { + /* + $sSql = $aSql[$properties["sgbd"]]["update_message"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + // Mise a jour du message + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $aParams['status'] = array('value' => 1, 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); + //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } + // Fin - OG 27/03/2013 for table job + $oPDOResult2 = $oBd->fermeResultat(); } - // Fin - OG 27/03/2013 for table job - $oPDOResult2 = $oBd->fermeResultat(); - } - if ($iStatut == 2 || $iStatut == 4) { + if ($iStatut == 2 || $iStatut == 4) { - /* - $sSql = $aSql[$properties["sgbd"]]["delete_message"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ - ///* - - // Mise a jour du message - $aParams = array(); - $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); - //*/ + /* + $sSql = $aSql[$properties["sgbd"]]["delete_message"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* - if ($oBd->erreurRencontree) { - writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + // Mise a jour du message + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $aParams['status'] = array('value' => 1, 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); + //*/ + + if ($oBd->erreurRencontree) { + writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } + // Fin - OG 27/03/2013 for table job + $oPDOResult2 = $oBd->fermeResultat(); } - // Fin - OG 27/03/2013 for table job - $oPDOResult2 = $oBd->fermeResultat(); - } - $sDateTraitement = Date('Y-m-d H:i:s'); - - /* - $sSql = $aSql[$properties["sgbd"]]["update_demande"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - if ($iStatut != 1) { - $timeFirst = strtotime(date_format($sDateDebut, 'Y-m-d H:i:s')); - $timeSecond = strtotime(date_format($sDateFin, 'Y-m-d H:i:s')); - $diff = $timeSecond - $timeFirst; - $iNbSec = $diff; - } else { - $iNbSec = "NULL"; - } - // OG 27/03/2013 for table job - $date = new DateTime(); - $iTimeStampEnd = $date->getTimestamp(); - $iDelay = $iTimeStampEnd - $iTimeStampBegin; - $sSql = str_replace("[iNbSec]", $iDelay, $sSql); - $sSql = str_replace("[$iStatut]", $iStatut, $sSql); - /* $sSql = str_replace("[sMessage]", $sMessage, $sSql); *//* - $sSql = str_replace("[sDateTraitement]", $sDateTraitement, $sSql); - $sSql = str_replace("[sResultat]", utf8_encode($sResultat), $sSql); - $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); - if ($sLogFme == "") { - $sSql = str_replace("'[sLogFme]'", "NULL", $sSql); - } else { - $sSql = str_replace("[sLogFme]", $sLogFme, $sSql); - } + $sDateTraitement = gmdate('Y-m-d H:i:s'); - $oPDOResult2 = $oBd->execute($sSql); - //*/ - - //* - // Mise a jour de la demande - $aParams = array(); - $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - if ($iStatut != 1) { - $timeFirst = strtotime(date_format($sDateDebut, 'Y-m-d H:i:s')); - $timeSecond = strtotime(date_format($sDateFin, 'Y-m-d H:i:s')); - $diff = $timeSecond - $timeFirst; - $iNbSec = $diff; - } else - $iNbSec = "NULL"; - // OG 27/03/2013 for table job - $date = new DateTime(); - $iTimeStampEnd = $date->getTimestamp(); - $iDelay = $iTimeStampEnd - $iTimeStampBegin; - $aParams['iNbSec'] = array('value' => $iDelay, 'type' => 'number'); - $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); - $aParams['sDateTraitement'] = array('value' => $sDateTraitement, 'type' => 'string'); - $aParams['sResultat'] = array('value' => utf8_encode($sResultat), 'type' => 'string'); - $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); - if ($sLogFme == "") { - $aParams['sLogFme'] = array('value' => "NULL", 'type' => 'string'); - } else { - $aParams['sLogFme'] = array('value' => $sLogFme, 'type' => 'string'); - } - - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande"], $aParams); - // */ - - - if ($oBd->erreurRencontree) { - writeToLog(INFO_ORDER_UPDATE_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); - } else { - writeToLog(INFO_ORDER_UPDATE, $properties["engine_log_file"]); - // La demande est terminée avec un résultat - sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( - 'action' => 'event', - 'service' => 'GtfEvents', - 'data' => array( - 'event' => 'order_finished_with_result', - 'order' => array( - 'order_id' => $aDemande['order_id'], - 'order_status_id' => $aDemande['order_status_id'], - 'user_id' => $aDemande['user_id'], - 'workspace_id' => $aDemande['workspace_id'] - ) - ) - )); - } - if ($iStatut != 1) { /* - $sSql = $aSql[$properties["sgbd"]]["insertJob"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); - $sSql = str_replace("[iWorkspaceId]", $aDemande["workspace_id"], $sSql); - $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); - $sSql = str_replace("[sBeginExecutionDate]", $sBeginExecutionDate, $sSql); - $sSql = str_replace("[iDelay]", $iDelay, $sSql); - $sSql = str_replace("[iGtfEngineId]", $properties["id_gtf_engine"], $sSql); - $sSql = str_replace("[iStatut]", $iStatut, $sSql); - //writeToLog('|INFORM|PHP|Test requête OG : '.$sSql,$properties["engine_log_file"]); - $oPDOResult2 = $oBd->execute($sSql); - // */ - ///* - // Insert de schemaGtf - $aParams = array(); - $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); - $aParams['iWorkspaceId'] = array('value' => $aDemande["workspace_id"], 'type' => 'number'); - $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); - $aParams['sBeginExecutionDate'] = array('value' => $sBeginExecutionDate, 'type' => 'string'); - $aParams['iDelay'] = array('value' => $iDelay, 'type' => 'number'); - $aParams['iGtfEngineId'] = array('value' => $properties["id_gtf_engine"], 'type' => 'number'); - $aParams['iDelay'] = array('value' => $iDelay, 'type' => 'number'); - $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); - //writeToLog('|INFORM|PHP|Test requête OG : '.$sSql,$properties["engine_log_file"]); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["insertJob"], $aParams); - //*/ - if ($oBd->erreurRencontree) { - writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + $sSql = $aSql[$properties["sgbd"]]["update_demande"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + if ($iStatut != 1) { + $timeFirst = strtotime(date_format($sDateDebut, 'Y-m-d H:i:s')); + $timeSecond = strtotime(date_format($sDateFin, 'Y-m-d H:i:s')); + $diff = $timeSecond - $timeFirst; + $iNbSec = $diff; + } else { + $iNbSec = "NULL"; + } + // OG 27/03/2013 for table job + $date = new DateTime(); + $iTimeStampEnd = $date->getTimestamp(); + $iDelay = $iTimeStampEnd - $iTimeStampBegin; + $sSql = str_replace("[iNbSec]", $iDelay, $sSql); + $sSql = str_replace("[$iStatut]", $iStatut, $sSql); + /* $sSql = str_replace("[sMessage]", $sMessage, $sSql); *//* + $sSql = str_replace("[sDateTraitement]", $sDateTraitement, $sSql); + $sSql = str_replace("[sResultat]", utf8_encode($sResultat), $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + if ($sLogFme == "") { + $sSql = str_replace("'[sLogFme]'", "NULL", $sSql); + } else { + $sSql = str_replace("[sLogFme]", $sLogFme, $sSql); } - // Fin - OG 27/03/2013 for table job - $oPDOResult2 = $oBd->fermeResultat(); - } - // Mise à jour de la demande si resultat est vide - if ($sResultat == "") { - /* - $sSql = $aSql[$properties["sgbd"]]["update_demande_resultat_null"]; - $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ + $oPDOResult2 = $oBd->execute($sSql); + //*/ - ///* - // Mise a jour de la demande si resultat null + //* + // Mise a jour de la demande $aParams = array(); $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande_resultat_null"], $aParams); - //*/ - - if ($oBd->erreurRencontree) { - writeToLog(INFO_ORDER_UPDATE_ERROR . ' (' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); - } else { - writeToLog(INFO_INFO_ORDER_UPDATE_NULL, $properties["engine_log_file"]); - // La demande est terminée où en erreur - sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( - 'action' => 'event', - 'service' => 'GtfEvents', - 'data' => array( - 'event' => 'order_finished_or_error', - 'order' => array( - 'order_id' => $aDemande['order_id'], - 'order_status_id' => $aDemande['order_status_id'], - 'user_id' => $aDemande['user_id'], - 'workspace_id' => $aDemande['workspace_id'] - ) - ) - )); + if ($iStatut != 1) { + $timeFirst = strtotime(date_format($sDateDebut, 'Y-m-d H:i:s')); + $timeSecond = strtotime(date_format($sDateFin, 'Y-m-d H:i:s')); + $diff = $timeSecond - $timeFirst; + $iNbSec = $diff; + } else + $iNbSec = "NULL"; + // OG 27/03/2013 for table job + $date = new DateTime(); + $iTimeStampEnd = $date->getTimestamp(); + $iDelay = $iTimeStampEnd - $iTimeStampBegin; + if (updateOrder($aDemande["order_id"], $iStatut, $sDateTraitement, $sLogFme, $iDelay, $sResultat)) { + writeToLog(INFO_ORDER_UPDATE, $properties["engine_log_file"]); + // La demande est terminée avec un résultat + sendWebsocketOrderStatusMessage('order_finished_with_result', $aDemande['order_status_id']); } - $oPDOResult2 = $oBd->fermeResultat(); - } - - // Notification par e-mail. - $bMailToSend = false; - switch (strtoupper(trim($aDemande['email_option_id']))) { - case 1: - $bMailToSend = True; - break; - case 2: - $bMailToSend = False; - break; - case 3: - if ($iStatut == $sEnErreur) { - $bMailToSend = True; - } - break; - case 4: - if ($iStatut != $sEnErreur) { - $bMailToSend = True; + if ($iStatut != 1) { + /* + $sSql = $aSql[$properties["sgbd"]]["insertJob"]; + $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); + $sSql = str_replace("[iWorkspaceId]", $aDemande["workspace_id"], $sSql); + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sBeginExecutionDate]", $sBeginExecutionDate, $sSql); + $sSql = str_replace("[iDelay]", $iDelay, $sSql); + $sSql = str_replace("[iGtfEngineId]", $properties["id_gtf_engine"], $sSql); + $sSql = str_replace("[iStatut]", $iStatut, $sSql); + //writeToLog('|INFORM|PHP|Test requête OG : '.$sSql,$properties["engine_log_file"]); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + // Insert de schemaGtf + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $aParams['iWorkspaceId'] = array('value' => $aDemande["workspace_id"], 'type' => 'number'); + $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); + $aParams['sBeginExecutionDate'] = array('value' => $sBeginExecutionDate, 'type' => 'string'); + $aParams['iDelay'] = array('value' => $iDelay, 'type' => 'number'); + $aParams['iGtfEngineId'] = array('value' => $properties["id_gtf_engine"], 'type' => 'number'); + $aParams['iDelay'] = array('value' => $iDelay, 'type' => 'number'); + $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); + //writeToLog('|INFORM|PHP|Test requête OG : '.$sSql,$properties["engine_log_file"]); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["insertJob"], $aParams); + //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_TEST_REQUEST_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); } - break; - default: - $bMailToSend = True; - } - - // Licence invalide. - if ($iStatut == 2 && !$bLicenceIsValid) { - // Mise à jour de l'état de la demande de traitement (en attente). - $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "order_status_id", "order_id", $aDemande['order_id'], 1); - // Licence invalide : 1 mail par jour max. - $sToday = date('d/m/Y'); - $sMailLicenceFile = __DIR__ . '\mail_licence.txt'; - if (file_exists($sMailLicenceFile)) - if (file_get_contents($sMailLicenceFile) == $sToday) - $bMailToSend = false; - file_put_contents($sMailLicenceFile, $sToday); - // Mise à jour du nombre de tentative dans la base. - $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "attempt", "order_id", $aDemande['order_id'], 0); - } - - // Envoi du mail. - if ($bMailToSend == true) { - /* - $sSql = $aSql[$properties["sgbd"]]["select_user"]; - $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); - $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); - $oPDOResult2 = $oBd->execute($sSql); - // */ - ///* - - //Mise a jour de la demande si resultat null - $aParams = array(); - $aParams['iUserId'] = array('value' => $aDemande['user_id'], 'type' => 'number'); - $aParams['sSchemaFramework'] = array('value' => $properties["schema_framework"], 'type' => 'schema_name'); - $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande_resultat_null"], $aParams); - //*/ - if ($oBd->erreurRencontree) { - writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); - } else { - // $aUser = $oBd->ligneSuivante ($oPDOResult2); - // $sContenuMail = "Bonjour ".$aUser["name"].",<br><br><br>".$sContenuMail; - // $sContenuMail .= "<br><br><cite><h6>Cet e-mail a été envoyé par un robot, merci de ne pas répondre.</h6></cite><br>"; - // - - if (empty($iEmailTemplateId)) - $iEmailTemplateId = $properties['default_mail_model']; + // Fin - OG 27/03/2013 for table job + $oPDOResult2 = $oBd->fermeResultat(); + } + // Mise à jour de la demande si resultat est vide + if ($sResultat == "") { /* - // Nom des moteurs GTF et FME - $sSql = $aSql[$properties["sgbd"]]["get_gtf_engine_name"]; + $sSql = $aSql[$properties["sgbd"]]["update_demande_resultat_null"]; $sSql = str_replace("[sSchemaGtf]", $properties["schema_gtf"], $sSql); - $sSql = str_replace('[gtf_engine_id]', $aDemande['gtf_engine_id'], $sSql); + $sSql = str_replace("[iOrderId]", $aDemande["order_id"], $sSql); $oPDOResult2 = $oBd->execute($sSql); // */ + ///* - // Nom des moteurs GTF et FME + // Mise a jour de la demande si resultat null $aParams = array(); $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); - $aParams['gtf_engine_id'] = array('value' => $aDemande['gtf_engine_id'], 'type' => 'number'); - $oPDOResult2 = $oBd->executeWithParams($sSql = $aSql[$properties["sgbd"]]["get_gtf_engine_name"], $aParams); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande_resultat_null"], $aParams); //*/ + if ($oBd->erreurRencontree) { + writeToLog(INFO_ORDER_UPDATE_ERROR . ' (' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + } else { + writeToLog(INFO_INFO_ORDER_UPDATE_NULL, $properties["engine_log_file"]); + // La demande est terminée où en erreur + sendWebsocketOrderStatusMessage('order_finished_or_error', $aDemande['order_status_id']); + } + $oPDOResult2 = $oBd->fermeResultat(); + } + + // Notification par e-mail. + $bMailToSend = false; + switch (strtoupper(trim($aDemande['email_option_id']))) { + case 1: + $bMailToSend = True; + break; + case 2: + $bMailToSend = False; + break; + case 3: + if ($iStatut == $sEnErreur) { + $bMailToSend = True; + } + break; + case 4: + if ($iStatut != $sEnErreur) { + $bMailToSend = True; + } + break; + default: + $bMailToSend = True; + } + + // Licence invalide. + if ($iStatut == 2 && !$bLicenceIsValid) { + // Mise à jour de l'état de la demande de traitement (en attente). + $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "order_status_id", "order_id", $aDemande['order_id'], 1); + // Licence invalide : 1 mail par jour max. + $sToday = date('d/m/Y'); + $sMailLicenceFile = __DIR__ . '\mail_licence.txt'; + if (file_exists($sMailLicenceFile)) + if (file_get_contents($sMailLicenceFile) == $sToday) + $bMailToSend = false; + file_put_contents($sMailLicenceFile, $sToday); + // Mise à jour du nombre de tentative dans la base. + updateOrderAttempt($aDemande['order_id'], 0); + } + // Envoi du mail. + if ($bMailToSend == true) { + /* + $sSql = $aSql[$properties["sgbd"]]["select_user"]; + $sSql = str_replace("[iUserId]", $aDemande['user_id'], $sSql); + $sSql = str_replace("[sSchemaFramework]", $properties["schema_framework"], $sSql); + $oPDOResult2 = $oBd->execute($sSql); + // */ + ///* + + //Mise a jour de la demande si resultat null + /* + $aParams = array(); + $aParams['iOrderId'] = array('value' => $aDemande['order_id'], 'type' => 'number'); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande_resultat_null"], $aParams); + // if ($oBd->erreurRencontree) { - writeToErrorLog(ERROR_0001); - } else - $aRow = $oBd->ligneSuivante($oPDOResult2); - - // URL de téléchargement du résultat - $sResultUrl = ''; - if ($sResultat != "") - $sResultUrl = '<a href="' . $properties["url_export"] . "/gtf/" . $sResultat . '">Télécharger le résultat</a>'; - - // Dates - $sOrderDate = date_format(date_create($aDemande['order_date']), 'd/m/Y H:i:s'); - - // Envoi du mail - $aObjects = array(); - if (!empty($sMessage)) - $aObjects['error'] = $sMessage; - $aObjects['oOrder'] = new OrderLib($oBd, $aDemande["order_id"], $properties, "v_order"); - $aObjects['oOrder']->formatOrderEmail(); - $oEmail = new Email($oBd, $iEmailTemplateId, $properties, $aObjects); - if (!empty($oEmail->oEmailTemplate->name)) - $oEmail->send(); + writeToLog(INFO_GET_USER_INFO_ERROR . $aDemande['user_id'] . ". (SQL = " . $sSql . ")", $properties["engine_log_file"]); + } else { + */ + // $aUser = $oBd->ligneSuivante ($oPDOResult2); + // $sContenuMail = "Bonjour ".$aUser["name"].",<br><br><br>".$sContenuMail; + // $sContenuMail .= "<br><br><cite><h6>Cet e-mail a été envoyé par un robot, merci de ne pas répondre.</h6></cite><br>"; + // + // Envoi du mail + sendOrderResultEmail($iEmailTemplateId, $sMessage); + //} + $oPDOResult2 = $oBd->fermeResultat(); + } else { + // pas d'envoi de mail + writeToLog(INFO_NO_MAIL_SEND, $properties["engine_log_file"]); } - $oPDOResult2 = $oBd->fermeResultat(); - } else { - // pas d'envoi de mail - writeToLog(INFO_NO_MAIL_SEND, $properties["engine_log_file"]); - } - } //fin du if sur les tentatives - //fin de while + } //fin du if sur les tentatives + //fin de while + } + catch (Exception $e) { + writeToErrorLog($e->getMessage()); + // Mise a jour du message + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties["schema_gtf"], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $aDemande["order_id"], 'type' => 'number'); + $aParams['status'] = array('value' => 1, 'type' => 'number'); + $oPDOResult2 = $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_message"], $aParams); + // Mise a jour de la demande. + updateOrder($aDemande["order_id"], 2, gmdate('Y-m-d H:i:s'), null, 0); + sendWebsocketOrderStatusMessage('order_finished_or_error', 2); + } } if ($iNbTraitement >= 1) { //Crée ou modifie le fichier de maj de l'application pour l'utilisateur. @@ -893,4 +804,414 @@ function TesteHeureCreuse($sHeureMin, $sHeureMax) { } } +/** + * Mise à jour d'une demande. + * @param {number} $iOrderId Id de la demande. + * @param {number} $iStatut Statut de la demande. + * @param {string} $sExecutionDate Date de la demande. + * @param {number} $iNbSec Durée de la demande en secondes. + * @param {string} $sResultUrl Url du résultat de la demande. + * @param {string} $sLogUrl Url du log de la demande. + */ +function updateOrder($iOrderId, $iStatut, $sExecutionDate, $sLogUrl = null, $iNbSec = null, $sResultUrl = null) { + // Variables globales. + global $oBd, $properties, $aSql; + // Requete d'update de la demande. + if (empty($sLogUrl)) + $sLogUrl = null; + if (empty($sResultUrl)) + $sResultUrl = null; + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties['schema_gtf'], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $iOrderId, 'type' => 'number'); + $aParams['iStatut'] = array('value' => $iStatut, 'type' => 'number'); + $aParams['sDateTraitement'] = array('value' => $sExecutionDate, 'type' => 'string'); + $aParams['iNbSec'] = array('value' => $iNbSec, 'type' => 'number'); + $aParams['sResultat'] = array('value' => $sResultUrl, 'type' => 'string'); + $aParams['sLogFme'] = array('value' => $sLogUrl, 'type' => 'string'); + $oBd->executeWithParams($aSql[$properties["sgbd"]]["update_demande"], $aParams); + if ($oBd->erreurRencontree) { + writeToLog(INFO_ORDER_UPDATE_ERROR . '(' . $oBd->getBDMessage() . ')', $properties["engine_log_file"]); + return false; + } + else + return true; +} + +/** + * Envoi un mail de résultat du traitement FME. + * @param {number} $iEmailTemplateId Id du template de mail. + * @param {string} $sMessage Message. + */ +function sendOrderResultEmail($iEmailTemplateId, $sMessage = '') { + // Variables globales. + global $oBd, $properties, $aDemande, $oTraitement; + if (!empty($oTraitement)) + $oTraitement = $oTraitement; + // Envoi du mail + $aObjects = array(); + if (!empty($sMessage)) + $aObjects['error'] = $sMessage; + $aObjects['oOrder'] = new OrderLib($oBd, $aDemande["order_id"], $properties, "v_order"); + $aObjects['oOrder']->formatOrderEmail(); + if (!empty($oTraitement)) + $aObjects['oOrder']->aFields['bDataEncrypt'] = $oTraitement->bDataEncrypt; + $aObjects['oOrder']->aFields['sApplicationUrl'] = $properties['web_server_name'] . '/' . $properties['application_name']; + if (empty($iEmailTemplateId)) + $iEmailTemplateId = $properties['default_mail_model']; + $oEmail = new Email($oBd, $iEmailTemplateId, $properties, $aObjects); + if (!empty($oEmail->oEmailTemplate->name)) + $oEmail->send(); +} + +/** + * Mise à jour du nombre de tentatives de la demande. + * @param {number} $iOrderId Id de la demande. + * @param {number} $iAttempt Nombre de tentatives. + */ +function updateOrderAttempt($iOrderId, $iAttempt) { + // Variables globales. + global $oBd, $properties; + // Mise à jour du nombre de tentatives de la demande. + $oBd->updateLinkedTable($properties["schema_gtf"] . ".order", "attempt", "order_id", $iOrderId, $iAttempt); +} + +/** + * Envoi un message au websocket contenant le statut de la demande. + * @param {string} $sEvent Nom de l'évènement. + * @param {number} $iStatut Statut de la demande. + */ +function sendWebsocketOrderStatusMessage($sEvent, $iStatut) { + // Variables globales. + global $properties, $aDemande; + // Envoi du message. + sendWebsocketMessage($properties['websocket_server'], $properties['websocket_port'], $properties['websocket_alias'], array( + 'action' => 'event', + 'service' => 'GtfEvents', + 'data' => array( + 'event' => $sEvent, + 'order' => array( + 'order_id' => $aDemande['order_id'], + 'order_status_id' => $iStatut, + 'user_id' => $aDemande['user_id'], + 'workspace_id' => $aDemande['workspace_id'] + ) + ) + )); +} + +/** + * Ajoute un traitement asynchrone en cours d'exécution. + * @param {object} $oTraitement Objet de la classe Traitement. + * @param {string} $sServerUrl Url to Fme Server. + * @param {string} $sLogin User login. + * @param {string} $sPassword User password. + */ +function addFmeServerRunningJob($oTraitement, $sServerUrl, $sLogin, $sPassword) { + // Variables globales. + global $properties, $aDemande, $aFmeEngine; + // Liste des demandes asynchrones en cours. + $aRunningJobs = getFmeServerRunningJobs(); + // Paramètres de la demande asynchrone à ajouter. + $aRunningJob = array( + 'order_id' => $aDemande['order_id'], + 'fme_engine_type_id' => $aFmeEngine['fme_engine_type_id'], + 'job_id' => $oTraitement->oJobResult->id, + 'result_directory_name' => $oTraitement->sResultDirectoryName, + 'result_directory_path' => $oTraitement->sResultDirectoryPath, + 'destination_files' => $oTraitement->aJobDestinationFiles, + 'resource' => $oTraitement->sJobResource, + 'path' => $oTraitement->sJobPath, + 'fme_server_url' => $sServerUrl, + 'login' => $sLogin, + 'password' => $sPassword + ); + $aRunningJobs[] = $aRunningJob; + // Sauve la liste. + file_put_contents($properties['running_jobs_file_path'], serialize($aRunningJobs)); + // Mise à jour du job id. + updateOrderPid($aDemande['order_id'], $oTraitement->oJobResult->id); +} + +/** + * Retourne la liste des traitements asynchrones en cours d'exécution. + * return array + */ +function getFmeServerRunningJobs() { + // Variables globales. + global $properties; + // Retourne la liste des demandes asynchrones en cours ou un tableau vide. + if (file_exists($properties['running_jobs_file_path'])) + return unserialize(file_get_contents($properties['running_jobs_file_path'])); + else + return array(); +} + +/** + * Vérification de la présence d'un traitement dans la liste des traitements asynchrones en cours d'exécution. + * @param {number} $iOrderId Id de la demande en cours. + * @param {array} $aRunningJobs Tableau contenant la liste des traitements asynchrones en cours d'exécution. + * return boolean + */ +function isFmeServerJobRunning($iOrderId, $aRunningJobs) { + foreach ($aRunningJobs as $aRunningJob) { + if ($aRunningJob['order_id'] == $iOrderId) + return true; + } + return false; +} + +/** + * Retourne les infos d'un traitement asynchrones en cours d'exécution. + * @param {number} $iOrderId I de la demande en cours. + * @param {array} $aRunningJobs Tableau contenant la liste des traitements asynchrones en cours d'exécution. + * return array + */ +function getFmeServerRunningJob($iOrderId, $aRunningJobs) { + foreach ($aRunningJobs as $aRunningJob) { + if ($aRunningJob['order_id'] == $iOrderId) + return $aRunningJob; + } + return false; +} + +/** + * Supprime un traitement asynchrone en cours d'exécution. + * @param {number} $iOrderId Id de la demande en cours. + */ +function removeFmeServerRunningJob($iOrderId, $oFmeServer = null) { + // Variables globales. + global $properties; + // Liste des demandes asynchrones en cours. + $iJobIndex = null; + $aRunningJobs = getFmeServerRunningJobs(); + foreach ($aRunningJobs as $iIndex => $aRunningJob) { + if ($aRunningJob['order_id'] == $iOrderId) { + $iJobIndex = $iIndex; + break; + } + } + // Sauve la liste. + if ($iJobIndex !== null) { + // Sauve la liste des demandes asynchrones en cours. + $aRunningJob = array_splice($aRunningJobs, $iJobIndex, 1)[0]; + if (empty($aRunningJobs)) + unlink($properties['running_jobs_file_path']); + else + file_put_contents($properties['running_jobs_file_path'], serialize($aRunningJobs)); + // Supprime le job id. + //updateOrderPid($iOrderId); + // Ecriture dans les logs. + writeToLog(INFO_FME_SERVER_ASYNCHRONOUS_JOB_REMOVED_FROM_LIST . " (demande $iOrderId).", $properties["engine_log_file"]); + // Supprime le traitement sur Fme Server. + if (is_object($oFmeServer)) { + if (!empty($aRunningJob['job_id'])) + $oFmeServer->deleteJob($aRunningJob['job_id']); + // Supprime le répertoire des ressources de la demande sur Fme Server. + if (!empty($aRunningJob['resource']) && !empty($aRunningJob['path'])) + $oFmeServer->serviceRequest('resources/connections/' . $aRunningJob['resource'] . '/filesys/' . $aRunningJob['path'], 'delete'); + } + } +} + +/** + * Traitement d'une demande sur Fme Server. + * @param {string} $sServerUrl Url to Fme Server. + * @param {string} $sLogin User login. + * @param {string} $sPassword User password. + * return boolean + */ +function processFmeServerOrder($sServerUrl, $sLogin, $sPassword) { + // Variables globales utilisées. + global $oFmeServer, $properties, $oBd, $aFmeEngine, $aDemande, $aTraitement, $aUser, $iEmailTemplateId, $bAsync, $sEnErreur, $iTentative, $sDateDebut, $sDateFin; + // Variables globales modifiées. + global $oTraitement, $sMessage, $bValidFmeLicence, $sResultat, $sLogFme, $iStatut, $sLienLogFme; + // Paramètres de connexion vers Fme Server pour la demande (si elle est dans la liste des traitements asynchrones en cours). + $aRunningJobs = getFmeServerRunningJobs(); + $aRunningJob = getFmeServerRunningJob($aDemande['order_id'], $aRunningJobs); + if ($aRunningJob !== false) { + $sServerUrl = $aRunningJob['fme_server_url']; + $sLogin = $aRunningJob['login']; + $sPassword = $aRunningJob['password']; + } + // + $oFmeServer = new FmeServer($sServerUrl, $sLogin, $sPassword, 'day', 1); + $oFmeServer->sLogFilePath = $properties["engine_log_file"]; + $sMessage = ''; + // Statut de la licence Fme Server (pas pour Fme Cloud). + $oLicenseStatus = ''; + if ($aFmeEngine['fme_engine_type_id'] == 'fme_server') + $oLicenseStatus = $oFmeServer->serviceRequest('licensing/license/status'); + if ($oLicenseStatus === false) + return false; + else if ($aFmeEngine['fme_engine_type_id'] == 'fme_cloud' || (!empty($oLicenseStatus) && $oLicenseStatus->isLicensed === true && $oLicenseStatus->isLicenseExpired === false)) { + $bValidFmeLicence = true; + $oTraitement = new Traitement($oBd, $aDemande['order_id'], $properties["engine_log_file"], $aDemande['wk_params'], $properties, $aDemande['workspace_id'] . "/fme/" . $aTraitement['fmw_file']); + $oTraitement->bFmeServer = true; + // Demande suivante si le traitement est déja en cours d'exécution. + if ($aRunningJob !== false) { + // Chemin et nom du répertoire de résultat déja crée. + $oTraitement->sResultDirectoryName = $aRunningJob['result_directory_name']; + $oTraitement->sResultDirectoryPath = $aRunningJob['result_directory_path']; + // Vérification du statut de la demande. + $oJobRecord = $oTraitement->getFmeServerJobRecord($aRunningJob['job_id'], $oFmeServer); + // Si le job id n'existe pas : 2 essais puis suppression de la demande dans la liste des traitements asynchrones en cours -> non traitable. + if ($oFmeServer->aLastCurlRequestInfo['http_code'] == 404) { + if ($iTentative >= $properties["max_attempt"]) + removeFmeServerRunningJob($aDemande['order_id']); + } + if ($oJobRecord === false) { + return false; + } + $oTraitement->oJobResult = $oJobRecord; + $aRunningStatus = array('SUBMITTED', 'QUEUED', 'DELAYED', 'PAUSED', 'IN_PROCESS', 'PULLED'); + $aErrorStatus = array('DELETED', 'ABORTED', 'FME_FAILURE', 'JOB_FAILURE'); + writeToLog(INFO_FME_SERVER_JOB_STATUS . $oJobRecord->status, $properties["engine_log_file"]); + // En cours de traitement. + if (in_array($oJobRecord->status, $aRunningStatus)) { + writeToLog(INFO_FME_SERVER_ASYNCHRONOUS_JOB_IN_PROGRESS, $properties["engine_log_file"]); + // Nombre de tentative = 0 (traitement en cours d'exécution). + updateOrderAttempt($aDemande["order_id"], 0); + return false; + } + else { + // Supprime la demande dans la liste des traitements asynchrones en cours. + removeFmeServerRunningJob($aDemande['order_id']); + // Télécharge les fichiers de log (et le résultat si présent). + $sResultat = $oTraitement->downloadFmeServerJobResult($aRunningJob['job_id'], $oFmeServer, $aRunningJob['destination_files'], $aRunningJob['resource'], $aRunningJob['path']); + $oDateDebut = date_create($oJobRecord->timeStarted); + $oDateFin = date_create($oJobRecord->timeFinished); + // Terminé en erreur. + if (in_array($oJobRecord->status, $aErrorStatus) || $sResultat === false) { + updateOrder($aDemande["order_id"], 2, $oJobRecord->timeStarted, $oTraitement->sLogFme, date_diff($oDateDebut, $oDateFin)->format('%s')); + sendWebsocketOrderStatusMessage('order_finished_or_error', 2); + // Envoi d'un mail de résultat. + sendOrderResultEmail($iEmailTemplateId); + // Ecriture dans les log. + writeToLog(INFO_TREATMENT_FAILURE, $oTraitement->sRobotLogFile); + // Demande suivante. + return false; + } + else if ($oJobRecord->status == 'SUCCESS') { + $iStatut = 3; + $sLogFme = $oTraitement->sLogFme; + } + } + } + else { + $oTraitement->setUseExternalDbConnection($aFmeEngine['useexternaldbconnection']); + $sResultat = $oTraitement->submitFmeServerJob($oFmeServer, $aDemande, $aTraitement, $aUser, null, $bAsync); + if ($bAsync) { + $iStatut = 5; + addFmeServerRunningJob($oTraitement, $sServerUrl, $sLogin, $sPassword); + writeToLog(INFO_FME_SERVER_JOB_ID . $oTraitement->oJobResult->id, $properties["engine_log_file"]); + // Nombre de tentative = 0 (traitement en cours d'exécution). + updateOrderAttempt($aDemande["order_id"], 0); + // Demande suivante. + return false; + } + else { + // Erreur retournée par FME Server. + if ($sResultat === false) { + updateOrder($aDemande["order_id"], 2, gmdate('Y-m-d H:i:s'), $oTraitement->sLogFme); + sendWebsocketOrderStatusMessage('order_finished_or_error', 2); + writeToLog(INFO_TREATMENT_FAILURE, $oTraitement->sRobotLogFile); + // Envoi d'un mail de résultat. + sendOrderResultEmail($iEmailTemplateId); + // Demande suivante. + return false; + } + //$sDateFin = new DateTime(date("Y-m-d H:i:s")); + $sLogFme = $oTraitement->sLogFme; + } + } + // http 200 : $oFmeServer->aLastCurlRequestInfo['http_code'] + $sDateDebut = $oJobRecord->timeStarted; + $sDateFin = $oJobRecord->timeFinished; + if ($oTraitement->oJobResult->status == 'SUCCESS') { + $iStatut = 3; + $sLienLogFme = $sLogFme; + } + else + $iStatut = $sEnErreur; + } + else { + // Erreur dans la vérification du statut de la licence FME ou expiration. + if ($oLicenseStatus->isLicenseExpired) + $ErrorMessage = INFO_FME_SERVER_EXPIRED_LICENCE; + else + $ErrorMessage = INFO_LICENCE_STATUS_VERIFICATION_ERROR; + writeToLog($ErrorMessage, $properties["engine_log_file"]); + writeToLog(INFO_CONTACT_ADMINISTRATOR, $properties["engine_log_file"]); + updateOrder($aDemande["order_id"], 2, gmdate('Y-m-d H:i:s')); + sendWebsocketOrderStatusMessage('order_finished_or_error', 2); + // Envoi d'un mail de résultat. + if ($oLicenseStatus->isLicenseExpired === true) + $sMessage = 'Invalid FME licence file'; + sendOrderResultEmail($iEmailTemplateId, $sMessage); + // Demande suivante. + return false; + } +} + +/** + * Mise à jour du processus ou job id d'une demande. + * @param {number} $iOrderId Id de la demande. + * @param {number} $iPid Id du processus (ou job id) de la demande. + */ +function updateOrderPid($iOrderId, $iPid = null) { + // Variables globales. + global $oBd, $properties, $aSql; + // Requete d'update de la demande. + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties['schema_gtf'], 'type' => 'schema_name'); + $aParams['iOrderId'] = array('value' => $iOrderId, 'type' => 'number'); + $aParams['iPid'] = array('value' => $iPid, 'type' => 'number'); + $oBd->executeWithParams($aSql[$properties['sgbd']]['update_order_pid'], $aParams); + if ($oBd->erreurRencontree) + writeToLog(INFO_ORDER_PID_UPDATE_ERROR . "(SQL : {$aSql[$properties['sgbd']]['update_order_pid']})", $properties['engine_log_file']); +} + +/** + * Supprime tous les traitements sur Fme Server dont la demande a été supprimée dans GTF. + */ +/* +function removeFmeServerDeletedJobs() { + // Variables globales. + global $properties, $oBd, $aSql; + // Requete d'update de la demande. + $aParams = array(); + $aParams['sSchemaGtf'] = array('value' => $properties['schema_gtf'], 'type' => 'schema_name'); + $oPDOResult = $oBd->executeWithParams($aSql[$properties['sgbd']]['getRunningOrders'], $aParams); + if ($oBd->erreurRencontree) + writeToLog(INFO_RUNNING_JOBS_ERROR . "(SQL : {$aSql[$properties['sgbd']]['getRunningOrders']})", $properties['engine_log_file']); + else { + $aOrders = array(); + while ($aOrder = $oBd->ligneSuivante($oPDOResult)) + $aOrders[] = $aOrder['order_id']; + // Liste des demandes asynchrones en cours. + $aRunningJobs = getFmeServerRunningJobs(); + foreach ($aRunningJobs as $iIndex => $aRunningJob) { + if (!in_array($aRunningJob['order_id'], $aOrders)) { + $oFmeServer = null; + // Supprime le traitement sur Fme Server. + if (!empty($aRunningJob['job_id'])) { + $oFmeServer = new FmeServer($aRunningJob['fme_server_url'], $aRunningJob['login'], $aRunningJob['password'], 'day', 1); + $oFmeServer->sLogFilePath = $properties["engine_log_file"]; + // Statut de la licence Fme Server (pas pour Fme Cloud). + if ($aRunningJob['fme_engine_type_id'] == 'fme_server') { + $oLicenseStatus = $oFmeServer->serviceRequest('licensing/license/status'); + if ($oLicenseStatus === false) + $oFmeServer = null; + else if (is_object($oLicenseStatus) && ($oLicenseStatus->isLicensed === false || $oLicenseStatus->isLicenseExpired === true)) + $oFmeServer = null; + } + } + // Supprime le traitement sur Fme Server. + removeFmeServerRunningJob($aRunningJob['order_id'], $oFmeServer); + } + } + } +} +*/ ?> diff --git a/gtf.engine/gtf.engines/engine.sql.inc b/gtf.engine/gtf.engines/engine.sql.inc index 9238c05f..24039bdf 100755 --- a/gtf.engine/gtf.engines/engine.sql.inc +++ b/gtf.engine/gtf.engines/engine.sql.inc @@ -1,7 +1,7 @@ <?php //Requête pour engine.php pgsql -$aSql['pgsql']['boucle_1']='SELECT "order".*, rt_priority.priority_id, rt_priority.label_id as priorite_libelle_id, workspace.name as workspace_nom FROM ([sSchemaGtf]."order" LEFT JOIN [sSchemaGtf].rt_priority ON "order".priority_id = rt_priority.priority_id) LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id WHERE (order_status_id = 1 or order_status_id = 5) AND "order".period_id IS NULL AND "order".deleted IS not TRUE AND "order".gtf_engine_id=[iIdGtfEngine] AND "order".priority_id >= [iPriorityId] ORDER BY "order".priority_id ASC, "order".order_id ASC LIMIT [iNnbrOrderMax]'; -$aSql['pgsql']['boucle_2']='SELECT "order".*, rt_priority.priority_id, rt_priority.label_id as priorite_libelle_id, workspace.name as workspace_nom FROM ([sSchemaGtf]."order" LEFT JOIN [sSchemaGtf].rt_priority ON "order".priority_id = rt_priority.priority_id) LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id WHERE (order_status_id = 1 or order_status_id = 5) AND "order".period_id IS NULL AND "order".deleted IS not TRUE AND "order".gtf_engine_id=[iIdGtfEngine] AND "order".priority_id > [iPriorityId] ORDER BY "order".priority_id ASC, "order".order_id ASC LIMIT [iNnbrOrderMax]'; +$aSql['pgsql']['boucle_1']='SELECT "order".*, rt_priority.priority_id, rt_priority.label_id as priorite_libelle_id, workspace.name as workspace_nom FROM ([sSchemaGtf]."order" LEFT JOIN [sSchemaGtf].rt_priority ON "order".priority_id = rt_priority.priority_id) LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id WHERE (order_status_id = 1 or order_status_id = 5) AND "order".period_id IS NULL AND "order".deleted IS not TRUE AND "order".gtf_engine_id=[iIdGtfEngine] AND "order".priority_id >= [iPriorityId] AND (minexecdate IS NULL or minexecdate AT TIME ZONE [sTimeZone] <=now() AT TIME ZONE [sTimeZone]) ORDER BY "order".priority_id ASC, "order".order_id ASC LIMIT [iNnbrOrderMax]'; +$aSql['pgsql']['boucle_2']='SELECT "order".*, rt_priority.priority_id, rt_priority.label_id as priorite_libelle_id, workspace.name as workspace_nom FROM ([sSchemaGtf]."order" LEFT JOIN [sSchemaGtf].rt_priority ON "order".priority_id = rt_priority.priority_id) LEFT JOIN [sSchemaGtf].workspace ON "order".workspace_id=workspace.workspace_id WHERE (order_status_id = 1 or order_status_id = 5) AND "order".period_id IS NULL AND "order".deleted IS not TRUE AND "order".gtf_engine_id=[iIdGtfEngine] AND "order".priority_id > [iPriorityId] AND (minexecdate IS NULL or minexecdate AT TIME ZONE [sTimeZone] <=now() AT TIME ZONE [sTimeZone]) ORDER BY "order".priority_id ASC, "order".order_id ASC LIMIT [iNnbrOrderMax]'; $aSql['pgsql']['right_user']='SELECT workspace."workspace_id" FROM [sSchemaGtf].workspace RIGHT JOIN [sSchemaGtf].workspace_group ON workspace.workspace_id = workspace_group.workspace_id GROUP BY workspace."workspace_id", workspace_group."group_id" HAVING workspace_group."group_id" IN ([sGroupListId]) ORDER BY workspace."workspace_id" ASC'; $aSql['pgsql']['update_demande']="UPDATE [sSchemaGtf].order SET length_sec = [iNbSec], order_status_id=[iStatut], execution_date=[sDateTraitement], result_url=[sResultat], log_url=[sLogFme] WHERE order_id=[iOrderId]"; $aSql['pgsql']['update_demande_resultat_null']="UPDATE [sSchemaGtf].order SET result_url=NULL WHERE order_id=[iOrderId]"; @@ -9,10 +9,12 @@ $aSql['pgsql']['select_user']='SELECT * FROM [sSchemaFramework].user WHERE "user $aSql['pgsql']['select_traitement']='SELECT * FROM [sSchemaGtf].workspace WHERE workspace_id=[iWorkspaceId]'; //$aSql['pgsql']['select_transit_dir']='SELECT value FROM [sSchemaGtf].property WHERE property=\'$properties["transit_dir"]\''; $aSql['pgsql']['select_fme_path']='select local_path as fme_path from [sSchemaGtf].gtf_engine, [sSchemaGtf].fme_engine where fme_engine.fme_engine_id = gtf_engine.fme_engine_id AND gtf_engine.gtf_engine_id = [gtf_engine_id]'; -$aSql['pgsql']['update_message']="UPDATE [sSchemaGtf].message SET status=1 WHERE order_id=[iOrderId]"; +$aSql['pgsql']['update_message']="UPDATE [sSchemaGtf].message SET status=[status] WHERE order_id=[iOrderId]"; $aSql['pgsql']['delete_message']="DELETE FROM [sSchemaGtf].message where order_id=[iOrderId]"; $aSql['pgsql']['get_gtf_engine_name'] = 'SELECT gtf_engine.name AS gtf_engine_name,fme_engine.name AS fme_engine_name FROM [sSchemaGtf].gtf_engine,[sSchemaGtf].fme_engine WHERE gtf_engine.fme_engine_id=fme_engine.fme_engine_id AND gtf_engine_id=[gtf_engine_id]'; $aSql['pgsql']["getLogin"] = 'SELECT "login" from [sSchemaFramework]."user" where user_id = [iUserId]'; $aSql['pgsql']["insertJob"] = 'INSERT INTO [sSchemaGtf].job(order_id, workspace_id, "user_id", execution_date, length_sec, engine_id, order_status_id) VALUES ([iOrderId], [iWorkspaceId], [iUserId], [sBeginExecutionDate] , [iDelay], [iGtfEngineId], [iStatut])'; $aSql['pgsql']["getFmeEngine"] = 'SELECT * from [sSchemaGtf].fme_engine WHERE fme_engine_id = (SELECT fme_engine_id FROM [sSchemaGtf].gtf_engine WHERE gtf_engine_id = [gtf_engine_id])'; +$aSql['pgsql']['update_order_pid']="UPDATE [sSchemaGtf].order SET pid=[iPid] WHERE order_id=[iOrderId]"; +$aSql['pgsql']['getRunningOrders'] = 'SELECT order_id FROM [sSchemaGtf]."order" WHERE order_status_id IN (1, 5)'; ?> diff --git a/gtf.engine/gtf.engines/lang_engines/fr-lang.inc b/gtf.engine/gtf.engines/lang_engines/fr-lang.inc index 7d5d5046..7b0356cd 100755 --- a/gtf.engine/gtf.engines/lang_engines/fr-lang.inc +++ b/gtf.engine/gtf.engines/lang_engines/fr-lang.inc @@ -1,8 +1,8 @@ <?php //Error Licence -define('E001', "Demandes non trait�es : Fichier de licence invalide"); -define('E002', "Demandes non trait�es : Fichier de licence expir�"); -define('E003', "Demandes non trait�es : Num�ro du moteur GTF sup�rieur au nombre de moteurs disponibles"); +define('E001', "Demandes non traitées : Fichier de licence invalide"); +define('E002', "Demandes non traitées : Fichier de licence expiré"); +define('E003', "Demandes non traitées : Numéro du moteur GTF supérieur au nombre de moteurs disponibles"); ?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/licenses/license.txt b/gtf.engine/gtf.engines/licenses/license.txt deleted file mode 100755 index 3c36fb54..00000000 --- a/gtf.engine/gtf.engines/licenses/license.txt +++ /dev/null @@ -1,7 +0,0 @@ -#GTF License file -Product: GTF -License number: Sgtf-0001 -Expiry date: Permanent -Engines: 5 -Owner: Veremes -Key: 295C919B 5A83DA4D 23CC819F 68CB7E3A A41BB54F \ No newline at end of file diff --git a/gtf.engine/gtf.engines/php_engine_conf.inc b/gtf.engine/gtf.engines/php_engine_conf.inc index f43c4cc8..6985372b 100755 --- a/gtf.engine/gtf.engines/php_engine_conf.inc +++ b/gtf.engine/gtf.engines/php_engine_conf.inc @@ -1,4 +1,4 @@ <?php -set_include_path('C:\svn\gtf_next_version\gtf.engines;C:\svn\vitis_next_version\vas\rest\conf;C:\svn\vitis_next_version\vas\rest\class'); -ini_set ('error_log', 'C:/svn/gtf_next_version/gtf.engines/php/log/php_errors.log'); +set_include_path(dirname($_SERVER["PHP_SELF"]). PATH_SEPARATOR .dirname($_SERVER["PHP_SELF"]).'/../vas/rest/conf'. PATH_SEPARATOR .dirname($_SERVER["PHP_SELF"]).'/../vas/rest/class'); +ini_set ('error_log', dirname($_SERVER["PHP_SELF"]).'/php/log/php.log'); ?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/string.inc b/gtf.engine/gtf.engines/string.inc index 26ffca30..6a6a266f 100755 --- a/gtf.engine/gtf.engines/string.inc +++ b/gtf.engine/gtf.engines/string.inc @@ -39,6 +39,10 @@ define('INFO_GTF_HOME', '|INFORM|PHP| GTF_HOME='); define('INFO_FME_PATH', '|INFORM|PHP| FME_PATH='); define('INFO_TREATMENT_COMMAND_LINE', '|INFORM|PHP| Ligne de commande exécutant le traitement : '); + define('INFO_NO_USE_PATTERN', '|INFORM|PHP| Utilisation des noms de fichier, sans utilisation des motifs'); + define('INFO_USE_PATTERN', '|INFORM|PHP| Utilisation des motifs '); + define('INFO_USER_RESTRICTION', '|ERROR|PHP| Impossible de récupérer la paramètre "restriction" de l\'utilisateur.'); + define('INFO_GTF_CONNECTION_STRING_TYPE_PASSWORD', '|WARNING|PHP| Le paramètre "GTF_CONNECTION_STRING" doit-être de type "PASSWORD".'); // engine.php define('INFO_BASE_CONNECTION', '|INFORM|PHP| Connexion à la base de données '); @@ -54,4 +58,20 @@ define('INFO_TEST_REQUEST_ERROR', '|ERROR|PHP|Requête test en erreur. '); define('INFO_INFO_ORDER_UPDATE_NULL', '|INFORM|PHP| Mise à jour de la demande (resultat null).'); define('INFO_NO_MAIL_SEND', "|INFORM|PHP| Aucun mail n'a été envoyé "); + define('INFO_NO_ASSOCIATED_USER', "|INFORM|PHP| La demande [aDemande['order_id']] n'a pas été traitée car aucun utilisateur est associé."); + define('INFO_USER_ENCRYPT_PARAMETERS', '|ERROR|PHP| Impossible de récupérer les paramètres de cryptage de l\'utilisateur et du projet.'); + define('INFO_NO_FME_CLOUD_INSTANCES', '|ERROR|PHP| Aucune instance FME Server sur FME Cloud n\'est active.'); + define('INFO_LICENCE_STATUS_VERIFICATION_ERROR', '|ERROR|PHP| Erreur dans la vérification du statut de la licence FME.'); + define('INFO_FME_CLOUD_INSTANCE_NOT_FOUND', '|ERROR|PHP| L\'instance FME Server spécifiée sur FME Cloud n\'existe pas.'); + define('INFO_FME_SERVER_JOB_IN_PROGRESS', '|INFORM|PHP| La demande est en cours de traitement par Fme Server.'); + define('INFO_FME_SERVER_JOB_STATUS', '|INFORM|PHP| Statut de la demande asynchrone sur Fme Server : '); + define('INFO_FME_SERVER_JOB_ID', '|INFORM|PHP| Job id pour la demande asynchrone sur Fme Server : '); + define('INFO_FME_SERVER_ASYNCHRONOUS_JOB_IN_PROGRESS', '|INFORM|PHP| La demande asynchrone est en cours de traitement par Fme Server.'); + define('INFO_LAUNCHING_FME_CLOUD_INSTANCE', '|INFORM|PHP| L\'instance FME Server sur FME Cloud est en cours de démarrage.'); + define('INFO_FME_CLOUD_INSTANCE_STATUS', '|INFORM|PHP| Statut de l\'instance FME Server sur FME Cloud : '); + define('INFO_FME_CLOUD_INSTANCE_RUNNING', '|INFORM|PHP| L\'instance FME Server sur FME Cloud est opérationnelle'); + define('INFO_FME_SERVER_EXPIRED_LICENCE', '|INFORM|PHP| La licence de Fme Server a expiré.'); + define('INFO_ORDER_PID_UPDATE_ERROR', '|ERROR|PHP| Impossible de mettre à jour le pid de la demande. '); + define('INFO_FME_SERVER_ASYNCHRONOUS_JOB_REMOVED_FROM_LIST', '|INFORM|PHP| Le traitement a été retirée de la liste des traitements asynchrones en cours'); + define('INFO_RUNNING_JOBS_ERROR', '|ERROR|PHP| Impossible de récupérer la liste des demandes en attente et en cours.'); ?> \ No newline at end of file diff --git a/gtf.engine/gtf.engines/subscription.php b/gtf.engine/gtf.engines/subscription.php index 68d50061..a6caaae0 100755 --- a/gtf.engine/gtf.engines/subscription.php +++ b/gtf.engine/gtf.engines/subscription.php @@ -41,7 +41,7 @@ require_once 'string.inc'; } */ //Pas besoin de mot de passe pour le robot -$oBd = new Vm($properties["login"], '', $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); +$oBd = new Vm($properties["login_scheduler"], $properties['password_scheduler'], $properties["database"], $properties["server"], $properties["port"], $properties["sgbd"], $properties["page_encoding"]); if ($oBd->erreurRencontree) { writeToLog(INFO_BASE_CONNECTION_ERROR . $properties["database"], $properties["subscription_log_file"]); } else { @@ -199,7 +199,7 @@ if ($oBd->erreurRencontree) { //deplacement vers le dossier upload en fonction du type $bCopie = false; $bIsFile = false; - $sUnique = UniqFileName(); + $sUnique = getUniqRandomId(); if (is_dir($sFolder . '/' . $Entry)) { $bIsFile = false; $bCopie = rename($sFolder . "/" . $Entry, $properties["upload_dir"] . "/" . $sUnique); -- GitLab