From 35ce09a31c434425af75a55e85367ace9e3f7523 Mon Sep 17 00:00:00 2001
From: Damien <damien.burel@maarch.org>
Date: Fri, 27 Apr 2018 17:36:54 +0200
Subject: [PATCH] FEAT #7621 GetImageSignature by rest

---
 .../Views/profile.component.html              |  2 +-
 .../Views/signature-book.component.html       |  2 +-
 .../Views/user-administration.component.html  |  2 +-
 rest/index.php                                |  1 +
 .../controllers/NotificationController.php    |  2 +-
 .../controllers/SignatureBookController.php   |  5 +-
 src/app/user/controllers/UserController.php   | 44 ++++++++++--
 src/app/user/models/UserModelAbstract.php     | 58 ----------------
 src/app/user/models/UserSignatureModel.php    | 19 ++++++
 .../models/UserSignatureModelAbstract.php     | 67 +++++++++++++++++++
 src/core/lang/lang-en.php                     |  5 +-
 src/core/lang/lang-fr.php                     |  5 +-
 12 files changed, 139 insertions(+), 73 deletions(-)
 create mode 100644 src/app/user/models/UserSignatureModel.php
 create mode 100644 src/app/user/models/UserSignatureModelAbstract.php

diff --git a/apps/maarch_entreprise/Views/profile.component.html b/apps/maarch_entreprise/Views/profile.component.html
index 920dfadd967..d797d6c489a 100755
--- a/apps/maarch_entreprise/Views/profile.component.html
+++ b/apps/maarch_entreprise/Views/profile.component.html
@@ -360,7 +360,7 @@
                             <div class="row" id="signList">
                                 <div *ngFor="let signature of user.signatures; let i = index" class="col-md-3">
                                     <div class="thumbnail">
-                                        <img src="{{signature.pathToSignatureOnTmp}}" alt="Signature" style="width:auto;height:60px;">
+                                        <img src="{{coreUrl}}rest/users/{{user.id}}/signatures/{{signature.id}}" alt="Signature" style="width:auto;height:60px;">
                                         <div class="caption" style="margin-bottom:0px;">
                                             <div *ngIf="selectedSignature != i">
                                                 <p title="{{signature.signature_label}}" style="text-overflow: ellipsis;">{{signature.signature_label}}</p>
diff --git a/apps/maarch_entreprise/Views/signature-book.component.html b/apps/maarch_entreprise/Views/signature-book.component.html
index de5925003a5..c72098ee68a 100755
--- a/apps/maarch_entreprise/Views/signature-book.component.html
+++ b/apps/maarch_entreprise/Views/signature-book.component.html
@@ -189,7 +189,7 @@
                          (mouseenter)="showSignaturesPanel = true" (mouseleave)="showSignaturesPanel = false" class="pjSign" [ngClass]="[signatureBook.attachments[rightSelectedThumbnail].status == 'SIGN' ? 'signed' : '']" [ngStyle]="{'box-shadow': signatureBook.listinstance.requested_signature ? 'inset 0px 0px 5px 0px red' : 'inset 0px 0px 5px 0px #656565;'}">
                         <span *ngIf="signatureBook.attachments[rightSelectedThumbnail].status != 'SIGN' && signatureBook.signatures[0]" style="cursor: pointer">
                             <span *ngIf="!loadingSign">
-                                <img *ngFor="let signature of signatureBook.signatures; let i = index" src="{{signature.pathToSignatureOnTmp}}" (click)="signFile(signatureBook.attachments[rightSelectedThumbnail], signature)" [ngStyle]="{'display': !showSignaturesPanel && i > 0 ? 'none' : ''}" title="{{signature.signature_label}}">
+                                <img *ngFor="let signature of signatureBook.signatures; let i = index" src="{{coreUrl}}rest/users/{{signature.user_serial_id}}/signatures/{{signature.id}}" (click)="signFile(signatureBook.attachments[rightSelectedThumbnail], signature)" [ngStyle]="{'display': !showSignaturesPanel && i > 0 ? 'none' : ''}" title="{{signature.signature_label}}">
                             </span>
                             <i *ngIf="loadingSign" class="fa fa-spinner fa-pulse fa-2x fa-fw"></i>
                         </span>
diff --git a/apps/maarch_entreprise/Views/user-administration.component.html b/apps/maarch_entreprise/Views/user-administration.component.html
index 56be1ceda8c..8c198cfcbe1 100755
--- a/apps/maarch_entreprise/Views/user-administration.component.html
+++ b/apps/maarch_entreprise/Views/user-administration.component.html
@@ -272,7 +272,7 @@
                                             <mat-icon class="fa fa-times"></mat-icon>
                                         </button>
                                     </mat-form-field>
-                                    <img src="{{signature.pathToSignatureOnTmp}}" alt="Signature" style="width:auto;height:60px;">
+                                    <img src="{{coreUrl}}rest/users/{{user.id}}/signatures/{{signature.id}}" alt="Signature" style="width:auto;height:60px;">
                                 </mat-card-content>
                             </mat-card>
                         </div>
diff --git a/rest/index.php b/rest/index.php
index 624362c8aa9..dbc13ee91d9 100755
--- a/rest/index.php
+++ b/rest/index.php
@@ -277,6 +277,7 @@ $app->put('/users/{id}/entities/{entityId}/primaryEntity', \User\controllers\Use
 $app->get('/users/{id}/entities/{entityId}', \User\controllers\UserController::class . ':isEntityDeletable');
 $app->delete('/users/{id}/entities/{entityId}', \User\controllers\UserController::class . ':deleteEntity');
 $app->post('/users/{id}/signatures', \User\controllers\UserController::class . ':addSignature');
+$app->get('/users/{id}/signatures/{signatureId}', \User\controllers\UserController::class . ':getImageSignature');
 $app->put('/users/{id}/signatures/{signatureId}', \User\controllers\UserController::class . ':updateSignature');
 $app->delete('/users/{id}/signatures/{signatureId}', \User\controllers\UserController::class . ':deleteSignature');
 $app->post('/users/{id}/redirectedBaskets', \User\controllers\UserController::class . ':setRedirectedBaskets');
diff --git a/src/app/notification/controllers/NotificationController.php b/src/app/notification/controllers/NotificationController.php
index 758a17fcb6b..756b526a790 100644
--- a/src/app/notification/controllers/NotificationController.php
+++ b/src/app/notification/controllers/NotificationController.php
@@ -277,7 +277,7 @@ class NotificationController
         if (!Validator::length(1, 254)->notEmpty()->validate($aArgs['description'])) {
             $errors[] = 'wrong format for description';
         }
-        if (!Validator::length(0, 254)->validate($dataArgsa['event_id'])) {
+        if (!Validator::length(0, 254)->validate($aArgs['event_id'])) {
             $errors[] = 'event_id is too long';
         }
         if (!Validator::length(0, 30)->validate($aArgs['notification_mode'])) {
diff --git a/src/app/signatureBook/controllers/SignatureBookController.php b/src/app/signatureBook/controllers/SignatureBookController.php
index 8d10b4907a1..8fdb01c14f2 100644
--- a/src/app/signatureBook/controllers/SignatureBookController.php
+++ b/src/app/signatureBook/controllers/SignatureBookController.php
@@ -31,6 +31,7 @@ use Slim\Http\Response;
 use SrcCore\controllers\PreparedClauseController;
 use SrcCore\models\ValidatorModel;
 use User\models\UserModel;
+use User\models\UserSignatureModel;
 
 
 class SignatureBookController
@@ -92,7 +93,7 @@ class SignatureBookController
         $datas['resList']       = [];
         $datas['nbNotes']       = NoteModel::countByResId(['resId' => $resId, 'userId' => $GLOBALS['userId']]);
         $datas['nbLinks']       = count(LinkModel::getByResId(['resId' => $resId]));
-        $datas['signatures']    = UserModel::getSignaturesById(['id' => $user['id']]);
+        $datas['signatures']    = UserSignatureModel::getByUserSerialId(['userSerialid' => $user['id']]);
         $datas['consigne']      = UserModel::getCurrentConsigneById(['resId' => $resId]);
         $datas['hasWorkflow']   = ((int)$listInstances[0]['count'] > 0);
         $datas['listinstance']  = ListInstanceModel::getCurrentStepByResId(['resId' => $resId]);
@@ -137,7 +138,7 @@ class SignatureBookController
         return $response->withJson(SignatureBookController::getAttachmentsForSignatureBook(['resId' => $aArgs['resId'], 'userId' => $GLOBALS['userId']]));
     }
 
-    private static function getIncomingMailAndAttachmentsForSignatureBook(array $aArgs = [])
+    private static function getIncomingMailAndAttachmentsForSignatureBook(array $aArgs)
     {
         $resId = $aArgs['resId'];
 
diff --git a/src/app/user/controllers/UserController.php b/src/app/user/controllers/UserController.php
index 9d993de9aa8..21745729f7c 100644
--- a/src/app/user/controllers/UserController.php
+++ b/src/app/user/controllers/UserController.php
@@ -17,6 +17,7 @@ namespace User\controllers;
 use Basket\models\BasketModel;
 use Basket\models\GroupBasketModel;
 use Docserver\controllers\DocserverController;
+use Docserver\models\DocserverModel;
 use Entity\models\ListInstanceModel;
 use Group\models\ServiceModel;
 use Entity\models\EntityModel;
@@ -35,6 +36,7 @@ use SrcCore\models\SecurityModel;
 use User\models\UserBasketPreferenceModel;
 use User\models\UserEntityModel;
 use User\models\UserModel;
+use User\models\UserSignatureModel;
 
 class UserController
 {
@@ -99,7 +101,7 @@ class UserController
         }
 
         $user = UserModel::getById(['id' => $aArgs['id'], 'select' => ['id', 'user_id', 'firstname', 'lastname', 'status', 'enabled', 'phone', 'mail', 'initials', 'thumbprint', 'loginmode']]);
-        $user['signatures'] = UserModel::getSignaturesById(['id' => $aArgs['id']]);
+        $user['signatures'] = UserSignatureModel::getByUserSerialId(['userSerialid' => $aArgs['id']]);
         $user['emailSignatures'] = UserModel::getEmailSignaturesById(['userId' => $user['user_id']]);
         $user['groups'] = UserModel::getGroupsByUserId(['userId' => $user['user_id']]);
         $user['allGroups'] = GroupModel::getAvailableGroupsByUserId(['userId' => $user['user_id']]);
@@ -234,7 +236,7 @@ class UserController
     public function getProfile(Request $request, Response $response)
     {
         $user = UserModel::getByUserId(['userId' => $GLOBALS['userId'], 'select' => ['id', 'user_id', 'firstname', 'lastname', 'phone', 'mail', 'initials', 'thumbprint']]);
-        $user['signatures'] = UserModel::getSignaturesById(['id' => $user['id']]);
+        $user['signatures'] = UserSignatureModel::getByUserSerialId(['userSerialid' => $user['id']]);
         $user['emailSignatures'] = UserModel::getEmailSignaturesById(['userId' => $user['user_id']]);
         $user['groups'] = UserModel::getGroupsByUserId(['userId' => $user['user_id']]);
         $user['entities'] = UserModel::getEntitiesById(['userId' => $user['user_id']]);
@@ -454,6 +456,38 @@ class UserController
         return $response->withJson(['user' => UserModel::getById(['id' => $aArgs['id'], 'select' => ['status']])]);
     }
 
+    public function getImageSignature(Request $request, Response $response, array $aArgs)
+    {
+        $error = $this->hasUsersRights(['id' => $aArgs['id'], 'himself' => true]);
+        if (!empty($error['error'])) {
+            return $response->withStatus($error['status'])->withJson(['errors' => $error['error']]);
+        }
+
+        $signatures = UserSignatureModel::get([
+            'select'    => ['signature_path', 'signature_file_name'],
+            'where'     => ['user_serial_id = ?', 'id = ?'],
+            'data'      => [$aArgs['id'], $aArgs['signatureId']]
+        ]);
+        if (empty($signatures[0])) {
+            return $response->withStatus(400)->withJson(['errors' => 'Signature does not exist']);
+        }
+
+        $docserver = DocserverModel::getByTypeId(['docserver_type_id' => 'TEMPLATES', 'select' => ['path_template']]);
+        if (!file_exists($docserver['path_template'])) {
+            return [];
+        }
+
+        $pathToSignature = $docserver['path_template'] . str_replace('#', '/', $signatures[0]['signature_path']) . $signatures[0]['signature_file_name'];
+        $image = file_get_contents($pathToSignature);
+        if ($image === false) {
+            return $response->withStatus(404)->withJson(['errors' => 'Signature not found on docserver']);
+        }
+
+        $response->write($image);
+
+        return $response->withHeader('Content-Type', FILEINFO_MIME_TYPE);
+    }
+
     public function addSignature(Request $request, Response $response, array $aArgs)
     {
         $error = $this->hasUsersRights(['id' => $aArgs['id'], 'himself' => true]);
@@ -519,7 +553,7 @@ class UserController
         ]);
 
         return $response->withJson([
-            'signatures' => UserModel::getSignaturesById(['id' => $aArgs['id']])
+            'signatures' => UserSignatureModel::getByUserSerialId(['userSerialid' => $aArgs['id']])
         ]);
     }
 
@@ -543,7 +577,7 @@ class UserController
         ]);
 
         return $response->withJson([
-            'signature' => UserModel::getSignatureWithSignatureIdById(['id' => $aArgs['id'], 'signatureId' => $aArgs['signatureId']])
+            'signature' => UserSignatureModel::getById(['id' => $aArgs['signatureId']])
         ]);
     }
 
@@ -557,7 +591,7 @@ class UserController
         UserModel::deleteSignature(['signatureId' => $aArgs['signatureId'], 'userSerialId' => $aArgs['id']]);
 
         return $response->withJson([
-            'signatures' => UserModel::getSignaturesById(['id' => $aArgs['id']])
+            'signatures' => UserSignatureModel::getByUserSerialId(['userSerialid' => $aArgs['id']])
         ]);
     }
 
diff --git a/src/app/user/models/UserModelAbstract.php b/src/app/user/models/UserModelAbstract.php
index 423315d77c6..ca757d73a38 100644
--- a/src/app/user/models/UserModelAbstract.php
+++ b/src/app/user/models/UserModelAbstract.php
@@ -300,64 +300,6 @@ class UserModelAbstract
         return true;
     }
 
-    public static function getSignaturesById(array $aArgs = [])
-    {
-        ValidatorModel::notEmpty($aArgs, ['id']);
-        ValidatorModel::intVal($aArgs, ['id']);
-
-        $aReturn = DatabaseModel::select([
-            'select'    => ['id', 'user_serial_id', 'signature_label', 'signature_path', 'signature_file_name'],
-            'table'     => ['user_signatures'],
-            'where'     => ['user_serial_id = ?'],
-            'data'      => [$aArgs['id']],
-            'order_by'  => ['id']
-        ]);
-
-        $docserver = [];
-        if (!empty($aReturn)) {
-            $docserver = DocserverModel::getByTypeId(['docserver_type_id' => 'TEMPLATES', 'select' => ['path_template']]);
-        }
-
-        if (!file_exists($docserver['path_template'])) {
-            return [];
-        }
-        $tmpPath = CoreConfigModel::getTmpPath();
-        $urlTmpPath = str_replace('rest/', '', \Url::coreurl()) . 'apps/maarch_entreprise/tmp/';
-        foreach($aReturn as $key => $value) {
-            $pathToSignature = $docserver['path_template'] . str_replace('#', '/', $value['signature_path']) . $value['signature_file_name'];
-
-            $extension = explode('.', $pathToSignature);
-            $extension = $extension[count($extension) - 1];
-            $fileNameOnTmp = 'tmp_file_' . $aArgs['id'] . '_' . rand() . '.' . strtolower($extension);
-            $filePathOnTmp = $tmpPath . $fileNameOnTmp;
-            if (file_exists($pathToSignature) && copy($pathToSignature, $filePathOnTmp)) {
-                $aReturn[$key]['pathToSignatureOnTmp'] = $urlTmpPath . $fileNameOnTmp;
-            } else {
-                $aReturn[$key]['pathToSignatureOnTmp'] = '';
-            }
-            $aReturn[$key]['pathToSignature'] = $pathToSignature;
-
-            unset($aReturn[$key]['signature_path'], $aReturn[$key]['signature_file_name']);
-        }
-
-        return $aReturn;
-    }
-
-    public static function getSignatureWithSignatureIdById(array $aArgs = [])
-    {
-        ValidatorModel::notEmpty($aArgs, ['id', 'signatureId']);
-        ValidatorModel::intVal($aArgs, ['id','signatureId']);
-
-        $aReturn = DatabaseModel::select([
-            'select'    => ['id', 'user_serial_id', 'signature_label'],
-            'table'     => ['user_signatures'],
-            'where'     => ['user_serial_id = ?', 'id = ?'],
-            'data'      => [$aArgs['id'], $aArgs['signatureId']],
-        ]);
-
-        return $aReturn[0];
-    }
-
     public static function getEmailSignaturesById(array $aArgs = [])
     {
         ValidatorModel::notEmpty($aArgs, ['userId']);
diff --git a/src/app/user/models/UserSignatureModel.php b/src/app/user/models/UserSignatureModel.php
new file mode 100644
index 00000000000..9a0dd36ddfb
--- /dev/null
+++ b/src/app/user/models/UserSignatureModel.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+* Copyright Maarch since 2008 under licence GPLv3.
+* See LICENCE.txt file at the root folder for more details.
+* This file is part of Maarch software.
+*
+*/
+
+/**
+* @brief User Signature Model
+* @author dev@maarch.org
+*/
+
+namespace User\models;
+
+class UserSignatureModel extends UserSignatureModelAbstract
+{
+}
diff --git a/src/app/user/models/UserSignatureModelAbstract.php b/src/app/user/models/UserSignatureModelAbstract.php
new file mode 100644
index 00000000000..52afd11013e
--- /dev/null
+++ b/src/app/user/models/UserSignatureModelAbstract.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * Copyright Maarch since 2008 under licence GPLv3.
+ * See LICENCE.txt file at the root folder for more details.
+ * This file is part of Maarch software.
+ *
+ */
+
+/**
+ * @brief User Signature Model Abstract
+ * @author dev@maarch.org
+ */
+
+namespace User\models;
+
+use SrcCore\models\DatabaseModel;
+use SrcCore\models\ValidatorModel;
+
+class UserSignatureModelAbstract
+{
+    public static function get(array $aArgs)
+    {
+        ValidatorModel::notEmpty($aArgs, ['select', 'where', 'data']);
+        ValidatorModel::arrayType($aArgs, ['select', 'where', 'data']);
+
+        $signatures = DatabaseModel::select([
+            'select'    => empty($aArgs['select']) ? ['*'] : $aArgs['select'],
+            'table'     => ['user_signatures'],
+            'where'     => $aArgs['where'],
+            'data'      => $aArgs['data']
+        ]);
+
+        return $signatures;
+    }
+
+    public static function getById(array $aArgs)
+    {
+        ValidatorModel::notEmpty($aArgs, ['id']);
+        ValidatorModel::intVal($aArgs, ['id']);
+
+        $signature = DatabaseModel::select([
+            'select'    => ['id', 'user_serial_id', 'signature_label'],
+            'table'     => ['user_signatures'],
+            'where'     => ['id = ?'],
+            'data'      => [$aArgs['id']],
+        ]);
+
+        return $signature[0];
+    }
+
+    public static function getByUserSerialId(array $aArgs)
+    {
+        ValidatorModel::notEmpty($aArgs, ['userSerialid']);
+        ValidatorModel::intVal($aArgs, ['userSerialid']);
+
+        $signatures = DatabaseModel::select([
+            'select'    => ['id', 'user_serial_id', 'signature_label'],
+            'table'     => ['user_signatures'],
+            'where'     => ['user_serial_id = ?'],
+            'data'      => [$aArgs['userSerialid']],
+            'order_by'  => ['id']
+        ]);
+
+        return $signatures;
+    }
+}
diff --git a/src/core/lang/lang-en.php b/src/core/lang/lang-en.php
index 2553deed59e..7b7d5473266 100644
--- a/src/core/lang/lang-en.php
+++ b/src/core/lang/lang-en.php
@@ -42,6 +42,7 @@ define('_DOCUMENT_NOT_FOUND', 'Document not found');
 define('_ENTITY_CREATION', 'Entity creation');
 define('_ENTITY_MODIFICATION', 'Entity modification');
 define('_ENTITY_SUPPRESSION', 'Entity suppression');
+define('_ID_TO_DISPLAY', 'res_id');
 define('_INVALID_CLAUSE', 'Clause is not valid');
 define('_INVALID_REQUEST', 'Request is not valid');
 define('_LIST_TEMPLATE_CREATION', 'List model creation');
@@ -58,6 +59,7 @@ define('_PRIORITY_CREATION', 'Priority creation');
 define('_PRIORITY_MODIFICATION', 'Priority modification');
 define('_PRIORITY_SUPPRESSION', 'Priority suppression');
 define('_PRIORITY_SORT_MODIFICATION', 'Priorities order modification');
+define('_QUOTA_EXCEEDED', 'Quota exceeded');
 define('_REPORT_MODIFICATION', 'Report modification');
 define('_STATUS_ADDED', 'Statut added');
 define('_STATUS_DELETED', 'Statut deleted');
@@ -80,6 +82,7 @@ define('_USER_ALREADY_LINK_ENTITY', 'User is already linked to this entity');
 define('_VISA_USER', 'For visa');
 define('_WRONG_FILE_TYPE', 'This type of file is not allowed');
 define('_CAN_NOT_MOVE_IN_CHILD_ENTITY', 'Parent entity must not be a subentity');
+define('_UNREACHABLE_DOCSERVER', 'Unreachable docserver path');
 
 define('_DOCUMENTS_LIST_WITH_ATTACHMENTS', 'List with filters and responses');
 define('_DOCUMENTS_LIST_WITH_AVIS', 'List of documents with recommendation');
@@ -90,5 +93,3 @@ define('_FOLDERS_LIST', 'folders list');
 define('_DOCTYPE_UPDATED', 'Document type updated');
 define('_DOCTYPE_ADDED', 'Document type added');
 define('_DOCTYPE_DELETED', 'Document type deleted');
-
-define('_QUOTA_EXCEEDED', 'Quota exceeded');
diff --git a/src/core/lang/lang-fr.php b/src/core/lang/lang-fr.php
index 1d40889e155..855a0f14e11 100644
--- a/src/core/lang/lang-fr.php
+++ b/src/core/lang/lang-fr.php
@@ -42,6 +42,7 @@ define('_DOCUMENT_NOT_FOUND', 'Document introuvable');
 define('_ENTITY_CREATION', 'Création entité');
 define('_ENTITY_MODIFICATION', 'Modification entité');
 define('_ENTITY_SUPPRESSION', 'Suppression entité');
+define('_ID_TO_DISPLAY', 'res_id');
 define('_INVALID_CLAUSE', 'Clause non valide');
 define('_INVALID_REQUEST', 'Requête non valide');
 define('_LIST_TEMPLATE_CREATION', 'Création liste de diffusion');
@@ -58,6 +59,7 @@ define('_PRIORITY_CREATION', 'Création priorité');
 define('_PRIORITY_MODIFICATION', 'Modification priorité');
 define('_PRIORITY_SUPPRESSION', 'Suppression priorité');
 define('_PRIORITY_SORT_MODIFICATION', 'Modification ordre priorités');
+define('_QUOTA_EXCEEDED', 'Quota dépassé');
 define('_REPORT_MODIFICATION', 'Modification états et édition');
 define('_STATUS_ADDED', 'Statut ajouté');
 define('_STATUS_DELETED', 'Statut supprimé');
@@ -80,6 +82,7 @@ define('_USER_ALREADY_LINK_ENTITY', 'L\'utilisateur est déjà lié à cette ent
 define('_VISA_USER', 'Pour visa');
 define('_WRONG_FILE_TYPE', 'Ce type de fichier n\'est pas permis');
 define('_CAN_NOT_MOVE_IN_CHILD_ENTITY', 'L\'entité parente ne doit pas être dans une entité fille');
+define('_UNREACHABLE_DOCSERVER', 'Chemin docserver inatteignable');
 
 define('_DOCUMENTS_LIST_WITH_ATTACHMENTS', 'Liste avec filtres et réponses');
 define('_DOCUMENTS_LIST_WITH_AVIS', 'Liste des documents avec avis');
@@ -90,5 +93,3 @@ define('_FOLDERS_LIST', 'Liste de dossiers');
 define('_DOCTYPE_UPDATED', 'Type de document modifié');
 define('_DOCTYPE_ADDED', 'Type de document ajouté');
 define('_DOCTYPE_DELETED', 'Type de document supprimé');
-
-define('_QUOTA_EXCEEDED', 'Quota dépassé');
-- 
GitLab