Newer
Older
* Copyright Maarch since 2008 under license.
* See LICENSE.txt file at the root folder for more details.
* This file is part of Maarch software.
*
*/
/**
* @brief Resource Controller
* @author dev@maarch.org
*/
namespace Document\controllers;
use Attachment\controllers\AttachmentController;
use Email\controllers\EmailController;
use Group\controllers\PrivilegeController;
use setasign\Fpdi\Tcpdf\Fpdi;
use SrcCore\controllers\UrlController;
use Document\models\DocumentModel;
use Slim\Http\Request;
use Slim\Http\Response;
use SrcCore\models\DatabaseModel;
use User\controllers\SignatureController;
use History\controllers\HistoryController;
use Workflow\controllers\YousignController;
use Workflow\models\WorkflowExternalInformationModel;
const ACTIONS = [1 => 'VAL', 2 => 'REF'];
const MODES = ['visa', 'sign', 'note'];
$queryParams = $request->getQueryParams();
$queryParams['offset'] = empty($queryParams['offset']) ? 0 : (int)$queryParams['offset'];
$queryParams['limit'] = empty($queryParams['limit']) ? 0 : (int)$queryParams['limit'];
$userId = $GLOBALS['id'];
if (!empty($queryParams['userId'])) {
if (!PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
return $response->withStatus(403)->withJson(['errors' => 'Privilege forbidden']);
}
if (!Validator::intVal()->notEmpty()->validate($queryParams['userId'])) {
return $response->withStatus(400)->withJson(['errors' => 'QueryParams userId is not an integer']);
}
$userId = $queryParams['userId'];
}
$substitutedUsers = UserModel::get(['select' => ['id'], 'where' => ['substitute = ?'], 'data' => [$userId]]);
foreach ($substitutedUsers as $value) {
$users[] = $value['id'];
}
$workflowSelect = "SELECT id FROM workflows ws WHERE workflows.main_document_id = main_document_id AND process_date IS NULL AND status IS NULL ORDER BY \"order\" LIMIT 1";
$where = ['user_id in (?)', "(id) in ({$workflowSelect})"];
$data = [$users];
$countWorkflows = WorkflowModel::get([
'select' => ['count(mode)', 'mode'],
'where' => $where,
'data' => $data,
'groupBy' => ['mode']
]);
if (!empty($queryParams['mode']) && in_array($queryParams['mode'], DocumentController::MODES)) {
$where[] = 'mode = ?';
$data[] = $queryParams['mode'];
'select' => ['main_document_id', 'mode', 'user_id'],
'where' => $where,
'data' => $data
$documentIds = [];
$workflowsShortcut = [];
foreach ($workflows as $workflow) {
$documentIds[] = $workflow['main_document_id'];
$workflowsShortcut[$workflow['main_document_id']] = $workflow;
}
$documents = [];
if (!empty($documentIds)) {
$where = ['id in (?)'];
$data = [$documentIds];
if (!empty($queryParams['search'])) {
$where[] = '(reference ilike ? OR translate(title, \'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿŔŕ\', \'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyrr\') ilike translate(?, \'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿŔŕ\', \'aaaaaaaceeeeiiiidnoooooouuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyrr\'))';
$data[] = "%{$queryParams['search']}%";
$data[] = "%{$queryParams['search']}%";
}
'select' => ['id', 'title', 'reference', 'mailing_id as "mailingId"', 'sender', 'creation_date as "creationDate"', 'count(1) OVER()'],
'limit' => $queryParams['limit'],
'offset' => $queryParams['offset'],
'orderBy' => ['creation_date desc']
]);
}
$count = ['visa' => 0, 'sign' => 0, 'note' => 0, 'current' => empty($documents[0]['count']) ? 0 : $documents[0]['count']];
foreach ($documents as $key => $document) {
unset($documents[$key]['count']);
$documents[$key]['mode'] = $workflowsShortcut[$document['id']]['mode'];
$documents[$key]['owner'] = $workflowsShortcut[$document['id']]['user_id'] == $userId;
foreach ($countWorkflows as $mode) {
$count[$mode['mode']] = $mode['count'];
}
return $response->withJson(['documents' => $documents, 'count' => $count]);
public function getById(Request $request, Response $response, array $args)
{
if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']]) && !PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
}
$document = DocumentModel::getById(['select' => ['*'], 'id' => $args['id']]);
if (empty($document)) {
return $response->withStatus(400)->withJson(['errors' => 'Document does not exist']);
}
'where' => ['main_document_id = ?', 'type != ?', 'type != ?', 'type != ?'],
'data' => [$args['id'], 'DOC', 'ESIGN', 'ORIGINAL']
if (empty($adr[0]['count']) && $document['status'] != 'CONVERTING') {
$configPath = CoreConfigModel::getConfigPath();
exec("php src/app/convert/scripts/ThumbnailScript.php '{$configPath}' {$args['id']} 'document' '{$GLOBALS['id']}' > /dev/null");
$adr[0]['count'] = 1;
$formattedDocument = [
'id' => $document['id'],
'title' => $document['title'],
'reference' => $document['reference'],
'description' => $document['description'],
'sender' => $document['sender'],
'notes' => !empty($document['notes']) ? json_decode($document['notes'], true) : null,
'creationDate' => $document['creation_date'],
'modificationDate' => $document['modification_date'],
Guillaume Heurtier
committed
'pages' => $adr[0]['count'],
'status' => $document['status'],
'mailingId' => $document['mailing_id']
];
if (!empty($document['deadline'])) {
$date = new \DateTime($document['deadline']);
$formattedDocument['deadline'] = $date->format('d-m-Y H:i');
}
$formattedDocument['metadata'] = [];
$metadata = json_decode($document['metadata'], true);
if (is_array($metadata)) {
foreach ($metadata as $key => $value) {
$formattedDocument['metadata'][] = ['label' => $key, 'value' => $value];
}
$workflow = WorkflowModel::getByDocumentId(['select' => ['id', 'user_id', 'mode', 'process_date', 'signature_mode', 'status', 'note', 'signature_positions', 'date_positions'], 'documentId' => $args['id'], 'orderBy' => ['"order"']]);
$currentId = null;
foreach ($workflow as $value) {
if (!empty($value['process_date'])) {
$date = new \DateTime($value['process_date']);
$value['process_date'] = $date->format('d-m-Y H:i');
}
$userLabel = '';
if (!empty($value['user_id'])) {
$userSignaturesModes = UserModel::getById(['id' => $value['user_id'], 'select' => ['signature_modes']]);
$userLabel = UserModel::getLabelledUserById(['id' => $value['user_id']]);
}
$workflowExternalInformations = WorkflowExternalInformationModel::getByWorkflowId(['select' => ['*'], 'workflowId' => $value['id']]);
if (!empty($workflowExternalInformations)) {
$userLabel = "{$workflowExternalInformations['firstname']} {$workflowExternalInformations['lastname']}";
$workflowExternalInformations['informations'] = json_decode($workflowExternalInformations['informations'], true);
unset($workflowExternalInformations['informations']['yousignFileId'], $workflowExternalInformations['informations']['yousignProcedureId']);
$formattedDocument['workflow'][] = [
'userDisplay' => $userLabel,
'mode' => $value['mode'],
'processDate' => $value['process_date'],
'current' => !$currentFound && empty($value['status']),
'signatureMode' => $value['signature_mode'],
'signaturePositions' => json_decode($value['signature_positions'], true),
'datePositions' => json_decode($value['date_positions'], true),
'userSignatureModes' => !empty($userSignaturesModes['signature_modes']) ? json_decode($userSignaturesModes['signature_modes'], true) : [],
'note' => $value['note'],
'status' => $value['status'],
'externalInformations' => $workflowExternalInformations

Florian Azizian
committed
if (!$currentId && empty($value['status'])) {
$currentId = $value['user_id'];
if (!empty($currentId) && $currentId != $GLOBALS['id']) {
$substitute = UserModel::getById(['id' => $currentId, 'select' => ['substitute']]);
$formattedDocument['readOnly'] = $GLOBALS['id'] != $substitute['substitute'] && PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents']);
}
$attachments = AttachmentModel::getByDocumentId(['select' => ['id', 'title'], 'documentId' => $args['id']]);
foreach ($attachments as $attachment) {
$pagesCount = 0;
$adr = AdrModel::getAttachmentsAdr([
'select' => ['count(1)'],
'where' => ['attachment_id = ?', 'type != ?'],
'data' => [$attachment['id'], 'ATTACH']
if (!empty($adr[0]['count'])) {
$pagesCount = $adr[0]['count'];
$formattedDocument['attachments'][] = [
'id' => $attachment['id'],
'title' => $attachment['title'],
'pages' => $pagesCount
if (!empty($document['link_id'])) {
$formattedDocument['linkedDocuments'] = DocumentController::getLinkedDocuments(['id' => $args['id'], 'userId' => $GLOBALS['id'], 'linkId' => $document['link_id']]);
}
'code' => 'OK',
'objectType' => 'main_documents',
'objectId' => $args['id'],
'type' => 'VIEW',
return $response->withJson(['document' => $formattedDocument]);
public function getContent(Request $request, Response $response, array $args)
{
if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']]) && !PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
}

Florian Azizian
committed
$queryParams = $request->getQueryParams();
if (!empty($queryParams['type']) && !in_array($queryParams['type'], ['esign', 'original', 'doc'])) {
return $response->withStatus(403)->withJson(['errors' => 'Query param type is not allowed']);
}
$content = DocumentController::getContentPath(['id' => $args['id'], 'type' => strtoupper($queryParams['type'])]);
if (!empty($content['errors'])) {
return $response->withStatus($content['code'])->withJson(['errors' => $content['errors']]);
} elseif (empty($content['path'])) {
return $response->withStatus(404)->withJson(['encodedDocument' => null]);
}
$pathToDocument = $content['path'];
$document = DocumentModel::getById(['select' => ['title'], 'id' => $args['id']]);
HistoryController::add([
'code' => 'OK',
'objectType' => 'main_documents',
'objectId' => $args['id'],
'type' => 'VIEW',
'message' => "{documentViewed} : {$document['title']}"
]);
$fileContent = file_get_contents($pathToDocument);
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->buffer($fileContent);
if (empty($queryParams['mode']) || $queryParams['mode'] == 'base64') {
return $response->withJson(['encodedDocument' => base64_encode($fileContent)]);
} else {
$pathInfo = pathinfo($pathToDocument);
$response->write($fileContent);
$response = $response->withAddedHeader('Content-Disposition', "inline; filename=maarch.{$pathInfo['extension']}");
return $response->withHeader('Content-Type', $mimeType);
}
}

Florian Azizian
committed
public function create(Request $request, Response $response)
{
if (!PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'indexation'])) {
return $response->withStatus(403)->withJson(['errors' => 'Privilege forbidden']);
}
if (empty($body)) {
return $response->withStatus(400)->withJson(['errors' => 'Body is not set or empty']);
} elseif (!Validator::notEmpty()->validate($body['encodedDocument'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body encodedDocument is empty']);
} elseif (!Validator::stringType()->notEmpty()->validate($body['title'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body title is empty or not a string']);
} elseif (!Validator::stringType()->notEmpty()->validate($body['sender'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body sender is empty or not a string']);
} elseif (!Validator::arrayType()->notEmpty()->validate($body['workflow'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body workflow is empty or not an array']);

Florian Azizian
committed
} elseif (!Validator::stringType()->length(0, 64)->validate($body['reference'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body reference is too loong or not a string']);
$body['attachments'] = empty($body['attachments']) ? [] : $body['attachments'];
foreach ($body['attachments'] as $key => $attachment) {
if (!Validator::notEmpty()->validate($attachment['encodedDocument'])) {
return $response->withStatus(400)->withJson(['errors' => "Body attachments[{$key}] encodedDocument is empty"]);
} elseif (!Validator::stringType()->notEmpty()->validate($attachment['title'])) {
return $response->withStatus(400)->withJson(['errors' => "Body attachments[{$key}] title is empty"]);

Florian Azizian
committed
$hasEidas = false;
Guillaume Heurtier
committed
$hasElectronicSignature = false;
foreach ($body['workflow'] as $key => $workflow) {
$processingUser = null;
if (empty($workflow['processingUser']) && empty($workflow['userId']) && !empty($workflow['externalInformations'])) {
if (!Validator::intVal()->notEmpty()->validate($workflow['externalInformations']['sourceId'] ?? null)) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][externalInformations] sourceId is empty or not an integer"]);
} elseif (!Validator::stringType()->notEmpty()->validate($workflow['externalInformations']['firstname'] ?? null)) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][externalInformations] firstname is empty or not a string"]);
} elseif (!Validator::stringType()->notEmpty()->validate($workflow['externalInformations']['lastname'] ?? null)) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][externalInformations] lastname is empty or not a string"]);
} elseif (empty($workflow['externalInformations']['email']) || !filter_var($workflow['externalInformations']['email'], FILTER_VALIDATE_EMAIL) || !Validator::stringType()->notEmpty()->length(1, 128)->validate($workflow['externalInformations']['email'])) {
return $response->withStatus(400)->withJson(['errors' => 'Body workflow[{$key}][externalInformations] email is empty or not valid']);
} elseif (!Validator::stringType()->notEmpty()->length(1, 128)->validate($workflow['externalInformations']['phone'] ?? null)) {
return $response->withStatus(400)->withJson(['errors' => 'Body workflow[{$key}][externalInformations] phone is empty or not valid']);
}
} else {
if (!empty($workflow['processingUser'])) {
$processingUser = UserModel::getByLogin(['select' => ['id'], 'login' => strtolower($workflow['processingUser'])]);
} elseif (!empty($workflow['userId'])) {
$processingUser = UserModel::getById(['select' => ['id'], 'id' => $workflow['userId']]);
}
if (empty($processingUser)) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}] processingUser/userId is empty or does not exist"]);
} elseif (!Validator::stringType()->notEmpty()->validate($workflow['mode']) || !in_array($workflow['mode'], DocumentController::MODES)) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}] mode is empty or not a string in ('visa', 'sign', 'note')"]);
}
$body['workflow'][$key]['userId'] = $processingUser['id'] ?? null;
Guillaume Heurtier
committed
if ($hasElectronicSignature && $workflow['signatureMode'] == 'stamp' && $workflow['mode'] == 'sign') {
Guillaume Heurtier
committed
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}] signatureMode cannot be stamp after an electronic signature", 'lang' => 'stampInTheMiddleImpossible']);
}
if (!empty($workflow['signaturePositions'])) {
if (!Validator::arrayType()->validate($workflow['signaturePositions'])) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}] signaturePositions is not an array"]);
}
foreach ($workflow['signaturePositions'] as $keySP => $signaturePosition) {
if (empty($signaturePosition['positionX']) || empty($signaturePosition['positionY']) || empty($signaturePosition['page'])) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][signaturePositions][{$keySP}] is wrong formatted"]);
}
}
}
if (!empty($workflow['datePositions'])) {
if (!Validator::arrayType()->validate($workflow['datePositions'])) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}] datePositions is not an array"]);
}
foreach ($workflow['datePositions'] as $keyDP => $datePosition) {
if (empty($datePosition['positionX']) || empty($datePosition['positionY']) || empty($datePosition['page'])) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][datePositions][{$keyDP}] is wrong formatted"]);
} elseif (empty($datePosition['color']) || empty($datePosition['font']) || empty($datePosition['format']) || empty($datePosition['width'])) {
return $response->withStatus(400)->withJson(['errors' => "Body workflow[{$key}][datePositions][{$keyDP}] is wrong formatted"]);
}
}
}

Florian Azizian
committed
if (in_array($workflow['signatureMode'], ['eidas', 'rgs_2stars_timestamped', 'inca_card_eidas'])) {

Florian Azizian
committed
$hasEidas = true;
}
Guillaume Heurtier
committed
if ($workflow['signatureMode'] != 'stamp' && $workflow['mode'] == 'sign') {
Guillaume Heurtier
committed
$hasElectronicSignature = true;
}
Loading
Loading full blame...