From 15ad93368dcdc84d6bccabdc0354d1b2bbcd69be Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 8 Nov 2018 08:42:51 -0400 Subject: [PATCH] PMC-10 --- workflow/engine/classes/model/AppDocument.php | 79 +++++++++ workflow/engine/classes/model/AppFolder.php | 18 ++ .../src/ProcessMaker/BusinessModel/Cases.php | 166 ++++++++++++++++-- .../Exception/UploadException.php | 57 ++++++ .../src/ProcessMaker/Services/Api/Cases.php | 34 ++++ 5 files changed, 343 insertions(+), 11 deletions(-) create mode 100644 workflow/engine/src/ProcessMaker/Exception/UploadException.php diff --git a/workflow/engine/classes/model/AppDocument.php b/workflow/engine/classes/model/AppDocument.php index 351473e36..115aecf33 100644 --- a/workflow/engine/classes/model/AppDocument.php +++ b/workflow/engine/classes/model/AppDocument.php @@ -672,5 +672,84 @@ class AppDocument extends BaseAppDocument } return false; } + + /** + * This function will upload a file related to the AppDocument + * + * @param string $appUid + * @param string $userUid + * @param integer $delIndex + * @param mixed $docUid + * @param array $file + * @param string $varName + * @param string $appDocUid + * + * @return object + * @throws Exception + */ + public function uploadAppDocument( + $appUid, + $userUid, + $delIndex = 1, + $docUid = -1, + $file = [], + $varName = null, + $appDocUid = null + ) { + $appDocType = 'ATTACHED'; + $folderId = ''; + // Create the folder + if ($docUid != -1) { + $appDocType = 'INPUT'; + $folder = new AppFolder(); + $folderId = $folder->createFolderFromInputDoc($docUid, $appUid); + } + $fieldsInput = [ + "DOC_VERSION" => 1, + "APP_UID" => $appUid, + "DEL_INDEX" => $delIndex, + "USR_UID" => $userUid, + "DOC_UID" => $docUid, + "APP_DOC_TYPE" => $appDocType, + "APP_DOC_CREATE_DATE" => date("Y-m-d H:i:s"), + "APP_DOC_COMMENT" => "", + "APP_DOC_TITLE" => "", + "APP_DOC_FILENAME" => $file["name"], + "APP_DOC_FIELDNAME" => !empty($varName) ? $varName : null, + "FOLDER_UID" => $folderId + ]; + + // If the APP_DOC_UID exist will create a new version + if (!empty($appDocUid)) { + $fieldsInput["APP_DOC_UID"] = $appDocUid; + } + + // Create the register in the database + $newInstance = new AppDocument(); + $appDocUid = $newInstance->create($fieldsInput); + $docVersion = $newInstance->getDocVersion(); + + // Move the uploaded file to the documents folder + try { + $info = pathinfo($file["name"]); + $extension = ((isset($info["extension"])) ? $info["extension"] : ""); + $pathCase = G::getPathFromUID($appUid); + $fileName = $appDocUid . "_" . $docVersion . "." . $extension; + + G::uploadFile( + $file["tmp_name"], + PATH_DOCUMENT . $pathCase . PATH_SEP, + $fileName + ); + } catch (Exception $e) { + // Delete the register from Database + $this->remove($appDocUid, $docVersion); + + throw $e; + } + + return $newInstance; + } } + diff --git a/workflow/engine/classes/model/AppFolder.php b/workflow/engine/classes/model/AppFolder.php index 48df924ec..2fdba2748 100644 --- a/workflow/engine/classes/model/AppFolder.php +++ b/workflow/engine/classes/model/AppFolder.php @@ -870,5 +870,23 @@ class AppFolder extends BaseAppFolder $oCriteria->add( AppFolderPeer::FOLDER_UID, $FolderUid ); AppFolderPeer::doDelete( $oCriteria ); } + + /** + * This function will create a folder related to the input document + * + * @param string $docUid + * @param string $appUid + * + * @return string + */ + public function createFolderFromInputDoc($docUid, $appUid) + { + $inputDocument = new InputDocument(); + $inputDocumentData = $inputDocument->load($docUid); + $folder = $this->createFromPath($inputDocumentData["INP_DOC_DESTINATION_PATH"], $appUid); + + return $folder; + } + } diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Cases.php b/workflow/engine/src/ProcessMaker/BusinessModel/Cases.php index 8df465021..2c73dff0d 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Cases.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Cases.php @@ -4,50 +4,54 @@ namespace ProcessMaker\BusinessModel; use AppCacheView; use AppCacheViewPeer; -use Applications; -use ApplicationPeer; -use AppSolr; -use AppDelegation; -use AppDelegationPeer; use AppDelay; use AppDelayPeer; +use AppDelegation; +use AppDelegationPeer; use AppDocument; use AppDocumentPeer; use AppHistoryPeer; -use AppThreadPeer; +use ApplicationPeer; +use Applications; use AppNotesPeer; +use AppSolr; use BasePeer; +use Bootstrap; use BpmnEngineServicesSearchIndex; use Cases as ClassesCases; use CasesPeer; -use Criteria; use Configurations; +use Criteria; use DBAdapter; -use Exception; use EntitySolrRequestData; +use Exception; use G; use Groups; use GroupUserPeer; +use InputDocument; use InvalidIndexSearchTextException; use ListParticipatedLast; use PmDynaform; +use ProcessMaker\BusinessModel\ProcessSupervisor as BmProcessSupervisor; use ProcessMaker\BusinessModel\Task as BmTask; use ProcessMaker\BusinessModel\User as BmUser; -use ProcessMaker\BusinessModel\ProcessSupervisor as BmProcessSupervisor; use ProcessMaker\Core\System; +use ProcessMaker\Exception\UploadException; use ProcessMaker\Plugins\PluginRegistry; use ProcessMaker\Services\OAuth2\Server; +use ProcessMaker\Validation\ExceptionRestApi; +use ProcessMaker\Validation\Validator as FileValidator; +use ProcessPeer; use ProcessUser; use ProcessUserPeer; -use ProcessPeer; use RBAC; use ResultSet; use RoutePeer; use SubApplication; use SubProcessPeer; use Task as ModelTask; -use Tasks as ClassesTasks; use TaskPeer; +use Tasks as ClassesTasks; use TaskUserPeer; use Users as ModelUsers; use UsersPeer; @@ -57,6 +61,8 @@ class Cases { private $formatFieldNameInUppercase = true; private $messageResponse = []; + const MB_IN_KB = 1024; + const UNIT_MB = 'MB'; /** * Set the format of the fields name (uppercase, lowercase) @@ -3734,4 +3740,142 @@ class Cases return $isSupervisor; } + + /** + * Upload file in the corresponding folder + * + * @param string $userUid + * @param string $appUid + * @param string $varName + * @param mixed $inpDocUid + * @param string $appDocUid + * + * @return array + * @throws Exception + */ + public function uploadFiles($userUid, $appUid, $varName, $inpDocUid = -1, $appDocUid = null) + { + $response = []; + if (isset($_FILES["form"]["name"]) && count($_FILES["form"]["name"]) > 0) { + // Get the delIndex related to the case + $cases = new ClassesCases(); + $delIndex = $cases->getCurrentDelegation($appUid, $userUid); + // Get information about the user + $user = new ModelUsers(); + $userCreator = $user->loadDetailed($userUid)['USR_FULLNAME']; + $i = 0; + foreach ($_FILES["form"]["name"] as $fieldIndex => $fieldValue) { + if (!is_array($fieldValue)) { + $arrayFileName = [ + 'name' => $_FILES["form"]["name"][$fieldIndex], + 'tmp_name' => $_FILES["form"]["tmp_name"][$fieldIndex], + 'error' => $_FILES["form"]["error"][$fieldIndex] + ]; + + // We will to review the validation related to the Input document + $file = [ + 'filename' => $arrayFileName["name"], + 'path' => $arrayFileName["tmp_name"] + ]; + $this->canUploadFileRelatedToInput($inpDocUid, $file); + + // There is no error, the file uploaded with success + if ($arrayFileName["error"] === UPLOAD_ERR_OK) { + $appDocument = new AppDocument(); + $objCreated = $appDocument->uploadAppDocument( + $appUid, + $userUid, + $delIndex, + $inpDocUid , + $arrayFileName, + $varName, + $appDocUid + ); + $response[$i] = [ + 'appDocUid' => $objCreated->getAppDocUid(), + 'docVersion' => $objCreated->getDocVersion(), + 'appDocFilename' => $objCreated->getAppDocFilename(), + 'appDocCreateDate' => $objCreated->getAppDocCreateDate(), + 'appDocType' => $objCreated->getAppDocType(), + 'appDocIndex' => $objCreated->getAppDocIndex(), + 'appDocCreateUser' => $userCreator + ]; + + $i++; + } else { + throw new UploadException($arrayFileName['error']); + } + } + } + } else { + throw new Exception(G::LoadTranslation('ID_ERROR_UPLOAD_FILE_CONTACT_ADMINISTRATOR')); + } + + return $response; + } + + /** + * Run the validations related to an Input Document + * + * @param array $file + * @param mixed $inpDocUid + * + * @return boolean + * @throws ExceptionRestApi + */ + private function canUploadFileRelatedToInput($file, $inpDocUid = -1) + { + if ($inpDocUid !== -1) { + $inputDocument = new InputDocument(); + $inputExist = $inputDocument->InputExists($inpDocUid); + if ($inputExist) { + $inputProperties = $inputDocument->load($inpDocUid); + $inpDocTypeFile = $inputProperties['INP_DOC_TYPE_FILE']; + $inpDocMaxFileSize = (int)$inputProperties["INP_DOC_MAX_FILESIZE"]; + $inpDocMaxFileSizeUnit = $inputProperties["INP_DOC_MAX_FILESIZE_UNIT"]; + + $validator = new FileValidator(); + // Rule: extension + $validator->addRule() + ->validate($file, function ($file) use ($inpDocTypeFile) { + $result = G::verifyInputDocExtension($inpDocTypeFile, $file->filename, $file->path); + + return $result->status === false; + }) + ->status(415) + ->message(G::LoadTranslation('ID_UPLOAD_INVALID_DOC_TYPE_FILE', [$inpDocTypeFile])) + ->log(function ($rule) { + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), + $rule->getData()->filename); + }); + // Rule: maximum file size + $validator->addRule() + ->validate($file, function ($file) use ($inpDocMaxFileSize, $inpDocMaxFileSizeUnit) { + if ($inpDocMaxFileSize > 0) { + $totalMaxFileSize = $inpDocMaxFileSize * ($inpDocMaxFileSizeUnit == self::UNIT_MB ? self::MB_TO_KB * self::MB_TO_KB : self::MB_TO_KB); + $fileSize = filesize($file->path); + if ($fileSize > $totalMaxFileSize) { + return true; + } + } + + return false; + }) + ->status(413) + ->message(G::LoadTranslation("ID_UPLOAD_INVALID_DOC_MAX_FILESIZE", + [$inpDocMaxFileSize . $inpDocMaxFileSizeUnit])) + ->log(function ($rule) { + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), + $rule->getData()->filename); + }); + $validator->validate(); + // We will to review if the validator has some error + if ($validator->fails()) { + throw new ExceptionRestApi($validator->getMessage(), $validator->getStatus()); + } + } + } + + return true; + } } diff --git a/workflow/engine/src/ProcessMaker/Exception/UploadException.php b/workflow/engine/src/ProcessMaker/Exception/UploadException.php new file mode 100644 index 000000000..5b7a0bf4f --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Exception/UploadException.php @@ -0,0 +1,57 @@ +getMessageByCode($code); + parent::__construct($message, $code); + } + + /** + * Get the message to the corresponding error code + * + * @param integer $code + * + * @return string + */ + private function getMessageByCode($code) + { + // These messages do not have translations because they will be caught in the exceptions + switch ($code) { + case UPLOAD_ERR_INI_SIZE: + $message = "The uploaded file exceeds the upload_max_filesize directive in php.ini"; + break; + case UPLOAD_ERR_FORM_SIZE: + $message = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"; + break; + case UPLOAD_ERR_PARTIAL: + $message = "The uploaded file was only partially uploaded"; + break; + case UPLOAD_ERR_NO_FILE: + $message = "No file was uploaded"; + break; + case UPLOAD_ERR_NO_TMP_DIR: + $message = "Missing a temporary folder"; + break; + case UPLOAD_ERR_CANT_WRITE: + $message = "Failed to write file to disk"; + break; + case UPLOAD_ERR_EXTENSION: + $message = "File upload stopped by extension"; + break; + default: + $message = "Unknown upload error"; + break; + } + + return $message; + } +} diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Cases.php b/workflow/engine/src/ProcessMaker/Services/Api/Cases.php index 4845e8847..5de5dacd2 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Cases.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Cases.php @@ -1370,4 +1370,38 @@ class Cases extends Api } } + /** + * Upload attachment related to the case, it does not need docUid + * Upload document related to the case, it does need docUid + * + * @url POST /:app_uid/upload/:var_name + * @url POST /:app_uid/upload/:var_name/:doc_uid + * @url POST /:app_uid/upload/:var_name/:doc_uid/:app_doc_uid + * + * @param string $app_uid + * @param string $var_name + * @param string $doc_uid + * @param string $app_doc_uid + * + * @return array + * @throws RestException + * + * @access protected + * @class AccessControl {@permission PM_CASES} + */ + public function uploadDocumentToCase($app_uid, $var_name, $doc_uid = '-1', $app_doc_uid = null) + { + try { + $userUid = $this->getUserId(); + $case = new BmCases(); + $response = $case->uploadFiles($userUid, $app_uid, $var_name, $doc_uid, $app_doc_uid); + } catch (ExceptionRestApi $e) { + throw new RestException($e->getCode(), $e->getMessage()); + } catch (Exception $e) { + throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); + } + + return $response; + } + }