Commit 3f4bf368 authored by Damien's avatar Damien
Browse files

Merge branch 'develop' into 'master'

Develop

See merge request maarch/MaarchParapheur!62
parents 43048cb7 e06b723f
......@@ -48,7 +48,8 @@
},
{ "glob": "**/*", "input": "node_modules/tinymce/skins", "output": "/tinymce/skins/" },
{ "glob": "**/*", "input": "node_modules/tinymce/themes", "output": "/tinymce/themes/" },
{ "glob": "**/*", "input": "node_modules/tinymce/plugins", "output": "/tinymce/plugins/" }
{ "glob": "**/*", "input": "node_modules/tinymce/plugins", "output": "/tinymce/plugins/" },
{ "glob": "**/*", "input": "node_modules/tinymce/icons", "output": "/tinymce/icons/" }
],
"styles": [
{
......
......@@ -503,6 +503,14 @@
"connectorIsUsed": "{{number}} visa workflow currently use this connector.<br>If you continue, these workflows will be interrupted.",
"connectorNotUsed": "This connector is not used",
"newTemplateDescWithOtp": "The current workflow will be saved without taking into account the external users.",
"otpUser": "External user"
"otpUser": "External user",
"accessLink": "Access link",
"recipientLastname": "Recipient's name",
"recipientFirstname": "Recipient's first name",
"expiresAt": "Expiration date",
"mergedVariablesMsg": "Use tag",
"mergedVariablesMsg2": "to display the generated security code.",
"mergedVariablesMsgEmail": "You can use the different tags below to enrich your email",
"updateOtp": "Update external user"
}
}
\ No newline at end of file
......@@ -474,7 +474,7 @@
"mailServerOfflineMsg": "Le serveur de messagerie doit être <b>testé</b> et <b>opérationnel</b> pour : <b>{{action}}</b>",
"testAndValidate": "Tester et valider",
"receiveActivationNotification": "Recevoir le courriel d'activation",
"newOtp": "Ajouter un OTP",
"newOtp": "Ajouter un utilisateur externe",
"otpUser": "Utilisateur externe",
"securityCodeSendMode": "Mode d'envoi du code de sécurité",
"phoneAlt": "Mobile",
......@@ -508,6 +508,15 @@
"expiresAt": "Date d'expiration",
"mergedVariablesMsg": "Utiliser la balise",
"mergedVariablesMsg2": "pour afficher le code de sécurité généré.",
"mergedVariablesMsgEmail": "Vous pouvez utiliser les différentes balises ci-dessous pour enrichir votre e-mail"
"mergedVariablesMsgEmail": "Vous pouvez utiliser les différentes balises ci-dessous pour enrichir votre e-mail",
"updateOtp": "Modifier l'utilisateur externe",
"smsCodeTagMandatory": "La balise <b>code</b> est requis pour envoie du code de sécurité par SMS",
"emailLinkTagMandatory": "La balise <b>lien d'accès</b> est requis pour la notification email",
"manage_customization": "Personnalisation",
"manage_customizationDesc": "Personnaliser le message à l'écran de connexion",
"loginMessage": "Message à l'écran de connexion :",
"customization": "Personnalisation",
"noConnector": "Le connecteur associé n'existe pas",
"otpVisaUser": "L'utilisateur sera notifié par <b>courriel</b> au moment de son tour dans le circuit."
}
}
......@@ -31,7 +31,7 @@
"@angular-devkit/build-angular": "^0.1100.7",
"@angular-eslint/eslint-plugin": "^2.1.1",
"@angular/animations": "^11.2.14",
"@angular/cdk": "^11.2.12",
"@angular/cdk": "^11.2.13",
"@angular/cli": "^11.2.13",
"@angular/common": "^11.2.14",
"@angular/compiler": "^11.2.14",
......@@ -39,7 +39,7 @@
"@angular/core": "^11.2.14",
"@angular/forms": "^11.2.14",
"@angular/language-service": "^11.2.14",
"@angular/material": "^11.2.12",
"@angular/material": "^11.2.13",
"@angular/platform-browser": "^11.2.14",
"@angular/platform-browser-dynamic": "^11.2.14",
"@angular/router": "^11.2.14",
......@@ -50,21 +50,21 @@
"@ngrx/store-devtools": "^9.2.1",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "^4.0.0",
"@peculiar/fortify-webcomponents": "^0.16.4",
"@peculiar/fortify-webcomponents": "^0.16.5",
"@types/hammerjs": "^2.0.39",
"@types/jasmine": "^3.7.4",
"@types/jasminewd2": "^2.0.9",
"@types/jquery": "^3.5.5",
"@types/node": "^13.13.52",
"@typescript-eslint/eslint-plugin": "^4.24.0",
"@typescript-eslint/eslint-plugin-tslint": "^4.24.0",
"@typescript-eslint/parser": "^4.24.0",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/eslint-plugin-tslint": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"angular2-draggable": "^2.3.2",
"angular2-signaturepad": "^3.0.4",
"codelyzer": "^5.1.2",
"eslint": "^7.26.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.2.0",
"eslint-plugin-import": "^2.23.2",
"eslint-plugin-import": "^2.23.3",
"exif-js": "^2.3.0",
"hammerjs": "^2.0.8",
"jquery": "^3.6.0",
......
......@@ -17,6 +17,9 @@ INSERT INTO groups (id, label) VALUES (3, 'Utilisateurs');
INSERT INTO groups (id, label) VALUES (4, 'Initiateurs');
INSERT INTO groups (id, label) VALUES (5, 'Administrateurs fonctionnels');
TRUNCATE TABLE external_signatory_book;
INSERT INTO external_signatory_book (id, label, type, connection_data, otp_code, message_content) VALUES (1, 'Signature externe Yousign', 'yousign', '{"apiKey": "a24291dc9d3475ec4fddded67df782b9", "apiUri": "https://staging-api.yousign.com"}', '["sms", "email"]', '{"otp_sms": "eSignature : votre code de sécurité pour confirmer la signature de vos documents est {{code}}", "notification": {"body": "<div>Bonjour&nbsp;<span class=\"mceNonEditable\"><tag data-tag-name=\"recipient.firstname\" data-tag-type=\"string\">Pr&eacute;nom du destinataire</tag></span>&nbsp;<span class=\"mceNonEditable\"><tag data-tag-name=\"recipient.lastname\" data-tag-type=\"string\">Nom du destinataire</tag></span>,<br /><br /></div>\n<div>Des documents mis &agrave; votre disposition requi&egrave;rent votre signature ou validation.<br /><br /></div>\n<div>Merci de cliquer sur le bouton ci-dessous pour les lire :<br />&nbsp;<span class=\"mceNonEditable\"><tag data-tag-name=\"url\" data-tag-type=\"button\" data-tag-title=\"Acc&eacute;dez aux documents\">Lien d''acc&egrave;s</tag></span>&nbsp;</div>\n<div>&nbsp;</div>", "subject": "Document à traiter"}}');
TRUNCATE TABLE groups_privileges;
INSERT INTO groups_privileges (id, group_id, privilege) VALUES (1, 1, 'manage_users');
INSERT INTO groups_privileges (id, group_id, privilege) VALUES (2, 1, 'manage_documents');
......
......@@ -194,6 +194,7 @@ class DocumentController
if (!empty($workflowExternalInformations)) {
$userLabel = "{$workflowExternalInformations['firstname']} {$workflowExternalInformations['lastname']}";
$workflowExternalInformations['informations'] = json_decode($workflowExternalInformations['informations'], true);
unset($workflowExternalInformations['informations']['yousignFileId'], $workflowExternalInformations['informations']['yousignProcedureId']);
}
$formattedDocument['workflow'][] = [
......@@ -506,6 +507,11 @@ class DocumentController
]);
$firstWorkflowId = null;
$pdf = new Fpdi('P', 'pt');
$veryTemporaryPath = CoreConfigModel::getTmpPath() . "tmp_file_{$GLOBALS['id']}_" .rand(). "_coord.pdf";
file_put_contents($veryTemporaryPath, base64_decode($encodedDocument['encodedDocument']));
$pdf->setSourceFile($veryTemporaryPath);
unlink($veryTemporaryPath);
foreach ($body['workflow'] as $key => $workflow) {
if (!SignatureController::isValidSignatureMode(['mode' => $workflow['signatureMode']])) {
$workflow['signatureMode'] = 'stamp';
......@@ -524,6 +530,22 @@ class DocumentController
$firstWorkflowId = $workflowId;
}
if (empty($workflow['userId'])) {
if (!empty($workflow['signaturePositions'])) {
foreach ($workflow['signaturePositions'] as $signaturePosition) {
$pageWidth = $pdf->getPageWidth($signaturePosition['page'] - 1);
$pageHeight = $pdf->getPageHeight($signaturePosition['page'] - 1);
$llx = $pageWidth * $signaturePosition['positionX'] / 100;
$llx = round($llx);
$lly = $pageHeight - ($pageHeight * $signaturePosition['positionY'] / 100) - 40;
$lly = round($lly);
$urx = $llx + 100;
$urx = round($urx);
$ury = $lly + 40;
$ury = round($ury);
$workflow['externalInformations']['signaturePositions'][] = ['page' => $signaturePosition['page'], 'position' => "{$llx},{$lly},{$urx},{$ury}"];
}
}
$informations = [];
if ($workflow['externalInformations']['type'] == 'yousign') {
$informations = YousignController::formatExternalInformations($workflow['externalInformations']);
......@@ -927,12 +949,16 @@ class DocumentController
}
}
DocumentController::endAction([
$end = DocumentController::endAction([
'id' => $args['id'],
'workflowId' => $workflow['id'],
'status' => DocumentController::ACTIONS[$args['actionId']],
'note' => $body['note'] ?? null
]);
if (!empty($end['errors'])) {
return $response->withStatus(400)->withJson(['errors' => $end['errors']]);
}
$historyMessagePart = "{actionDone} : ";
if ($workflow['user_id'] != $GLOBALS['id']) {
......@@ -1063,13 +1089,16 @@ class DocumentController
$content = file_get_contents($content['path']);
$document = DocumentModel::getById(['select' => ['title', 'description'], 'id' => $args['id']]);
YousignController::createProcedure([
$procedureCreated = YousignController::createProcedure([
'documentId' => $args['id'],
'encodedDocument' => base64_encode($content),
'name' => $document['title'],
'description' => $document['description'],
'workflowId' => $nextWorkflow['id']
]);
if (!empty($procedureCreated['errors'])) {
return ['errors' => $procedureCreated['errors']];
}
}
} else {
EmailController::sendNotification(['documentId' => $args['id'], 'status' => $args['status']]);
......@@ -1089,7 +1118,7 @@ class DocumentController
}
$workflow = WorkflowModel::getCurrentStep(['select' => ['user_id'], 'documentId' => $args['id']]);
if (empty($workflow)) {
if (empty($workflow) || empty($workflow['user_id'])) {
return false;
}
......
......@@ -28,7 +28,7 @@ class ExternalSignatoryBookController
{
public function get(Request $request, Response $response)
{
$connectors = ExternalSignatoryBookModel::get(['select' => ['id', 'label', 'type', 'otp_code']]);
$connectors = ExternalSignatoryBookModel::get(['select' => ['id', 'label', 'type', 'otp_code'], 'orderBy' => ['label']]);
foreach ($connectors as $key => $data) {
$connectors[$key]['securityModes'] = json_decode($data['otp_code'], true);
unset($connectors[$key]['otp_code']);
......@@ -74,38 +74,9 @@ class ExternalSignatoryBookController
$body = $request->getParsedBody();
if (empty($body)) {
return $response->withStatus(400)->withJson(['errors' => 'Body is not set or empty']);
} elseif (!Validator::stringType()->notEmpty()->length(1, 255)->validate($body['label'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body label is empty or not a string or longer than 255 caracteres']);
} elseif (!Validator::stringType()->notEmpty()->length(1, 128)->validate($body['type'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body type is empty or not a string or longer than 128 caracteres']);
} elseif (!Validator::arrayType()->notEmpty()->validate($body['securityModes'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body securityModes is empty or not an array']);
}
if ($body['type'] != 'yousign') {
return $response->withStatus(400)->withJson(['errors' => 'Only type yousign is allowed']);
}
$invalidOtpCode = array_diff($body['securityModes'], ['sms', 'email']);
if (!empty($invalidOtpCode)) {
return $response->withStatus(400)->withJson(['errors' => 'Only sms and/or email are allowed for securityModes']);
}
if ($body['type'] == 'yousign') {
if (empty($body['apiUri']) || empty($body['apiKey'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body apiUri or apiKey is empty']);
}
if (empty($body['message']['notification']['subject']) || empty($body['message']['notification']['body'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body message notification must have subject and body attributes']);
}
if (!empty($body['message']['otp_sms']) && !Validator::stringType()->notEmpty()->length(1, 150)->validate($body['message']['otp_sms'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body message otp_sms length must be less than 150 caracteres']);
}
if (in_array('sms', $body['securityModes']) && empty($body['message']['otp_sms'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body message otp_sms must be set']);
}
$control = ExternalSignatoryBookController::control(['body' => $body]);
if (!empty($control['errors'])) {
return $response->withStatus(400)->withJson(['errors' => $control['errors']]);
}
$connectionData = ['apiUri' => $body['apiUri'], 'apiKey' => $body['apiKey']];
......@@ -137,40 +108,13 @@ class ExternalSignatoryBookController
$body = $request->getParsedBody();
if (empty($body)) {
return $response->withStatus(400)->withJson(['errors' => 'Body is not set or empty']);
} elseif (!Validator::stringType()->notEmpty()->length(1, 255)->validate($body['label'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body label is empty or not a string or longer than 255 caracteres']);
} elseif (!Validator::stringType()->notEmpty()->length(1, 128)->validate($body['type'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body type is empty or not a string or longer than 128 caracteres']);
} elseif (!Validator::arrayType()->notEmpty()->validate($body['securityModes'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body securityModes is empty or not an array']);
} elseif (!Validator::intVal()->notEmpty()->validate($aArgs['id'])) {
if (!Validator::intVal()->notEmpty()->validate($aArgs['id'])) {
return $response->withStatus(400)->withJson(['errors' => 'Id must be an integer']);
}
if ($body['type'] != 'yousign') {
return $response->withStatus(400)->withJson(['errors' => 'Only type yousign is allowed']);
}
$invalidOtpCode = array_diff($body['securityModes'], ['sms', 'email']);
if (!empty($invalidOtpCode)) {
return $response->withStatus(400)->withJson(['errors' => 'Only sms and/or email are allowed for securityModes']);
}
if ($body['type'] == 'yousign') {
if (empty($body['apiUri']) || empty($body['apiKey'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body apiUri or apiKey is empty']);
}
if (empty($body['message']['notification']['subject']) || empty($body['message']['notification']['body'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body message notification must have subject and body attributes']);
}
if (!empty($body['message']['otp_sms']) && !Validator::stringType()->notEmpty()->length(1, 150)->validate($body['message']['otp_sms'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body message otp_sms length must be less than 150 caracteres']);
}
if (in_array('sms', $body['securityModes']) && empty($body['message']['otp_sms'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body message otp_sms must be set']);
}
$control = ExternalSignatoryBookController::control(['body' => $body]);
if (!empty($control['errors'])) {
return $response->withStatus(400)->withJson(['errors' => $control['errors']]);
}
$connectionData = ['apiUri' => $body['apiUri'], 'apiKey' => $body['apiKey']];
......@@ -203,6 +147,48 @@ class ExternalSignatoryBookController
return $response->withStatus(204);
}
public static function control(array $args)
{
if (empty($args['body'])) {
return ['errors' => 'Body is not set or empty'];
} elseif (!Validator::stringType()->notEmpty()->length(1, 255)->validate($args['body']['label'])) {
return ['errors' => 'Body label is empty or not a string or longer than 255 caracteres'];
} elseif (!Validator::stringType()->notEmpty()->length(1, 128)->validate($args['body']['type'])) {
return ['errors' => 'Body type is empty or not a string or longer than 128 caracteres'];
} elseif (!Validator::arrayType()->notEmpty()->validate($args['body']['securityModes'])) {
return ['errors' => 'Body securityModes is empty or not an array'];
}
if ($args['body']['type'] != 'yousign') {
return ['errors' => 'Only type yousign is allowed'];
}
$invalidOtpCode = array_diff($args['body']['securityModes'], ['sms', 'email']);
if (!empty($invalidOtpCode)) {
return ['errors' => 'Only sms and/or email are allowed for securityModes'];
}
if ($args['body']['type'] == 'yousign') {
if (empty($args['body']['apiUri']) || empty($args['body']['apiKey'])) {
return ['errors' => 'Body apiUri or apiKey is empty'];
}
if (empty($args['body']['message']['notification']['subject']) || empty($args['body']['message']['notification']['body'])) {
return ['errors' => 'Body message notification must have subject and body attributes'];
}
if (!empty($args['body']['message']['otp_sms']) && !Validator::stringType()->notEmpty()->length(1, 150)->validate($args['body']['message']['otp_sms'])) {
return ['errors' => 'Body message otp_sms length must be less than 150 caracteres'];
}
if (!empty($args['body']['message']['otp_sms']) && strpos($args['body']['message']['otp_sms'], '{{code}}') === false) {
return ['errors' => 'Body message otp_sms must contains {{code}}'];
}
if (in_array('sms', $args['body']['securityModes']) && empty($args['body']['message']['otp_sms'])) {
return ['errors' => 'Body message otp_sms must be set'];
}
}
return [];
}
public function delete(Request $request, Response $response, array $aArgs)
{
if (!PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_otp_connectors'])) {
......@@ -220,16 +206,18 @@ class ExternalSignatoryBookController
$workflows = WorkflowModel::getWorkflowWithExternalInfo([
'select' => ['DISTINCT(w.main_document_id)'],
'where' => ['w.process_date is null', 'wei.id is not null', 'external_signatory_book_id = ?'],
'where' => ['w.status is null', 'wei.external_signatory_book_id = ?'],
'data' => [$aArgs['id']]
]);
$currentDocumentsId = array_column($workflows, 'main_document_id');
$documents = DocumentModel::get(['select' => ['typist', 'title', 'id'], 'where' => ['id in (?)'], 'data' => [$currentDocumentsId]]);
foreach ($documents as $document) {
$process = WorkflowController::interruptProcess(['id' => $document['id'], 'documentTypist' => $document['typist'], 'documentTitle' => $document['title']]);
if (!empty($process['errors'])) {
return $response->withStatus(400)->withJson(['errors' => $process['errors']]);
if (!empty($currentDocumentsId)) {
$documents = DocumentModel::get(['select' => ['typist', 'title', 'id'], 'where' => ['id in (?)'], 'data' => [$currentDocumentsId]]);
foreach ($documents as $document) {
$process = WorkflowController::interruptProcess(['id' => $document['id'], 'documentTypist' => $document['typist'], 'documentTitle' => $document['title']]);
if (!empty($process['errors'])) {
return $response->withStatus(400)->withJson(['errors' => $process['errors']]);
}
}
}
......@@ -258,7 +246,7 @@ class ExternalSignatoryBookController
$workflows = WorkflowModel::getWorkflowWithExternalInfo([
'select' => ['DISTINCT(w.main_document_id)'],
'where' => ['w.process_date is null', 'wei.id is not null', 'external_signatory_book_id = ?'],
'where' => ['w.status is null', 'wei.external_signatory_book_id = ?'],
'data' => [$aArgs['id']]
]);
......
......@@ -28,6 +28,7 @@ class PrivilegeController
['id' => 'manage_password_rules', 'type' => 'admin', 'icon' => 'lock-closed', 'route' => '/administration/passwordRules'],
['id' => 'manage_history', 'type' => 'admin', 'icon' => 'timer-outline', 'route' => '/administration/history'],
['id' => 'manage_otp_connectors', 'type' => 'admin', 'icon' => 'people-circle-outline', 'route' => '/administration/otps'],
['id' => 'manage_customization', 'type' => 'admin', 'icon' => 'color-wand-outline', 'route' => '/administration/customization'],
['id' => 'manage_documents', 'type' => 'simple'],
['id' => 'indexation', 'type' => 'simple']
];
......
......@@ -444,17 +444,46 @@ class HistoryController
$documentPathToZip[] = ['path' => $historyXmlPath, 'filename' => 'maarchProof.xml'];
}
if ($queryParams['onlyProof']) {
if (!empty($proofDocument)) {
$content = $proofDocument;
$format = 'pdf';
$mimeType = 'application/pdf';
} else {
$content = $historyXml;
$format = 'xml';
$mimeType = 'application/xml';
$notesWorkflow = [];
foreach ($workflow as $step) {
$workflowExternalInformations = WorkflowExternalInformationModel::getByWorkflowId(['select' => ['informations', 'firstname', 'lastname'], 'workflowId' => $step['id']]);
if (!empty($step['note'])) {
if (empty($step['user_id'])) {
if (!empty($workflowExternalInformations)) {
$userDisplay = "{$workflowExternalInformations['firstname']} {$workflowExternalInformations['lastname']}";
}
} else {
$userDisplay = UserModel::getLabelledUserById(['id' => $step['user_id']]);
}
$notesWorkflow[] = [
'creator' => $userDisplay,
'creationDate' => $step['process_date'],
'value' => $step['note']
];
}
} else {
if (!empty($workflowExternalInformations)) {
$informations = json_decode($workflowExternalInformations['informations'], true);
if ($informations['type'] == 'yousign' && !empty($informations['yousignProcedureId'])) {
$yousignProof = YousignController::getProofByWorkflowId(['workflowId' => $step['id']]);
if (empty($yousignProof['errors'])) {
$archivePath = $tmpPath . 'yousignProof' . $GLOBALS['id'] . "_" . rand() . '.zip';
file_put_contents($archivePath, base64_decode($yousignProof));
$zipArchive = new \ZipArchive();
$zipArchive->open($archivePath);
$nameInArchive = $zipArchive->getNameIndex(0);
$yousignPdfProof = $zipArchive->getFromIndex(0);
$yousignPath = $tmpPath . 'yousignProof' . $GLOBALS['id'] . "_" . rand() . '.pdf';
file_put_contents($yousignPath, $yousignPdfProof);
unlink($archivePath);
$documentPathToZip[] = ['path' => $yousignPath, 'filename' => $nameInArchive];
}
}
}
}
if (!$queryParams['onlyProof']) {
if ($workflowCompleted && ($queryParams['eSignDocument'] ?? $hasCertificate)) {
$contentType = 'ESIGN';
}
......@@ -508,56 +537,20 @@ class HistoryController
if (!empty($documentNotes)) {
$notes[] = $documentNotes;
}
foreach ($workflow as $step) {
if (!empty($step['note'])) {
if (empty($step['user_id'])) {
$workflowExternalInformations = WorkflowExternalInformationModel::getByWorkflowId(['select' => ['firstname', 'lastname'], 'workflowId' => $step['id']]);
if (!empty($workflowExternalInformations)) {
$userDisplay = "{$workflowExternalInformations['firstname']} {$workflowExternalInformations['lastname']}";
}
} else {
$userDisplay = UserModel::getLabelledUserById(['id' => $step['user_id']]);
}
$notes[] = [
'creator' => $userDisplay,
'creationDate' => $step['process_date'],
'value' => $step['note']
];
}
$workflowExternalInformations = WorkflowExternalInformationModel::getByWorkflowId(['select' => ['informations'], 'workflowId' => $step['id']]);
if (!empty($workflowExternalInformations)) {
$informations = json_decode($workflowExternalInformations['informations'], true);
if ($informations['type'] == 'yousign' && !empty($informations['yousignProcedureId'])) {
$yousignProof = YousignController::getProofByWorkflowId(['workflowId' => $step['id']]);
$archivePath = $tmpPath . 'yousignProof' . $GLOBALS['id'] . "_" . rand() . '.zip';
file_put_contents($archivePath, base64_decode($yousignProof));
$zipArchive = new \ZipArchive();
$zipArchive->open($archivePath);
$nameInArchive = $zipArchive->getNameIndex(0);
$yousignPdfProof = $zipArchive->getFromIndex(0);
$yousignPath = $tmpPath . 'yousignProof' . $GLOBALS['id'] . "_" . rand() . '.pdf';
file_put_contents($yousignPath, $yousignPdfProof);
unlink($archivePath);
$documentPathToZip[] = ['path' => $yousignPath, 'filename' => $nameInArchive];
}
}
}
$notes = array_merge($notes, $notesWorkflow);
if (!empty($notes)) {
$documentPathToZip[] = HistoryController::createNotesFile(['notes' => $notes]);
}
}
$content = HistoryController::createZip(['documents' => $documentPathToZip]);
if (!empty($content['errors'])) {
return $response->withStatus(400)->withJson(['errors' => $content['errors']]);
}
$content = $content['fileContent'];
$format = 'zip';
$mimeType = 'application/zip';
$content = HistoryController::createZip(['documents' => $documentPathToZip]);
if (!empty($content['errors'])) {
return $response->withStatus(400)->withJson(['errors' => $content['errors']]);
}
$content = $content['fileContent'];
$format = 'zip';
$mimeType = 'application/zip';
HistoryController::add([
'code' => 'OK',
......
......@@ -48,7 +48,7 @@ class YousignController
$externalSB['message_content'] = json_decode($externalSB['message_content'], true);
$workflowExternalInformations['informations'] = json_decode($workflowExternalInformations['informations'], true);
if (empty($workflowExternalInformations['informations']['signaturePositions']) && $workflowExternalInformations['role'] == 'sign') {
if (empty($workflowExternalInformations['informations']['signaturePositions']) && $workflowExternalInformations['informations']['role'] == 'sign') {
return ['errors' => 'Signature positions are needed for signer'];
}
......@@ -70,7 +70,7 @@ class YousignController
$trunkedFileId = str_replace('/files/', '', $fileId);
$fileObjects = [['file' => $fileId, 'page' => 0]];
if ($workflowExternalInformations['role'] == 'sign') {
if ($workflowExternalInformations['informations']['role'] == 'sign') {
$fileObjects = [];
foreach ($workflowExternalInformations['informations']['signaturePositions'] as $signaturePosition) {
$fileObjects[] = [
......@@ -98,7 +98,7 @@ class YousignController
'operationLevel' => 'custom',
'operationCustomModes' => [$workflowExternalInformations['informations']['security']],
'operationModeSmsConfig' => ['content' => $externalSB['message_content']['otp_sms']],
'type' => $workflowExternalInformations['role'] == 'visa' ? 'validator' : 'signer',
'type' => $workflowExternalInformations['informations']['role'] == 'visa' ? 'validator' : 'signer',
'fileObjects' => $fileObjects
]
],
......@@ -356,7 +356,7 @@ class YousignController
'type' => 'yousign',
'security' => 'sms',
'role' => 'sign',
'signaturePositions' => [['page' => 1, 'position' => '364,105,462,145']] //TODO Remove after front
'signaturePositions' => []
];
if (!empty($args['security']) && $args['security'] == 'email') {
......
<ion-header [translucent]="true">
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-menu-button menu="left-menu"></ion-menu-button>
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>{{'lang.customization' | translate}}</ion-title>
</ion-toolbar>
</ion-header>
<form *ngIf="!loading" style="display: contents;" id="customizationForm" (ngSubmit)="onSubmit()" #customizationForm="ngForm">
<ion-content>
<ion-list-header>