Skip to content
Snippets Groups Projects
DocumentController.php 21.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Damien's avatar
    Damien committed
    <?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 Resource Controller
    * @author dev@maarch.org
    */
    
    namespace Document\controllers;
    
    
    use Attachment\controllers\AttachmentController;
    
    use Docserver\models\AdrModel;
    
    Damien's avatar
    Damien committed
    use Respect\Validation\Validator;
    
    use setasign\Fpdi\Tcpdf\Fpdi;
    
    use SrcCore\models\CoreConfigModel;
    
    Damien's avatar
    Damien committed
    use Attachment\models\AttachmentModel;
    
    Damien's avatar
    Damien committed
    use Docserver\controllers\DocserverController;
    
    use Docserver\models\DocserverModel;
    
    Damien's avatar
    Damien committed
    use Document\models\DocumentModel;
    use Slim\Http\Request;
    use Slim\Http\Response;
    
    use SrcCore\models\DatabaseModel;
    
    use SrcCore\models\ValidatorModel;
    use Status\models\StatusModel;
    
    use User\controllers\UserController;
    
    Damien's avatar
    Damien committed
    use User\models\UserModel;
    
    use History\controllers\HistoryController;
    
    Florian Azizian's avatar
    Florian Azizian committed
    use Action\models\ActionModel;
    
    Damien's avatar
    Damien committed
    
    class DocumentController
    {
        public function get(Request $request, Response $response)
        {
    
            $queryParams = $request->getQueryParams();
    
    Damien's avatar
    Damien committed
    
    
            $queryParams['offset'] = empty($queryParams['offset']) ? 0 : (int)$queryParams['offset'];
            $queryParams['limit'] = empty($queryParams['limit']) ? 0 : (int)$queryParams['limit'];
    
    Damien's avatar
    Damien committed
    
    
            $status = StatusModel::getByReference(['select' => ['id'], 'reference' => 'NEW']);
    
    Damien's avatar
    Damien committed
    
    
            $where = ['processing_user = ?', 'status = ?'];
    
            $dataGet = [$GLOBALS['id'], $status['id']];
    
            if (!empty($queryParams['mode'])) {
    
                $where[] = 'mode = ?';
    
                $dataGet[] = $queryParams['mode'];
                $secondMode = ($queryParams['mode'] == 'SIGN' ? 'NOTE' : 'SIGN');
    
                $documents = DocumentModel::get([
                    'select'    => ['count(1) OVER()'],
                    'where'     => $where,
                    'data'      => [$GLOBALS['id'], $status['id'], $secondMode]
                ]);
    
    Damien's avatar
    Damien committed
                $count[$secondMode] = empty($documents[0]['count']) ? 0 : $documents[0]['count'];
    
            $documents = DocumentModel::get([
    
                'select'    => ['id', 'title', 'reference', 'status', 'mode', 'count(1) OVER()'],
    
                'where'     => $where,
                'data'      => $dataGet,
    
                'limit'     => $queryParams['limit'],
                'offset'    => $queryParams['offset'],
    
    Damien's avatar
    Damien committed
                'orderBy'   => ['creation_date desc']
    
            $count[$queryParams['mode']] = empty($documents[0]['count']) ? 0 : $documents[0]['count'];
    
            foreach ($documents as $key => $document) {
                $status = StatusModel::getById(['select' => ['label'], 'id' => $document['status']]);
                $documents[$key]['statusDisplay'] = $status['label'];
    
    Damien's avatar
    Damien committed
                unset($documents[$key]['count']);
    
    Damien's avatar
    Damien committed
    
    
            return $response->withJson(['documents' => $documents, 'count' => $count]);
    
    Damien's avatar
    Damien committed
        }
    
    
        public function getById(Request $request, Response $response, array $args)
        {
    
            if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']]) && !UserController::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']);
            }
    
    
    Damien's avatar
    Damien committed
            $adr = AdrModel::getDocumentsAdr([
    
    Damien's avatar
    Damien committed
                'select'    => ['path', 'filename', 'fingerprint'],
    
                'where'     => ['main_document_id = ?', 'type = ?'],
                'data'      => [$args['id'], 'DOC']
            ]);
    
            $docserver = DocserverModel::getByType(['type' => 'DOC', 'select' => ['path']]);
            if (empty($docserver['path']) || !file_exists($docserver['path'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Docserver does not exist']);
            }
    
    
    Damien's avatar
    Damien committed
            $pathToDocument = $docserver['path'] . $adr[0]['path'] . $adr[0]['filename'];
    
            if (!file_exists($pathToDocument)) {
                return $response->withStatus(404)->withJson(['errors' => 'Document not found on docserver']);
            }
    
    
    Damien's avatar
    Damien committed
            $fingerprint = DocserverController::getFingerPrint(['path' => $pathToDocument]);
            if ($adr[0]['fingerprint'] != $fingerprint) {
                return $response->withStatus(404)->withJson(['errors' => 'Fingerprints do not match']);
            }
    
    Damien's avatar
    Damien committed
    
    
            $formattedDocument = [
                'id'                => $document['id'],
                'title'             => $document['title'],
                'reference'         => $document['reference'],
                'description'       => $document['description'],
                'mode'              => $document['mode'],
                'sender'            => $document['sender'],
                'metadata'          => json_decode($document['metadata']),
                'creationDate'      => $document['creation_date'],
                'modificationDate'  => $document['modification_date']
            ];
            if (!empty($document['deadline'])) {
                $date = new \DateTime($document['deadline']);
                $formattedDocument['deadline'] = $date->format('d-m-Y H:i');
            }
            $formattedDocument['status'] = StatusModel::getById(['select' => ['*'], 'id' => $document['status']]);
            $processingUser = UserModel::getById(['select' => ['firstname', 'lastname', 'login'], 'id' => $document['processing_user']]);
            $formattedDocument['processingUser'] = $processingUser['login'];
            $formattedDocument['processingUserDisplay'] = "{$processingUser['firstname']} {$processingUser['lastname']}";
            $creator = UserModel::getById(['select' => ['firstname', 'lastname', 'login'], 'id' => $document['creator']]);
            $formattedDocument['creator'] = $creator['login'];
            $formattedDocument['creatorDisplay'] = "{$creator['firstname']} {$creator['lastname']}";
            $formattedDocument['encodedDocument'] = base64_encode(file_get_contents($pathToDocument));
    
            $actions = ActionModel::get(['select' => ['id'], 'where' => ['mode = ?', 'status_id = ?'], 'data' => [$document['mode'], $document['status']], 'orderBy' => ['id']]);
            foreach ($actions as $action) {
                $formattedDocument['actionsAllowed'][] = $action['id'];
            }
            $attachments = AttachmentModel::getByDocumentId(['select' => ['id'], 'documentId' => $args['id']]);
            foreach ($attachments as $attachment) {
                $formattedDocument['attachments'][] = $attachment['id'];
            }
    
    Damien's avatar
    Damien committed
            HistoryController::add([
                'tableName' => 'main_documents',
                'recordId'  => $args['id'],
                'eventType' => 'VIEW',
    
                'info'      => "documentViewed {$document['title']}"
    
            return $response->withJson(['document' => $formattedDocument]);
    
    Damien's avatar
    Damien committed
        public function create(Request $request, Response $response)
        {
    
            if (!UserController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
                return $response->withStatus(403)->withJson(['errors' => 'Privilege forbidden']);
            }
    
    
            $body = $request->getParsedBody();
    
            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['mode'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Body mode is empty or not a string']);
            } elseif (!Validator::stringType()->notEmpty()->validate($body['processingUser'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Body processingUser 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']);
    
            $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"]);
    
            $processingUser = UserModel::getByLogin(['select' => ['id'], 'login' => $body['processingUser']]);
            if (empty($processingUser)) {
                return $response->withStatus(400)->withJson(['errors' => 'Processing user does not exist']);
            }
    
            $encodedDocument = DocumentController::getEncodedDocumentFromEncodedZip(['encodedZipDocument' => $body['encodedDocument']]);
    
    Damien's avatar
    Damien committed
            if (!empty($encodedDocument['errors'])) {
                return $response->withStatus(500)->withJson(['errors' => $encodedDocument['errors']]);
            }
    
            $storeInfos = DocserverController::storeResourceOnDocServer([
                'encodedFile'       => $encodedDocument['encodedDocument'],
                'format'            => 'pdf',
                'docserverType'     => 'DOC'
            ]);
            if (!empty($storeInfos['errors'])) {
                return $response->withStatus(500)->withJson(['errors' => $storeInfos['errors']]);
            }
    
    
    Damien's avatar
    Damien committed
            $status = StatusModel::get(['select' => ['id'], 'where' => ['reference = ?'], 'data' => ['NEW']]);
    
            DatabaseModel::beginTransaction();
    
            $id = DocumentModel::create([
                'title'             => $body['title'],
                'reference'         => empty($body['reference']) ? null : $body['reference'],
                'description'       => empty($body['description']) ? null : $body['description'],
                'mode'              => $body['mode'] == 'SIGN' ? 'SIGN' : 'NOTE',
                'status'            => $status[0]['id'],
                'processing_user'   => $processingUser['id'],
                'sender'            => $body['sender'],
                'deadline'          => empty($body['deadline']) ? null : $body['deadline'],
                'metadata'          => empty($body['metadata']) ? '{}' : json_encode($body['metadata']),
                'creator'           => $GLOBALS['id']
            ]);
    
    Damien's avatar
    Damien committed
    
            AdrModel::createDocumentAdr([
                'documentId'     => $id,
                'type'           => 'DOC',
                'path'           => $storeInfos['path'],
                'filename'       => $storeInfos['filename'],
                'fingerprint'    => $storeInfos['fingerprint']
            ]);
    
    
            foreach ($body['attachments'] as $key => $value) {
                $value['mainDocumentId'] = $id;
    
                $attachment = AttachmentController::create($value);
                if (!empty($attachment['errors'])) {
                    DatabaseModel::rollbackTransaction();
                    return $response->withStatus(500)->withJson(['errors' => "An error occured for attachment {$key} : {$attachment['errors']}"]);
                }
            }
    
    Damien's avatar
    Damien committed
            HistoryController::add([
                'tableName' => 'main_documents',
                'recordId'  => $id,
                'eventType' => 'CREATION',
    
                'info'      => "documentAdded {$body['subject']}"
    
            DatabaseModel::commitTransaction();
    
    
            return $response->withJson(['id' => $id]);
    
    Damien's avatar
    Damien committed
        public function setAction(Request $request, Response $response, array $args)
    
            if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']])) {
    
                return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
            }
    
    
    Damien's avatar
    Damien committed
            $document = DocumentModel::getById(['select' => ['mode', 'status'], 'id' => $args['id']]);
            $action = ActionModel::get([
                'select'    => ['next_status_id'],
                'where'     => ['mode = ?', 'status_id = ?', 'id = ?'],
                'data'      => [$document['mode'], $document['status'], $args['actionId']]
            ]);
    
            if (empty($action[0])) {
    
    Damien's avatar
    Damien committed
                return $response->withStatus(400)->withJson(['errors' => 'Action does not exist']);
    
    Damien's avatar
    Damien committed
            $action = $action[0];
    
    Damien's avatar
    Damien committed
            $data = $request->getParams();
            if (!empty($data['signatures'])) {
                foreach ($data['signatures'] as $signature) {
                    foreach (['encodedImage', 'width', 'positionX', 'positionY', 'page', 'type'] as $value) {
                        if (!isset($signature[$value])) {
                            return $response->withStatus(400)->withJson(['errors' => $value . ' is empty']);
                        }
                    }
                }
    
    Damien's avatar
    Damien committed
                $adr = AdrModel::getDocumentsAdr([
                    'select'  => ['path', 'filename'],
                    'where'   => ['main_document_id = ?', 'type = ?'],
                    'data'    => [$args['id'], 'DOC']
                ]);
                if (empty($adr)) {
                    return $response->withStatus(400)->withJson(['errors' => 'Document does not exist']);
                }
    
    Damien's avatar
    Damien committed
                $docserver = DocserverModel::getByType(['type' => 'DOC', 'select' => ['path']]);
                if (empty($docserver['path']) || !file_exists($docserver['path'])) {
                    return $response->withStatus(400)->withJson(['errors' => 'Docserver does not exist']);
                }
    
    Damien's avatar
    Damien committed
                $pathToDocument = $docserver['path'] . $adr[0]['path'] . $adr[0]['filename'];
                if (!file_exists($pathToDocument)) {
                    return $response->withStatus(404)->withJson(['errors' => 'Document not found on docserver']);
                }
    
    Damien's avatar
    Damien committed
                $tmpPath     = CoreConfigModel::getTmpPath();
    
                $tmpFilename = $tmpPath . $GLOBALS['id'] . '_' . rand() . '_' . $adr[0]['filename'];
    
    Damien's avatar
    Damien committed
                copy($pathToDocument, $tmpFilename);
    
                $pdf     = new Fpdi('P');
                $nbPages = $pdf->setSourceFile($tmpFilename);
                $pdf->setPrintHeader(false);
    
                for ($i = 1; $i <= $nbPages; $i++) {
                    $page = $pdf->importPage($i);
                    $size = $pdf->getTemplateSize($page);
                    $pdf->AddPage($size['orientation'], $size);
                    $pdf->useImportedPage($page);
                    $pdf->SetAutoPageBreak(false, 0);
                    $pdf->SetMargins(0, 0, 0);
                    $pdf->SetAutoPageBreak(false, 0);
    
                    foreach ($data['signatures'] as $signature) {
                        if ($signature['page'] == $i) {
    
                            if ($signature['positionX'] == 0 && $signature['positionY'] == 0) {
                                $signWidth = $size['width'];
                                $signPosX = 0;
                                $signPosY = 0;
                            } else {
    
                                $signWidth = ($signature['width'] * $size['width']) / 100;
    
                                $signPosX = ($signature['positionX'] * $size['width']) / 100;
                                $signPosY = ($signature['positionY'] * $size['height']) / 100;
                            }
    
                            if ($signature['type'] == 'SVG') {
    
    Damien's avatar
    Damien committed
                                $image = str_replace('data:image/svg+xml;base64,', '', $signature['encodedImage']);
                                $image = base64_decode($image);
    
                                if ($image === false) {
                                    return $response->withStatus(400)->withJson(['errors' => 'base64_decode failed']);
    
                                $imageTmpPath = $tmpPath . $GLOBALS['id'] . '_' . rand() . '_writing.svg';
    
                                file_put_contents($imageTmpPath, $image);
                                $pdf->ImageSVG($imageTmpPath, $signPosX, $signPosY, $signWidth);
    
                            } else {
    
    Damien's avatar
    Damien committed
                                $image = base64_decode($signature['encodedImage']);
    
                                if ($image === false) {
                                    return $response->withStatus(400)->withJson(['errors' => 'base64_decode failed']);
                                }
    
    
                                $imageTmpPath = $tmpPath . $GLOBALS['id'] . '_' . rand() . '_writing.png';
    
                                file_put_contents($imageTmpPath, $image);
                                $pdf->Image($imageTmpPath, $signPosX, $signPosY, $signWidth);
                            }
    
    Damien's avatar
    Damien committed
                $fileContent = $pdf->Output('', 'S');
    
                $storeInfos = DocserverController::storeResourceOnDocServer([
                    'encodedFile'     => base64_encode($fileContent),
                    'format'          => 'pdf',
                    'docserverType'   => 'HANDWRITTEN'
                ]);
    
                AdrModel::createDocumentAdr([
                    'documentId'     => $args['id'],
                    'type'           => 'HANDWRITTEN',
                    'path'           => $storeInfos['path'],
                    'filename'       => $storeInfos['filename'],
                    'fingerprint'    => $storeInfos['fingerprint']
                ]);
    
    Florian Azizian's avatar
    Florian Azizian committed
            DocumentModel::update([
    
    Damien's avatar
    Damien committed
                'set'   => ['status' => $action['next_status_id']],
    
    Florian Azizian's avatar
    Florian Azizian committed
                'where' => ['id = ?'],
    
    Damien's avatar
    Damien committed
                'data'  => [$args['id']]
    
    Florian Azizian's avatar
    Florian Azizian committed
            ]);
    
    
            HistoryController::add([
                'tableName' => 'main_documents',
                'recordId'  => $args['id'],
                'eventType' => 'UP',
    
    Damien's avatar
    Damien committed
                'info'      => "actionDone : {$args['actionId']}"
    
            ]);
    
            return $response->withJson(['success' => 'success']);
        }
    
    
        public function getProcessedDocumentById(Request $request, Response $response, array $args)
    
            if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']]) && !UserController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
                return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
    
            $adr = AdrModel::getDocumentsAdr([
                'select'    => ['path', 'filename', 'fingerprint'],
                'where'     => ['main_document_id = ?', 'type = ?'],
                'data'      => [$args['id'], 'HANDWRITTEN']
            ]);
    
            if (empty($adr[0])) {
                return $response->withJson(['encodedDocument' => null]);
            }
    
    
            $docserver = DocserverModel::getByType(['type' => 'HANDWRITTEN', 'select' => ['path']]);
            if (empty($docserver['path']) || !file_exists($docserver['path'])) {
                return $response->withStatus(400)->withJson(['errors' => 'Docserver does not exist']);
            }
    
            $pathToDocument = $docserver['path'] . $adr[0]['path'] . $adr[0]['filename'];
            if (!file_exists($pathToDocument)) {
                return $response->withStatus(404)->withJson(['errors' => 'Document not found on docserver']);
            }
    
            $fingerprint = DocserverController::getFingerPrint(['path' => $pathToDocument]);
            if ($adr[0]['fingerprint'] != $fingerprint) {
    
                return $response->withStatus(400)->withJson(['errors' => 'Fingerprints do not match']);
    
            }
    
            return $response->withJson(['encodedDocument' => base64_encode(file_get_contents($pathToDocument))]);
        }
    
        public function getStatusById(Request $request, Response $response, array $args)
        {
    
            if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']]) && !UserController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
                return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
    
    Damien's avatar
    Damien committed
            $document = DocumentModel::getById(['select' => ['status', 'mode'], 'id' => $args['id']]);
    
            if (empty($document)) {
                return $response->withStatus(400)->withJson(['errors' => 'Document does not exist']);
            }
    
    
    Damien's avatar
    Damien committed
            $status = StatusModel::getById(['select' => ['id', 'reference', 'label'], 'id' => $document['status']]);
    
            $status['mode'] = $document['mode'];
    
    Damien's avatar
    Damien committed
            return $response->withJson(['status' => $status]);
    
        public static function hasRightById(array $args)
        {
    
            ValidatorModel::notEmpty($args, ['id', 'userId']);
            ValidatorModel::intVal($args, ['id', 'userId']);
    
            $document = DocumentModel::get(['select' => [1], 'where' => ['processing_user = ?', 'id = ?'], 'data' => [$args['userId'], $args['id']]]);
    
            if (empty($document)) {
                return false;
            }
    
            return true;
        }
    
    Damien's avatar
    Damien committed
    
        public static function getEncodedDocumentFromEncodedZip(array $args)
        {
            ValidatorModel::notEmpty($args, ['encodedZipDocument']);
            ValidatorModel::stringType($args, ['encodedZipDocument']);
    
            $tmpPath = CoreConfigModel::getTmpPath();
    
            $zipDocumentOnTmp = $tmpPath . mt_rand() . '_parapheur.zip';
            file_put_contents($zipDocumentOnTmp, base64_decode($args['encodedZipDocument']));
    
            $zipArchive = new \ZipArchive();
            $open = $zipArchive->open($zipDocumentOnTmp);
            if ($open != true) {
                return ['errors' => "getDocumentFromEncodedZip : $open"];
            }
    
            $dirOnTmp = $tmpPath . mt_rand() . '_parapheur';
            if (!$zipArchive->extractTo($dirOnTmp)) {
                return ['errors' => "getDocumentFromEncodedZip : Extract failed"];
            }
    
            $filesOnTmp = scandir($dirOnTmp);
            foreach ($filesOnTmp as $fileOnTmp) {
                if ($fileOnTmp != '.' && $fileOnTmp != '..') {
                    return ['encodedDocument' => base64_encode(file_get_contents("{$dirOnTmp}/{$fileOnTmp}"))];
                }
            }
    
            return ['errors' => "getDocumentFromEncodedZip : No document was found in Zip"];
        }
    
    Damien's avatar
    Damien committed
    }