From 0edf36e5b579a697e0b18f36c450b9f60286b77c Mon Sep 17 00:00:00 2001
From: Damien <damien.burel@maarch.org>
Date: Thu, 16 Jan 2020 18:11:03 +0100
Subject: [PATCH] FEAT #11788 TIME 2:50 Substituted signatures are back

---
 lang/en.json                                  |  5 ++-
 lang/fr.json                                  |  4 +-
 rest/index.php                                |  1 +
 sql/20XX.sql                                  |  3 ++
 sql/structure.sql                             |  1 +
 .../controllers/DocumentController.php        | 15 ++++---
 .../user/controllers/SignatureController.php  | 45 +++++++++++++++++--
 .../app/document/document.component.html      | 14 +-----
 .../app/document/document.component.ts        | 14 +++++-
 .../app/profile/profile.component.html        |  8 +++-
 src/frontend/app/profile/profile.component.ts |  8 ++++
 .../app/service/signatures.service.ts         |  2 +
 .../app/signatures/signatures.component.html  | 12 ++++-
 .../app/signatures/signatures.component.ts    |  3 +-
 14 files changed, 107 insertions(+), 28 deletions(-)

diff --git a/lang/en.json b/lang/en.json
index 821c9616c8..abf3e395b0 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -1,6 +1,7 @@
 {
 	"lang": {
 		"actionDone"                         : "Action done",
+		"actionDoneInPlaceOf"               	: "Action done in place of",
 		"administrations"                    : "Administrations",
 		"afterClickingSendLinkChangePassword": "After clicking on <b>Send</b>, you will receive an email containing a link to change your password. This link will be active <b>1 hour</b>.",
 		"annotateDocument"                   : "Annotate this document",
@@ -158,6 +159,7 @@
 		"search": "Search",
 		"substitute": "Substitute user",
 		"substitution": "Substitution",
+		"signSubstituted": "Signatures to substitute",
 		"chooseSubstitute": "Choose a substitute",
 		"noResult": "No result",
 		"circuit": "Circuit",
@@ -167,8 +169,9 @@
 		"attachment": "attachment",
 		"deleteSubstitution": "Delete substitution",
 		"substitutionDeleted": "Substitution deleted",
-		"substitutionInfo": "You are in substitution mode, you can't make any action",
+		"substitutionInfo": "You are in substitution mode",
 		"substitutedDoc": "Substituted document",
+		"substitutedSignature": "Substituted signature",
 		"substituteMsg": "You act as",
 		"substitutionWarn": "You choose a substitution, you will not be able to make any action.",
 		"manage_users": "Users",
diff --git a/lang/fr.json b/lang/fr.json
index 6e8b5d6c65..722b82e00e 100755
--- a/lang/fr.json
+++ b/lang/fr.json
@@ -1,6 +1,7 @@
 {
 	"lang": {
 		"actionDone"                         : "Action effectuée",
+		"actionDoneInPlaceOf"               	: "Action effectuée à la place de",
 		"administrations"                    : "Administrations",
 		"afterClickingSendLinkChangePassword": "Après avoir cliqué sur <b>Envoyer</b>, vous recevrez un courriel contenant un lien pour modifier votre mot de passe. Ce lien sera actif <b>1 heure</b>.",
 		"annotateDocument"                   : "Annoter ce document",
@@ -167,8 +168,9 @@
 		"attachment": "annexe",
 		"deleteSubstitution": "Supprimer la délégation",
 		"substitutionDeleted": "Délégation supprimée",
-		"substitutionInfo": "Vous avez délégué vos documents, vous ne pouvez pas faire d'action",
+		"substitutionInfo": "Vous avez délégué vos documents",
 		"substitutedDoc": "Document délégué",
+		"substitutedSignature": "Signature déléguée",
 		"substituteMsg": "Vous agissez en tant que",
 		"substitutionWarn": "Vous avez choisi une délégation, vous ne pourrez plus faire d'action.",
 		"manage_users": "Utilisateurs",
diff --git a/rest/index.php b/rest/index.php
index 32d1c85c3c..cd26cf3a80 100755
--- a/rest/index.php
+++ b/rest/index.php
@@ -113,6 +113,7 @@ $app->get('/users/{id}/signatures', \User\controllers\SignatureController::class
 $app->post('/users/{id}/signatures', \User\controllers\SignatureController::class . ':create');
 $app->delete('/users/{id}/signatures/{signatureId}', \User\controllers\SignatureController::class . ':delete');
 $app->put('/users/{id}/externalSignatures', \User\controllers\SignatureController::class . ':updateExternalSignatures');
+$app->patch('/users/{id}/signatures/{signatureId}/substituted', \User\controllers\SignatureController::class . ':updateSubstituted');
 
 //Emails
 $app->post('/emails', \Email\controllers\EmailController::class . ':send');
diff --git a/sql/20XX.sql b/sql/20XX.sql
index da835ac059..963df04f35 100755
--- a/sql/20XX.sql
+++ b/sql/20XX.sql
@@ -11,3 +11,6 @@ ALTER TABLE main_documents ADD COLUMN notes jsonb;
 
 ALTER TABLE main_documents DROP COLUMN IF EXISTS link_id;
 ALTER TABLE main_documents ADD COLUMN link_id text;
+
+ALTER TABLE signatures DROP COLUMN IF EXISTS substituted;
+ALTER TABLE signatures ADD COLUMN substituted boolean DEFAULT FALSE NOT NULL;
diff --git a/sql/structure.sql b/sql/structure.sql
index a8dc4a9c5c..0bffda92e6 100755
--- a/sql/structure.sql
+++ b/sql/structure.sql
@@ -183,6 +183,7 @@ CREATE TABLE signatures
   path character varying(255) NOT NULL,
   filename character varying(255) NOT NULL,
   fingerprint character varying(255) NOT NULL,
+  substituted boolean DEFAULT FALSE NOT NULL,
   external_application CHARACTER VARYING(255),
   CONSTRAINT signatures_pkey PRIMARY KEY (id)
 )
diff --git a/src/app/document/controllers/DocumentController.php b/src/app/document/controllers/DocumentController.php
index bdcd9b0d80..c647d7c1f3 100755
--- a/src/app/document/controllers/DocumentController.php
+++ b/src/app/document/controllers/DocumentController.php
@@ -398,16 +398,11 @@ class DocumentController
             return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
         }
 
-        $currentUser = UserModel::getById(['id' => $GLOBALS['id'], 'select' => ['substitute']]);
-        if (!empty($currentUser['substitute'])) {
-            return $response->withStatus(403)->withJson(['errors' => 'User can not make action with substituted account']);
-        }
-
         if (empty(DocumentController::ACTIONS[$args['actionId']])) {
             return $response->withStatus(400)->withJson(['errors' => 'Action does not exist']);
         }
 
-        $workflow = WorkflowModel::getCurrentStep(['select' => ['id', 'mode'], 'documentId' => $args['id']]);
+        $workflow = WorkflowModel::getCurrentStep(['select' => ['id', 'mode', 'user_id'], 'documentId' => $args['id']]);
 
         $body = $request->getParsedBody();
         if (!empty($body['signatures'])) {
@@ -580,12 +575,18 @@ class DocumentController
 
         EmailController::sendNotificationToNextUserInWorkflow(['documentId' => $args['id'], 'userId' => $GLOBALS['id']]);
 
+        $historyMessagePart = "{actionDone} : ";
+        if ($workflow['user_id'] != $GLOBALS['id']) {
+            $user = UserModel::getLabelledUserById(['id' => $workflow['user_id']]);
+            $historyMessagePart = "{actionDoneInPlaceOf} {$user} : ";
+        }
+
         HistoryController::add([
             'code'          => 'OK',
             'objectType'    => 'main_documents',
             'objectId'      => $args['id'],
             'type'          => 'ACTION',
-            'message'       => "{actionDone} : " . DocumentController::ACTIONS[$args['actionId']],
+            'message'       => $historyMessagePart . DocumentController::ACTIONS[$args['actionId']],
             'data'          => ['actionId' => $args['actionId']]
         ]);
 
diff --git a/src/app/user/controllers/SignatureController.php b/src/app/user/controllers/SignatureController.php
index 0fdc9198d5..87f1c8d1aa 100755
--- a/src/app/user/controllers/SignatureController.php
+++ b/src/app/user/controllers/SignatureController.php
@@ -28,18 +28,30 @@ class SignatureController
 {
     public function get(Request $request, Response $response, array $args)
     {
+        if ($GLOBALS['id'] != $args['id']) {
+            $user = UserModel::getById(['id' => $args['id'], 'select' => ['substitute']]);
+            if (empty($user) || $user['substitute'] != $GLOBALS['id']) {
+                return $response->withStatus(403)->withJson(['errors' => 'Privilege forbidden']);
+            }
+        }
+
         $docserver = DocserverModel::getByType(['type' => 'SIGNATURE', 'select' => ['path']]);
         if (empty($docserver['path']) || !is_dir($docserver['path'])) {
             return $response->withStatus(400)->withJson(['errors' => 'Docserver \'SIGNATURE\' does not exist']);
         }
 
+        $where = ['user_id = ?'];
+        if ($GLOBALS['id'] != $args['id']) {
+            $where[] = 'substituted = true';
+        }
         $rawSignatures = SignatureModel::get([
-            'select'    => ['id', 'path', 'filename', 'fingerprint'],
-            'where'     => ['user_id = ?'],
-            'data'      => [$GLOBALS['id']],
+            'select'    => ['id', 'path', 'filename', 'fingerprint', 'substituted'],
+            'where'     => $where,
+            'data'      => [$args['id']],
             'orderBy'   => ['id DESC']
         ]);
 
+
         $signatures = [];
         foreach ($rawSignatures as $signature) {
             $pathToSignature = $docserver['path'] . $signature['path'] . $signature['filename'];
@@ -48,6 +60,7 @@ class SignatureController
                 if ($signature['fingerprint'] == $fingerprint) {
                     $signatures[] = [
                         'id'                => $signature['id'],
+                        'substituted'       => $signature['substituted'],
                         'encodedSignature'  => base64_encode(file_get_contents($pathToSignature))
                     ];
                 }
@@ -191,4 +204,30 @@ class SignatureController
 
         return $response->withStatus(204);
     }
+
+    public function updateSubstituted(Request $request, Response $response, array $args)
+    {
+        if ($GLOBALS['id'] != $args['id'] && !PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_users'])) {
+            return $response->withStatus(403)->withJson(['errors' => 'Privilege forbidden']);
+        }
+
+        $user = UserModel::getById(['select' => [1], 'id' => $args['id']]);
+        if (empty($user)) {
+            return $response->withStatus(400)->withJson(['errors' => 'User does not exist']);
+        }
+
+        $body = $request->getParsedBody();
+
+        if (!Validator::boolType()->validate($body['substituted'])) {
+            return $response->withStatus(400)->withJson(['errors' => "Body substituted is not set or not a boolean"]);
+        }
+
+        SignatureModel::update([
+            'set'   => ['substituted' => $body['substituted'] ? 'true' : 'false'],
+            'where' => ['user_id = ?', 'id = ?'],
+            'data'  => [$args['id'], $args['signatureId']]
+        ]);
+
+        return $response->withStatus(204);
+    }
 }
diff --git a/src/frontend/app/document/document.component.html b/src/frontend/app/document/document.component.html
index 92accb587e..1dd55e6c59 100755
--- a/src/frontend/app/document/document.component.html
+++ b/src/frontend/app/document/document.component.html
@@ -76,23 +76,13 @@
       <div class="page-info-page">{{'lang.page' | translate}} {{ pageNum }} / {{ totalPages }}</div>
     </section>
     <footer class="footer"
-      *ngIf="!this.signaturesService.annotationMode && !freezeSidenavClose && currentDoc === 0 && authService.user.substitute === null"
+      *ngIf="!this.signaturesService.annotationMode && !freezeSidenavClose && currentDoc === 0"
       [@slideUp]>
       <ng-container *ngFor="let action of actionsList;">
         <button [style.color]="action.color" [style.borderColor]="action.color" class="btn"
           (click)="launchEvent(action)"><i class="{{action.logo}} fa-2x"></i>{{action.label | translate}}</button>
       </ng-container>
     </footer>
-    <footer class="footer substutionModal" *ngIf="!freezeSidenavClose && currentDoc === 0 && authService.user.substitute !== null" [@slideUp]>
-      <div class="msgModal">
-        {{'lang.substitutionInfo' | translate}}
-      </div>
-      <div>
-        <button mat-icon-button title="{{'lang.deleteSubstitution' | translate}}" (click)="deleteSubstution()">
-          <mat-icon fontSet="fas" fontIcon="fa-times"></mat-icon>
-        </button>
-      </div>
-    </footer>
     <app-drawer></app-drawer>
   </mat-sidenav-content>
   <mat-sidenav #snavRight disableClose [mode]="this.signaturesService.mobileMode ? 'over': 'side'"
@@ -109,4 +99,4 @@
     <app-main-document-detail #appMainDocumentDetail [snavRightComponent]="this.snavRight" [mainDocument]="mainDocument"
       *ngIf="signaturesService.sideNavRigtDatas.mode == 'mainDocumentDetail'"></app-main-document-detail>
   </mat-sidenav>
-</mat-sidenav-container>
\ No newline at end of file
+</mat-sidenav-container>
diff --git a/src/frontend/app/document/document.component.ts b/src/frontend/app/document/document.component.ts
index 8adc5539a4..0a48cee7bf 100755
--- a/src/frontend/app/document/document.component.ts
+++ b/src/frontend/app/document/document.component.ts
@@ -173,6 +173,18 @@ export class DocumentComponent implements OnInit {
 
                         this.initDoc();
 
+                        const realUserWorkflow = this.mainDocument.workflow.filter((line: { current: boolean; }) => line.current === true)[0];
+
+                        if (realUserWorkflow.userId !== this.authService.user.id) {
+                            this.http.get('../rest/users/' + realUserWorkflow.userId + '/signatures')
+                                .subscribe((dataSign: any) => {
+                                    this.signaturesService.signaturesListSubstituted = dataSign.signatures;
+                                    this.signaturesService.loadingSign = false;
+                                });
+                        } else {
+                            this.signaturesService.signaturesListSubstituted = [];
+                        }
+
                         this.docList.push({ 'id': this.mainDocument.id, 'title': this.mainDocument.title, 'pages': this.mainDocument.pages, 'imgContent': [], 'imgUrl': '../rest/documents/' + this.mainDocument.id + '/thumbnails' });
                         this.mainDocument.attachments.forEach((attach: any) => {
                             this.docList.push({ 'id': attach.id, 'title': attach.title, 'pages': attach.pages, 'imgContent': [], 'imgUrl': '../rest/attachments/' + attach.id + '/thumbnails' });
@@ -328,7 +340,7 @@ export class DocumentComponent implements OnInit {
 
         e = e.srcEvent;
 
-        if (!this.signaturesService.annotationMode && this.currentDoc === 0 && this.authService.user.substitute === null) {
+        if (!this.signaturesService.annotationMode && this.currentDoc === 0) {
 
             this.backToDetails();
 
diff --git a/src/frontend/app/profile/profile.component.html b/src/frontend/app/profile/profile.component.html
index e7979e10b4..2dfb918d7e 100644
--- a/src/frontend/app/profile/profile.component.html
+++ b/src/frontend/app/profile/profile.component.html
@@ -218,6 +218,12 @@
                                         </plugin-autocomplete>
                                     </div>
                                 </div>
+                                <fieldset *ngIf="authService.user.substitute != null && signaturesService.signaturesList.length > 0">
+                                    <legend align="left">{{'lang.signSubstituted' | translate}} :</legend>
+                                    <ng-container>
+                                        <button type="button" class="signListButton" mat-stroked-button *ngFor="let signature of signaturesService.signaturesList; let i=index" (click)="toggleSignature(i)" [class.selected]="signature.substituted"><img [src]="sanitizer.bypassSecurityTrustUrl('data:image/png;base64,' + signature.encodedSignature)"/></button>
+                                    </ng-container>
+                                </fieldset>
                             </fieldset>
                         </div>
                     </div>
@@ -234,4 +240,4 @@
             </span>
         </form>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/frontend/app/profile/profile.component.ts b/src/frontend/app/profile/profile.component.ts
index 31af9acaab..29a2fe3b8e 100644
--- a/src/frontend/app/profile/profile.component.ts
+++ b/src/frontend/app/profile/profile.component.ts
@@ -381,4 +381,12 @@ export class ProfileComponent implements OnInit {
     setLang(lang: any) {
         this.translate.use(lang);
     }
+
+    toggleSignature(i: number) {
+        this.http.patch('../rest/users/' + this.authService.user.id + '/signatures/' + this.signaturesService.signaturesList[i].id  + '/substituted', { 'substituted': !this.signaturesService.signaturesList[i].substituted })
+            .subscribe(() => {
+                this.signaturesService.signaturesList[i].substituted = !this.signaturesService.signaturesList[i].substituted;
+                this.notificationService.success('lang.modificationSaved');
+            });
+    }
 }
diff --git a/src/frontend/app/service/signatures.service.ts b/src/frontend/app/service/signatures.service.ts
index 51110434ae..71d30a0198 100755
--- a/src/frontend/app/service/signatures.service.ts
+++ b/src/frontend/app/service/signatures.service.ts
@@ -7,6 +7,7 @@ export class SignaturesContentService {
     signaturesContent: any[] = [];
     notesContent: any[] = [];
     signaturesList: any[] = [];
+    signaturesListSubstituted: any[] = [];
     currentPage = 1;
     totalPage = 1;
     isTaggable = true;
@@ -62,6 +63,7 @@ export class SignaturesContentService {
         this.signaturesContent = [];
         this.notesContent = [];
         this.signaturesList = [];
+        this.signaturesListSubstituted = [];
         this.currentPage = 1;
         this.totalPage = 1;
         this.isTaggable = true;
diff --git a/src/frontend/app/signatures/signatures.component.html b/src/frontend/app/signatures/signatures.component.html
index 089425ad66..10debd86e5 100755
--- a/src/frontend/app/signatures/signatures.component.html
+++ b/src/frontend/app/signatures/signatures.component.html
@@ -9,6 +9,16 @@
       <i class="fas fa-pen-nib fa-2x"></i>
       {{'lang.createNewSignature' | translate}}
     </div>
+    <div [@listAnimation]="signaturesService.signaturesListSubstituted.length" style="display: contents;">
+      <div *ngFor="let signature of signaturesService.signaturesListSubstituted;let i=index" class="list-item" [title]="'lang.selectSignature' | translate" style="position: relative;overflow: hidden" (tap)="tapEvent(signature,i,'substitute')">
+        <button mat-mini-fab color="primary" [title]="'lang.selectSignature' | translate" class="sign_icon add_icon" (tap)="selectSignature(signature,'imgSignSub_'+i);">
+          <mat-icon class="fa fa-arrow-up"></mat-icon>
+        </button>
+        <img id="imgSignSub_{{i}}" [src]="sanitization.bypassSecurityTrustUrl('data:image/png;base64,' + signature.encodedSignature)"
+          style="width: 190px;">
+        <div class="substituteInfo">{{'lang.substitutedSignature' | translate}}</div>
+      </div>
+    </div>
     <div [@listAnimation]="signaturesService.signaturesList.length" style="display: contents;">
       <div *ngFor="let signature of signaturesService.signaturesList;let i=index" class="list-item" [title]="'lang.selectSignature' | translate" style="position: relative;overflow: hidden" (tap)="tapEvent(signature,i,'')">
         <button mat-mini-fab color="warn" [title]="'lang.removeSignature' | translate" class="sign_icon remove_icon" (tap)="removeSignature(signature,i);">
@@ -23,4 +33,4 @@
     </div>
   </section>
 </article>
-<app-pad (reloaded)="reloadSignatures()"></app-pad>
\ No newline at end of file
+<app-pad (reloaded)="reloadSignatures()"></app-pad>
diff --git a/src/frontend/app/signatures/signatures.component.ts b/src/frontend/app/signatures/signatures.component.ts
index b70f12a0a4..832bd8a6fd 100755
--- a/src/frontend/app/signatures/signatures.component.ts
+++ b/src/frontend/app/signatures/signatures.component.ts
@@ -119,7 +119,8 @@ export class SignaturesComponent implements OnInit {
                 this.count = 0;
             } else if (this.count > 1) {
                 this.count = 0;
-                this.selectSignature(signature, 'imgSign_' + i);
+                const id = mode === 'substitute' ? ('imgSignSub_' + i) : ('imgSign_' + i);
+                this.selectSignature(signature, id);
             }
         }, 250);
     }
-- 
GitLab