diff --git a/src/module_gtf/module/javascript/script_module.js b/src/module_gtf/module/javascript/script_module.js
index fbd07bc90c128c795025aee28c2a4d1c0d431e82..72413cbd5daed4ed1b32c1006b34b0bb0b13cb66 100644
--- a/src/module_gtf/module/javascript/script_module.js
+++ b/src/module_gtf/module/javascript/script_module.js
@@ -154,7 +154,8 @@ vitisApp.on('appMainDrtvLoaded', function () {
                 "iTreatedOrders": 0,
                 "iNonTreatableOrders": 0,
                 "iProcessingOrders": 0,
-                "iNonAuthorizedOrders": 0
+                "iNonAuthorizedOrders": 0,
+                "iStoppedOrders": 0
             },
             "subscription": {
                 "iNonEnabledSubscription": 0,
@@ -428,7 +429,7 @@ vitisApp.on('appMainDrtvLoaded', function () {
                 // Traduction des libellés de statuts des demandes.
                 if (typeof(scope["gtf_order_status_label"]) == "undefined") {
                     var $translate = angular.element(vitisApp.appWorkspaceListDrtv).injector().get(["$translate"]);
-                    var aOrderStatusLabel = ["WAITING_BOX_TITLE_SUPERVISION_ORDER", "ERROR_BOX_TITLE_SUPERVISION_ORDER", "TREATED_BOX_TITLE_SUPERVISION_ORDER", "NON_TREATABLE_BOX_TITLE_SUPERVISION_ORDER", "PROCESSING_BOX_TITLE_SUPERVISION_ORDER", "NON_AUTHORIZED_BOX_TITLE_SUPERVISION_ORDER"];
+                    var aOrderStatusLabel = ["WAITING_BOX_TITLE_SUPERVISION_ORDER", "ERROR_BOX_TITLE_SUPERVISION_ORDER", "TREATED_BOX_TITLE_SUPERVISION_ORDER", "NON_TREATABLE_BOX_TITLE_SUPERVISION_ORDER", "PROCESSING_BOX_TITLE_SUPERVISION_ORDER", "NON_AUTHORIZED_BOX_TITLE_SUPERVISION_ORDER", "STOPPED_BOX_TITLE_SUPERVISION_ORDER"];
                     scope.$root["gtf_order_status_label"] = [];
                     $translate(aOrderStatusLabel).then(function (translations) {
                         for(var i= 0; i < aOrderStatusLabel.length; i++)
diff --git a/src/module_gtf/module/less/main.less b/src/module_gtf/module/less/main.less
index c220a40001548b885fc48ab63e78dac96b2bcd6c..cfbf3a465262ac906dc7a0da53983fc2a47daf64 100755
--- a/src/module_gtf/module/less/main.less
+++ b/src/module_gtf/module/less/main.less
@@ -48,7 +48,7 @@
 	display : inline;
 	background-color:#ffffff;
         cursor: pointer;
-	
+
 }
 
 .box_number {
@@ -74,16 +74,29 @@
     text-transform: uppercase;
     text-align: center;
 }
+// Etat des demandes et messages.
+.box-dot-waiting {background-color: #5ac6de;}
+.box-dot-error {background-color: #ff0000;}
+.box-dot-treated {background-color: #9cce29;}
+.box-dot-non-treatable {background-color: #000000;}
+.box-dot-non-authorized {background-color: #c6c6c6;}
+.box-dot-order-stopped {background-color: darkorange;}
+// En cours (point clignotant).
+.box-dot-processing {background-color: #f79cce; animation: blinking 1s linear infinite;}
+@keyframes blinking {from, 49.9% {opacity: 0;} 50%, to {opacity: 1;}}
+// Abonnements.
+.box-dot-enabled-subscription {background-color: #9cce29;}
+.box-dot-non-enabled-subscription {background-color: #ff0000;}
+// Surveillances.
+.box-dot-enabled-survey {background-color: #9cce29;}
+.box-dot-non-enabled-survey {background-color: #ff0000;}
 
-.box_waiting {background: #ffffff url("../../../images/sql_list/st1.gif") 2px 2px no-repeat;}
-.box_error {background: #ffffff url("../../../images/sql_list/st2.png") 2px 2px no-repeat;}
-.box_treated {background: #ffffff url("../../../images/sql_list/st3.png") 2px 2px no-repeat;}
-.box_non_treatable {background: #ffffff url("../../../images/sql_list/st4.png") 2px 2px no-repeat;}
-.box_processing {background: #ffffff url("../../../images/sql_list/st5.gif") 2px 2px no-repeat;}
-.box_non_authorized {background: #ffffff url("../../../images/sql_list/st6.png") 2px 2px no-repeat;}
-
-.box_enabled_subscription {background: #ffffff url("../../../images/sql_list/st3.png") 2px 2px no-repeat;}
-.box_non_enabled_subscription {background: #ffffff url("../../../images/sql_list/st2.png") 2px 2px no-repeat;}
-
-.box_enabled_survey {background: #ffffff url("../../../images/sql_list/st3.png") 2px 2px no-repeat;}
-.box_non_enabled_survey {background: #ffffff url("../../../images/sql_list/st2.png") 2px 2px no-repeat;}
+// Point indiquant la couleur de la demande.
+.box-dot {
+    position: absolute;
+    display: inline-block;
+    height: 10px;
+    width: 10px;
+    margin: 3px;
+    border-radius: 50%;
+}
diff --git a/src/module_gtf/module/templates/supervisionStatusTpl.html b/src/module_gtf/module/templates/supervisionStatusTpl.html
index f0ad53d6a5f5c1b05ad134e240c7797b36476303..7a31275f06dcc6ec55c4268fc7fdce122fa3931d 100755
--- a/src/module_gtf/module/templates/supervisionStatusTpl.html
+++ b/src/module_gtf/module/templates/supervisionStatusTpl.html
@@ -3,39 +3,52 @@
                 <div class="recap_title" data-translate="STATUS_TITLE_SUPERVISION_ORDER"></div>
                 <div class="recap_boxes">
                         <div class="box_recap box_waiting" ng-click="setSupervisionRecapFilter(1)">
+                                <span class="box-dot box-dot-waiting"></span>
                                 <div class="box_number">{{oSupervisionStatus.order.iWaitingOrders}}</div>
                                 <div class="box_text" data-translate="WAITING_BOX_TITLE_SUPERVISION_ORDER"></div>
                         </div>
                         <div class="box_recap box_error" ng-click="setSupervisionRecapFilter(2)">
+                                <span class="box-dot box-dot-error"></span>
                                 <div class="box_number">{{oSupervisionStatus.order.iErrors}}</div>
                                 <div class="box_text" data-translate="ERROR_BOX_TITLE_SUPERVISION_ORDER"></div>
                         </div>
                         <div class="box_recap box_treated" ng-click="setSupervisionRecapFilter(3)">
+                                <span class="box-dot box-dot-treated"></span>
                                 <div class="box_number">{{oSupervisionStatus.order.iTreatedOrders}}</div>
                                 <div class="box_text" data-translate="TREATED_BOX_TITLE_SUPERVISION_ORDER"></div>
                         </div>
                         <div class="box_recap box_non_treatable" ng-click="setSupervisionRecapFilter(4)">
+                                <span class="box-dot box-dot-non-treatable"></span>
                                 <div class="box_number">{{oSupervisionStatus.order.iNonTreatableOrders}}</div>
                                 <div class="box_text" data-translate="NON_TREATABLE_BOX_TITLE_SUPERVISION_ORDER"></div>
                         </div>
                         <div class="box_recap box_processing" ng-click="setSupervisionRecapFilter(5)">
+                                <span class="box-dot box-dot-processing"></span>
                                 <div class="box_number">{{oSupervisionStatus.order.iProcessingOrders}}</div>
                                 <div class="box_text" data-translate="PROCESSING_BOX_TITLE_SUPERVISION_ORDER"></div>
                         </div>
                         <div class="box_recap box_non_authorized" ng-click="setSupervisionRecapFilter(6)">
+                                <span class="box-dot box-dot-non-authorized"></span>
                                 <div class="box_number">{{oSupervisionStatus.order.iNonAuthorizedOrders}}</div>
                                 <div class="box_text" data-translate="NON_AUTHORIZED_BOX_TITLE_SUPERVISION_ORDER"></div>
                         </div>
+                        <div class="box_recap box_stopped" ng-click="setSupervisionRecapFilter(7)">
+                                <span class="box-dot box-dot-order-stopped"></span>
+                                <div class="box_number">{{oSupervisionStatus.order.iStoppedOrders}}</div>
+                                <div class="box_text" data-translate="STOPPED_BOX_TITLE_SUPERVISION_ORDER"></div>
+                        </div>
                 </div>
         </div>
         <div ng-if="sSelectedObjectName == 'supervision_gtf_subscription'" class="recap">
                 <div class="recap_title" data-translate="STATUS_TITLE_SUPERVISION_SUBSCRIPTION"></div>
                 <div class="recap_boxes">
                         <div class="box_recap box_enabled_subscription" ng-click="setSupervisionRecapFilter('TRUE')">
+                                <span class="box-dot box-dot-enabled-subscription"></span>
                                 <div class="box_number">{{oSupervisionStatus.subscription.iEnabledSubscription}}</div>
                                 <div class="box_text" data-translate="ENABLED_SUBSCRIPTION_BOX_TITLE_SUPERVISION_SUBSCRIPTION"></div>
                         </div>
                         <div class="box_recap box_non_enabled_subscription" ng-click="setSupervisionRecapFilter('FALSE')">
+                                <span class="box-dot box-dot-non-enabled-subscription"></span>
                                 <div class="box_number">{{oSupervisionStatus.subscription.iNonEnabledSubscription}}</div>
                                 <div class="box_text" data-translate="NON_ENABLED_SUBSCRIPTION_BOX_TITLE_SUPERVISION_SUBSCRIPTION"></div>
                         </div>
@@ -45,10 +58,12 @@
                 <div class="recap_title" data-translate="STATUS_TITLE_SUPERVISION_SURVEY"></div>
                 <div class="recap_boxes">
                         <div class="box_recap box_enabled_survey" ng-click="setSupervisionRecapFilter('TRUE')">
+                                <span class="box-dot box-dot-enabled-survey"></span>
                                 <div class="box_number">{{oSupervisionStatus.survey.iEnabledSurvey}}</div>
                                 <div class="box_text" data-translate="ENABLED_SURVEY_BOX_TITLE_SUPERVISION_SURVEY"></div>
                         </div>
                         <div class="box_recap box_non_enabled_survey" ng-click="setSupervisionRecapFilter('FALSE')">
+                                <span class="box-dot box-dot-non-enabled-survey"></span>
                                 <div class="box_number">{{oSupervisionStatus.survey.iNonEnabledSurvey}}</div>
                                 <div class="box_text" data-translate="NON_ENABLED_SURVEY_BOX_TITLE_SUPERVISION_SURVEY"></div>
                         </div>
@@ -58,18 +73,22 @@
                 <div class="recap_title" data-translate="STATUS_TITLE_SUPERVISION_MESSAGE"></div>
                 <div class="recap_boxes">
                         <div class="box_recap box_waiting" ng-click="setSupervisionRecapFilter(1)">
+                                <span class="box-dot box-dot-waiting"></span>
                                 <div class="box_number">{{oSupervisionStatus.message.iWaitingMessages}}</div>
                                 <div class="box_text" data-translate="WAITING_BOX_TITLE_SUPERVISION_MESSAGE"></div>
                         </div>
                         <div class="box_recap box_error" ng-click="setSupervisionRecapFilter(2)">
+                                <span class="box-dot box-dot-error"></span>
                                 <div class="box_number">{{oSupervisionStatus.message.iErrorMessage}}</div>
                                 <div class="box_text" data-translate="ERROR_BOX_TITLE_SUPERVISION_MESSAGE"></div>
                         </div>
                         <div class="box_recap box_treated" ng-click="setSupervisionRecapFilter(3)">
+                                <span class="box-dot box-dot-treated"></span>
                                 <div class="box_number">{{oSupervisionStatus.message.iTreatedMessages}}</div>
                                 <div class="box_text" data-translate="TREATED_BOX_TITLE_SUPERVISION_MESSAGE"></div>
                         </div>
                         <div class="box_recap box_processing" ng-click="setSupervisionRecapFilter(5)">
+                                <span class="box-dot box-dot-processing"></span>
                                 <div class="box_number">{{oSupervisionStatus.message.iProcessingMessages}}</div>
                                 <div class="box_text" data-translate="PROCESSING_BOX_TITLE_SUPERVISION_MESSAGE"></div>
                         </div>
diff --git a/src/module_gtf/web_service/class/gtf_lib/FmeServer.class - Copie.inc b/src/module_gtf/web_service/class/gtf_lib/FmeServer.class - Copie.inc
new file mode 100644
index 0000000000000000000000000000000000000000..db5b8de52861f6ed1e9961318fccc212c49675db
--- /dev/null
+++ b/src/module_gtf/web_service/class/gtf_lib/FmeServer.class - Copie.inc	
@@ -0,0 +1,700 @@
+<?php
+require_once("vmlib/logUtil.inc");
+
+/**
+ * \file FmeServer.php
+ * \brief Main class to use Fme Server services.
+ *
+ * \author Frederic Carretero <frederic.carretero@veremes.com>
+ */
+
+Class FmeServer {
+    const CURL_CONNECTION_TIMEOUT = 0;
+    const CURL_TIMEOUT = 360;
+    const INFO_FME_SERVER_JOB_CANCELED = '|INFORM|PHP| Le traitement a été annulé sur Fme Server';
+    const INFO_FME_SERVER_JOB_STOPPED = '|INFORM|PHP| Le traitement a été arrêté sur Fme Server';
+    const INFO_FME_SERVER_JOB_REMOVED = '|INFORM|PHP| Le traitement a été supprimé sur Fme Server';
+    
+    public $sUrl;
+    public $sTimeUnit;
+    public $iExpiration;
+    public $aLastCurlRequestInfo;
+    public $sLogFilePath;   // Chemin optionnel vers le fichier de log pour Fme Server.
+    private $sUser;
+    private $sPassword;
+    private $sToken;
+    
+    /**
+     * construct
+     * @param {string} $sUrl Url of the fme server.
+     * @param {string} $sUser User id.
+     * @param {string} $sPassword User password.
+     * @param {string} $sTimeUnit Time unit (day, hour, minute, second).
+     * @param {number} $iExpiration Token expiration.
+     * @param {boolean} $bUpdate New (true) or current token if exists (false).
+     */
+    function __construct ($sUrl, $sUser, $sPassword, $sTimeUnit = 'day', $iExpiration = 1, $bUpdate = false) {
+        $this->sUrl = $sUrl;
+        $this->sUser = $sUser;
+        $this->sPassword = $sPassword;
+        $this->sTimeUnit = $sTimeUnit;  // Unité de temps pour la durée du token (day, hour, minute, second).
+        $this->iExpiration = $iExpiration;  // Durée de validité du token.
+        $this->bUpdate = $bUpdate;  // true -> génère un nouveau token écrase l’ancien / false -> retourne le token courant.
+    }
+
+    /**
+     * Get a FME Server Token
+     * @return Token
+     */
+    function getToken() {
+        // Url vers le serveur fme.
+        $sUrl = $this->sUrl;
+        // Url vers le service de génération d'un token.
+        $sUrl = $this->sUrl . '/fmetoken/service/generate';
+        // Données de la requête.
+        $aRequestData = array (
+            'user' => $this->sUser,
+            'password' => $this->sPassword,
+            'expiration' => $this->iExpiration,
+            'timeunit' => $this->sTimeUnit,
+            'update' => $this->bUpdate
+        );
+        // Transfert cURL
+        $sRequestResponse = $this->curlRequest($sUrl, 'post', $aRequestData);
+        if ($this->aLastCurlRequestInfo['http_code'] != 200) {
+            $this->writeToErrorLog($sRequestResponse);
+            $this->sToken = false;
+        }
+        else
+            $this->sToken = $sRequestResponse;
+        return $this->sToken;
+    }
+    
+    /**
+     * Curl request
+     * @param {string} $sUrl Url of the Curl request.
+     * @param {string} $sType Type of the Curl request (get, post).
+     * @param {array} $aData Data of the Curl request.
+     * @param {array} $aHeaders Headers of the Curl request.
+     * @return Request result
+     */
+    // 
+    function curlRequest($sUrl, $sType, $aData = array(), $aHeaders = array()) {
+        //
+        $this->aLastCurlRequestInfo = '';
+        //
+        $ch = curl_init();
+        $sType = strtoupper($sType);
+        // Force la méthode de requête utilisée (GET, POST...).
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $sType);
+        // Url à utiliser.
+        if (($sType == 'GET' || $sType == 'DELETE') && !empty($aData))
+            $sUrl .= '?' . http_build_query($aData);
+        curl_setopt($ch, CURLOPT_URL, $sUrl);
+        // Retour sous forme de texte. 
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        // Requête POST.
+        if ($sType == 'POST') {
+            curl_setopt($ch, CURLOPT_POST, true);
+            // Chaîne de requête en encodage URL.
+            if (is_array($aData))
+                $aData = http_build_query($aData);
+            // Données de la requête.
+            curl_setopt($ch, CURLOPT_POSTFIELDS, $aData);
+            //
+            curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
+            // Entête pour la requête en POST.
+            //$aHeaders[] = 'Content-Type: application/x-www-form-urlencoded';
+        }
+        // Entête pour la requête
+        $aHeaders[] = 'Accept: application/json';
+        if (!empty($this->sToken))
+            $aHeaders[] = 'Authorization: fmetoken token=' . $this->sToken; // Token obligatoire pour éxécuter la requête.
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeaders);
+        // Le nombre de secondes à attendre durant la tentative de connexion.
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::CURL_CONNECTION_TIMEOUT);
+        // Le temps maximum d'exécution de la fonction cURL (secondes).
+        curl_setopt($ch, CURLOPT_TIMEOUT, self::CURL_TIMEOUT);
+        // Exécute la session CURL.
+        // Log.
+        /*
+        $handle = fopen("curl_log.txt", "a");
+        fwrite($handle, PHP_EOL . '-----------------------' . PHP_EOL);
+        curl_setopt($ch, CURLOPT_VERBOSE, true);
+        curl_setopt($ch, CURLOPT_STDERR , $handle);
+        */
+        curl_setopt($ch, CURLINFO_HEADER_OUT, true);
+        // Curl error: SSL certificate problem: unable to get local issuer certificate
+        // Curl error n°60
+        curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, false);
+        // Curl error: SSL: no alternative certificate subject name matches target host name '52.205.208.184' (si utilisation de l'adresse ip et non )
+        // Curl error n°51
+        curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
+        //
+        $output = curl_exec($ch);
+        // Erreur de la requête CURL.
+        if(curl_errno($ch)) {
+            $this->writeToErrorLog('Curl error: ' . curl_error($ch));
+            $this->writeToErrorLog('Curl error n°' . curl_errno($ch));
+        }
+        // Informations de la requête.
+        $aCurlInfo = curl_getinfo($ch);
+        $this->aLastCurlRequestInfo = $aCurlInfo;
+        //file_put_contents('C:/svn/gtf_cloud/gtf.engines/log/response.log', print_r($aCurlInfo, true));
+        //writeToErrorLog(print_r(curl_getinfo($ch), true));
+        
+        // Ferme la session CURL.
+        curl_close($ch);
+        //
+        //fclose($handle);
+        //
+        return $output;
+    }
+    
+    /**
+     * Send a request to the REST API.
+     * @param {string} $sService Name of the service.
+     * @param {string} $sMethod Method of the service (get, post...).
+     * @param {array} $aData Data of the Curl request.
+     * @return Request result
+     */
+    function serviceRequest($sService, $sMethod = 'get', $aData = array(), $bWriteToErrorLog = true) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url vers le serveur fme.
+            $sUrl = $this->sUrl;
+            // Url vers le service d'information.
+            $sUrl = $this->sUrl . '/fmerest/v3/' . $sService;
+            // Transfert cURL
+            $sRequestResponse = $this->curlRequest($sUrl, $sMethod, $aData);
+            if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                if ($bWriteToErrorLog) {
+                    $oRequestResponse = json_decode($sRequestResponse);
+                    if (is_object($oRequestResponse) && !empty($oRequestResponse->message))
+                        $this->writeToErrorLog('|' . $sService . '|' . ' ' . $oRequestResponse->message);
+                    else
+                        $this->writeToErrorLog('|' . $sService . '|' . ' ' . $sRequestResponse);
+                }
+                return false;
+            }
+            else
+                return json_decode($sRequestResponse);
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Upload a file
+     * @param {string} $sFilePath Path of the file to upload.
+     * @param {string} $sServiceUrl Url of the web service to upload the file.
+     * @param {string} $sFileName Name of the created file.
+     * @return Request result
+     */
+    function uploadFile($sServiceUrl, $sFilePath, $sFileName = '') {
+        if (file_exists($sFilePath)) {
+            // Demande de token.
+            $this->getToken();
+            if ($this->sToken !== false) {
+                // Nom du fichier.
+                if (empty($sFileName))
+                    $sFileName = pathinfo($sFilePath, PATHINFO_BASENAME);
+                // Type MIME du fichier.
+                //$finfo = finfo_open(FILEINFO_MIME_TYPE);            
+                // Crée un objet CURLFile.
+                //$oCfile = new CURLFile($sFilePath, finfo_file($finfo, $sFilePath), $sFileName);
+                //finfo_close($finfo);
+                // Entêtes de la requête Curl.
+                $aRequestHeaders = array(
+                    'Content-Type: application/octet-stream',
+                    'Content-Disposition: attachment; filename="' . $sFileName . '"',
+                    //'Content-Length: ' . filesize($sFilePath)
+                );
+                //
+                $sRequestResponse = $this->curlRequest($sServiceUrl, 'post', file_get_contents($sFilePath), $aRequestHeaders);
+                if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                    $oRequestResponse = json_decode($sRequestResponse);
+                    if (is_object($oRequestResponse) && !empty($oRequestResponse->message))
+                        $this->writeToErrorLog($oRequestResponse->message);
+                    else
+                        $this->writeToErrorLog($sRequestResponse);
+                    return false;
+                }
+                else
+                    return json_decode($sRequestResponse);
+            }
+            else
+                return false;
+        }
+        else {
+            $this->writeToErrorLog('The file does not exist (' . $sFilePath . ')');
+            return false;
+        }
+    }
+    
+    /**
+     * Upload a file to the resource connection
+     * @param {string} $sFilePath Path of the file to upload.
+     * @param {string} $sResource Name of a resource connection.
+     * @param {string} $sPath Path, relative to the resource connection.
+     * @param {boolean} $bCreateDirectories Create directories corresponding to the file path.
+     * @param {boolean} $bOverwrite Overwrite the file if it already exists.
+     * @return Request result
+     */
+    function uploadResourceFile($sFilePath, $sResource, $sPath, $bCreateDirectories = false, $bOverwrite = false) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url du service de ressources.
+            $sUrl = $this->sUrl . '/fmerest/v3/resources/connections/' . $sResource . '/filesys/' . $sPath;
+            $sUrl .= '?createDirectories=' . var_export($bCreateDirectories, true) . '&overwrite=' . var_export($bOverwrite, true);
+            return $this->uploadFile($sUrl, $sFilePath);
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Publish a file to a repository
+     * @param {string} $sFilePath Path of the file to upload.
+     * @param {string} $sRepository Name of the repository.
+     * @param {boolean} $bOverwrite Overwrite the file if it already exists.
+     * @param {string} $sRepositoryFileName File name in the repository.
+     * @return Request result
+     */
+    function uploadWorkspaceFile($sFilePath, $sRepository, $bOverwrite = false, $sRepositoryFileName = '') {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Vérification de l'existence du fichier.
+            $bUploadFile = true;
+            if (!empty($sRepositoryFileName))
+                $sSourceFileName = $sRepositoryFileName;
+            else
+                $sSourceFileName = pathinfo($sFilePath, PATHINFO_BASENAME);
+            $this->serviceRequest('repositories/' . $sRepository . '/items/' . $sSourceFileName, 'get', null, false);
+            // Supprime le fichier s'il existe déja.
+            if ($this->aLastCurlRequestInfo['http_code'] == 200) {
+                if ($bOverwrite) {
+                    if ($this->serviceRequest('repositories/' . $sRepository . '/items/' . $sSourceFileName, 'delete') === false)
+                        return false;
+                }
+                else
+                    $bUploadFile = false;
+            }
+            //Upload du fichier.
+            if ($bUploadFile) {
+                $sUrl = $this->sUrl . '/fmerest/v3/repositories/' . $sRepository . '/items';
+                return $this->uploadFile($sUrl, $sFilePath, $sRepositoryFileName);
+            }
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Upload a resource to a repository item
+     * @param {string} $sFilePath Path of the file to upload.
+     * @param {string} $sRepository Name of the repository.
+     * @param {string} $sItem Name of the repository item.
+     * @param {boolean} $bOverwrite Overwrite the file if it already exists.
+     * @return Request result
+     */
+    function uploadWorkspaceResourceFile($sFilePath, $sRepository, $sItem, $bOverwrite = false) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            $bUploadFile = true;
+            $sFileName = pathinfo($sFilePath, PATHINFO_BASENAME);
+            // Télécharge la liste des ressources complémentaires du projet.
+            $aResources = $this->serviceRequest('repositories/' . $sRepository . '/items/' . $sItem . '/resources', 'get');
+            if ($aResources !== false) {
+                // Vérification de l'existence du fichier.
+                foreach ($aResources as $oResource) {
+                    // Supprime le fichier s'il existe déja.
+                    if ($oResource->name == $sFileName) {
+                        if ($bOverwrite) {
+                            if ($this->serviceRequest('repositories/' . $sRepository . '/items/' . $sItem . '/resources/' . $sFileName, 'delete') === false)
+                                return false;
+                        }
+                        else
+                            $bUploadFile = false;
+                    }
+                }
+            }
+            else
+                return false;
+            // Upload du fichier.
+            if ($bUploadFile) {
+                $sUrl = $this->sUrl . '/fmerest/v3/repositories/' . $sRepository . '/items/' . $sItem . '/resources';
+                return $this->uploadFile($sUrl, $sFilePath);
+            }
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Get information about a repository item.
+     * @param {string} $sRepository Name of the repository.
+     * @param {string} $sWorkspace Name of the repository item.
+     * @return Request result
+     */
+    function getWorkspaceParams($sRepository, $sWorkspace) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url du service vers le traitement (fichier.fmw) spécifié.
+            $sUrl = $this->sUrl . '/fmerest/v3/repositories/' . $sRepository . '/items/' . $sWorkspace;
+            // Transfert cURL.
+            $sRequestResponse = $this->curlRequest($sUrl, 'get');
+            if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                $oRequestResponse = json_decode($sRequestResponse);
+                if (is_object($oRequestResponse) && !empty($oRequestResponse->message))
+                    $this->writeToErrorLog($oRequestResponse->message);
+                else
+                    $this->writeToErrorLog($sRequestResponse);
+                return false;
+            }
+            else
+                return json_decode($sRequestResponse);
+            
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Submit a job to run a transformation.
+     * @param {string} $sRepository Name of the repository.
+     * @param {string} $sWorkspace Name of the repository item.
+     * @param {array} $aParams The definition of the transformation request.
+     * @param {boolean} $bAsync Asynchronous job.
+     * @return Request result
+     */
+    function submitJob($sRepository, $sWorkspace, $aParams, $bAsync = false) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url du service vers le traitement (fichier.fmw) spécifié.
+            if ($bAsync)
+                $sUrl = $this->sUrl . '/fmerest/v3/transformations/submit/' . $sRepository . '/' . $sWorkspace;
+            else
+                $sUrl = $this->sUrl . '/fmerest/v3/transformations/transact/' . $sRepository . '/' . $sWorkspace;
+            // Données au format json.
+            $aHeaders = array('Content-Type: application/json');
+            // Transfert cURL.
+            $sRequestResponse = $this->curlRequest($sUrl, 'post', json_encode($aParams), $aHeaders);
+            $oRequestResponse = json_decode($sRequestResponse);
+            if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                if (is_object($oRequestResponse)) {
+                    if (empty($oRequestResponse->id)) {
+                        $this->writeToErrorLog($oRequestResponse->message);
+                        return false;
+                    }
+                    else
+                        $this->writeToErrorLog($oRequestResponse->statusMessage);
+                }
+                else
+                    $this->writeToErrorLog($sRequestResponse);
+            }
+            return $oRequestResponse;
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Downloads a file from a resource connection.
+     * @param {string} $sResource Name of a resource connection.
+     * @param {string} $sPath Path, relative to the resource connection.
+     * @param {string} $sFilePath Path of the file to save.
+     * @return File content
+     */
+    function downloadResourceFile($sResource, $sPath, $sFilePath = null) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url du service vers le traitement (fichier.fmw) spécifié.
+            $sUrl = $this->sUrl . '/fmerest/v3/resources/connections/' . $sResource . '/filesys/' . $sPath;
+            // Obligatoire pour récupérer le contenu du fichier.
+            $aHeaders = array('Accept: application/octet-stream');
+            // Données de la requête.
+            /*
+            $aRequestData = array (
+                'accept' => 'contents',
+                'disposition' => 'inline'
+            );
+            */
+            // Transfert cURL.
+            $sRequestResponse = $this->curlRequest($sUrl, 'get', null, $aHeaders);
+            if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                $oRequestResponse = json_decode($sRequestResponse);
+                if (is_object($oRequestResponse) && !empty($oRequestResponse->message))
+                    $this->writeToErrorLog($oRequestResponse->message);
+                else
+                    $this->writeToErrorLog($sRequestResponse);
+                return false;
+            }
+            else {
+                if (!empty($sFilePath))
+                    file_put_contents($sFilePath, $sRequestResponse);
+                else
+                    return $sRequestResponse;
+            }
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Get the log file of a job.
+     * @param {number} $sJobId Job id.
+     * @return Log file content
+     */
+    function getJobLog($sJobId, $sFilePath = '') {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url du service vers le traitement (fichier.fmw) spécifié.
+            $sUrl = $this->sUrl . '/fmerest/v3/transformations/jobs/id/' . $sJobId . '/log';
+            // Obligatoire pour récupérer le contenu du fichier.
+            $aHeaders = array('Accept: text/plain');
+            // Transfert cURL.
+            $sRequestResponse = $this->curlRequest($sUrl, 'get', null, $aHeaders);
+            if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                $oRequestResponse = json_decode($sRequestResponse);
+                if (is_object($oRequestResponse) && !empty($oRequestResponse->message))
+                    $this->writeToErrorLog($oRequestResponse->message);
+                else
+                    $this->writeToErrorLog($sRequestResponse);
+                return false;
+            }
+            else {
+                if (!empty($sFilePath))
+                    file_put_contents($sFilePath, $sRequestResponse);
+                else
+                    return $sRequestResponse;
+            }
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Create a repository to the FME Server instance.
+     * @param {string} $sRepository Name of the repository.
+     * @param {string} $sDescription Description of the repository.
+     * @return Request result
+     */
+    function createRepository($sRepository, $sDescription) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Crée le dépot s'il n'existe pas déja.
+            $aRequestResult = $this->serviceRequest('repositories/' . $sRepository, 'get', null, false);
+            if ($this->aLastCurlRequestInfo['http_code'] == 404) {
+                $aRequestData = array (
+                    'name' => $sRepository,
+                    'description' => $sDescription
+                );
+                $aRequestResult = $this->serviceRequest('repositories', 'post', $aRequestData);
+            }
+            return $aRequestResult;
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Upload a file to the resource connection from Amazon S3 service.
+     * @param {string} $aS3Params Parameters of the Amazon S3 service.
+     * @param {string} $sRepository Name of the repository.
+     * @param {string} $sResource Name of a resource connection.
+     * @param {string} $sPath Path, relative to the resource connection.
+     * @return Request result
+     */
+    function uploadResourceFileFromS3($aS3Params, $sRepository, $sResource, $sPath) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            //
+            $aJobParams = array(
+                'publishedParameters' => array(
+                    array('name' => 'access_key_id', 'value' => $aS3Params['access_key_id']),
+                    array('name' => 'secret_access_key', 'value' => $aS3Params['secret_access_key']),
+                    array('name' => 'bucket_name', 'value' => $aS3Params['bucket_name']),
+                    array('name' => 'object_key', 'value' => $aS3Params['object_key']),
+                    array('name' => 'target_file', 'value' => '$(' . $sResource . ')/' . $sPath . '/' . pathinfo($aS3Params['object_key'], PATHINFO_BASENAME))
+                )
+            );
+            $oJobResult = $this->submitJob($sRepository, 'S3Downloader.fmw', $aJobParams);
+            return $oJobResult;
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Create a repository to the FME Server instance.
+     * @param {string} $sResource Name of a resource connection.
+     * @param {string} $sPath Path, relative to the resource connection.
+     * @param {string} $DirectoryName Name of the directory.
+     * @return Request result
+     */
+    function createResourceConnectionDirectory($sResource, $sPath, $DirectoryName) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Crée le répertoire s'il n'existe pas déja.
+            $aRequestResult = $this->serviceRequest('resources/connections/' . $sResource . '/filesys/' . $sPath . '/' . $DirectoryName, 'get', null, false);
+            if ($this->aLastCurlRequestInfo['http_code'] == 404) {
+                $aRequestData = array (
+                    'directoryname' => $DirectoryName
+                );
+                $aRequestResult = $this->serviceRequest('resources/connections/' . $sResource . '/filesys/' . $sPath, 'post', $aRequestData);
+            }
+            return $aRequestResult;
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Get the FME Server build number and version.
+     * @return Request result
+     */
+    function getInfo() {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url du web service info.
+            $sUrl = $this->sUrl . '/fmerest/v3/info';
+            // Transfert cURL.
+            $sRequestResponse = $this->curlRequest($sUrl, 'get');
+            if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                $oRequestResponse = json_decode($sRequestResponse);
+                if (is_object($oRequestResponse) && !empty($oRequestResponse->message))
+                    $this->writeToErrorLog($oRequestResponse->message);
+                else
+                    $this->writeToErrorLog($sRequestResponse);
+                return false;
+            }
+            else
+                return json_decode($sRequestResponse);
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Get status of the installed FME Server license.
+     * @return Request result
+     */
+    function getLicenceStatus() {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Url du web service info.
+            $sUrl = $this->sUrl . '/fmerest/v3/licensing/license/status';
+            // Transfert cURL.
+            $sRequestResponse = $this->curlRequest($sUrl, 'get');
+            if ($this->aLastCurlRequestInfo['http_code'] >= 400) {
+                $this->writeToErrorLog($sRequestResponse);
+                return false;
+            }
+            else
+                return json_decode($sRequestResponse);
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Write a message to the error log file.
+     * @param {string} $sMessage Message to write to the log file.
+     */
+    function writeToErrorLog($sMessage) {
+        $aDebugBacktrace = debug_backtrace();
+        $sLogMessage = '|ERROR|' . $aDebugBacktrace[1]['class'] . '::' . $aDebugBacktrace[1]['function'] . '| ' . $sMessage;
+        if (empty($this->sLogFilePath))
+            writeToErrorLog($sLogMessage);
+        else
+            writeToLog($sLogMessage, $this->sLogFilePath);
+    }
+    
+    /**
+     * Delete a job.
+     * @param {number} $iJobId The id of the job.
+     * @return Request result
+     */
+    function deleteJob($iJobId) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Annule une demande en attente ou arrête une demande en cours.
+            if ($this->stopJob($iJobId) !== false) {
+                // Supprime définitivement la demande sur Fme Server.
+                if ($this->serviceRequest('transformations/jobs/completed/' . $iJobId, 'delete') !== false)
+                    writeToLog(self::INFO_FME_SERVER_JOB_REMOVED . " (job id $iJobId).", $this->sLogFilePath);
+                else
+                    return false;
+            }
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Cancel a queued job.
+     * @param {number} $iJobId The id of the job.
+     * @return Request result
+     */
+    function cancelJob($iJobId) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            // Annule le traitement.
+            if ($this->serviceRequest('transformations/jobs/queued/' . $iJobId, 'delete') !== false)
+                writeToLog(self::INFO_FME_SERVER_JOB_CANCELED . " (job id $iJobId).", $this->sLogFilePath);
+            else
+                return false;
+        }
+        else
+            return false;
+    }
+    
+    /**
+     * Stop a queued or running job.
+     * @param {number} $iJobId The id of the job.
+     * @return Request result
+     */
+    function stopJob($iJobId) {
+        // Demande de token.
+        $this->getToken();
+        if ($this->sToken !== false) {
+            $oJobLog = $this->serviceRequest('transformations/jobs/id/' . $iJobId);
+            if ($oJobLog !== false) {
+                // Annule une demande en attente ou arrête une demande en cours.
+                $aQueuedStatus = array('SUBMITTED', 'QUEUED', 'DELAYED', 'PAUSED');
+                $aRunningStatus = array('IN_PROCESS', 'PULLED');
+                $aStoppedStatus = array('DELETED', 'ABORTED', 'FME_FAILURE', 'JOB_FAILURE', 'SUCCESS');
+                if (in_array($oJobLog->status, $aQueuedStatus))
+                    $this->cancelJob($iJobId);
+                else if (!in_array($oJobLog->status, $aStoppedStatus)){
+                    // Annule le traitement.
+                    if ($this->serviceRequest('transformations/jobs/running/' . $iJobId, 'delete') !== false)
+                        writeToLog(self::INFO_FME_SERVER_JOB_STOPPED . " (job id $iJobId).", $this->sLogFilePath);
+                    else
+                        return false;
+                }
+            }
+            else
+                return false;
+        }
+        else
+            return false;
+    }
+}
+?>
\ No newline at end of file
diff --git a/src/module_gtf/web_service/ws/Gtf.class.sql.inc b/src/module_gtf/web_service/ws/Gtf.class.sql.inc
index 724684fd24dc3c6678a3d13df317140b5edf6c46..fcb43b1bdbe0ef0b48611d17f267a8703b75e6f1 100644
--- a/src/module_gtf/web_service/ws/Gtf.class.sql.inc
+++ b/src/module_gtf/web_service/ws/Gtf.class.sql.inc
@@ -31,7 +31,8 @@ $aSql['getOrdersToDelete'] = 'SELECT fme_engine.fme_engine_type_id, "order".orde
 $aSql['getGtfEngineHomeFromOrderId'] = 'SELECT engines_home, "order".gtf_engine_id, "order".order_id FROM [sSchemaGtf]."server", [sSchemaGtf].gtf_engine, [sSchemaGtf]."order" WHERE gtf_engine.server_id = "server".server_id AND gtf_engine.gtf_engine_id = "order".gtf_engine_id AND order_id = [order_id]';
 $aSql['getGtfEngineHomeFromEngineId'] = 'SELECT engines_home, "order".gtf_engine_id, "order".order_id FROM [sSchemaGtf]."server", [sSchemaGtf].gtf_engine, [sSchemaGtf]."order" WHERE gtf_engine.server_id = "server".server_id AND gtf_engine.gtf_engine_id = "order".gtf_engine_id AND gtf_engine.gtf_engine_id = [iGtfEngineId]';
 $aSql['getFmeServerOrder'] = 'SELECT fme_engine.fme_engine_type_id, fme_engine.server_url, fme_engine.login, fme_engine.password, fme_engine.fme_cloud_api_token, fme_engine.fme_server_instance_name, "order".gtf_engine_id, "order".pid, server.engines_home, "order".log_url FROM [sSchemaGtf].order, [sSchemaGtf].gtf_engine, [sSchemaGtf].fme_engine, [sSchemaGtf].server WHERE gtf_engine.gtf_engine_id = "order".gtf_engine_id AND fme_engine.fme_engine_id = gtf_engine.fme_engine_id AND server.server_id = fme_engine.server_id AND order_id = [order_id] LIMIT 1';
-$aSql['getOrderPid'] = 'SELECT fme_engine.fme_engine_type_id, "order".gtf_engine_id, "order".pid FROM [sSchemaGtf]."order", [sSchemaGtf].gtf_engine, [sSchemaGtf].fme_engine, [sSchemaGtf].server WHERE gtf_engine.gtf_engine_id = "order".gtf_engine_id AND fme_engine.fme_engine_id = gtf_engine.fme_engine_id AND server.server_id = fme_engine.server_id AND order_id = [order_id] LIMIT 1';
+//$aSql['getOrderPid'] = 'SELECT fme_engine.fme_engine_type_id, "order".gtf_engine_id, "order".pid FROM [sSchemaGtf]."order", [sSchemaGtf].gtf_engine, [sSchemaGtf].fme_engine, [sSchemaGtf].server WHERE gtf_engine.gtf_engine_id = "order".gtf_engine_id AND fme_engine.fme_engine_id = gtf_engine.fme_engine_id AND server.server_id = fme_engine.server_id AND order_id = [order_id] LIMIT 1';
+$aSql['getOrderPid'] = 'SELECT pid FROM [sSchemaGtf]."order" WHERE order_id = [order_id] AND pid IS NOT NULL';
 $aSql['getOrderFmeEngineType'] = 'SELECT fme_engine.fme_engine_type_id FROM [sSchemaGtf]."order", [sSchemaGtf].gtf_engine, [sSchemaGtf].fme_engine WHERE gtf_engine.gtf_engine_id = "order".gtf_engine_id AND fme_engine.fme_engine_id = gtf_engine.fme_engine_id AND order_id = [order_id] LIMIT 1';
 // UserSubscriptions
 $aSql['setSubscriptionEngineId'] = 'UPDATE [sSchemaGtf].subscription SET gtf_engine_id=[gtf_engine_id] WHERE subscription_id=[subscription_id]';
diff --git a/src/module_gtf/web_service/ws/Orders.class.inc b/src/module_gtf/web_service/ws/Orders.class.inc
index c3142fb17ae5c8e40a2c483535967333fadd400b..3c8565a7d261b2484f518dd2bfe9a7a331b893ec 100644
--- a/src/module_gtf/web_service/ws/Orders.class.inc
+++ b/src/module_gtf/web_service/ws/Orders.class.inc
@@ -852,38 +852,35 @@ class Orders extends GTF {
         $aParams = array();
         $aParams['sSchemaGtf'] = array('value' => $this->aProperties['schema_gtf'], 'type' => 'schema_name');
         $aParams['order_id'] = array('value' => $iOrderId, 'type' => 'number');
-        $oPDOresult = $this->oConnection->oBd->executeWithParams($aSql['getGtfEngineHomeFromOrderId'], $aParams);
-        if (!$this->oConnection->oBd->enErreur()) {
-            $aGtfEngineHome = $this->oConnection->oBd->ligneSuivante($oPDOresult);
-            $sPidFilePath = $aGtfEngineHome['engines_home'] . '/' . 'pid_' . $aGtfEngineHome['gtf_engine_id'] . '.txt';
-            if (file_exists($sPidFilePath)) {
-                $iPid = file_get_contents($sPidFilePath);
-                if (is_numeric($iPid)) {
-                    if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
-                        // Supprime le processus "engine.exe".
-                        $this->killProcess($iPid);
-                        // Tue tous les processus liés à une demande et un traitement sur Fme Desktop.
-                        $aProcessName = array('cmd.exe', 'php.exe', 'cmd.exe', 'fme.exe');
-                        foreach ($aProcessName as $sProcessName) {
-                            $iPid = $this->getChildProcess($iPid, $sProcessName);
-                            if ($iPid !== false)
-                                $this->killProcess($iPid);
-                        }
-                    }
-                    else {
-                        // Supprime tous les processus enfants.
-                        $aResult = array();
-                        $sResultCommande = exec('pkill -9 -P ' . $iPid, $aResult, $iResult);
-                        // Supprime le processus du moteur.
+        $oPDOresult = $this->oConnection->oBd->executeWithParams($aSql['getOrderPid'], $aParams);
+        if (!$this->oConnection->oBd->enErreur() && $this->oConnection->oBd->nombreLigne($oPDOresult) > 0) {
+            $aPid = $this->oConnection->oBd->ligneSuivante($oPDOresult);
+            $iPid = $aPid['pid'];
+            if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
+                // Supprime le processus "engine.exe".
+                $this->killProcess($iPid);
+                // Tue tous les processus liés à une demande et un traitement sur Fme Desktop.
+                $aProcessName = array('cmd.exe', 'php.exe', 'cmd.exe', 'fme.exe');
+                foreach ($aProcessName as $sProcessName) {
+                    $iPid = $this->getChildProcess($iPid, $sProcessName);
+                    if ($iPid !== false)
                         $this->killProcess($iPid);
-                    }
                 }
-                // Suppression du fichier contenant le pid du processus "engine.exe".
-                unlink($sPidFilePath);
+            }
+            else {
+                // Récupère l'id de session du processus du moteur GTF.
+                $aResult = array();
+                $sResultCommande = exec('ps -p ' . $iPid . ' -o sess', $aResult, $iResult);
+                // Supprime tous les processus enfants.
+                if ($iResult == 0 && !empty($aResult) && is_numeric($aResult[1])) {
+                    $iSess = $aResult[1];
+                    $aResult = array();
+                    $sResultCommande = exec('pkill -9 -s ' . $iSess, $aResult, $iResult);
+                }
             }
         }
     }    
-    
+
     /**
      * function killProcess
      * \param $iPid Id of the process.