diff --git a/api/v1/_uploadPublicFile/PKPUploadPublicFileHandler.inc.php b/api/v1/_uploadPublicFile/PKPUploadPublicFileHandler.inc.php index e9d952e9dc6..d5b8cc84c1a 100644 --- a/api/v1/_uploadPublicFile/PKPUploadPublicFileHandler.inc.php +++ b/api/v1/_uploadPublicFile/PKPUploadPublicFileHandler.inc.php @@ -79,7 +79,7 @@ public function uploadFile($slimRequest, $response, $args) { $request = $this->getRequest(); if (empty($_FILES) || empty($_FILES['file'])) { - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.noUpload'); + return $response->withStatus(400)->withJsonError('api.files.400.noUpload'); } $siteDir = Core::getBaseDir() . '/' . Config::getVar('files', 'public_files_dir') . '/site'; @@ -163,18 +163,18 @@ public function uploadFile($slimRequest, $response, $args) { switch ($fileManager->getUploadErrorCode($filename)) { case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.fileSize', ['maxSize' => Application::getReadableMaxFileSize()]); + return $response->withStatus(400)->withJsonError('api.files.400.fileSize', ['maxSize' => Application::getReadableMaxFileSize()]); case UPLOAD_ERR_PARTIAL: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.409.uploadFailed'); + return $response->withStatus(400)->withJsonError('api.files.400.uploadFailed'); case UPLOAD_ERR_NO_FILE: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.noUpload'); + return $response->withStatus(400)->withJsonError('api.files.400.noUpload'); case UPLOAD_ERR_NO_TMP_DIR: case UPLOAD_ERR_CANT_WRITE: case UPLOAD_ERR_EXTENSION: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.config'); + return $response->withStatus(400)->withJsonError('api.files.400.config'); } } - return $response->withStatus(400)->withJsonError('api.temporaryFiles.409.uploadFailed'); + return $response->withStatus(400)->withJsonError('api.files.400.uploadFailed'); } return $this->getResponse($response->withJson([ diff --git a/api/v1/submissions/PKPSubmissionFileHandler.inc.php b/api/v1/submissions/PKPSubmissionFileHandler.inc.php new file mode 100644 index 00000000000..072b01eb2e1 --- /dev/null +++ b/api/v1/submissions/PKPSubmissionFileHandler.inc.php @@ -0,0 +1,455 @@ +_handlerPath = 'submissions/{submissionId}/files'; + $this->_endpoints = [ + 'GET' => [ + [ + 'pattern' => $this->getEndpointPattern(), + 'handler' => [$this, 'getMany'], + 'roles' => [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_ASSISTANT, ROLE_ID_AUTHOR], + ], + [ + 'pattern' => $this->getEndpointPattern() . '/{submissionFileId}', + 'handler' => [$this, 'get'], + 'roles' => [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_ASSISTANT, ROLE_ID_AUTHOR], + ], + ], + 'POST' => [ + [ + 'pattern' => $this->getEndpointPattern(), + 'handler' => [$this, 'add'], + 'roles' => [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_ASSISTANT, ROLE_ID_AUTHOR], + ], + ], + 'PUT' => [ + [ + 'pattern' => $this->getEndpointPattern() . '/{submissionFileId}', + 'handler' => [$this, 'edit'], + 'roles' => [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_ASSISTANT, ROLE_ID_AUTHOR], + ], + ], + 'DELETE' => [ + [ + 'pattern' => $this->getEndpointPattern() . '/{submissionFileId}', + 'handler' => [$this, 'delete'], + 'roles' => [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_ASSISTANT, ROLE_ID_AUTHOR], + ], + ], + ]; + parent::__construct(); + } + + // + // Implement methods from PKPHandler + // + function authorize($request, &$args, $roleAssignments) { + $route = $this->getSlimRequest()->getAttribute('route'); + import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); // SUBMISSION_FILE_ACCESS_ + + import('lib.pkp.classes.security.authorization.ContextAccessPolicy'); + $this->addPolicy(new ContextAccessPolicy($request, $roleAssignments)); + + import('lib.pkp.classes.security.authorization.SubmissionAccessPolicy'); + $this->addPolicy(new SubmissionAccessPolicy($request, $args, $roleAssignments)); + + if ($route->getName() === 'add') { + $params = $this->getSlimRequest()->getParsedBody(); + $fileStage = isset($params['fileStage']) ? (int) $params['fileStage'] : 0; + import('lib.pkp.classes.security.authorization.internal.SubmissionFileStageAccessPolicy'); + $this->addPolicy(new SubmissionFileStageAccessPolicy($fileStage, SUBMISSION_FILE_ACCESS_MODIFY, 'api.submissionFiles.403.unauthorizedFileStageIdWrite')); + + } elseif ($route->getName() === 'getMany') { + // Anyone passing SubmissionAccessPolicy is allowed to access getMany, + // but the endpoint will return different files depending on the user's + // stage assignments. + + } else { + $accessMode = $this->getSlimRequest()->getMethod() === 'GET' + ? SUBMISSION_FILE_ACCESS_READ + : SUBMISSION_FILE_ACCESS_MODIFY; + import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); + $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, $accessMode, (int) $route->getArgument('submissionFileId'))); + } + + return parent::authorize($request, $args, $roleAssignments); + } + + /** + * Get a collection of submission files + * + * @param \Slim\Http\Request $slimRequest + * @param APIResponse $response + * @param array $args arguments + * @return Response + */ + public function getMany($slimRequest, $response, $args) { + $request = $this->getRequest(); + + $params = []; + + foreach ($slimRequest->getQueryParams() as $param => $val) { + switch ($param) { + case 'fileStages': + case 'reviewRoundIds': + if (is_string($val) && strpos($val, ',') > -1) { + $val = explode(',', $val); + } elseif (!is_array($val)) { + $val = array($val); + } + $params[$param] = array_map('intval', $val); + break; + } + } + + $userRoles = $this->getAuthorizedContextObject(ASSOC_TYPE_USER_ROLES); + $stageAssignments = $this->getAuthorizedContextObject(ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES); + + // Managers can access files for submissions they are not assigned to + if (empty($stageAssignments)) { + if (!in_array(ROLE_ID_MANAGER, $userRoles)) { + return $response->withStatus(403)->withJsonError('api.403.unauthorized'); + } + // @see PKPSubmissionFileService::getAssignedFileStages() for excluded file stages + $params['fileStages'] = [ + SUBMISSION_FILE_SUBMISSION, + SUBMISSION_FILE_REVIEW_FILE, + SUBMISSION_FILE_FINAL, + SUBMISSION_FILE_COPYEDIT, + SUBMISSION_FILE_PROOF, + SUBMISSION_FILE_PRODUCTION_READY, + SUBMISSION_FILE_ATTACHMENT, + SUBMISSION_FILE_REVIEW_REVISION, + SUBMISSION_FILE_INTERNAL_REVIEW_FILE, + SUBMISSION_FILE_INTERNAL_REVIEW_REVISION, + ]; + + // Set the allowed file stages based on stage assignment + // @see PKPSubmissionFileService::getAssignedFileStages() for excluded file stages + } else { + $allowedFileStages = Services::get('submissionFile')->getAssignedFileStages($stageAssignments, SUBMISSION_FILE_ACCESS_READ); + if (empty($params['fileStages'])) { + $params['fileStages'] = $allowedFileStages; + } else { + foreach ($params['fileStages'] as $fileStage) { + if (!in_array($fileStage, $allowedFileStages)) { + return $response->withStatus(403)->withJsonError('api.submissionFiles.403.unauthorizedFileStageId'); + } + } + } + } + + // Check if requested reviewRounds are valid + if (!empty($params['reviewRoundIds'])) { + + // Get the valid review round ids for allowed file stage ids + $allowedReviewRoundIds = []; + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + if (!empty(array_intersect([SUBMISSION_FILE_INTERNAL_REVIEW_FILE, SUBMISSION_FILE_INTERNAL_REVIEW_REVISION], $params['fileStages']))) { + $result = $reviewRoundDao->getBySubmissionId($submission->getId(), WORKFLOW_STAGE_ID_INTERNAL_REVIEW,); + while ($reviewRound = $result->next()) { + $allowedReviewRoundIds[] = $reviewRound->getId(); + } + } + if (!empty(array_intersect([SUBMISSION_FILE_REVIEW_FILE, SUBMISSION_FILE_REVIEW_REVISION], $params['fileStages']))) { + $result = $reviewRoundDao->getBySubmissionId($submission->getId(), WORKFLOW_STAGE_ID_EXTERNAL_REVIEW); + while ($reviewRound = $result->next()) { + $allowedReviewRoundIds[] = $reviewRound->getId(); + } + } + + foreach ($params['reviewRoundIds'] as $reviewRoundId) { + if (!in_array($reviewRoundId, $allowedReviewRoundIds)) { + return $response->withStatus(403)->withJsonError('api.submissionFiles.403.unauthorizedReviewRound'); + } + } + } + + \HookRegistry::call('API::submissions::files::params', [&$params, $slimRequest]); + + $params['submissionIds'] = [$this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION)->getId()]; + + $items = []; + $filesIterator = Services::get('submissionFile')->getMany($params); + if (count($filesIterator)) { + $propertyArgs = [ + 'request' => $request, + 'slimRequest' => $slimRequest, + 'submission' => $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION), + ]; + foreach ($filesIterator as $file) { + $items[] = Services::get('submissionFile')->getSummaryProperties($file, $propertyArgs); + } + } + + $data = [ + 'itemsMax' => Services::get('submissionFile')->getCount($params), + 'items' => $items, + ]; + + return $response->withJson($data, 200); + } + + /** + * Get a single submission file + * + * @param \Slim\Http\Request $slimRequest + * @param APIResponse $response + * @param array $args arguments + * @return Response + */ + public function get($slimRequest, $response, $args) { + $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); + + $data = Services::get('submissionFile')->getFullProperties($submissionFile, [ + 'request' => $this->getRequest(), + 'slimRequest' => $slimRequest, + 'submission' => $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION), + ]); + + return $response->withJson($data, 200); + } + + /** + * Add a new submission file + * + * @param \Slim\Http\Request $slimRequest + * @param APIResponse $response + * @param array $args arguments + * @return Response + */ + public function add($slimRequest, $response, $args) { + $request = $this->getRequest(); + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + + if (empty($_FILES)) { + return $response->withStatus(400)->withJsonError('api.files.400.noUpload'); + } + + if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) { + return $this->getUploadErrorResponse($response, $_FILES['file']['error']); + } + + import('lib.pkp.classes.file.FileManager'); + $fileManager = new FileManager(); + $extension = $fileManager->parseFileExtension($_FILES['file']['name']); + + $submissionDir = Services::get('submissionFile')->getSubmissionDir($request->getContext()->getId(), $submission->getId()); + $fileId = Services::get('file')->add( + $_FILES['file']['tmp_name'], + $submissionDir . '/' . uniqid() . '.' . $extension + ); + + $params = $this->convertStringsToSchema(SCHEMA_SUBMISSION_FILE, $slimRequest->getParsedBody()); + $params['fileId'] = $fileId; + $params['submissionId'] = $submission->getId(); + $params['uploaderUserId'] = (int) $request->getUser()->getId(); + + $primaryLocale = $request->getContext()->getPrimaryLocale(); + $allowedLocales = $request->getContext()->getData('supportedSubmissionLocales'); + + // Set the name if not passed with the request + if (empty($params['name'])) { + $params['name'][$primaryLocale] = $_FILES['file']['name']; + } + + // If no genre has been set and there is only one genre possible, set it automatically + if (empty($params['genreId'])) { + $genres = DAORegistry::getDAO('GenreDAO')->getEnabledByContextId($request->getContext()->getId()); + if ($genres->count === 1) { + $params['genreId'] = $genres->next()->getId(); + } + } + + $errors = Services::get('submissionFile')->validate(VALIDATE_ACTION_ADD, $params, $allowedLocales, $primaryLocale); + + if (!empty($errors)) { + return $response->withStatus(400)->withJson($errors); + } + + // Review attachments and discussion files can not be uploaded through this API endpoint + $notAllowedFileStages = [ + SUBMISSION_FILE_NOTE, + SUBMISSION_FILE_REVIEW_ATTACHMENT, + SUBMISSION_FILE_QUERY, + ]; + if (in_array($params['fileStage'], $notAllowedFileStages)) { + return $response->withStatus(400)->withJsonError('api.submissionFiles.403.unauthorizedFileStageIdWrite'); + } + + // A valid review round is required when uploading to a review file stage + $reviewFileStages = [ + SUBMISSION_FILE_INTERNAL_REVIEW_FILE, + SUBMISSION_FILE_INTERNAL_REVIEW_REVISION, + SUBMISSION_FILE_REVIEW_FILE, + SUBMISSION_FILE_REVIEW_REVISION, + ]; + if (in_array($params['fileStage'], $reviewFileStages)) { + if (empty($params['assocType']) || $params['assocType'] !== ASSOC_TYPE_REVIEW_ROUND || empty($params['assocId'])) { + return $response->withStatus(400)->withJsonError('api.submissionFiles.400.missingReviewRoundAssocType'); + } + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ + $reviewRound = $reviewRoundDao->getById($params['assocId']); + $stageId = in_array($params['fileStage'], [SUBMISSION_FILE_INTERNAL_REVIEW_FILE, SUBMISSION_FILE_INTERNAL_REVIEW_REVISION]) + ? WORKFLOW_STAGE_ID_INTERNAL_REVIEW + : WORKFLOW_STAGE_ID_EXTERNAL_REVIEW; + if (!$reviewRound + || $reviewRound->getData('submissionId') != $params['submissionId'] + || $reviewRound->getData('stageId') != $stageId) { + return $response->withStatus(400)->withJsonError('api.submissionFiles.400.reviewRoundSubmissionNotMatch'); + } + } + + $submissionFile = DAORegistry::getDao('SubmissionFileDAO')->newDataObject(); + $submissionFile->_data = $params; + + $submissionFile = Services::get('submissionFile')->add($submissionFile, $request); + + $data = Services::get('submissionFile')->getFullProperties($submissionFile, [ + 'request' => $request, + 'slimRequest' => $slimRequest, + 'submission' => $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION), + ]); + + return $response->withJson($data, 200); + } + + /** + * Edit a submission file + * + * @param \Slim\Http\Request $slimRequest + * @param APIResponse $response + * @param array $args arguments + * @return Response + */ + public function edit($slimRequest, $response, $args) { + $request = $this->getRequest(); + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); + + $params = $this->convertStringsToSchema(SCHEMA_SUBMISSION_FILE, $slimRequest->getParsedBody()); + + // Don't allow these properties to be modified + unset($params['submissionId']); + unset($params['fileId']); + unset($params['uploaderUserId']); + + if (empty($params) && empty($_FILES['file'])) { + return $response->withStatus(400)->withJsonError('api.submissions.files.400.noParams'); + } + + $primaryLocale = $request->getContext()->getPrimaryLocale(); + $allowedLocales = $request->getContext()->getData('supportedSubmissionLocales'); + + $errors = Services::get('submissionFile')->validate(VALIDATE_ACTION_EDIT, $params, $allowedLocales, $primaryLocale); + + if (!empty($errors)) { + return $response->withStatus(400)->withJson($errors); + } + + // Upload a new file + if (!empty($_FILES['file'])) { + + if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) { + return $this->getUploadErrorResponse($response, $_FILES['file']['error']); + } + + import('lib.pkp.classes.file.FileManager'); + $fileManager = new FileManager(); + $extension = $fileManager->parseFileExtension($_FILES['file']['name']); + $submissionDir = Services::get('submissionFile')->getSubmissionDir($request->getContext()->getId(), $submission->getId()); + $fileId = Services::get('file')->add( + $_FILES['file']['tmp_name'], + $submissionDir . '/' . uniqid() . '.' . $extension + ); + + $params['fileId'] = $fileId; + $params['uploaderUserId'] = $request->getUser()->getId(); + if (empty($params['name'])) { + $params['name'][$primaryLocale] = $_FILES['file']['name']; + } + } + + $submissionFile = Services::get('submissionFile')->edit($submissionFile, $params, $request); + + $data = Services::get('submissionFile')->getFullProperties($submissionFile, [ + 'request' => $request, + 'slimRequest' => $slimRequest, + 'submission' => $submission, + ]); + + return $response->withJson($data, 200); + } + + /** + * Delete a submission file + * + * @param \Slim\Http\Request $slimRequest + * @param APIResponse $response + * @param array $args arguments + * @return Response + */ + public function delete($slimRequest, $response, $args) { + $request = $this->getRequest(); + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); + + $data = Services::get('submissionFile')->getFullProperties($submissionFile, [ + 'request' => $request, + 'slimRequest' => $slimRequest, + 'submission' => $submission, + ]); + + Services::get('submissionFile')->delete($submissionFile); + + return $response->withJson($data, 200); + } + + /** + * Helper method to get the appropriate response when an error + * has occurred during a file upload + * + * @param APIResponse $response + * @param int $error One of the UPLOAD_ERR_ constants + * @return APIResponse + */ + private function getUploadErrorResponse($response, $error) { + switch ($error) { + case UPLOAD_ERR_INI_SIZE: + case UPLOAD_ERR_FORM_SIZE: + return $response->withStatus(400)->withJsonError('api.files.400.fileSize', ['maxSize' => Application::getReadableMaxFileSize()]); + case UPLOAD_ERR_PARTIAL: + return $response->withStatus(400)->withJsonError('api.files.400.uploadFailed'); + case UPLOAD_ERR_NO_FILE: + return $response->withStatus(400)->withJsonError('api.files.400.noUpload'); + case UPLOAD_ERR_NO_TMP_DIR: + case UPLOAD_ERR_CANT_WRITE: + case UPLOAD_ERR_EXTENSION: + return $response->withStatus(400)->withJsonError('api.files.400.config'); + } + return $response->withStatus(400)->withJsonError('api.files.400.uploadFailed'); + } +} diff --git a/api/v1/temporaryFiles/PKPTemporaryFilesHandler.inc.php b/api/v1/temporaryFiles/PKPTemporaryFilesHandler.inc.php index 1a7e24e62ce..a5f102ac9f0 100644 --- a/api/v1/temporaryFiles/PKPTemporaryFilesHandler.inc.php +++ b/api/v1/temporaryFiles/PKPTemporaryFilesHandler.inc.php @@ -79,7 +79,7 @@ public function uploadFile($slimRequest, $response, $args) { $request = $this->getRequest(); if (empty($_FILES)) { - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.noUpload'); + return $response->withStatus(400)->withJsonError('api.files.400.noUpload'); } import('lib.pkp.classes.file.TemporaryFileManager'); @@ -92,18 +92,18 @@ public function uploadFile($slimRequest, $response, $args) { switch ($temporaryFileManager->getUploadErrorCode($fileName)) { case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.fileSize', ['maxSize' => Application::getReadableMaxFileSize()]); + return $response->withStatus(400)->withJsonError('api.files.400.fileSize', ['maxSize' => Application::getReadableMaxFileSize()]); case UPLOAD_ERR_PARTIAL: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.409.uploadFailed'); + return $response->withStatus(400)->withJsonError('api.files.400.uploadFailed'); case UPLOAD_ERR_NO_FILE: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.noUpload'); + return $response->withStatus(400)->withJsonError('api.files.400.noUpload'); case UPLOAD_ERR_NO_TMP_DIR: case UPLOAD_ERR_CANT_WRITE: case UPLOAD_ERR_EXTENSION: - return $response->withStatus(400)->withJsonError('api.temporaryFiles.400.config'); + return $response->withStatus(400)->withJsonError('api.files.400.config'); } } - return $response->withStatus(400)->withJsonError('api.temporaryFiles.409.uploadFailed'); + return $response->withStatus(400)->withJsonError('api.files.400.uploadFailed'); } return $this->getResponse($response->withJson(['id' => $uploadedFile->getId()])); diff --git a/classes/components/forms/submission/PKPSubmissionFileForm.inc.php b/classes/components/forms/submission/PKPSubmissionFileForm.inc.php new file mode 100644 index 00000000000..22ebbd460ac --- /dev/null +++ b/classes/components/forms/submission/PKPSubmissionFileForm.inc.php @@ -0,0 +1,49 @@ +action = $action; + + $this->addField(new FieldOptions('genreId', [ + 'label' => __('submission.submit.genre.label'), + 'description' => __('submission.submit.genre.description'), + 'type' => 'radio', + 'options' => array_map(function($genre) { + return [ + 'value' => (int) $genre->getId(), + 'label' => $genre->getLocalizedName(), + ]; + }, $genres), + 'value' => 0, + ])); + } +} diff --git a/classes/controllers/modals/editorDecision/form/EditorDecisionForm.inc.php b/classes/controllers/modals/editorDecision/form/EditorDecisionForm.inc.php index 2ab9f0980de..d95cb3326ea 100644 --- a/classes/controllers/modals/editorDecision/form/EditorDecisionForm.inc.php +++ b/classes/controllers/modals/editorDecision/form/EditorDecisionForm.inc.php @@ -178,19 +178,17 @@ function _initiateReviewRound($submission, $stageId, $request, $status = null) { // Add the selected files to the new round. $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - // Bring in the SUBMISSION_FILE_* constants. - import('lib.pkp.classes.submission.SubmissionFile'); - // Bring in the Manager (we need it). - import('lib.pkp.classes.file.SubmissionFileManager'); - $submissionFileManager = new SubmissionFileManager($submission->getContextId(), $submission->getId()); foreach (array('selectedFiles', 'selectedAttachments') as $userVar) { $selectedFiles = $this->getData($userVar); if(is_array($selectedFiles)) { foreach ($selectedFiles as $fileId) { - // Retrieve the file last revision number. - $revisionNumber = $submissionFileDao->getLatestRevisionNumber($fileId); - list($newFileId, $newRevision) = $submissionFileManager->copyFileToFileStage($fileId, $revisionNumber, SUBMISSION_FILE_REVIEW_FILE, null, true); - $submissionFileDao->assignRevisionToReviewRound($newFileId, $newRevision, $reviewRound); + $newSubmissionFile = Services::get('submissionFile')->get($fileId); + $newSubmissionFile->setData('fileStage', SUBMISSION_FILE_REVIEW_FILE); + $newSubmissionFile->setData('sourceSubmissionFileId', $fileId); + $newSubmissionFile->setData('assocType', null); + $newSubmissionFile->setData('assocId', null); + $newSubmissionFile = Services::get('submissionFile')->add($newSubmissionFile, $request); + $submissionFileDao->assignRevisionToReviewRound($newSubmissionFile->getId(), $reviewRound); } } } diff --git a/classes/core/PKPApplication.inc.php b/classes/core/PKPApplication.inc.php index 3ef20f269a5..27ddfe43acb 100644 --- a/classes/core/PKPApplication.inc.php +++ b/classes/core/PKPApplication.inc.php @@ -52,6 +52,7 @@ define('ASSOC_TYPE_QUERY', 0x010000a); define('ASSOC_TYPE_QUEUED_PAYMENT', 0x010000b); define('ASSOC_TYPE_PUBLICATION', 0x010000c); +define('ASSOC_TYPE_ACCESSIBLE_FILE_STAGES', 0x010000d); // Constant used in UsageStats for submission files that are not full texts define('ASSOC_TYPE_SUBMISSION_FILE_COUNTER_OTHER', 0x0000213); @@ -471,7 +472,7 @@ public function getDAOMap() { 'SubmissionDisciplineEntryDAO' => 'lib.pkp.classes.submission.SubmissionDisciplineEntryDAO', 'SubmissionEmailLogDAO' => 'lib.pkp.classes.log.SubmissionEmailLogDAO', 'SubmissionEventLogDAO' => 'lib.pkp.classes.log.SubmissionEventLogDAO', - 'SubmissionFileDAO' => 'lib.pkp.classes.submission.SubmissionFileDAO', + 'SubmissionFileDAO' => 'classes.submission.SubmissionFileDAO', 'SubmissionFileEventLogDAO' => 'lib.pkp.classes.log.SubmissionFileEventLogDAO', 'QueryDAO' => 'lib.pkp.classes.query.QueryDAO', 'SubmissionLanguageDAO' => 'lib.pkp.classes.submission.SubmissionLanguageDAO', diff --git a/classes/db/SchemaDAO.inc.php b/classes/db/SchemaDAO.inc.php index 7a1add21d11..62811d2c548 100644 --- a/classes/db/SchemaDAO.inc.php +++ b/classes/db/SchemaDAO.inc.php @@ -281,10 +281,15 @@ private function _getPrimaryDbProps($object) { $primaryDbProps = []; foreach ($this->primaryTableColumns as $propName => $columnName) { if ($propName !== 'id' && array_key_exists($propName, $sanitizedProps)) { - $primaryDbProps[$columnName] = $this->convertToDB($sanitizedProps[$propName], $schema->properties->{$propName}->type); + // If the value is null and the prop is nullable, leave it null + if (is_null($sanitizedProps[$propName]) + && isset($schema->properties->{$propName}->validation) + && in_array('nullable', $schema->properties->{$propName}->validation)) { + $primaryDbProps[$columnName] = null; + // Convert empty string values for DATETIME columns into null values // because an empty string can not be saved to a DATETIME column - if ($primaryDbProps[$columnName] === '' + } elseif ($sanitizedProps[$columnName] === '' && isset($schema->properties->{$propName}->validation) && ( in_array('date_format:Y-m-d H:i:s', $schema->properties->{$propName}->validation) @@ -292,6 +297,8 @@ private function _getPrimaryDbProps($object) { ) ) { $primaryDbProps[$columnName] = null; + } else { + $primaryDbProps[$columnName] = $this->convertToDB($sanitizedProps[$propName], $schema->properties->{$propName}->type); } } } diff --git a/classes/file/BaseSubmissionFileManager.inc.php b/classes/file/BaseSubmissionFileManager.inc.php deleted file mode 100644 index 08bde5dfab5..00000000000 --- a/classes/file/BaseSubmissionFileManager.inc.php +++ /dev/null @@ -1,66 +0,0 @@ -_submissionId = (int) $submissionId; - } - - - // - // Public methods - // - /** - * Get the base path for file storage. - * @return string - */ - function getBasePath() { - $dirNames = Application::getFileDirectories(); - return parent::getBasePath() . $dirNames['submission'] . $this->_submissionId . '/'; - } - - /** - * Get the submission ID that this manager operates upon. - * @return int - */ - function getSubmissionId() { - return $this->_submissionId; - } -} - - diff --git a/classes/file/FileArchive.inc.php b/classes/file/FileArchive.inc.php index 5382a29e45f..955a9fd35de 100644 --- a/classes/file/FileArchive.inc.php +++ b/classes/file/FileArchive.inc.php @@ -15,9 +15,6 @@ class FileArchive { - function __construct() { - } - /** * Assembles an array of filenames into either a tar.gz or a .zip * file, based on what is available. Returns a string representing diff --git a/classes/file/FileManager.inc.php b/classes/file/FileManager.inc.php index 1538c833650..ffe7a58ed13 100644 --- a/classes/file/FileManager.inc.php +++ b/classes/file/FileManager.inc.php @@ -24,12 +24,14 @@ define('DIRECTORY_MODE_MASK', 0777); define('DOCUMENT_TYPE_DEFAULT', 'default'); +define('DOCUMENT_TYPE_AUDIO', 'audio'); define('DOCUMENT_TYPE_EXCEL', 'excel'); define('DOCUMENT_TYPE_HTML', 'html'); define('DOCUMENT_TYPE_IMAGE', 'image'); define('DOCUMENT_TYPE_PDF', 'pdf'); define('DOCUMENT_TYPE_WORD', 'word'); define('DOCUMENT_TYPE_EPUB', 'epub'); +define('DOCUMENT_TYPE_VIDEO', 'video'); define('DOCUMENT_TYPE_ZIP', 'zip'); class FileManager { diff --git a/classes/file/PKPFile.inc.php b/classes/file/PKPFile.inc.php index dfb94254cdb..11638df2d86 100644 --- a/classes/file/PKPFile.inc.php +++ b/classes/file/PKPFile.inc.php @@ -103,12 +103,7 @@ function setFileSize($fileSize) { * @return string */ function getNiceFileSize() { - $niceFileSizeUnits = array('B', 'KB', 'MB', 'GB'); - $size = $this->getData('fileSize'); - for($i = 0; $i < 4 && $size > 1024; $i++) { - $size >>= 10; - } - return $size . $niceFileSizeUnits[$i]; + return Services::get('file')->getNiceFileSize($this->getFileSize()); } diff --git a/classes/file/SubmissionFileManager.inc.php b/classes/file/SubmissionFileManager.inc.php deleted file mode 100644 index b25d37c2f47..00000000000 --- a/classes/file/SubmissionFileManager.inc.php +++ /dev/null @@ -1,398 +0,0 @@ -_handleUpload( - $fileName, $fileStage, $uploaderUserId, - $revisedFileId, $genreId, $assocType, $assocId - ); - } - - /** - * Copy a submission file. - * @param $filePath string the path of the file on the file system - * @param $fileStage int submission file workflow stage - * @param $copyUserId int The id of the user that originates the file copy - * @param $revisedFileId int - * @param $genreId int (e.g. Manuscript, Appendix, etc.) - * @return SubmissionFile - */ - function copySubmissionFile($filePath, $fileStage, $copyUserId, - $revisedFileId = null, $genreId = null, $assocType = null, $assocId = null) { - return $this->_handleCopy( - $filePath, $fileStage, $copyUserId, - $revisedFileId, $genreId, $assocType, $assocId - ); - } - - /** - * Delete a file. - * @param $fileId integer - * @param $revisionId integer - * @return boolean returns true if successful - */ - function deleteById($fileId, $revision = null) { - $submissionFile = $this->_getFile($fileId, $revision); - if (isset($submissionFile)) { - return parent::deleteByPath($submissionFile->getfilePath()); - } else { - return false; - } - } - - /** - * Download a file. - * @param $fileId int the file id of the file to download - * @param $revision int the revision of the file to download - * @param $inline boolean print file as inline instead of attachment, optional - * @param $filename string The client-side download filename (optional) - * @return boolean - */ - function downloadById($fileId, $revision = null, $inline = false, $filename = null) { - $returner = false; - $submissionFile = $this->_getFile($fileId, $revision); - if (isset($submissionFile)) { - // Make sure that the file belongs to the submission. - if ($submissionFile->getSubmissionId() != $this->getSubmissionId()) fatalError('Invalid file id!'); - - $this->recordView($submissionFile); - - // Send the file to the user. - $filePath = $submissionFile->getFilePath(); - $mediaType = $submissionFile->getFileType(); - if(!isset($filename)) $filename = $submissionFile->getClientFileName(); - $returner = parent::downloadByPath($filePath, $mediaType, $inline, $filename); - } - - return $returner; - } - - /** - * Record a file view in database. - * @param $submissionFile SubmissionFile - */ - function recordView($submissionFile) { - // Mark the file as viewed by this user. - $sessionManager = SessionManager::getManager(); - $session = $sessionManager->getUserSession(); - $user = $session->getUser(); - if (is_a($user, 'User')) { - $viewsDao = DAORegistry::getDAO('ViewsDAO'); /* @var $viewsDao ViewsDAO */ - $viewsDao->recordView( - ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getFileIdAndRevision(), - $user->getId() - ); - } - } - - /** - * Copies an existing SubmissionFile and renames it. - * @param $sourceFileId int - * @param $sourceRevision int - * @param $fileStage int - * @param $destFileId int (optional) - * @param $viewable boolean (optional) - * @return array? array(file_id, revision) on success; null on failure - */ - function copyFileToFileStage($sourceFileId, $sourceRevision, $newFileStage, $destFileId = null, $viewable = false) { - if (HookRegistry::call('SubmissionFileManager::copyFileToFileStage', array(&$sourceFileId, &$sourceRevision, &$newFileStage, &$destFileId, &$result))) return $result; - - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $sourceFile = $submissionFileDao->getRevision($sourceFileId, $sourceRevision); /* @var $sourceFile SubmissionFile */ - if (!$sourceFile) return false; - - // Rename the variable just so that we don't get confused. - $destFile = $sourceFile; - - // Find out where the source file lives. - $sourcePath = $sourceFile->getFilePath(); - - // Update the ID (or clear if making a new file) and get new revision number. - if ($destFileId != null) { - $currentRevision = $submissionFileDao->getLatestRevisionNumber($destFileId); - $revision = $currentRevision + 1; - $destFile->setFileId($destFileId); - } else { - $destFile->setFileId(null); - $revision = 1; - } - - // Update the necessary fields of the destination file. - $destFile->setRevision($revision); - $destFile->setFileStage($newFileStage); - $destFile->setDateModified(Core::getCurrentDate()); - $destFile->setViewable($viewable); - // Set the old file as the source - $destFile->setSourceFileId($sourceFileId); - $destFile->setSourceRevision($sourceRevision); - - // Find out where the file should go. - $destPath = $destFile->getFilePath(); - - // Now insert the row into the DB and get the inserted file id. - $insertedFile = $submissionFileDao->insertObject($destFile, $sourcePath); - - return $insertedFile ? array($insertedFile->getFileId(), $insertedFile->getRevision()) : null; - } - - // - // Private helper methods - // - /** - * Upload the file and add it to the database. - * @param $fileName string index into the $_FILES array - * @param $fileStage int submission file stage (one of the SUBMISSION_FILE_* constants) - * @param $uploaderUserId int The id of the user that uploaded the file. - * @param $revisedFileId int ID of an existing file to revise - * @param $genreId int foreign key into genres table (e.g. manuscript, etc.) - * @param $assocType int - * @param $assocId int - * @return SubmissionFile the uploaded submission file or null if an error occured. - */ - function _handleUpload($fileName, $fileStage, $uploaderUserId, - $revisedFileId = null, $genreId = null, $assocType = null, $assocId = null) { - - // Ensure that the file has been correctly uploaded to the server. - if (!$this->uploadedFileExists($fileName)) return null; - - // Retrieve the location of the uploaded file. - $sourceFile = $this->getUploadedFilePath($fileName); - - // Instantiate and pre-populate a new submission file object. - $submissionFile = $this->_instantiateSubmissionFile($sourceFile, $fileStage, $revisedFileId, $genreId, $assocType, $assocId); - if (is_null($submissionFile)) return null; - - // Retrieve and copy the file type of the uploaded file. - $fileType = $this->getUploadedFileType($fileName); - assert($fileType !== false); - $submissionFile->setFileType($fileType); - - // Retrieve and copy the file name of the uploaded file. - $originalFileName = $this->getUploadedFileName($fileName); - assert($originalFileName !== false); - $submissionFile->setOriginalFileName($this->truncateFileName($originalFileName)); - - // Set the uploader's user and user group id. - $submissionFile->setUploaderUserId($uploaderUserId); - - // Copy the uploaded file to its final destination and - // persist its meta-data to the database. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - return $submissionFileDao->insertObject($submissionFile, $fileName, true); - } - - /** - * Copy a file and add it to the database. - * @param $filePath string full path to file on the file system - * @param $fileStage int submission file stage (one of the SUBMISSION_FILE_* constants) - * @param $copyUserId int The id of the user that is copying the file. - * @param $revisedFileId int ID of an existing file to revise - * @param $genreId int foreign key into genres table (e.g. manuscript, etc.) - * @param $assocType int - * @param $assocId int - * @return SubmissionFile the submission file or null if an error occured. - */ - function _handleCopy($filePath, $fileStage, $copyUserId, - $revisedFileId = null, $genreId = null, $assocType = null, $assocId = null) { - - // Ensure that the file exists on the file system - if (!$this->fileExists($filePath)) return null; - - // Instantiate and pre-populate a new submission file object. - $submissionFile = $this->_instantiateSubmissionFile($filePath, $fileStage, $revisedFileId, $genreId, $assocType, $assocId); - if (is_null($submissionFile)) return null; - - // Retrieve and copy the file type of the uploaded file. - $fileType = PKPString::mime_content_type($filePath); - assert($fileType !== false); - $submissionFile->setFileType($fileType); - - // Retrieve the file name from the file path - $originalFileName = basename($filePath); - assert($originalFileName !== false); - $submissionFile->setOriginalFileName($this->truncateFileName($originalFileName)); - - // Set the user and user group id for the copied file - $submissionFile->setUploaderUserId($copyUserId); - - // Save the submission file to the database. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - return $submissionFileDao->insertObject($submissionFile, $filePath, false); - } - - /** - * Routine to instantiate and pre-populate a new submission file. - * @param $sourceFilePath string - * @param $fileStage integer SUBMISSION_FILE_... - * @param $revisedFileId integer optional - * @param $genreId integer optional - * @param $assocId integer optional - * @param $assocType integer optional - * @return SubmissionFile returns the instantiated submission file or null if an error occurs. - */ - function _instantiateSubmissionFile($sourceFilePath, $fileStage, $revisedFileId = null, $genreId = null, $assocType = null, $assocId = null) { - $revisedFile = null; - - // Retrieve the submission file DAO. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - - // Except for reviewer file attachments we either need a genre id or a - // revised file, otherwise we cannot identify the target file - // implementation. - if ($fileStage != SUBMISSION_FILE_REVIEW_ATTACHMENT) { - assert(isset($genreId) || isset($revisedFileId)); - if (!$genreId || $revisedFileId) { - // Retrieve the revised file. (null $fileStage in case the revision is from a previous stage). - $revisedFile = $submissionFileDao->getLatestRevision($revisedFileId, null, $this->getSubmissionId()); - if (!is_a($revisedFile, 'SubmissionFile')) return null; - } - } - - // If we don't have a genre then use the genre from the - // existing file. - if ($revisedFile && !$genreId) { - $genreId = $revisedFile->getGenreId(); - } - - // Instantiate a new submission file implementation. - $submissionFile = $submissionFileDao->newDataObjectByGenreId($genreId); /* @var $submissionFile SubmissionFile */ - $submissionFile->setSubmissionId($this->getSubmissionId()); - - // Instantiate submission locale for the file - $submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */ - $submission = $submissionDao->getById($submissionFile->getSubmissionId()); - $submissionFile->setSubmissionLocale($submission->getLocale()); - - // Do we create a new file or a new revision of an existing file? - if ($revisedFileId) { - // Make sure that the submission of the revised file is - // the same as that of the uploaded file. - if ($revisedFile->getSubmissionId() != $this->getSubmissionId()) return null; - - // If file stages are different we reference with the sourceFileId - // Otherwise, we keep the file id, update the revision, and copy other fields. - if(!is_null($fileStage) && $fileStage !== $revisedFile->getFileStage()) { - $submissionFile->setSourceFileId($revisedFileId); - $submissionFile->setSourceRevision($revisedFile->getRevision()); - $submissionFile->setRevision(1); - $submissionFile->setViewable(false); - } else { - // Create a new revision of the file with the existing file id. - $submissionFile->setFileId($revisedFileId); - $submissionFile->setRevision($revisedFile->getRevision()+1); - - // Copy the file stage (in case of null passed in). - $fileStage = (int)$revisedFile->getFileStage(); - - // Copy the assoc type. - if(!is_null($assocType) && $assocType !== $revisedFile->getAssocType()) fatalError('Invalid submission file assoc type!'); - $assocType = (int)$revisedFile->getAssocType(); - - // Copy the assoc id. - if (!is_null($assocId) && $assocId !== $revisedFile->getAssocId()) fatalError('Invalid submission file assoc ID!'); - $assocId = (int)$revisedFile->getAssocId(); - - // Copy the viewable flag. - $submissionFile->setViewable($revisedFile->getViewable()); - } - - // Copy assorted user-facing metadata. - $submissionFile->copyEditableMetadataFrom($revisedFile); - } else { - // Create the first revision of a new file. - $submissionFile->setRevision(1); - $submissionFile->setViewable($fileStage == SUBMISSION_FILE_SUBMISSION?true:false); // Bug #8308: Submission files should be selected for promotion by default - } - - // Determine and set the file size of the file. - $submissionFile->setFileSize(filesize($sourceFilePath)); - - // Set the file file stage. - $submissionFile->setFileStage($fileStage); - - // Set the file genre. - $submissionFile->setGenreId($genreId); - - // Set dates to the current system date. - $submissionFile->setDateUploaded(Core::getCurrentDate()); - $submissionFile->setDateModified(Core::getCurrentDate()); - - // Is the submission file associated to another entity? - if(isset($assocId)) { - assert(isset($assocType)); - $submissionFile->setAssocType($assocType); - $submissionFile->setAssocId($assocId); - } - - // Return the pre-populated submission file. - return $submissionFile; - } - - /** - * Internal helper method to retrieve file - * information by file ID. - * @param $fileId integer - * @param $revision integer - * @return SubmissionFile - */ - function _getFile($fileId, $revision = null) { - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - if ($revision) { - return $submissionFileDao->getRevision($fileId, $revision); - } else { - return $submissionFileDao->getLatestRevision($fileId); - } - } -} - - diff --git a/classes/file/TemporaryFileManager.inc.php b/classes/file/TemporaryFileManager.inc.php index 65673ce4fdf..77381c729fa 100644 --- a/classes/file/TemporaryFileManager.inc.php +++ b/classes/file/TemporaryFileManager.inc.php @@ -111,44 +111,6 @@ function handleUpload($fileName, $userId) { } } - /** - * Create a new temporary file from a submission file. - * @param $submissionFile object - * @param $userId int - * @return object The new TemporaryFile or false on failure - */ - function submissionToTemporaryFile($submissionFile, $userId) { - // Get the file extension, then rename the file. - $fileExtension = $this->parseFileExtension($submissionFile->getServerFileName()); - - if (!$this->fileExists($this->filesDir, 'dir')) { - // Try to create destination directory - $this->mkdirtree($this->filesDir); - } - - $newFileName = basename(tempnam($this->filesDir, $fileExtension)); - if (!$newFileName) return false; - - if (copy($submissionFile->getFilePath(), $this->filesDir . $newFileName)) { - $temporaryFileDao = DAORegistry::getDAO('TemporaryFileDAO'); /* @var $temporaryFileDao TemporaryFileDAO */ - $temporaryFile = $temporaryFileDao->newDataObject(); - - $temporaryFile->setUserId($userId); - $temporaryFile->setServerFileName($newFileName); - $temporaryFile->setFileType($submissionFile->getFileType()); - $temporaryFile->setFileSize($submissionFile->getFileSize()); - $temporaryFile->setOriginalFileName($submissionFile->getOriginalFileName()); - $temporaryFile->setDateUploaded(Core::getCurrentDate()); - - $temporaryFileDao->insertObject($temporaryFile); - - return $temporaryFile; - - } else { - return false; - } - } - /** * Perform periodic cleanup tasks. This is used to occasionally * remove expired temporary files. diff --git a/classes/handler/APIHandler.inc.php b/classes/handler/APIHandler.inc.php index 3ea293cccf0..305681b82f9 100644 --- a/classes/handler/APIHandler.inc.php +++ b/classes/handler/APIHandler.inc.php @@ -138,7 +138,7 @@ public function __construct() { /** * Return PKP request object * - * @return PKPRequest + * @return Request */ public function getRequest() { return $this->_request; @@ -273,8 +273,7 @@ public function getParameter($parameterName, $default = null) { * @return array Converted parameters */ public function convertStringsToSchema($schema, $params) { - $schemaService = Services::get('schema'); - $schema = $schemaService->get($schema); + $schema = Services::get('schema')->get($schema); foreach ($params as $paramName => $paramValue) { if (!property_exists($schema->properties, $paramName)) { diff --git a/classes/linkAction/LinkAction.inc.php b/classes/linkAction/LinkAction.inc.php index d4a752a60f5..c093d23a017 100644 --- a/classes/linkAction/LinkAction.inc.php +++ b/classes/linkAction/LinkAction.inc.php @@ -99,22 +99,6 @@ function getToolTip() { return $this->_toolTip; } - /** - * Get a title for display when a user hovers over the - * link action. Default to the regular title if it is set. - * @return string - */ - function getHoverTitle() { - if ($this->getToolTip()) { - return $this->getToolTip(); - } else { - // for the locale key, remove any unique ids from the id. - $id = preg_replace('/([^-]+)\-.+$/', '$1', $this->getId()); - $title = __('grid.action.' . $id); - return $title; - } - } - /** * Get the action image. * @return string diff --git a/classes/log/EventLogEntry.inc.php b/classes/log/EventLogEntry.inc.php index a32f5f92ff9..fac9f0448df 100644 --- a/classes/log/EventLogEntry.inc.php +++ b/classes/log/EventLogEntry.inc.php @@ -172,10 +172,9 @@ function getTranslatedMessage($locale = null, $hideReviewerName = false) { if (isset($params['fileStage']) && $params['fileStage'] === SUBMISSION_FILE_REVIEW_ATTACHMENT) { assert(isset($params['fileId']) && isset($params['submissionId'])); $anonymousAuthor = true; - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFile = $submissionFileDao->getLatestRevision($params['fileId']); - if ($submissionFile && $submissionFile->getAssocType() === ASSOC_TYPE_REVIEW_ASSIGNMENT) { - $reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getAssocId()); + $submissionFile = Services::get('submissionFile')->get($params['id']); + if ($submissionFile && $submissionFile->getData('assocType') === ASSOC_TYPE_REVIEW_ASSIGNMENT) { + $reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getData('assocId')); if ($reviewAssignment && !in_array($reviewAssignment->getReviewMethod(), array(SUBMISSION_REVIEW_METHOD_ANONYMOUS, SUBMISSION_REVIEW_METHOD_DOUBLEANONYMOUS))) { $anonymousAuthor = false; } diff --git a/classes/log/PKPSubmissionEventLogEntry.inc.php b/classes/log/PKPSubmissionEventLogEntry.inc.php index 8ea80ef72ee..8490632576b 100644 --- a/classes/log/PKPSubmissionEventLogEntry.inc.php +++ b/classes/log/PKPSubmissionEventLogEntry.inc.php @@ -58,9 +58,6 @@ define('SUBMISSION_LOG_REVIEW_READY', 0x40000018); define('SUBMISSION_LOG_REVIEW_CONFIRMED', 0x40000019); -// Deletion of the last revision of a file -define('SUBMISSION_LOG_LAST_REVISION_DELETED', 0x50000003); - // Production events define('SUBMISSION_LOG_PROOFS_APPROVED', 0x50000008); diff --git a/classes/log/SubmissionFileEventLogDAO.inc.php b/classes/log/SubmissionFileEventLogDAO.inc.php index 90a59c84651..2354d0c92ae 100644 --- a/classes/log/SubmissionFileEventLogDAO.inc.php +++ b/classes/log/SubmissionFileEventLogDAO.inc.php @@ -31,11 +31,11 @@ function newDataObject() { /** * Get event log entries by submission file ID. - * @param $fileId int + * @param $submissionFileId int * @return DAOResultFactory */ - function getByFileId($fileId) { - return $this->getByAssoc(ASSOC_TYPE_SUBMISSION_FILE, $fileId); + function getById($submissionFileId) { + return $this->getByAssoc(ASSOC_TYPE_SUBMISSION_FILE, $submissionFileId); } } diff --git a/classes/log/SubmissionFileEventLogEntry.inc.php b/classes/log/SubmissionFileEventLogEntry.inc.php index cc87ea39163..13bf8fa6d5d 100644 --- a/classes/log/SubmissionFileEventLogEntry.inc.php +++ b/classes/log/SubmissionFileEventLogEntry.inc.php @@ -21,7 +21,7 @@ define('SUBMISSION_LOG_FILE_UPLOAD', 0x50000001); define('SUBMISSION_LOG_FILE_DELETE', 0x50000002); define('SUBMISSION_LOG_FILE_REVISION_UPLOAD', 0x50000008); -define('SUBMISSION_LOG_FILE_REVISION_DELETE', 0x50000009); +define('SUBMISSION_LOG_FILE_EDIT', 0x50000009); // Audit events define('SUBMISSION_LOG_FILE_AUDITOR_ASSIGN', 0x50000004); @@ -30,22 +30,6 @@ define('SUBMISSION_LOG_FILE_SIGNOFF_SIGNOFF', 0x50000007); class SubmissionFileEventLogEntry extends EventLogEntry { - - /** - * Set the associated file ID. - * @param $fileId int File ID - */ - function setFileId($fileId) { - return $this->setAssocId($fileId); - } - - /** - * Get the associated file ID. - * @return int File ID - */ - function getFileId() { - return $this->getAssocId(); - } } diff --git a/classes/log/SubmissionFileLog.inc.php b/classes/log/SubmissionFileLog.inc.php index 306712f112b..0153041d7d9 100644 --- a/classes/log/SubmissionFileLog.inc.php +++ b/classes/log/SubmissionFileLog.inc.php @@ -37,7 +37,7 @@ static function logEvent($request, $submissionFile, $eventType, $messageKey, $pa if ($user) $entry->setUserId($user->getId()); $entry->setAssocType(ASSOC_TYPE_SUBMISSION_FILE); - $entry->setAssocId($submissionFile->getFileId()); + $entry->setAssocId($submissionFile->getId()); // Set explicit parts of the log entry $entry->setEventType($eventType); diff --git a/classes/migration/FilesMigration.inc.php b/classes/migration/FilesMigration.inc.php new file mode 100644 index 00000000000..a13fd044e3b --- /dev/null +++ b/classes/migration/FilesMigration.inc.php @@ -0,0 +1,38 @@ +create('files', function (Blueprint $table) { + $table->bigInteger('file_id')->autoIncrement(); + $table->string('path', 255); + }); + } + + /** + * Reverse the migration. + * @return void + */ + public function down() { + Capsule::schema()->drop('files'); + } +} diff --git a/classes/migration/ReviewsMigration.inc.php b/classes/migration/ReviewsMigration.inc.php index ef398d4b383..bf6d7e41b5a 100644 --- a/classes/migration/ReviewsMigration.inc.php +++ b/classes/migration/ReviewsMigration.inc.php @@ -77,18 +77,20 @@ public function up() { $table->bigInteger('submission_id'); $table->bigInteger('review_round_id'); $table->smallInteger('stage_id'); - $table->bigInteger('file_id'); + $table->bigInteger('submission_file_id'); $table->bigInteger('revision')->default(1); $table->index(['submission_id'], 'review_round_files_submission_id'); - $table->unique(['submission_id', 'review_round_id', 'file_id', 'revision'], 'review_round_files_pkey'); + $table->unique(['submission_id', 'review_round_id', 'submission_file_id'], 'review_round_files_pkey'); + $table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files'); }); // Associates reviewable submission files with reviews Capsule::schema()->create('review_files', function (Blueprint $table) { $table->bigInteger('review_id'); - $table->bigInteger('file_id'); + $table->bigInteger('submission_file_id'); $table->index(['review_id'], 'review_files_review_id'); - $table->unique(['review_id', 'file_id'], 'review_files_pkey'); + $table->unique(['review_id', 'submission_file_id'], 'review_files_pkey'); + $table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files'); }); } diff --git a/classes/migration/SubmissionFilesMigration.inc.php b/classes/migration/SubmissionFilesMigration.inc.php index 976d15a2dc2..835c3d214d6 100644 --- a/classes/migration/SubmissionFilesMigration.inc.php +++ b/classes/migration/SubmissionFilesMigration.inc.php @@ -12,7 +12,6 @@ */ use Illuminate\Database\Migrations\Migration; -use Illuminate\Database\Schema\Builder; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Capsule\Manager as Capsule; @@ -24,63 +23,44 @@ class SubmissionFilesMigration extends Migration { public function up() { // Files associated with submission. Includes submission files, etc. Capsule::schema()->create('submission_files', function (Blueprint $table) { - $table->bigInteger('file_id')->autoIncrement(); - $table->bigInteger('revision'); - $table->bigInteger('source_file_id')->nullable(); - $table->bigInteger('source_revision')->nullable(); + $table->bigInteger('submission_file_id')->autoIncrement(); $table->bigInteger('submission_id'); - $table->string('file_type', 255); + $table->bigInteger('file_id'); + $table->bigInteger('source_submission_file_id')->nullable(); $table->bigInteger('genre_id')->nullable(); - $table->bigInteger('file_size'); - $table->string('original_file_name', 127)->nullable(); $table->bigInteger('file_stage'); $table->string('direct_sales_price', 255)->nullable(); $table->string('sales_type', 255)->nullable(); $table->smallInteger('viewable')->nullable(); - $table->datetime('date_uploaded'); - $table->datetime('date_modified'); + $table->datetime('created_at'); + $table->datetime('updated_at'); $table->bigInteger('uploader_user_id')->nullable(); $table->bigInteger('assoc_type')->nullable(); $table->bigInteger('assoc_id')->nullable(); $table->index(['submission_id'], 'submission_files_submission_id'); - // pkp/pkp-lib#5804 + // pkp/pkp-lib#5804 $table->index(['file_stage', 'assoc_type', 'assoc_id'], 'submission_files_stage_assoc'); + $table->foreign('file_id')->references('file_id')->on('files'); }); - // Work-around for compound primary key - switch (Capsule::connection()->getDriverName()) { - case 'mysql': Capsule::connection()->unprepared("ALTER TABLE submission_files DROP PRIMARY KEY, ADD PRIMARY KEY (file_id, revision)"); break; - case 'pgsql': Capsule::connection()->unprepared("ALTER TABLE submission_files DROP CONSTRAINT submission_files_pkey; ALTER TABLE submission_files ADD PRIMARY KEY (file_id, revision);"); break; - } // Article supplementary file metadata. Capsule::schema()->create('submission_file_settings', function (Blueprint $table) { - $table->bigInteger('file_id'); + $table->bigInteger('submission_file_id'); $table->string('locale', 14)->default(''); $table->string('setting_name', 255); $table->text('setting_value')->nullable(); - $table->string('setting_type', 6)->comment('(bool|int|float|string|object|date)'); - $table->index(['file_id'], 'submission_file_settings_id'); - $table->unique(['file_id', 'locale', 'setting_name'], 'submission_file_settings_pkey'); - }); - - // Submission visuals. - Capsule::schema()->create('submission_artwork_files', function (Blueprint $table) { - $table->bigInteger('file_id'); - $table->bigInteger('revision'); - $table->text('caption')->nullable(); - $table->string('credit', 255)->nullable(); - $table->string('copyright_owner', 255)->nullable(); - $table->text('copyright_owner_contact')->nullable(); - $table->text('permission_terms')->nullable(); - $table->bigInteger('permission_file_id')->nullable(); - $table->bigInteger('chapter_id')->nullable(); - $table->bigInteger('contact_author')->nullable(); + $table->string('setting_type', 6)->default('string')->comment('(bool|int|float|string|object|date)'); + $table->index(['submission_file_id'], 'submission_file_settings_id'); + $table->unique(['submission_file_id', 'locale', 'setting_name'], 'submission_file_settings_pkey'); }); - // Submission supplementary content. - Capsule::schema()->create('submission_supplementary_files', function (Blueprint $table) { + // Submission file revisions + Capsule::schema()->create('submission_file_revisions', function (Blueprint $table) { + $table->bigInteger('revision_id')->autoIncrement(); + $table->bigInteger('submission_file_id'); $table->bigInteger('file_id'); - $table->bigInteger('revision'); + $table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files'); + $table->foreign('file_id')->references('file_id')->on('files'); }); } @@ -89,8 +69,7 @@ public function up() { * @return void */ public function down() { - Capsule::schema()->drop('submission_supplementary_files'); - Capsule::schema()->drop('submission_artwork_files'); + Capsule::schema()->drop('submission_file_revisions'); Capsule::schema()->drop('submission_file_settings'); Capsule::schema()->drop('submission_files'); } diff --git a/classes/migration/SubmissionsMigration.inc.php b/classes/migration/SubmissionsMigration.inc.php index d0882c085a4..d51c7e57345 100644 --- a/classes/migration/SubmissionsMigration.inc.php +++ b/classes/migration/SubmissionsMigration.inc.php @@ -31,12 +31,13 @@ public function up() { $table->datetime('date_submitted')->nullable(); $table->datetime('last_modified')->nullable(); $table->bigInteger('stage_id')->default(WORKFLOW_STAGE_ID_SUBMISSION); + $table->string('locale', 14)->nullable(); import('lib.pkp.classes.submission.PKPSubmission'); // for constant $table->smallInteger('status')->default(STATUS_QUEUED); $table->smallInteger('submission_progress')->default(1); - // Used in OMP only; should not be null there + // Used in OMP only; should not be null there $table->smallInteger('work_type')->default(0)->nullable(); $table->index(['context_id'], 'submissions_context_id'); $table->index(['current_publication_id'], 'submissions_publication_id'); diff --git a/classes/migration/upgrade/PKPv3_3_0UpgradeMigration.inc.php b/classes/migration/upgrade/PKPv3_3_0UpgradeMigration.inc.php index ba5935dff09..88776ea5de2 100755 --- a/classes/migration/upgrade/PKPv3_3_0UpgradeMigration.inc.php +++ b/classes/migration/upgrade/PKPv3_3_0UpgradeMigration.inc.php @@ -114,6 +114,26 @@ public function up() { $this->_populateEmailTemplates(); $this->_makeRemoteUrlLocalizable(); + + // pkp/pkp-lib#6057: Migrate locale property from publications to submissions + Capsule::schema()->table('submissions', function (Blueprint $table) { + $table->string('locale', 14)->nullable(); + }); + $currentPublicationIds = Capsule::table('submissions')->pluck('current_publication_id'); + $submissionLocales = Capsule::table('publications') + ->whereIn('publication_id', $currentPublicationIds) + ->pluck('locale', 'submission_id'); + foreach ($submissionLocales as $submissionId => $locale) { + Capsule::table('submissions as s') + ->where('s.submission_id', '=', $submissionId) + ->update(['locale' => $locale]); + } + Capsule::schema()->table('publications', function (Blueprint $table) { + $table->dropColumn('locale'); + }); + + // pkp/pkp-lib#6057 Submission files refactor + $this->_migrateSubmissionFiles(); } /** @@ -185,4 +205,235 @@ private function _makeRemoteUrlLocalizable() { $table->dropColumn('url'); }); } + + /** + * Migrate submission files after major refactor + * + * - Add files table to manage underlying file storage + * - Replace the use of file_id/revision as a unique id with a single + * auto-incrementing submission_file_id, and update all references. + * - Move revisions to a submission_file_revisons table. + * - Drop unused columns in submission_files table. + * + * @see pkp/pkp-lib#6057 + */ + private function _migrateSubmissionFiles() { + import('lib.pkp.classes.submission.SubmissionFile'); // SUBMISSION_FILE_ constants + + // Create a new table to track files in file storage + Capsule::schema()->create('files', function (Blueprint $table) { + $table->bigInteger('file_id')->autoIncrement(); + $table->string('path', 255); + }); + + // Create a new table to track submission file revisions + Capsule::schema()->create('submission_file_revisions', function (Blueprint $table) { + $table->bigInteger('revision_id')->autoIncrement(); + $table->bigInteger('submission_file_id'); + $table->bigInteger('file_id'); + }); + + // Add columns to submission_files table + Capsule::schema()->table('submission_files', function (Blueprint $table) { + $table->bigInteger('new_file_id'); // Renamed at the end of the migration + }); + + // Drop unique keys that will cause trouble while we're migrating + Capsule::schema()->table('review_round_files', function (Blueprint $table) { + $table->dropUnique('review_round_files_pkey'); + }); + + // Create entry in files and revisions tables for every submission_file + import('lib.pkp.classes.file.FileManager'); + $fileManager = new FileManager(); + $rows = Capsule::table('submission_files') + ->orderBy('file_id') + ->orderBy('revision') + ->get([ + 'file_id', + 'revision', + 'submission_id', + 'genre_id', + 'file_stage', + 'date_uploaded', + 'original_file_name' + ]); + foreach ($rows as $row) { + // Reproduces the removed method SubmissionFile::_generateFileName() + // genre is %s because it can be blank with review attachments + $filename = sprintf( + '%d-%s-%d-%d-%d-%s.%s', + $row->submission_id, + $row->genre_id, + $row->file_id, + $row->revision, + $row->file_stage, + date('Ymd', strtotime($row->date_uploaded)), + strtolower_codesafe($fileManager->parseFileExtension($row->original_file_name)) + ); + $contextId = Capsule::table('submissions')->where('submission_id', '=', $row->submission_id)->first()->context_id; + $path = sprintf( + '%s/%s/%s', + Services::get('submissionFile')->getSubmissionDir($contextId, $row->submission_id), + $this->_fileStageToPath($row->file_stage), + $filename + ); + if (!Services::get('file')->fs->has($path)) { + throw new Exception("A submission file was expected but not found at $path."); + } + $newFileId = Capsule::table('files')->insertGetId(['path' => $path]); + Capsule::table('submission_files') + ->where('file_id', $row->file_id) + ->where('revision', $row->revision) + ->update(['new_file_id' => $newFileId]); + Capsule::table('submission_file_revisions')->insert([ + 'submission_file_id' => $row->file_id, + 'file_id' => $newFileId, + ]); + + // Update revision data in event logs + $eventLogIds = Capsule::table('event_log_settings') + ->where('setting_name', '=', 'fileId') + ->where('setting_value', '=', $row->file_id) + ->pluck('log_id'); + Capsule::table('event_log_settings') + ->whereIn('log_id', $eventLogIds) + ->where('setting_name', 'fileRevision') + ->where('setting_value', '=', $row->revision) + ->update(['setting_value' => $newFileId]); + } + + // Collect rows that will be deleted because they are old revisions + // They are identified by the new_file_id column, which is the only unique + // column on the table at this point. + $newFileIdsToDelete = []; + + // Get all the unique file_ids. For each one, determine the latest revision + // in order to keep it in the table. The others will be flagged for removal + $revisionRowFileIds = Capsule::table('submission_files') + ->groupBy('file_id') + ->pluck('file_id'); + foreach ($revisionRowFileIds as $revisionRowFileId) { + $submissionFileRows = Capsule::table('submission_files') + ->where('file_id', '=', $revisionRowFileId) + ->orderBy('revision', 'desc') + ->get([ + 'file_id', + 'new_file_id', + ]); + $latestFileId = $submissionFileRows[0]->new_file_id; + foreach ($submissionFileRows as $submissionFileRow) { + if ($submissionFileRow->new_file_id !== $latestFileId) { + $newFileIdsToDelete[] = $submissionFileRow->new_file_id; + } + } + } + + // Delete the rows for old revisions + Capsule::table('submission_files') + ->whereIn('new_file_id', $newFileIdsToDelete) + ->delete(); + + // Set assoc_type and assoc_id for all review round files + // Run this before migration to internal review file stages + $rows = Capsule::table('review_round_files')->get(); + foreach ($rows as $row) { + Capsule::table('submission_files') + ->where('file_id', '=', $row->file_id) + ->whereIn('file_stage', [SUBMISSION_FILE_REVIEW_FILE, SUBMISSION_FILE_REVIEW_REVISION]) + ->update([ + 'assoc_type' => ASSOC_TYPE_REVIEW_ROUND, + 'assoc_id' => $row->review_round_id, + ]); + } + + // Update file stage for all internal review files + Capsule::table('submission_files as sf') + ->leftJoin('review_round_files as rrf', 'sf.file_id', '=', 'rrf.file_id') + ->where('sf.file_stage', '=', SUBMISSION_FILE_REVIEW_FILE) + ->where('rrf.stage_id', '=', WORKFLOW_STAGE_ID_INTERNAL_REVIEW) + ->update(['sf.file_stage' => SUBMISSION_FILE_INTERNAL_REVIEW_FILE]); + Capsule::table('submission_files as sf') + ->leftJoin('review_round_files as rrf', 'sf.file_id', '=', 'rrf.file_id') + ->where('sf.file_stage', '=', SUBMISSION_FILE_REVIEW_REVISION) + ->where('rrf.stage_id', '=', WORKFLOW_STAGE_ID_INTERNAL_REVIEW) + ->update(['sf.file_stage' => SUBMISSION_FILE_INTERNAL_REVIEW_REVISION]); + + // Update name of event log params to reflect new file structure + Capsule::table('event_log_settings') + ->where('setting_name', 'fileId') + ->update(['setting_name' => 'submissionFileId']); + Capsule::table('event_log_settings') + ->where('setting_name', 'fileRevision') + ->update(['setting_name' => 'fileId']); + + // Restructure submission_files and submission_file_settings tables + Capsule::schema()->table('submission_files', function (Blueprint $table) { + $table->renameColumn('file_id', 'submission_file_id'); + $table->renameColumn('new_file_id', 'file_id'); + $table->renameColumn('source_file_id', 'source_submission_file_id'); + $table->renameColumn('date_uploaded', 'created_at'); + $table->renameColumn('date_modified', 'updated_at'); + $table->dropColumn('revision'); + $table->dropColumn('source_revision'); + $table->dropColumn('file_size'); + $table->dropColumn('file_type'); + $table->dropColumn('original_file_name'); + $table->foreign('file_id')->references('file_id')->on('files'); + }); + Capsule::schema()->table('submission_file_settings', function (Blueprint $table) { + $table->renameColumn('file_id', 'submission_file_id'); + $table->string('setting_type', 6)->default('string')->change(); + }); + + // Update columns in related tables + Capsule::schema()->table('review_round_files', function (Blueprint $table) { + $table->renameColumn('file_id', 'submission_file_id'); + $table->dropColumn('revision'); + $table->unique(['submission_id', 'review_round_id', 'submission_file_id'], 'review_round_files_pkey'); + $table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files'); + }); + Capsule::schema()->table('review_files', function (Blueprint $table) { + $table->renameColumn('file_id', 'submission_file_id'); + $table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files'); + }); + Capsule::schema()->table('publication_galleys', function (Blueprint $table) { + $table->renameColumn('file_id', 'submission_file_id'); + $table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files'); + }); + Capsule::schema()->table('submission_file_revisions', function (Blueprint $table) { + $table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files'); + $table->foreign('file_id')->references('file_id')->on('files'); + }); + } + + /** + * Get the directory of a file based on its file stage + * + * @param int $fileStage ONe of SUBMISSION_FILE_ constants + * @return string + */ + private function _fileStageToPath($fileStage) { + import('lib.pkp.classes.submission.SubmissionFile'); + static $fileStagePathMap = [ + SUBMISSION_FILE_SUBMISSION => 'submission', + SUBMISSION_FILE_NOTE => 'note', + SUBMISSION_FILE_REVIEW_FILE => 'submission/review', + SUBMISSION_FILE_REVIEW_ATTACHMENT => 'submission/review/attachment', + SUBMISSION_FILE_REVIEW_REVISION => 'submission/review/revision', + SUBMISSION_FILE_FINAL => 'submission/final', + SUBMISSION_FILE_COPYEDIT => 'submission/copyedit', + SUBMISSION_FILE_DEPENDENT => 'submission/proof', + SUBMISSION_FILE_PROOF => 'submission/proof', + SUBMISSION_FILE_PRODUCTION_READY => 'submission/productionReady', + SUBMISSION_FILE_ATTACHMENT => 'attachment', + SUBMISSION_FILE_QUERY => 'submission/query', + ]; + + if (!isset($fileStagePathMap[$fileStage])) { + throw new Exception('A file assigned to the file stage ' . $fileStage . ' could not be migrated.'); + } + + return $fileStagePathMap[$fileStage]; + } } diff --git a/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.inc.php b/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.inc.php index 3c66a9886e9..cc164956bd0 100644 --- a/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.inc.php +++ b/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.inc.php @@ -91,9 +91,11 @@ public function updateNotification($request, $userIds, $assocType, $assocId) { $productionQueries = $queryDao->getByAssoc(ASSOC_TYPE_SUBMISSION, $submissionId, WORKFLOW_STAGE_ID_PRODUCTION); // Get the copyedited files - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ import('lib.pkp.classes.submission.SubmissionFile'); - $copyeditedFiles = $submissionFileDao->getLatestRevisions($submissionId, SUBMISSION_FILE_COPYEDIT); + $countCopyeditedFiles = Services::get('submissionFile')->getCount([ + 'submissionIds' => [$submissionId], + 'fileStages' => [SUBMISSION_FILE_COPYEDIT], + ]); // Get representations $representationDao = Application::getRepresentationDAO(); @@ -147,7 +149,7 @@ public function updateNotification($request, $userIds, $assocType, $assocId) { } break; case WORKFLOW_STAGE_ID_EDITING: - if (!empty($copyeditedFiles)) { + if ($countCopyeditedFiles) { // Remove 'assign a copyeditor' and 'awaiting copyedits' notification $this->_removeNotification($submissionId, $editorStageAssignment->getUserId(), $notificationType, $contextId); } else { diff --git a/classes/plugins/ImportExportPlugin.inc.php b/classes/plugins/ImportExportPlugin.inc.php index a7c184a2fb0..c114aa2477f 100644 --- a/classes/plugins/ImportExportPlugin.inc.php +++ b/classes/plugins/ImportExportPlugin.inc.php @@ -178,7 +178,7 @@ function displayXMLValidationErrors($errors, $xml) { echo '

' . htmlspecialchars($xml) . '

'; echo ''; } - fatalError(__('plugins.importexport.common.error.validation')); + throw new Exception(__('plugins.importexport.common.error.validation')); } } diff --git a/classes/plugins/PKPPubIdPlugin.inc.php b/classes/plugins/PKPPubIdPlugin.inc.php index 1fb4a2d1fc4..20e56eb42a7 100644 --- a/classes/plugins/PKPPubIdPlugin.inc.php +++ b/classes/plugins/PKPPubIdPlugin.inc.php @@ -35,13 +35,6 @@ function register($category, $path, $mainContextId = null) { } else { // For non-schema-backed DAOs, DAOName::getAdditionalFieldNames can be used. HookRegistry::register(strtolower_codesafe(get_class($dao)).'::getAdditionalFieldNames', array($this, 'getAdditionalFieldNames')); - if (strtolower_codesafe(get_class($dao)) == 'submissionfiledao') { - // if it is a file, consider all file delegates - $fileDAOdelegates = $this->getFileDAODelegates(); - foreach ($fileDAOdelegates as $fileDAOdelegate) { - HookRegistry::register(strtolower_codesafe($fileDAOdelegate).'::getAdditionalFieldNames', array($this, 'getAdditionalFieldNames')); - } - } } } } @@ -296,14 +289,6 @@ function getDAOs() { ); } - /** - * Get the possible submission file DAO delegates. - * @return array - */ - function getFileDAODelegates() { - return array('SubmissionFileDAODelegate', 'SupplementaryFileDAODelegate', 'SubmissionArtworkFileDAODelegate'); - } - /** * Can a pub id be assigned to the object. * @param $pubObject object diff --git a/classes/plugins/PKPPubIdPluginHelper.inc.php b/classes/plugins/PKPPubIdPluginHelper.inc.php index c36caf7591c..3b19097a79f 100644 --- a/classes/plugins/PKPPubIdPluginHelper.inc.php +++ b/classes/plugins/PKPPubIdPluginHelper.inc.php @@ -197,7 +197,7 @@ function clearPubId($contextId, $pubIdPlugInClassName, $pubObject) { $dao = $pubObject->getDAO(); $pubObjectId = $pubObject->getId(); if (is_a($pubObject, 'SubmissionFile')) { - $pubObjectId = $pubObject->getFileId(); + $pubObjectId = $pubObject->getId(); } $dao->deletePubId($pubObjectId, $pubIdPlugin->getPubIdType()); // set the object setting/data 'pub-id::...' to null, in order diff --git a/classes/plugins/importexport/PKPImportExportDeployment.inc.php b/classes/plugins/importexport/PKPImportExportDeployment.inc.php index 5863a1d3f5e..149715b22fa 100644 --- a/classes/plugins/importexport/PKPImportExportDeployment.inc.php +++ b/classes/plugins/importexport/PKPImportExportDeployment.inc.php @@ -39,9 +39,12 @@ class PKPImportExportDeployment { /** @var array Errors keyed by object IDs */ var $_processedObjectsWarnings = array(); - /** @var array Connection between the file and revision IDs from the XML import file and the DB file IDs */ + /** @var array Connection between the file from the XML import file and the new IDs after they are imported */ var $_fileDBIds; + /** @var array Connection between the submission file IDs from the XML import file and the new IDs after they are imported */ + var $_submissionFileDBIds; + /** @var array Connection between the author id from the XML import file and the DB file IDs */ var $_authorDBIds; @@ -59,6 +62,7 @@ function __construct($context, $user=null) { $this->setSubmission(null); $this->setPublication(null); $this->setFileDBIds(array()); + $this->setSubmissionFileDBIds(array()); $this->_processedObjectsIds = array(); } @@ -277,36 +281,60 @@ function setFileDBIds($fileDBIds) { /** * Get the file DB Id. - * @param $fileId integer - * @param $revisionId integer - * @return integer + * @param $fileId integer The old file id + * @return integer The new file id */ - function getFileDBId($fileId, $revisionId = null) { + function getFileDBId($fileId) { if (array_key_exists($fileId, $this->_fileDBIds)) { - // is there already the revisionId? - if ($revisionId) { - if (array_key_exists($revisionId, $this->_fileDBIds[$fileId])) { - return $this->_fileDBIds[$fileId][$revisionId]; - } else { - return null; - } - } else { - // the revisionId is not important, but the fileId - // the DB Id is unique for a fileId - return current($this->_fileDBIds[$fileId]); - } + return $this->_fileDBIds[$fileId]; } return null; } /** * Set the file DB Id. - * @param $fileId integer - * @param $revisionId integer - * @param $DBId integer + * @param $fileId integer The old file id + * @param $DBId integer The new file id + */ + function setFileDBId($fileId, $DBId) { + return $this->_fileDBIds[$fileId] = $DBId; + } + + /** + * Get the array of the inserted submission file DB Ids. + * @return array + */ + function getSubmissionFileDBIds() { + return $this->_submissionFileDBIds; + } + + /** + * Set the array of the inserted submission file DB Ids. + * @param $submissionFileDBIds array + */ + function setSubmissionFileDBIds($submissionFileDBIds) { + return $this->_submissionFileDBIds = $submissionFileDBIds; + } + + /** + * Get the submission file DB Id. + * @param $fileId integer The old submission file id + * @return integer The new submission file id + */ + function getSubmissionFileDBId($submissionFileDBId) { + if (array_key_exists($submissionFileDBId, $this->_submissionFileDBIds)) { + return $this->_submissionFileDBIds[$submissionFileDBId]; + } + return null; + } + + /** + * Set the submission file DB Id. + * @param $submissionFileDBId integer The old submission file id + * @param $DBId integer The new submission file id */ - function setFileDBId($fileId, $revisionId, $DBId) { - return $this->_fileDBIds[$fileId][$revisionId]= $DBId; + function setSubmissionFileDBId($submissionFileDBId, $DBId) { + return $this->_submissionFileDBIds[$submissionFileDBId] = $DBId; } /** diff --git a/classes/publication/PKPPublicationDAO.inc.php b/classes/publication/PKPPublicationDAO.inc.php index fc04808264c..ae3a57caf76 100644 --- a/classes/publication/PKPPublicationDAO.inc.php +++ b/classes/publication/PKPPublicationDAO.inc.php @@ -13,22 +13,24 @@ * * @brief Operations for retrieving and modifying publication objects. */ +use Illuminate\Database\Capsule\Manager as Capsule; + import('lib.pkp.classes.db.SchemaDAO'); import('lib.pkp.classes.plugins.PKPPubIdPluginDAO'); import('classes.publication.Publication'); import('lib.pkp.classes.services.PKPSchemaService'); // SCHEMA_ constants class PKPPublicationDAO extends SchemaDAO implements PKPPubIdPluginDAO { - /** @copydoc SchemaDao::$schemaName */ + /** @copydoc SchemaDAO::$schemaName */ public $schemaName = SCHEMA_PUBLICATION; - /** @copydoc SchemaDao::$tableName */ + /** @copydoc SchemaDAO::$tableName */ public $tableName = 'publications'; - /** @copydoc SchemaDao::$settingsTableName */ + /** @copydoc SchemaDAO::$settingsTableName */ public $settingsTableName = 'publication_settings'; - /** @copydoc SchemaDao::$primaryKeyColumn */ + /** @copydoc SchemaDAO::$primaryKeyColumn */ public $primaryKeyColumn = 'publication_id'; /** @var array List of properties that are stored in the controlled_vocab tables. */ @@ -49,6 +51,12 @@ public function newDataObject() { public function _fromRow($primaryRow) { $publication = parent::_fromRow($primaryRow); + // Set the primary locale from the submission + $locale = Capsule::table('submissions as s') + ->where('s.submission_id', '=', $publication->getData('submissionId')) + ->value('locale'); + $publication->setData('locale', $locale); + // Get authors $publication->setData('authors', iterator_to_array( Services::get('author')->getMany(['publicationIds' => $publication->getId()]) diff --git a/classes/search/SearchFileParser.inc.php b/classes/search/SearchFileParser.inc.php index b12257896b4..36c4479205e 100644 --- a/classes/search/SearchFileParser.inc.php +++ b/classes/search/SearchFileParser.inc.php @@ -94,11 +94,14 @@ function doRead() { /** * Create a text parser for a file. - * @param $file [Article|Paper]File + * @param SubmissionFile $submissionFile * @return SearchFileParser */ - static function fromFile($file) { - return SearchFileParser::fromFileType($file->getFileType(), $file->getFilePath()); + static function fromFile($submissionFile) { + $path = Services::get('file')->getPath($submissionFile->getData('fileId')); + $mimetype = Services::get('file')->fs->getMimetype($path); + $fullPath = rtrim(Config::getVar('files', 'files_dir'), '/') . '/' . $path; + return SearchFileParser::fromFileType($mimetype, $fullPath); } /** diff --git a/classes/security/authorization/AuthorizationPolicy.inc.php b/classes/security/authorization/AuthorizationPolicy.inc.php index 6ea622f1f79..732a99e8f0f 100644 --- a/classes/security/authorization/AuthorizationPolicy.inc.php +++ b/classes/security/authorization/AuthorizationPolicy.inc.php @@ -55,8 +55,8 @@ function __construct($message = null) { * @param $adviceType integer * @param $adviceContent mixed */ - function setAdvice($adviceType, &$adviceContent) { - $this->_advice[$adviceType] =& $adviceContent; + function setAdvice($adviceType, $adviceContent) { + $this->_advice[$adviceType] = $adviceContent; } /** diff --git a/classes/security/authorization/NoteAccessPolicy.inc.php b/classes/security/authorization/NoteAccessPolicy.inc.php new file mode 100644 index 00000000000..9e491254263 --- /dev/null +++ b/classes/security/authorization/NoteAccessPolicy.inc.php @@ -0,0 +1,94 @@ +_request = $request; + $this->_noteId = $noteId; + $this->_accessMode = $accessMode; + } + + // + // Implement template methods from AuthorizationPolicy + // + /** + * @see AuthorizationPolicy::effect() + */ + function effect() { + + if (!$this->_noteId) { + return AUTHORIZATION_DENY; + } + + $query = $this->getAuthorizedContextObject(ASSOC_TYPE_QUERY); + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + $assignedStages = $this->getAuthorizedContextObject(ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES); + + if (!$query || !$submission || empty($assignedStages)) { + return AUTHORIZATION_DENY; + } + + $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ + $note = $noteDao->getById($this->_noteId); + + if (!is_a($note, 'Note')) { + return AUTHORIZATION_DENY; + } + + // Note, query, submission and assigned stages must match + if ($note->getAssocId() != $query->getId() + || $note->getAssocType() != ASSOC_TYPE_QUERY + || $query->getAssocId() != $submission->getId() + || $query->getAssocType() != ASSOC_TYPE_SUBMISSION + || !array_key_exists($query->getStageId(), $assignedStages) + || empty($assignedStages[$query->getStageId()])) { + return AUTHORIZATION_DENY; + } + + // Notes can only be edited by their original creators + if ($this->_accessMode === NOTE_ACCESS_WRITE + && $note->getUserId() != $this->_request->getUser()->getId()) { + return AUTHORIZATION_DENY; + } + + $this->addAuthorizedContextObject(ASSOC_TYPE_NOTE, $note); + + return AUTHORIZATION_PERMIT; + } +} diff --git a/classes/security/authorization/ReviewAssignmentFileWritePolicy.inc.php b/classes/security/authorization/ReviewAssignmentFileWritePolicy.inc.php new file mode 100644 index 00000000000..4e803708b4e --- /dev/null +++ b/classes/security/authorization/ReviewAssignmentFileWritePolicy.inc.php @@ -0,0 +1,100 @@ +_request = $request; + $this->_reviewAssignmentId = $reviewAssignmentId; + } + + // + // Implement template methods from AuthorizationPolicy + // + /** + * @see AuthorizationPolicy::effect() + */ + function effect() { + + if (!$this->_reviewAssignmentId) { + return AUTHORIZATION_DENY; + } + + $reviewRound = $this->getAuthorizedContextObject(ASSOC_TYPE_REVIEW_ROUND); + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + $assignedStages = $this->getAuthorizedContextObject(ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES); + $userRoles = $this->getAuthorizedContextObject(ASSOC_TYPE_USER_ROLES); + + if (!$reviewRound || !$submission) { + return AUTHORIZATION_DENY; + } + + $reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /* @var $noteDao ReviewAssignmentDAO */ + $reviewAssignment = $reviewAssignmentDao->getById($this->_reviewAssignmentId); + + if (!is_a($reviewAssignment, 'ReviewAssignment')) { + return AUTHORIZATION_DENY; + } + + // Review assignment, review round and submission must match + if ($reviewAssignment->getReviewRoundId() != $reviewRound->getId() + || $reviewRound->getSubmissionId() != $submission->getId()) { + return AUTHORIZATION_DENY; + } + + // Managers can write review attachments when they are not assigned to a submission + if (empty($stageAssignments) && in_array(ROLE_ID_MANAGER, $userRoles)) { + $this->addAuthorizedContextObject(ASSOC_TYPE_REVIEW_ASSIGNMENT, $reviewAssignment); + return AUTHORIZATION_PERMIT; + } + + // Managers, editors and assistants can write review attachments when they are assigned + // to the correct stage. + if (!empty($assignedStages[$reviewRound->getStageId()])) { + $allowedRoles = [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_ASSISTANT]; + if (!empty(array_intersect($allowedRoles, $assignedStages[$reviewRound->getStageId()]))) { + $this->addAuthorizedContextObject(ASSOC_TYPE_REVIEW_ASSIGNMENT, $reviewAssignment); + return AUTHORIZATION_PERMIT; + } + } + + // Reviewers can write review attachments to their own review assigments, + // if the assignment is not yet complete, cancelled or declined. + if ($reviewAssignment->getReviewerId() == $this->_request->getUser()->getId()) { + $notAllowedStatuses = [REVIEW_ASSIGNMENT_STATUS_DECLINED, REVIEW_ASSIGNMENT_STATUS_COMPLETE, REVIEW_ASSIGNMENT_STATUS_THANKED, REVIEW_ASSIGNMENT_STATUS_CANCELLED]; + if (!in_array($reviewAssignment->getStatus(), $notAllowedStatuses)) { + $this->addAuthorizedContextObject(ASSOC_TYPE_REVIEW_ASSIGNMENT, $reviewAssignment); + return AUTHORIZATION_PERMIT; + } + } + + return AUTHORIZATION_DENY; + } +} diff --git a/classes/security/authorization/SubmissionFileAccessPolicy.inc.php b/classes/security/authorization/SubmissionFileAccessPolicy.inc.php index 8b70fb74d53..ad5e6c2b9cd 100644 --- a/classes/security/authorization/SubmissionFileAccessPolicy.inc.php +++ b/classes/security/authorization/SubmissionFileAccessPolicy.inc.php @@ -31,17 +31,17 @@ class SubmissionFileAccessPolicy extends ContextPolicy { * @param $args array request parameters * @param $roleAssignments array * @param $mode int bitfield SUBMISSION_FILE_ACCESS_... - * @param $fileIdAndRevision string + * @param $submissionFileId int * @param $submissionParameterName string the request parameter we expect * the submission id in. */ - function __construct($request, $args, $roleAssignments, $mode, $fileIdAndRevision = null, $submissionParameterName = 'submissionId') { + function __construct($request, $args, $roleAssignments, $mode, $submissionFileId = null, $submissionParameterName = 'submissionId') { // TODO: Refine file access policies. Differentiate between // read and modify access using bitfield: // $mode & SUBMISSION_FILE_ACCESS_... parent::__construct($request); - $this->_baseFileAccessPolicy = $this->buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileIdAndRevision, $submissionParameterName); + $this->_baseFileAccessPolicy = $this->buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $submissionFileId, $submissionParameterName); } /** @@ -50,15 +50,15 @@ function __construct($request, $args, $roleAssignments, $mode, $fileIdAndRevisio * @param array $args * @param array $roleAssignments * @param int bitfield $mode - * @param string $fileIdAndRevision + * @param int $submissionFileId * @param string $submissionParameterName */ - function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileIdAndRevision, $submissionParameterName) { + function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $submissionFileId, $submissionParameterName) { // We need a submission matching the file in the request. import('lib.pkp.classes.security.authorization.internal.SubmissionRequiredPolicy'); $this->addPolicy(new SubmissionRequiredPolicy($request, $args, $submissionParameterName)); import('lib.pkp.classes.security.authorization.internal.SubmissionFileMatchesSubmissionPolicy'); - $this->addPolicy(new SubmissionFileMatchesSubmissionPolicy($request, $fileIdAndRevision)); + $this->addPolicy(new SubmissionFileMatchesSubmissionPolicy($request, $submissionFileId)); // Authors, managers and series editors potentially have // access to submission files. We'll have to define @@ -77,7 +77,7 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId $stageId = $request->getUserVar('stageId'); // WORKFLOW_STAGE_ID_... import('lib.pkp.classes.security.authorization.internal.SubmissionFileMatchesWorkflowStageIdPolicy'); - $managerFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $fileIdAndRevision, $stageId)); + $managerFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $submissionFileId, $stageId)); import('lib.pkp.classes.security.authorization.WorkflowStageAccessPolicy'); $managerFileAccessPolicy->addPolicy(new WorkflowStageAccessPolicy($request, $args, $roleAssignments, 'submissionId', $stageId)); import('lib.pkp.classes.security.authorization.AssignedStageRoleHandlerOperationPolicy'); @@ -100,7 +100,7 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId import('lib.pkp.classes.security.authorization.WorkflowStageAccessPolicy'); $authorFileAccessPolicy->addPolicy(new WorkflowStageAccessPolicy($request, $args, $roleAssignments, 'submissionId', $stageId)); import('lib.pkp.classes.security.authorization.internal.SubmissionFileMatchesWorkflowStageIdPolicy'); - $managerFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $fileIdAndRevision, $stageId)); + $authorFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $submissionFileId, $stageId)); import('lib.pkp.classes.security.authorization.AssignedStageRoleHandlerOperationPolicy'); $authorFileAccessPolicy->addPolicy(new AssignedStageRoleHandlerOperationPolicy($request, ROLE_ID_AUTHOR, $roleAssignments[ROLE_ID_AUTHOR], $stageId)); @@ -109,12 +109,12 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId // 3a) If the file was uploaded by the current user, allow... import('lib.pkp.classes.security.authorization.internal.SubmissionFileUploaderAccessPolicy'); - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileUploaderAccessPolicy($request, $fileIdAndRevision)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileUploaderAccessPolicy($request, $submissionFileId)); // 3b) ...or if the file is a file in a review round with requested revision decision, allow... // Note: This loads the application-specific policy class import('lib.pkp.classes.security.authorization.internal.SubmissionFileRequestedRevisionRequiredPolicy'); - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileRequestedRevisionRequiredPolicy($request, $fileIdAndRevision)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileRequestedRevisionRequiredPolicy($request, $submissionFileId)); // ...or if we don't want to modify the file... if (!($mode & SUBMISSION_FILE_ACCESS_MODIFY)) { @@ -122,23 +122,23 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId // 3c) ...the file is at submission stage... import('lib.pkp.classes.security.authorization.internal.SubmissionFileStageRequiredPolicy'); - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $fileIdAndRevision, SUBMISSION_FILE_SUBMISSION)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $submissionFileId, SUBMISSION_FILE_SUBMISSION)); // 3d) ...or the file is a viewable reviewer response... - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $fileIdAndRevision, SUBMISSION_FILE_REVIEW_ATTACHMENT, true)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $submissionFileId, SUBMISSION_FILE_REVIEW_ATTACHMENT, true)); // 3e) ...or if the file is part of a query assigned to the user... import('lib.pkp.classes.security.authorization.internal.SubmissionFileAssignedQueryAccessPolicy'); - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedQueryAccessPolicy($request, $fileIdAndRevision)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedQueryAccessPolicy($request, $submissionFileId)); // 3f) ...or the file is at revision stage... - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $fileIdAndRevision, SUBMISSION_FILE_REVIEW_REVISION)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $submissionFileId, SUBMISSION_FILE_REVIEW_REVISION)); // 3g) ...or the file is a copyedited file... - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $fileIdAndRevision, SUBMISSION_FILE_COPYEDIT)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $submissionFileId, SUBMISSION_FILE_COPYEDIT)); // 3h) ...or the file is a representation (galley/publication format)... - $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $fileIdAndRevision, SUBMISSION_FILE_PROOF)); + $authorFileAccessOptionsPolicy->addPolicy(new SubmissionFileStageRequiredPolicy($request, $submissionFileId, SUBMISSION_FILE_PROOF)); } // Add the rules from 3) @@ -161,18 +161,18 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId // 2a) If the file was uploaded by the current user, allow. import('lib.pkp.classes.security.authorization.internal.SubmissionFileUploaderAccessPolicy'); - $reviewerFileAccessOptionsPolicy->addPolicy(new SubmissionFileUploaderAccessPolicy($request, $fileIdAndRevision)); + $reviewerFileAccessOptionsPolicy->addPolicy(new SubmissionFileUploaderAccessPolicy($request, $submissionFileId)); // 2b) If the file is part of an assigned review, and we're not // trying to modify it, allow. import('lib.pkp.classes.security.authorization.internal.SubmissionFileAssignedReviewerAccessPolicy'); if (!($mode & SUBMISSION_FILE_ACCESS_MODIFY)) { - $reviewerFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedReviewerAccessPolicy($request, $fileIdAndRevision)); + $reviewerFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedReviewerAccessPolicy($request, $submissionFileId)); } // 2c) If the file is part of a query assigned to the user, allow. import('lib.pkp.classes.security.authorization.internal.SubmissionFileAssignedQueryAccessPolicy'); - $reviewerFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedQueryAccessPolicy($request, $fileIdAndRevision)); + $reviewerFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedQueryAccessPolicy($request, $submissionFileId)); // Add the rules from 2) $reviewerFileAccessPolicy->addPolicy($reviewerFileAccessOptionsPolicy); @@ -196,7 +196,7 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId import('lib.pkp.classes.security.authorization.WorkflowStageAccessPolicy'); $contextAssistantFileAccessPolicy->addPolicy(new WorkflowStageAccessPolicy($request, $args, $roleAssignments, 'submissionId', $stageId)); import('lib.pkp.classes.security.authorization.internal.SubmissionFileMatchesWorkflowStageIdPolicy'); - $managerFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $fileIdAndRevision, $stageId)); + $contextAssistantFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $submissionFileId, $stageId)); import('lib.pkp.classes.security.authorization.AssignedStageRoleHandlerOperationPolicy'); $contextAssistantFileAccessPolicy->addPolicy(new AssignedStageRoleHandlerOperationPolicy($request, ROLE_ID_ASSISTANT, $roleAssignments[ROLE_ID_ASSISTANT], $stageId)); @@ -205,11 +205,11 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId // 3a) ...the file not part of a query... import('lib.pkp.classes.security.authorization.internal.SubmissionFileNotQueryAccessPolicy'); - $contextAssistantFileAccessOptionsPolicy->addPolicy(new SubmissionFileNotQueryAccessPolicy($request, $fileIdAndRevision)); + $contextAssistantFileAccessOptionsPolicy->addPolicy(new SubmissionFileNotQueryAccessPolicy($request, $submissionFileId)); // 3b) ...or the file is part of a query they are assigned to... import('lib.pkp.classes.security.authorization.internal.SubmissionFileAssignedQueryAccessPolicy'); - $contextAssistantFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedQueryAccessPolicy($request, $fileIdAndRevision)); + $contextAssistantFileAccessOptionsPolicy->addPolicy(new SubmissionFileAssignedQueryAccessPolicy($request, $submissionFileId)); // Add the rules from 3 $contextAssistantFileAccessPolicy->addPolicy($contextAssistantFileAccessOptionsPolicy); @@ -227,21 +227,38 @@ function buildFileAccessPolicy($request, $args, $roleAssignments, $mode, $fileId // 2) ... but only if they have been assigned as a subeditor to the requested submission ... $stageId = $request->getUserVar('stageId'); + import('lib.pkp.classes.security.authorization.WorkflowStageAccessPolicy'); + $subEditorFileAccessPolicy->addPolicy(new WorkflowStageAccessPolicy($request, $args, $roleAssignments, 'submissionId', $stageId)); import('lib.pkp.classes.security.authorization.internal.UserAccessibleWorkflowStageRequiredPolicy'); $subEditorFileAccessPolicy->addPolicy(new UserAccessibleWorkflowStageRequiredPolicy($request)); import('lib.pkp.classes.security.authorization.AssignedStageRoleHandlerOperationPolicy'); $subEditorFileAccessPolicy->addPolicy(new AssignedStageRoleHandlerOperationPolicy($request, ROLE_ID_SUB_EDITOR, $roleAssignments[ROLE_ID_SUB_EDITOR], $stageId)); import('lib.pkp.classes.security.authorization.internal.SubmissionFileMatchesWorkflowStageIdPolicy'); - $managerFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $fileIdAndRevision, $stageId)); + $subEditorFileAccessPolicy->addPolicy(new SubmissionFileMatchesWorkflowStageIdPolicy($request, $submissionFileId, $stageId)); + + // 3) ...and if they meet one of the following requirements: + $subEditorQueryFileAccessPolicy = new PolicySet(COMBINING_PERMIT_OVERRIDES); + + // 3a) ...the file not part of a query... + import('lib.pkp.classes.security.authorization.internal.SubmissionFileNotQueryAccessPolicy'); + $subEditorQueryFileAccessPolicy->addPolicy(new SubmissionFileNotQueryAccessPolicy($request, $submissionFileId)); - // 3) ... and only if they are not also assigned as an author and this is not part of an anonymous review + // 3b) ...or the file is part of a query they are assigned to... + import('lib.pkp.classes.security.authorization.internal.SubmissionFileAssignedQueryAccessPolicy'); + $subEditorQueryFileAccessPolicy->addPolicy(new SubmissionFileAssignedQueryAccessPolicy($request, $submissionFileId)); + + // Add the rules from 3 + $subEditorFileAccessPolicy->addPolicy($subEditorQueryFileAccessPolicy); + + // 4) ... and only if they are not also assigned as an author and this is not part of a anonymous review import('lib.pkp.classes.security.authorization.internal.SubmissionFileAuthorEditorPolicy'); - $subEditorFileAccessPolicy->addPolicy(new SubmissionFileAuthorEditorPolicy($request, $fileIdAndRevision)); + $subEditorFileAccessPolicy->addPolicy(new SubmissionFileAuthorEditorPolicy($request, $submissionFileId)); $fileAccessPolicy->addPolicy($subEditorFileAccessPolicy); } $this->addPolicy($fileAccessPolicy); + return $fileAccessPolicy; } } diff --git a/classes/security/authorization/internal/RepresentationUploadAccessPolicy.inc.php b/classes/security/authorization/internal/RepresentationUploadAccessPolicy.inc.php new file mode 100644 index 00000000000..94705eaacc6 --- /dev/null +++ b/classes/security/authorization/internal/RepresentationUploadAccessPolicy.inc.php @@ -0,0 +1,93 @@ +_representationId = $representationId; + } + + // + // Implement template methods from AuthorizationPolicy + // + /** + * @see DataObjectRequiredPolicy::dataObjectEffect() + */ + function dataObjectEffect() { + AppLocale::requireComponents([LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION]); + + $assignedFileStages = $this->getAuthorizedContextObject(ASSOC_TYPE_ACCESSIBLE_FILE_STAGES); + if (empty($assignedFileStages) || !in_array(SUBMISSION_FILE_PROOF, $assignedFileStages)) { + return AUTHORIZATION_DENY; + } + + if (empty($this->_representationId)) { + $this->setAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE, 'user.authorization.representationNotFound'); + return AUTHORIZATION_DENY; + } + + $representationDao = Application::get()->getRepresentationDAO(); + $representation = $representationDao->getById($this->_representationId); + + if (!$representation) { + return AUTHORIZATION_DENY; + } + + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + if (!$submission) { + $this->setAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE, 'user.authorization.invalidSubmission'); + return AUTHORIZATION_DENY; + } + + $publication = Services::get('publication')->get($representation->getData('publicationId')); + if (!$publication) { + $this->setAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE, 'galley.publicationNotFound'); + return AUTHORIZATION_DENY; + } + + // Publication and submission must match + if ($publication->getData('submissionId') !== $submission->getId()) { + $this->setAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE, 'user.authorization.invalidPublication'); + return AUTHORIZATION_DENY; + } + + // Representations can not be modified on published publications + if ($publication->getData('status') === STATUS_PUBLISHED) { + $this->setAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE, 'galley.editPublishedDisabled'); + return AUTHORIZATION_DENY; + } + + $this->addAuthorizedContextObject(ASSOC_TYPE_REPRESENTATION, $representation); + + return AUTHORIZATION_PERMIT; + } +} + + diff --git a/classes/security/authorization/internal/ReviewRoundRequiredPolicy.inc.php b/classes/security/authorization/internal/ReviewRoundRequiredPolicy.inc.php index c3d056833df..85cd9df1019 100644 --- a/classes/security/authorization/internal/ReviewRoundRequiredPolicy.inc.php +++ b/classes/security/authorization/internal/ReviewRoundRequiredPolicy.inc.php @@ -15,6 +15,10 @@ import('lib.pkp.classes.security.authorization.DataObjectRequiredPolicy'); class ReviewRoundRequiredPolicy extends DataObjectRequiredPolicy { + + /** @var int Review round id. */ + public $_reviewRoundId; + /** * Constructor * @param $request PKPRequest @@ -22,9 +26,13 @@ class ReviewRoundRequiredPolicy extends DataObjectRequiredPolicy { * @param $parameterName string the request parameter we expect * the submission id in. * @param $operations array Optional list of operations for which this check takes effect. If specified, operations outside this set will not be checked against this policy. + * @param $reviewRoundId int Optionally pass the review round id directly. If passed, the $parameterName will be ignored. */ - function __construct($request, &$args, $parameterName = 'reviewRoundId', $operations = null) { + function __construct($request, &$args, $parameterName = 'reviewRoundId', $operations = null, $reviewRoundId = null) { parent::__construct($request, $args, $parameterName, 'user.authorization.invalidReviewRound', $operations); + if ($reviewRoundId) { + $this->_reviewRoundId = $reviewRoundId; + } } // @@ -35,12 +43,14 @@ function __construct($request, &$args, $parameterName = 'reviewRoundId', $operat */ function dataObjectEffect() { // Get the review round id. - $reviewRoundId = $this->getDataObjectId(); - if ($reviewRoundId === false) return AUTHORIZATION_DENY; + if (!$this->_reviewRoundId) { + $this->_reviewRoundId = $this->getDataObjectId(); + } + if ($this->_reviewRoundId === false) return AUTHORIZATION_DENY; // Validate the review round id. $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ - $reviewRound = $reviewRoundDao->getById($reviewRoundId); + $reviewRound = $reviewRoundDao->getById($this->_reviewRoundId); if (!is_a($reviewRound, 'ReviewRound')) return AUTHORIZATION_DENY; // Ensure that the review round actually belongs to the diff --git a/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.inc.php index f607cc41841..c5830db666d 100644 --- a/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.inc.php @@ -21,8 +21,8 @@ class SubmissionFileAssignedQueryAccessPolicy extends SubmissionFileBaseAccessPo * Constructor * @param $request PKPRequest */ - function __construct($request, $fileIdAndRevision = null) { - parent::__construct($request, $fileIdAndRevision); + function __construct($request, $submissionFileId = null) { + parent::__construct($request, $submissionFileId); } @@ -44,10 +44,10 @@ function effect() { if (!is_a($submissionFile, 'SubmissionFile')) return AUTHORIZATION_DENY; // Check if it's associated with a note. - if ($submissionFile->getAssocType() != ASSOC_TYPE_NOTE) return AUTHORIZATION_DENY; + if ($submissionFile->getData('assocType') != ASSOC_TYPE_NOTE) return AUTHORIZATION_DENY; $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ - $note = $noteDao->getById($submissionFile->getAssocId()); + $note = $noteDao->getById($submissionFile->getData('assocId')); if (!is_a($note, 'Note')) return AUTHORIZATION_DENY; if ($note->getAssocType() != ASSOC_TYPE_QUERY) return AUTHORIZATION_DENY; diff --git a/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.inc.php index fc7d87e4376..cca15adbc5d 100644 --- a/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.inc.php @@ -21,8 +21,8 @@ class SubmissionFileAssignedReviewerAccessPolicy extends SubmissionFileBaseAcces * Constructor * @param $request PKPRequest */ - function __construct($request, $fileIdAndRevision = null) { - parent::__construct($request, $fileIdAndRevision); + function __construct($request, $submissionFileId = null) { + parent::__construct($request, $submissionFileId); } @@ -51,10 +51,11 @@ function effect() { if ($context->getData('restrictReviewerFileAccess') && !$reviewAssignment->getDateConfirmed()) continue; if ( - $submissionFile->getSubmissionId() == $reviewAssignment->getSubmissionId() && - $submissionFile->getFileStage() == SUBMISSION_FILE_REVIEW_FILE && - $reviewFilesDao->check($reviewAssignment->getId(), $submissionFile->getFileId()) + $submissionFile->getData('submissionId') == $reviewAssignment->getSubmissionId() && + $submissionFile->getData('fileStage') == SUBMISSION_FILE_REVIEW_FILE && + $reviewFilesDao->check($reviewAssignment->getId(), $submissionFile->getId()) ) { + $this->addAuthorizedContextObject(ASSOC_TYPE_REVIEW_ASSIGNMENT, $reviewAssignment); return AUTHORIZATION_PERMIT; } } diff --git a/classes/security/authorization/internal/SubmissionFileAuthorEditorPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileAuthorEditorPolicy.inc.php index 916edab468f..f392687684f 100644 --- a/classes/security/authorization/internal/SubmissionFileAuthorEditorPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileAuthorEditorPolicy.inc.php @@ -36,7 +36,7 @@ public function effect() { foreach ($userRoles as $stageRoles) { if (in_array(ROLE_ID_AUTHOR, $stageRoles)) { $reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /* @var $reviewAssignmentDao ReviewAssignmentDAO */ - $reviewAssignment = $reviewAssignmentDao->getById((int) $submissionFile->getAssocId()); + $reviewAssignment = $reviewAssignmentDao->getById((int) $submissionFile->getData('assocId')); if ($reviewAssignment && $reviewAssignment->getReviewMethod() != SUBMISSION_REVIEW_METHOD_OPEN){ return AUTHORIZATION_DENY; } diff --git a/classes/security/authorization/internal/SubmissionFileBaseAccessPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileBaseAccessPolicy.inc.php index 6f08867b12e..61b00631c9a 100644 --- a/classes/security/authorization/internal/SubmissionFileBaseAccessPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileBaseAccessPolicy.inc.php @@ -19,19 +19,19 @@ class SubmissionFileBaseAccessPolicy extends AuthorizationPolicy { /** @var PKPRequest */ var $_request; - /** @var string File id and revision, separated with a dash (e.g. 15-1) */ - var $_fileIdAndRevision; + /** @var int Submission file id */ + var $_submissionFileId; /** * Constructor * @param $request PKPRequest - * @param $fileIdAndRevision string If passed, this policy will try to + * @param $submissionFileId int If passed, this policy will try to * get the submission file from this data. */ - function __construct($request, $fileIdAndRevision = null) { + function __construct($request, $submissionFileId = null) { parent::__construct('user.authorization.submissionFile'); $this->_request = $request; - $this->_fileIdAndRevision = $fileIdAndRevision; + $this->_submissionFileId = $submissionFileId; } @@ -59,34 +59,19 @@ function &_getCache() { * @return SubmissionFile */ function getSubmissionFile($request) { - // Try to get the submission file info. - $fileIdAndRevision = $this->_fileIdAndRevision; - if (!is_null($fileIdAndRevision)) { - $fileData = explode('-', $fileIdAndRevision); - $fileId = (int) $fileData[0]; - $revision = isset($fileData[1]) ? (int) $fileData[1] : 0; // -0 for most recent revision - $cacheId = $fileIdAndRevision; - } else { - // Get the identifying info from the request - $fileId = (int) $request->getUserVar('fileId'); - $revision = (int) $request->getUserVar('revision'); - assert($fileId>0); - $cacheId = "$fileId-$revision"; // -0 for most recent revision + // Get the identifying info from the request + if (is_null($this->_submissionFileId)) { + $this->_submissionFileId = (int) $request->getUserVar('submissionFileId'); + assert($this->_submissionFileId > 0); } // Fetch the object, caching if possible $cache =& $this->_getCache(); - if (!isset($cache[$cacheId])) { - // Cache miss - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - if ($revision) { - $cache[$cacheId] = $submissionFileDao->getRevision($fileId, $revision); - } else { - $cache[$cacheId] = $submissionFileDao->getLatestRevision($fileId); - } + if (!isset($cache[$this->_submissionFileId])) { + $cache[$this->_submissionFileId] = Services::get('submissionFile')->get($this->_submissionFileId); } - return $cache[$cacheId]; + return $cache[$this->_submissionFileId]; } /** diff --git a/classes/security/authorization/internal/SubmissionFileMatchesSubmissionPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileMatchesSubmissionPolicy.inc.php index 9f796d108a4..41fa901a795 100644 --- a/classes/security/authorization/internal/SubmissionFileMatchesSubmissionPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileMatchesSubmissionPolicy.inc.php @@ -19,11 +19,10 @@ class SubmissionFileMatchesSubmissionPolicy extends SubmissionFileBaseAccessPolicy { /** - * Constructor - * @param $request PKPRequest + * @copydoc SubmissionFileBaseAccessPolicy */ - function __construct($request, $fileIdAndRevision = null) { - parent::__construct($request, $fileIdAndRevision); + function __construct($request, $submissionFileId = null) { + parent::__construct($request, $submissionFileId); } @@ -43,8 +42,9 @@ function effect() { $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); if (!is_a($submission, 'Submission')) return AUTHORIZATION_DENY; + // Check if the submission file belongs to the submission. - if ($submissionFile->getSubmissionId() == $submission->getId()) { + if ($submissionFile->getData('submissionId') == $submission->getId()) { // We add this submission file to the context submission files array. $submissionFilesArray = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILES); if (is_null($submissionFilesArray)) { @@ -53,7 +53,7 @@ function effect() { array_push($submissionFilesArray, $submissionFile); $this->addAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILES, $submissionFilesArray); - // Save the submission to the authorization context. + // Save the submission file to the authorization context. $this->addAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE, $submissionFile); return AUTHORIZATION_PERMIT; } else { diff --git a/classes/security/authorization/internal/SubmissionFileMatchesWorkflowStageIdPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileMatchesWorkflowStageIdPolicy.inc.php index 4c5c4324430..9e5490520af 100644 --- a/classes/security/authorization/internal/SubmissionFileMatchesWorkflowStageIdPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileMatchesWorkflowStageIdPolicy.inc.php @@ -23,8 +23,8 @@ class SubmissionFileMatchesWorkflowStageIdPolicy extends SubmissionFileBaseAcces * @param $request PKPRequest * @param $stageId int Workflow stage ID (WORKFLOW_STAGE_ID_...) */ - function __construct($request, $fileIdAndRevision = null, $stageId = null) { - parent::__construct($request, $fileIdAndRevision); + function __construct($request, $submissionFileId = null, $stageId = null) { + parent::__construct($request, $submissionFileId); $this->_stageId = (int) $stageId; } @@ -41,11 +41,10 @@ function effect() { $submissionFile = $this->getSubmissionFile($request); if (!is_a($submissionFile, 'SubmissionFile')) return AUTHORIZATION_DENY; - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $workflowStageId = $submissionFileDao->getWorkflowStageId($submissionFile); + $workflowStageId = Services::get('submissionFile')->getWorkflowStageId($submissionFile); // Check if the submission file belongs to the specified workflow stage. - if ($workflowStageId !== $this->_stageId) return AUTHORIZATION_DENY; + if ($workflowStageId != $this->_stageId) return AUTHORIZATION_DENY; return AUTHORIZATION_PERMIT; } diff --git a/classes/security/authorization/internal/SubmissionFileNotQueryAccessPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileNotQueryAccessPolicy.inc.php index 96e13750872..feb18eec887 100644 --- a/classes/security/authorization/internal/SubmissionFileNotQueryAccessPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileNotQueryAccessPolicy.inc.php @@ -29,11 +29,11 @@ function effect() { if (!is_a($submissionFile, 'SubmissionFile')) return AUTHORIZATION_DENY; // Check if it's associated with a note. - if ($submissionFile->getAssocType() != ASSOC_TYPE_NOTE) return AUTHORIZATION_PERMIT; + if ($submissionFile->getData('assocType') != ASSOC_TYPE_NOTE) return AUTHORIZATION_PERMIT; // Check if that note is associated with a query $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ - $note = $noteDao->getById($submissionFile->getAssocId()); + $note = $noteDao->getById($submissionFile->getData('assocId')); if ($note->getAssocType() != ASSOC_TYPE_QUERY) return AUTHORIZATION_PERMIT; return AUTHORIZATION_DENY; diff --git a/classes/security/authorization/internal/SubmissionFileRequestedRevisionRequiredPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileRequestedRevisionRequiredPolicy.inc.php index 045577020dd..da77400e34d 100644 --- a/classes/security/authorization/internal/SubmissionFileRequestedRevisionRequiredPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileRequestedRevisionRequiredPolicy.inc.php @@ -21,8 +21,8 @@ class SubmissionFileRequestedRevisionRequiredPolicy extends SubmissionFileBaseAc * Constructor * @param $request PKPRequest */ - function __construct($request, $fileIdAndRevision = null) { - parent::__construct($request, $fileIdAndRevision); + function __construct($request, $submissionFileId = null) { + parent::__construct($request, $submissionFileId); } @@ -44,29 +44,25 @@ function effect() { // Make sure the file is part of a review round // with a requested revision decision. - $reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getFileId()); + $reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getId()); if (!is_a($reviewRound, 'ReviewRound')) return AUTHORIZATION_DENY; import('classes.workflow.EditorDecisionActionsManager'); if (!(new EditorDecisionActionsManager())->getEditorTakenActionInReviewRound($request->getContext(), $reviewRound, array(SUBMISSION_EDITOR_DECISION_PENDING_REVISIONS))) { return AUTHORIZATION_DENY; } - // Make sure that it's in the review stage. - $reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getFileId()); - if (!is_a($reviewRound, 'ReviewRound')) return AUTHORIZATION_DENY; - // Make sure review round stage is the same of the current stage in request. $stageId = $this->getAuthorizedContextObject(ASSOC_TYPE_WORKFLOW_STAGE); if ($reviewRound->getStageId() != $stageId) return AUTHORIZATION_DENY; // Make sure the file stage is SUBMISSION_FILE_REVIEW_REVISION. - if ($submissionFile->getFileStage() != SUBMISSION_FILE_REVIEW_REVISION) return AUTHORIZATION_DENY; + if ($submissionFile->getData('fileStage') != SUBMISSION_FILE_REVIEW_REVISION) return AUTHORIZATION_DENY; $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ // Make sure that the last review round editor decision is request revisions. $editDecisionDao = DAORegistry::getDAO('EditDecisionDAO'); /* @var $editDecisionDao EditDecisionDAO */ - $reviewRoundDecisions = $editDecisionDao->getEditorDecisions($submissionFile->getSubmissionId(), $reviewRound->getStageId(), $reviewRound->getRound()); + $reviewRoundDecisions = $editDecisionDao->getEditorDecisions($submissionFile->getData('submissionId'), $reviewRound->getStageId(), $reviewRound->getRound()); if (empty($reviewRoundDecisions)) return AUTHORIZATION_DENY; $lastEditorDecision = array_pop($reviewRoundDecisions); diff --git a/classes/security/authorization/internal/SubmissionFileStageAccessPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileStageAccessPolicy.inc.php new file mode 100644 index 00000000000..3cbcffd6231 --- /dev/null +++ b/classes/security/authorization/internal/SubmissionFileStageAccessPolicy.inc.php @@ -0,0 +1,112 @@ +_fileStage = $fileStage; + $this->_action = $action; + } + + + // + // Implement template methods from AuthorizationPolicy + // + /** + * @see AuthorizationPolicy::effect() + */ + function effect() { + $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); + $userRoles = $this->getAuthorizedContextObject(ASSOC_TYPE_USER_ROLES); + $stageAssignments = $this->getAuthorizedContextObject(ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES); + + // File stage required + if (empty($this->_fileStage)) { + $this->setAdvice(AUTHORIZATION_ADVICE_DENY_MESSAGE, $msg = 'api.submissionFiles.400.noFileStageId'); + return AUTHORIZATION_DENY; + } + + // Managers can access file stages when not assigned or when assigned as a manager + if (empty($stageAssignments)) { + if (in_array(ROLE_ID_MANAGER, $userRoles)) { + return AUTHORIZATION_PERMIT; + } + return AUTHORIZATION_DENY; + } + + // Determine the allowed file stages + $assignedFileStages = Services::get('submissionFile')->getAssignedFileStages($stageAssignments, $this->_action); + + // Authors may write to the submission files stage if the submission + // is not yet complete + if ($this->_fileStage === SUBMISSION_FILE_SUBMISSION && $this->_action === SUBMISSION_FILE_ACCESS_MODIFY) { + if (!empty($stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION]) + && count($stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION]) === 1 + && in_array(ROLE_ID_AUTHOR, $stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION]) + && $submission->getData('submissionProgress') > 0) { + $assignedFileStages[] = SUBMISSION_FILE_SUBMISSION; + } + } + + // Authors may write to the revision files stage if an accept or request revisions + // decision has been made in the latest round + if (in_array($this->_fileStage, [SUBMISSION_FILE_INTERNAL_REVIEW_REVISION, SUBMISSION_FILE_REVIEW_REVISION]) && $this->_action === SUBMISSION_FILE_ACCESS_MODIFY) { + $reviewStage = $this->_fileStage === SUBMISSION_FILE_INTERNAL_REVIEW_REVISION + ? WORKFLOW_STAGE_ID_INTERNAL_REVIEW + : WORKFLOW_STAGE_ID_EXTERNAL_REVIEW; + + if (count($stageAssignments[$reviewStage]) === 1 && in_array(ROLE_ID_AUTHOR, $stageAssignments[$reviewStage])) { + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ + $reviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $reviewStage); + if ($reviewRound) { + $editDecisionDao = DAORegistry::getDAO('EditDecisionDAO'); /* @var $editDecisionDao EditDecisionDAO */ + $decisions = $editDecisionDao->getEditorDecisions($submission->getId(), $reviewRound->getStageId(), $reviewRound->getRound()); + if (!empty($decisions)) { + foreach ($decisions as $decision) { + if ($decision['decision'] == SUBMISSION_EDITOR_DECISION_ACCEPT + || $decision['decision'] == SUBMISSION_EDITOR_DECISION_PENDING_REVISIONS + || $decision['decision'] == SUBMISSION_EDITOR_DECISION_NEW_ROUND) { + $assignedFileStages[] = $this->_fileStage; + break; + } + } + } + } + } + } + + if (in_array($this->_fileStage, $assignedFileStages)) { + $this->addAuthorizedContextObject(ASSOC_TYPE_ACCESSIBLE_FILE_STAGES, $assignedFileStages); + return AUTHORIZATION_PERMIT; + } + + return AUTHORIZATION_DENY; + } +} diff --git a/classes/security/authorization/internal/SubmissionFileStageRequiredPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileStageRequiredPolicy.inc.php index 3053c3d8f1f..167c114cdf5 100644 --- a/classes/security/authorization/internal/SubmissionFileStageRequiredPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileStageRequiredPolicy.inc.php @@ -25,13 +25,13 @@ class SubmissionFileStageRequiredPolicy extends SubmissionFileBaseAccessPolicy { /** * Constructor * @param $request PKPRequest - * @param $fileIdAndRevision string This policy will try to + * @param $submissionFileId int This policy will try to * get the submission file from this data. * @param $fileStage int SUBMISSION_FILE_... * @param $viewable boolean Whether the file has to be viewable */ - function __construct($request, $fileIdAndRevision, $fileStage, $viewable = false) { - parent::__construct($request, $fileIdAndRevision); + function __construct($request, $submissionFileId, $fileStage, $viewable = false) { + parent::__construct($request, $submissionFileId); $this->_fileStage = $fileStage; $this->_viewable = $viewable; } @@ -56,9 +56,9 @@ function effect() { if ($this->_viewable) { // Make sure the file is visible. Unless file is included in an open review. if (!$submissionFile->getViewable()){ - if ($submissionFile->getAssocType() === ASSOC_TYPE_REVIEW_ASSIGNMENT){ + if ($submissionFile->getData('assocType') === ASSOC_TYPE_REVIEW_ASSIGNMENT){ $reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /* @var $reviewAssignmentDao ReviewAssignmentDAO */ - $reviewAssignment = $reviewAssignmentDao->getById((int) $submissionFile->getAssocId()); + $reviewAssignment = $reviewAssignmentDao->getById((int) $submissionFile->getData('assocId')); if ($reviewAssignment->getReviewMethod() != SUBMISSION_REVIEW_METHOD_OPEN){ return AUTHORIZATION_DENY; } diff --git a/classes/security/authorization/internal/SubmissionFileUploaderAccessPolicy.inc.php b/classes/security/authorization/internal/SubmissionFileUploaderAccessPolicy.inc.php index c8c2579e757..60b5e270243 100644 --- a/classes/security/authorization/internal/SubmissionFileUploaderAccessPolicy.inc.php +++ b/classes/security/authorization/internal/SubmissionFileUploaderAccessPolicy.inc.php @@ -21,8 +21,8 @@ class SubmissionFileUploaderAccessPolicy extends SubmissionFileBaseAccessPolicy * Constructor * @param $request PKPRequest */ - function __construct($request, $fileIdAndRevision = null) { - parent::__construct($request, $fileIdAndRevision); + function __construct($request, $submissionFileId = null) { + parent::__construct($request, $submissionFileId); } diff --git a/classes/services/PKPAnnouncementService.inc.php b/classes/services/PKPAnnouncementService.inc.php index e0a1d78a20e..c3dfa988176 100644 --- a/classes/services/PKPAnnouncementService.inc.php +++ b/classes/services/PKPAnnouncementService.inc.php @@ -17,7 +17,6 @@ use \Core; use \DAOResultFactory; use \DAORegistry; -use \DBResultRange; use \Services; use \PKP\Services\interfaces\EntityPropertyInterface; use \PKP\Services\interfaces\EntityReadInterface; @@ -52,7 +51,7 @@ public function getIds($args = []) { /** * @copydoc \PKP\Services\interfaces\EntityReadInterface::getMany() */ - public function getMany($args = null) { + public function getMany($args = []) { $range = null; if (isset($args['count'])) { import('lib.pkp.classes.db.DBResultRange'); @@ -81,6 +80,9 @@ public function getMax($args = null) { return (int) $this->getQueryBuilder($args)->getCount(); } + /** + * @copydoc \PKP\Services\interfaces\EntityReadInterface::getQueryBuilder() + */ public function getQueryBuilder($args = []) { $defaultArgs = [ diff --git a/classes/services/PKPFileService.inc.php b/classes/services/PKPFileService.inc.php new file mode 100644 index 00000000000..263ec1d34e7 --- /dev/null +++ b/classes/services/PKPFileService.inc.php @@ -0,0 +1,251 @@ + [ + 'public' => FILE_MODE_MASK, + 'private' => FILE_MODE_MASK, + ], + 'dir' => [ + 'public' => DIRECTORY_MODE_MASK, + 'private' => DIRECTORY_MODE_MASK, + ] + ] + ); + + HookRegistry::call('File::adapter', [$adapter, $this]); + + $this->fs = new Filesystem($adapter); + } + + /** + * Get a file path by its id + * + * @param int $id + * @return string + */ + public function getPath($id) { + $file = Capsule::table('files') + ->where('file_id', '=', $id) + ->first(); + return $file ? $file->path : ''; + } + + /** + * Add a file + * + * @param string $from + * @param string $to + * @return int file id + */ + public function add($from, $to) { + $stream = fopen($from, 'r+'); + if (!$stream) { + throw new Exception("Unable to copy $from to $to."); + } + if (!$this->fs->writeStream($to, $stream)) { + throw new Exception("Unable to write file at $to."); + } + if (is_resource($stream)) { + fclose($stream); + } + return Capsule::table('files')->insertGetId(['path' => $to]); + } + + /** + * Delete an uploaded file + * + * @param int $id + * @return File + */ + public function delete($id) { + $path = $this->getPath($id); + if (!$path) { + throw new Exception("Unable to locate file $id."); + } + if (!$this->fs->delete($path)) { + throw new Exception("Unable to delete file $id at $path."); + } + Capsule::table('files') + ->where('file_id', '=', $id) + ->delete(); + } + + /** + * Download a file + * + * This method sends a HTTP response and ends the request handling. + * No code will run after this method is called. + * + * @param int|string $idOrPath The id or path to the file + * @param string $filename Filename to give to the downloaded file + * @param boolean $inline Whether to stream the file to the browser + */ + public function download($idOrPath, $filename, $inline = false) { + + if (is_int($idOrPath)) { + $path = $this->getPath($idOrPath); + } else { + $path = $idOrPath; + } + + if (!$this->fs->has($path)) { + throw new Exception('File download failed because no file was found at ' . $path); + } + + if (HookRegistry::call('File::download', [$idOrPath, &$filename, $inline])) { + return; + } + + // Stream the file to the end user. + $localPath = rtrim(Config::getVar('files', 'files_dir'), '/') . '/' . $path; + $mimetype = $this->fs->getMimetype($path) ?? 'application/octet-stream'; + $filesize = $this->fs->getSize($path); + header("Content-Type: $mimetype"); + header("Content-Length: $filesize"); + header('Accept-Ranges: none'); + header('Content-Disposition: ' . ($inline ? 'inline' : 'attachment') . "; filename=\"$filename\""); + header('Cache-Control: private'); // Workarounds for IE weirdness + header('Pragma: public'); + + if (is_readable($localPath)) { + $f = fopen($localPath, 'rb'); + if (!$f) return false; + while (!feof($f)) { + echo fread($f, 4096); + } + fclose($f); + } + } + + /** + * Convert a filename into a consistent format with the correct extension + * + * @param string $path Path to the file + * @param string $filename Source filename to sanitize + * @return string + */ + public function formatFilename($path, $filename) { + $extension = \Stringy\Stringy::create(pathinfo($path, PATHINFO_EXTENSION))->toLowerCase(); + $newFilename = \Stringy\Stringy::create($filename)->toLowerCase()->dasherize()->regexReplace('[^a-z0-9\-\_.]', ''); + if (!empty($extension) && substr($newFilename, (strlen($extension) * -1)) != $extension) { + $newFilename .= '.' . $extension; + } + + HookRegistry::call('File::formatFilename', [$path, $filename]); + + return $newFilename; + } + + /** + * Get document type based on the mimetype + * + * @param string $mimetype + * @return string One of the DOCUMENT_TYPE_ constants + */ + public function getDocumentType($mimetype) { + switch ($mimetype) { + case 'application/pdf': + case 'application/x-pdf': + case 'text/pdf': + case 'text/x-pdf': + return DOCUMENT_TYPE_PDF; + case 'application/msword': + case 'application/word': + case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': + return DOCUMENT_TYPE_WORD; + case 'application/excel': + case 'application/vnd.ms-excel': + case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': + return DOCUMENT_TYPE_EXCEL; + case 'text/html': + return DOCUMENT_TYPE_HTML; + case 'application/zip': + case 'application/x-zip': + case 'application/x-zip-compressed': + case 'application/x-compress': + case 'application/x-compressed': + case 'multipart/x-zip': + return DOCUMENT_TYPE_ZIP; + case 'application/epub': + case 'application/epub+zip': + return DOCUMENT_TYPE_EPUB; + case 'image/gif': + case 'image/jpeg': + case 'image/pjpeg': + case 'image/png': + case 'image/x-png': + case 'image/vnd.microsoft.icon': + case 'image/x-icon': + case 'image/x-ico': + case 'image/ico': + case 'image/svg+xml': + case 'image/svg': + return DOCUMENT_TYPE_IMAGE; + case 'application/x-shockwave-flash': + case 'video/x-flv': + case 'application/x-flash-video': + case 'flv-application/octet-stream': + case 'video/mpeg': + case 'video/quicktime': + case 'video/mp4': + return DOCUMENT_TYPE_VIDEO; + case 'audio/mpeg': + case 'audio/x-aiff': + case 'audio/x-wav': + return DOCUMENT_TYPE_AUDIO; + default: + return DOCUMENT_TYPE_DEFAULT; + } + } + + /** + * Get a pretty file size string + * + * Examples: 82B, 12KB, 2MB, 2GB + * + * @param integer $size File size in bytes + * @return string + */ + function getNiceFileSize($size) { + $niceFileSizeUnits = array('B', 'KB', 'MB', 'GB'); + for($i = 0; $i < 4 && $size > 1024; $i++) { + $size >>= 10; + } + return $size . $niceFileSizeUnits[$i]; + } +} diff --git a/classes/services/PKPSubmissionFileService.inc.php b/classes/services/PKPSubmissionFileService.inc.php new file mode 100644 index 00000000000..7da27dc654e --- /dev/null +++ b/classes/services/PKPSubmissionFileService.inc.php @@ -0,0 +1,837 @@ +getById($id); + } + + /** + * @copydoc \PKP\Services\interfaces\EntityReadInterface::getCount() + */ + public function getCount($args = []) { + return $this->getQueryBuilder($args)->getCount(); + } + + /** + * @copydoc \PKP\Services\interfaces\EntityReadInterface::getIds() + */ + public function getIds($args = []) { + return $this->getQueryBuilder($args)->getIds(); + } + + /** + * @copydoc \PKP\Services\interfaces\EntityReadInterface::getMany() + */ + public function getMany($args = null) { + $submissionFileQO = $this->getQueryBuilder($args)->getQuery(); + $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); + $result = $submissionFileDao->retrieve($submissionFileQO->toSql(), $submissionFileQO->getBindings()); + $queryResults = new DAOResultFactory($result, $submissionFileDao, '_fromRow'); + + return $queryResults->toIterator(); + + } + + /** + * @copydoc \PKP\Services\interfaces\EntityReadInterface::getMax() + */ + public function getMax($args = null) { + return $this->getCount($args); + } + + /** + * Get the file ids for each revision of a submission file + * + * @param int $submissionFileId + * @return array + */ + public function getRevisionFileIds($submissionFileId) { + $q = new PKPSubmissionFileQueryBuilder(); + return $q->getRevisionFileIds($submissionFileId); + } + + /** + * @copydoc \PKP\Services\interfaces\EntityReadInterface::getQueryBuilder() + */ + public function getQueryBuilder($args = []) { + + $defaultArgs = [ + 'assocTypes' => [], + 'assocIds' => [], + 'fileIds' => [], + 'fileStages' => [], + 'genreIds' => [], + 'includeDependentFiles' => false, + 'reviewIds' => [], + 'reviewRoundIds' => [], + 'submissionIds' => [], + 'uploaderUserIds' => [], + ]; + + $args = array_merge($defaultArgs, $args); + + $submissionFileQB = new PKPSubmissionFileQueryBuilder(); + $submissionFileQB + ->filterByAssoc($args['assocTypes'], $args['assocIds']) + ->filterByFileIds($args['fileIds']) + ->filterByFileStages($args['fileStages']) + ->filterByGenreIds($args['genreIds']) + ->filterByReviewIds($args['reviewIds']) + ->filterByReviewRoundIds($args['reviewRoundIds']) + ->filterBySubmissionIds($args['submissionIds']) + ->filterByUploaderUserIds($args['uploaderUserIds']) + ->includeDependentFiles($args['includeDependentFiles']); + + HookRegistry::call('SubmissionFile::getMany::queryBuilder', [$submissionFileQB, $args]); + + return $submissionFileQB; + } + + /** + * @copydoc \PKP\Services\interfaces\EntityPropertyInterface::getProperties() + */ + public function getProperties($submissionFile, $props, $args = null) { + $request = $args['request']; + $submission = $args['submission']; + $dispatcher = $request->getDispatcher(); + $path = Services::get('file')->getPath($submissionFile->getData('fileId')); + $mimetype = Services::get('file')->fs->getMimeType($path); + + $values = []; + + foreach ($props as $prop) { + switch ($prop) { + case '_href': + $values[$prop] = $dispatcher->url( + $request, + ROUTE_API, + $request->getContext()->getData('urlPath'), + 'submissions/' . $submission->getId() . '/files/' . $submissionFile->getId() + ); + break; + case 'dependentFiles': + $dependentFilesIterator = Services::get('submissionFile')->getMany([ + 'assocTypes' => [ASSOC_TYPE_SUBMISSION_FILE], + 'assocIds' => [$submissionFile->getId()], + 'submissionIds' => [$submission->getId()], + 'fileStages' => [SUBMISSION_FILE_DEPENDENT], + 'includeDependentFiles' => true, + ]); + $dependentFiles = []; + foreach ($dependentFilesIterator as $dependentFile) { + $dependentFiles[] = $this->getFullProperties($dependentFile, $args); + } + $values[$prop] = $dependentFiles; + break; + case 'documentType': + $values[$prop] = Services::get('file')->getDocumentType($mimetype); + break; + case 'mimetype': + $values[$prop] = $mimetype; + break; + case 'path': + $values[$prop] = $path; + break; + case 'revisions': + $revisions = []; + $qb = new PKPSubmissionFileQueryBuilder(); + $revisionFileIds = $qb->getRevisionFileIds($submissionFile->getId()); + foreach ($revisionFileIds as $revisionFileId) { + if ($revisionFileId === $submissionFile->getData('fileId')) { + continue; + } + $revisionPath = Services::get('file')->getPath($revisionFileId); + $mimetype = Services::get('file')->fs->getMimeType($revisionPath); + $revisions[] = [ + 'documentType' => Services::get('file')->getDocumentType($mimetype), + 'fileId' => $revisionFileId, + 'mimetype' => $mimetype, + 'path' => $revisionPath, + 'size' => Services::get('file')->fs->getSize($revisionPath), + 'url' => $dispatcher->url( + $request, + ROUTE_COMPONENT, + $request->getContext()->getData('urlPath'), + 'api.file.FileApiHandler', + 'downloadFile', + null, + [ + 'fileId' => $revisionFileId, + 'submissionFileId' => $submissionFile->getId(), + 'submissionId' => $submissionFile->getData('submissionId'), + 'stageId' => $this->getWorkflowStageId($submissionFile), + ] + ), + ]; + } + $values[$prop] = $revisions; + break; + case 'size': + $values[$prop] = Services::get('file')->fs->getSize($path); + break; + case 'url': + $values[$prop] = $dispatcher->url( + $request, + ROUTE_COMPONENT, + $request->getContext()->getData('urlPath'), + 'api.file.FileApiHandler', + 'downloadFile', + null, + [ + 'submissionFileId' => $submissionFile->getId(), + 'submissionId' => $submissionFile->getData('submissionId'), + 'stageId' => $this->getWorkflowStageId($submissionFile), + ] + ); + break; + default: + $values[$prop] = $submissionFile->getData($prop); + break; + } + } + + $values = Services::get('schema')->addMissingMultilingualValues(SCHEMA_SUBMISSION_FILE, $values, $request->getContext()->getSupportedFormLocales()); + + HookRegistry::call('SubmissionFile::getProperties', [&$values, $submissionFile, $props, $args]); + + ksort($values); + + return $values; + } + + /** + * @copydoc \PKP\Services\interfaces\EntityPropertyInterface::getSummaryProperties() + */ + public function getSummaryProperties($submissionFile, $args = null) { + $props = Services::get('schema')->getSummaryProps(SCHEMA_SUBMISSION_FILE); + + return $this->getProperties($submissionFile, $props, $args); + } + + /** + * @copydoc \PKP\Services\interfaces\EntityPropertyInterface::getFullProperties() + */ + public function getFullProperties($submissionFile, $args = null) { + $props = Services::get('schema')->getFullProps(SCHEMA_SUBMISSION_FILE); + + return $this->getProperties($submissionFile, $props, $args); + } + + /** + * @copydoc \PKP\Services\interfaces\EntityWriteInterface::validate() + */ + public function validate($action, $props, $allowedLocales, $primaryLocale) { + \AppLocale::requireComponents( + LOCALE_COMPONENT_PKP_MANAGER, + LOCALE_COMPONENT_APP_MANAGER + ); + $schemaService = Services::get('schema'); + + import('lib.pkp.classes.validation.ValidatorFactory'); + $validator = \ValidatorFactory::make( + $props, + $schemaService->getValidationRules(SCHEMA_SUBMISSION_FILE, $allowedLocales) + ); + + // Check required fields if we're adding a context + \ValidatorFactory::required( + $validator, + $action, + $schemaService->getRequiredProps(SCHEMA_SUBMISSION_FILE), + $schemaService->getMultilingualProps(SCHEMA_SUBMISSION_FILE), + $allowedLocales, + $primaryLocale + ); + + // Check for input from disallowed locales + \ValidatorFactory::allowedLocales($validator, $schemaService->getMultilingualProps(SCHEMA_SUBMISSION_FILE), $allowedLocales); + + // Do not allow the uploaderUserId to be modified + if ($action === VALIDATE_ACTION_EDIT) { + $validator->after(function($validator) use ($props) { + if (!empty($props['uploaderUserId']) && !$validator->errors()->get('uploaderUserId')) { + $validator->errors()->add('uploaderUserId', __('submission.file.notAllowedUploaderUserId')); + } + }); + } + + // Make sure that file stage and assocType match + if (!empty($props['assocType'])) { + $validator->after(function($validator) use ($props) { + if (empty($props['fileStage'])) { + $validator->errors()->add('assocType', __('api.submissionFiles.400.noFileStageId')); + } elseif ($props['assocType'] === ASSOC_TYPE_REVIEW_ROUND && !in_array($props['fileStage'], [SUBMISSION_FILE_REVIEW_FILE, SUBMISSION_FILE_REVIEW_REVISION, SUBMISSION_FILE_INTERNAL_REVIEW_FILE, SUBMISSION_FILE_INTERNAL_REVIEW_REVISION])) { + $validator->errors()->add('assocType', __('api.submissionFiles.400.badReviewRoundAssocType')); + } elseif ($props['assocType'] === ASSOC_TYPE_REVIEW_ASSIGNMENT && $props['fileStage'] !== SUBMISSION_FILE_REVIEW_ATTACHMENT) { + $validator->errors()->add('assocType', __('api.submissionFiles.400.badReviewAssignmentAssocType')); + } elseif ($props['assocType'] === ASSOC_TYPE_SUBMISSION_FILE && $props['fileStage'] !== SUBMISSION_FILE_DEPENDENT) { + $validator->errors()->add('assocType', __('api.submissionFiles.400.badDependentFileAssocType')); + } elseif ($props['assocType'] === ASSOC_TYPE_NOTE && $props['fileStage'] !== SUBMISSION_FILE_NOTE) { + $validator->errors()->add('assocType', __('api.submissionFiles.400.badNoteAssocType')); + } elseif ($props['assocType'] === ASSOC_TYPE_REPRESENTATION && $props['fileStage'] !== SUBMISSION_FILE_PROOF) { + $validator->errors()->add('assocType', __('api.submissionFiles.400.badRepresentationAssocType')); + } + }); + } + + if ($validator->fails()) { + $errors = $schemaService->formatValidationErrors($validator->errors(), $schemaService->get(SCHEMA_SUBMISSION_FILE), $allowedLocales); + } + + HookRegistry::call('SubmissionFile::validate', array(&$errors, $action, $props, $allowedLocales, $primaryLocale)); + + return $errors; + } + + /** + * @copydoc \PKP\Services\EntityProperties\EntityWriteInterface::add() + */ + public function add($submissionFile, $request) { + $submissionFile->setData('createdAt', Core::getCurrentDate()); + $submissionFile->setData('updatedAt', Core::getCurrentDate()); + DAORegistry::getDAO('SubmissionFileDAO')->insertObject($submissionFile); + + $submission = Services::get('submission')->get($submissionFile->getData('submissionId')); + + import('lib.pkp.classes.log.SubmissionFileLog'); + import('lib.pkp.classes.log.SubmissionFileEventLogEntry'); // constants + \SubmissionFileLog::logEvent( + $request, + $submissionFile, + SUBMISSION_LOG_FILE_UPLOAD, + 'submission.event.fileUploaded', + [ + 'fileStage' => $submissionFile->getData('fileStage'), + 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), + 'submissionFileId' => $submissionFile->getId(), + 'fileId' => $submissionFile->getData('fileId'), + 'submissionId' => $submissionFile->getData('submissionId'), + 'originalFileName' => $submissionFile->getLocalizedData('name'), + 'username' => $request->getUser()->getUsername(), + ] + ); + + import('lib.pkp.classes.log.SubmissionLog'); + import('classes.log.SubmissionEventLogEntry'); + $user = $request->getUser(); + \SubmissionLog::logEvent( + $request, $submission, + SUBMISSION_LOG_FILE_REVISION_UPLOAD, + 'submission.event.fileRevised', + [ + 'fileStage' => $submissionFile->getFileStage(), + 'submissionFileId' => $submissionFile->getId(), + 'fileId' => $submissionFile->getData('fileId'), + 'submissionId' => $submissionFile->getData('submissionId'), + 'username' => $user->getUsername(), + 'name' => $submissionFile->getLocalizedData('name'), + ] + ); + + // Update status and notifications when revisions have been uploaded + if (in_array($submissionFile->getData('fileStage'), [SUBMISSION_FILE_REVIEW_REVISION, SUBMISSION_FILE_INTERNAL_REVIEW_REVISION])) { + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ + $reviewRound = $reviewRoundDao->getById($submissionFile->getData('assocId')); + if (!$reviewRound) { + throw new \Exception('Submission file added to review round that does not exist.'); + } + + $reviewRoundDao->updateStatus($reviewRound); + + // Update author notifications + $authorUserIds = []; + $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /* @var $stageAssignmentDao StageAssignmentDAO */ + $authorAssignments = $stageAssignmentDao->getBySubmissionAndRoleId($submissionFile->getData('submissionId'), ROLE_ID_AUTHOR); + while ($assignment = $authorAssignments->next()) { + if ($assignment->getStageId() == $reviewRound->getStageId()) { + $authorUserIds[] = (int) $assignment->getUserId(); + } + } + $notificationMgr = new \NotificationManager(); + $notificationMgr->updateNotification( + $request, + [NOTIFICATION_TYPE_PENDING_INTERNAL_REVISIONS, NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS], + $authorUserIds, + ASSOC_TYPE_SUBMISSION, + $submissionFile->getData('submissionId') + ); + + // Notify editors if the file is uploaded by an author + if (in_array($submissionFile->getData('uploaderUserId'), $authorUserIds)) { + if (!$submission) { + throw new \Exception('Submission file added to submission that does not exist.'); + } + + $context = $request->getContext(); + if ($context->getId() != $submission->getData('contextId')) { + $context = Services::get('context')->get($submission->getData('contextId')); + } + + $uploader = $request->getUser(); + if ($uploader->getId() != $submissionFile->getData('uploaderUserId')) { + $uploader = Services::get('user')->get($submissionFile->getData('uploaderUserId')); + } + + // Fetch the latest notification email timestamp + import('lib.pkp.classes.log.SubmissionEmailLogEntry'); // Import email event constants + $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /* @var $submissionEmailLogDao SubmissionEmailLogDAO */ + $submissionEmails = $submissionEmailLogDao->getByEventType($submission->getId(), SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION); + $lastNotification = null; + $sentDates = []; + if ($submissionEmails){ + while ($email = $submissionEmails->next()) { + if ($email->getDateSent()){ + $sentDates[] = $email->getDateSent(); + } + } + if (!empty($sentDates)){ + $lastNotification = max(array_map('strtotime', $sentDates)); + } + } + + import('lib.pkp.classes.mail.SubmissionMailTemplate'); + $mail = new \SubmissionMailTemplate($submission, 'REVISED_VERSION_NOTIFY'); + $mail->setEventType(SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION); + $mail->setReplyTo($context->getData('contactEmail'), $context->getData('contactName')); + // Get editors assigned to the submission, consider also the recommendOnly editors + $userDao = DAORegistry::getDAO('UserDAO'); /* @var $userDao UserDAO */ + $editorsStageAssignments = $stageAssignmentDao->getEditorsAssignedToStage($submission->getId(), $reviewRound->getStageId()); + foreach ($editorsStageAssignments as $editorsStageAssignment) { + $editor = $userDao->getById($editorsStageAssignment->getUserId()); + // IF no prior notification exists + // OR if editor has logged in after the last revision upload + // OR the last upload and notification was sent more than a day ago, + // THEN send a new notification + if (is_null($lastNotification) || strtotime($editor->getDateLastLogin()) > $lastNotification || strtotime('-1 day') > $lastNotification){ + $mail->addRecipient($editor->getEmail(), $editor->getFullName()); + } + } + // Get uploader name + $mail->assignParams(array( + 'authorName' => $uploader->getFullName(), + 'editorialContactSignature' => $context->getData('contactName'), + 'submissionUrl' => $request->getDispatcher()->url( + $request, + ROUTE_PAGE, + null, + 'workflow', + 'index', + [ + $submission->getId(), + $reviewRound->getStageId(), + ] + ), + )); + + if ($mail->getRecipients()){ + if (!$mail->send($request)) { + import('classes.notification.NotificationManager'); + $notificationMgr = new \NotificationManager(); + $notificationMgr->createTrivialNotification($request->getUser()->getId(), NOTIFICATION_TYPE_ERROR, ['contents' => __('email.compose.error')]); + } + } + } + } + + HookRegistry::call('SubmissionFile::add', [$submissionFile, $request]); + + return $submissionFile; + } + + /** + * @copydoc \PKP\Services\EntityProperties\EntityWriteInterface::edit() + */ + public function edit($submissionFile, $params, $request) { + $newFileUploaded = !empty($params['fileId']) && $params['fileId'] !== $submissionFile->getData('fileId'); + $submissionFile->_data = array_merge($submissionFile->_data, $params); + $submissionFile->setData('updatedAt', Core::getCurrentDate()); + + HookRegistry::call('SubmissionFile::edit', [$submissionFile, $submissionFile, $params, $request]); + + DAORegistry::getDAO('SubmissionFileDAO')->updateObject($submissionFile); + + import('lib.pkp.classes.log.SubmissionFileLog'); + import('lib.pkp.classes.log.SubmissionFileEventLogEntry'); // constants + \SubmissionFileLog::logEvent( + $request, + $submissionFile, + $newFileUploaded ? SUBMISSION_LOG_FILE_REVISION_UPLOAD : SUBMISSION_LOG_FILE_EDIT, + $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited', + [ + 'fileStage' => $submissionFile->getData('fileStage'), + 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), + 'submissionFileId' => $submissionFile->getId(), + 'fileId' => $submissionFile->getData('fileId'), + 'submissionId' => $submissionFile->getData('submissionId'), + 'originalFileName' => $submissionFile->getLocalizedData('name'), + 'username' => $request->getUser()->getUsername(), + ] + ); + + import('lib.pkp.classes.log.SubmissionLog'); + import('classes.log.SubmissionEventLogEntry'); + $user = $request->getUser(); + $submission = Services::get('submission')->get($submissionFile->getData('submissionId')); + \SubmissionLog::logEvent( + $request, $submission, + $newFileUploaded ? SUBMISSION_LOG_FILE_REVISION_UPLOAD : SUBMISSION_LOG_FILE_EDIT, + $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited', + [ + 'fileStage' => $submissionFile->getFileStage(), + 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), + 'submissionFileId' => $submissionFile->getId(), + 'fileId' => $submissionFile->getData('fileId'), + 'submissionId' => $submissionFile->getData('submissionId'), + 'username' => $user->getUsername(), + 'originalFileName' => $submissionFile->getLocalizedData('name'), + 'name' => $submissionFile->getLocalizedData('name'), + ] + ); + + return $this->get($submissionFile->getId()); + } + + /** + * @copydoc \PKP\Services\EntityProperties\EntityWriteInterface::delete() + */ + public function delete($submissionFile) { + $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ + + HookRegistry::call('SubmissionFile::delete::before', [$submissionFile]); + + // Delete dependent files + $dependentFilesIterator = $this->getMany([ + 'includeDependentFiles' => true, + 'fileStages' => [SUBMISSION_FILE_DEPENDENT], + 'assocTypes' => [ASSOC_TYPE_SUBMISSION_FILE], + 'assocIds' => [$submissionFile->getId()], + ]); + foreach ($dependentFilesIterator as $dependentFile) { + $this->delete($dependentFile); + } + + // Delete review round associations + if ($submissionFile->getData('fileStage') === SUBMISSION_FILE_REVIEW_REVISION) { + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ + $reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getId()); + $submissionFileDao->deleteReviewRoundAssignment($submissionFile->getId()); + $reviewRoundDao->updateStatus($reviewRound); + } + + // Delete notes for this submission file + $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ + $noteDao->deleteByAssoc(ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getId()); + + // Update tasks + import('classes.notification.NotificationManager'); + $notificationMgr = new \NotificationManager(); + switch ($submissionFile->getData('fileStage')) { + case SUBMISSION_FILE_REVIEW_REVISION: + $authorUserIds = []; + $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /* @var $stageAssignmentDao StageAssignmentDAO */ + $submitterAssignments = $stageAssignmentDao->getBySubmissionAndRoleId($submissionFile->getData('submissionId'), ROLE_ID_AUTHOR); + while ($assignment = $submitterAssignments->next()) { + $authorUserIds[] = $assignment->getUserId(); + } + $notificationMgr->updateNotification( + Application::get()->getRequest(), + [NOTIFICATION_TYPE_PENDING_INTERNAL_REVISIONS, NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS], + $authorUserIds, + ASSOC_TYPE_SUBMISSION, + $submissionFile->getData('submissionId') + ); + break; + + case SUBMISSION_FILE_COPYEDIT: + $notificationMgr->updateNotification( + Application::get()->getRequest(), + [NOTIFICATION_TYPE_ASSIGN_COPYEDITOR, NOTIFICATION_TYPE_AWAITING_COPYEDITS], + null, + ASSOC_TYPE_SUBMISSION, + $submissionFile->getData('submissionId') + ); + break; + } + + // Get all revision file ids before they are deleted in SubmissionFileDAO::deleteObject + $revisionFileIds = $this->getRevisionFileIds($submissionFile->getId()); + + // Delete the submission file + $submissionFileDao->deleteObject($submissionFile); + + // Delete all files not referenced by other files + foreach ($revisionFileIds as $fileId) { + $countFileShares = $this->getCount([ + 'fileIds' => [$fileId], + 'includeDependentFiles' => true, + ]); + if (!$countFileShares) { + Services::get('file')->delete($fileId); + } + } + + // Log the deletion + import('lib.pkp.classes.log.SubmissionFileLog'); + import('lib.pkp.classes.log.SubmissionFileEventLogEntry'); // constants + \SubmissionFileLog::logEvent( + Application::get()->getRequest(), + $submissionFile, + SUBMISSION_LOG_FILE_DELETE, + 'submission.event.fileDeleted', + [ + 'fileStage' => $submissionFile->getData('fileStage'), + 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), + 'submissionFileId' => $submissionFile->getId(), + 'submissionId' => $submissionFile->getData('submissionId'), + 'username' => Application::get()->getRequest()->getUser()->getUsername(), + ] + ); + + HookRegistry::call('SubmissionFile::delete', [$submissionFile]); + } + + /** + * Get the file stage ids that a user can access based on their + * stage assignments + * + * This does not return file stages for ROLE_ID_REVIEWER or ROLE_ID_READER. + * These roles are not granted stage assignments and this method should not + * be used for these roles. + * + * This method does not define access to review attachments, discussion + * files or dependent files. Access to these files are not determined by + * stage assignment. + * + * In some cases it may be necessary to apply additional restrictions. For example, + * authors are granted write access to submission files or revisions only when other + * conditions are met. This method only considers these an assigned file stage for + * authors when read access is requested. + * + * @param array $stageAssignments The stage assignments of this user. + * Each key is a workflow stage and value is an array of assigned roles + * @param int $action Read or write to file stages. One of SUBMISSION_FILE_ACCESS_ + * @return array List of file stages (SUBMISSION_FILE_*) + */ + public function getAssignedFileStages($stageAssignments, $action) { + $allowedRoles = [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_ASSISTANT, ROLE_ID_AUTHOR]; + $notAuthorRoles = array_diff($allowedRoles, [ROLE_ID_AUTHOR]); + + $allowedFileStages = []; + + if (array_key_exists(WORKFLOW_STAGE_ID_SUBMISSION, $stageAssignments) + && !empty(array_intersect($allowedRoles, $stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION]))) { + $hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION])); + // Authors only have read access + if ($action === SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_SUBMISSION; + } + } + + if (array_key_exists(WORKFLOW_STAGE_ID_INTERNAL_REVIEW, $stageAssignments)) { + $hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_INTERNAL_REVIEW])); + // Authors can only write revision files under specific conditions + if ($action === SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_INTERNAL_REVIEW_REVISION; + } + // Authors can never access review files + if ($hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_INTERNAL_REVIEW_FILE; + } + } + + if (array_key_exists(WORKFLOW_STAGE_ID_EXTERNAL_REVIEW, $stageAssignments)) { + $hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_EXTERNAL_REVIEW])); + // Authors can only write revision files under specific conditions + if ($action === SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_REVIEW_REVISION; + } + // Authors can never access review files + if ($hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_REVIEW_FILE; + } + } + + if (array_key_exists(WORKFLOW_STAGE_ID_EDITING, $stageAssignments) + && !empty(array_intersect($allowedRoles, $stageAssignments[WORKFLOW_STAGE_ID_EDITING]))) { + $hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_EDITING])); + // Authors only have read access + if ($action === SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_COPYEDIT; + } + if ($hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_FINAL; + } + } + + if (array_key_exists(WORKFLOW_STAGE_ID_PRODUCTION, $stageAssignments) + && !empty(array_intersect($allowedRoles, $stageAssignments[WORKFLOW_STAGE_ID_PRODUCTION]))) { + $hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_PRODUCTION])); + // Authors only have read access + if ($action === SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_PROOF; + } + if ($hasEditorialAssignment) { + $allowedFileStages[] = SUBMISSION_FILE_PRODUCTION_READY; + } + } + + HookRegistry::call('SubmissionFile::assignedFileStages', [&$allowedFileStages, $stageAssignments, $action]); + + return $allowedFileStages; + } + + /** + * Get all valid file stages + * + * @return array + */ + public function getFileStages() { + import('lib.pkp.classes.submission.SubmissionFile'); + $stages = [ + SUBMISSION_FILE_SUBMISSION, + SUBMISSION_FILE_NOTE, + SUBMISSION_FILE_REVIEW_FILE, + SUBMISSION_FILE_REVIEW_ATTACHMENT, + SUBMISSION_FILE_FINAL, + SUBMISSION_FILE_COPYEDIT, + SUBMISSION_FILE_PROOF, + SUBMISSION_FILE_PRODUCTION_READY, + SUBMISSION_FILE_ATTACHMENT, + SUBMISSION_FILE_REVIEW_REVISION, + SUBMISSION_FILE_DEPENDENT, + SUBMISSION_FILE_QUERY, + ]; + + HookRegistry::call('SubmissionFile::fileStages', [&$stages]); + + return $stages; + } + + /** + * Get the path to a submission's file directory + * + * This returns the relative path from the files_dir set in the config. + * + * @param int $contextId + * @param int $submissionId + * @return string + */ + public function getSubmissionDir($contextId, $submissionId) { + $dirNames = Application::getFileDirectories(); + return sprintf( + '%s/%d/%s/%d', + str_replace('/', '', $dirNames['context']), + $contextId, + str_replace('/', '', $dirNames['submission']), + $submissionId + ); + } + + /** + * Get the workflow stage for a submission file + * + * @param SubmissionFile $submissionFile + * @return int|null WORKFLOW_STAGE_ID_* + */ + public function getWorkflowStageId($submissionFile) { + switch ($submissionFile->getData('fileStage')) { + case SUBMISSION_FILE_SUBMISSION: + return WORKFLOW_STAGE_ID_SUBMISSION; + case SUBMISSION_FILE_FINAL: + case SUBMISSION_FILE_COPYEDIT: + return WORKFLOW_STAGE_ID_EDITING; + case SUBMISSION_FILE_PROOF: + case SUBMISSION_FILE_PRODUCTION_READY: + return WORKFLOW_STAGE_ID_PRODUCTION; + case SUBMISSION_FILE_DEPENDENT: + $parentFile = Services::get('submissionFile')->get($submissionFile->getData('assocId')); + return $this->getWorkflowStageId($parentFile); + case SUBMISSION_FILE_REVIEW_FILE: + case SUBMISSION_FILE_REVIEW_ATTACHMENT: + case SUBMISSION_FILE_REVIEW_REVISION: + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ + $reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getId()); + return $reviewRound->getStageId(); + case SUBMISSION_FILE_QUERY: + $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ + $note = $noteDao->getById($submissionFile->getData('assocId')); + $queryDao = DAORegistry::getDAO('QueryDAO'); /* @var $queryDao QueryDAO */ + $query = $queryDao->getById($note->getAssocId()); + return $query ? $query->getStageId() : null; + } + return null; + } + + /** + * Record that a submission file has been viewed + * + * @param SubmissionFile $submissionFile + */ + public function recordView($submissionFile) { + $user = Application::get()->getRequest()->getUser(); + if (is_a($user, 'User')) { + $viewsDao = DAORegistry::getDAO('ViewsDAO'); /* @var $viewsDao ViewsDAO */ + $viewsDao->recordView( + ASSOC_TYPE_SUBMISSION_FILE, + $submissionFile->getId(), + $user->getId() + ); + } + } + + /** + * Check if a submission file supports dependent files + * + * @param SubmissionFile $submissionFile + * @param string $path + * @return boolean + */ + public function supportsDependentFiles($submissionFile, $path) { + $fileStage = $submissionFile->getData('fileStage'); + $mimetype = Services::get('file')->fs->getMimeType($path); + $excludedFileStages = [ + SUBMISSION_FILE_DEPENDENT, + SUBMISSION_FILE_QUERY, + ]; + $allowedMimetypes = [ + 'text/html', + 'application/xml', + 'text/xml', + ]; + + $result = !in_array($fileStage, $excludedFileStages) && in_array($mimetype, $allowedMimetypes); + + HookRegistry::call('SubmissionFile::supportsDependentFiles', [&$result, $submissionFile, $path]); + + return $result; + } +} diff --git a/classes/services/PKPSubmissionService.inc.php b/classes/services/PKPSubmissionService.inc.php index 0edb3030abe..a6bc1213f50 100644 --- a/classes/services/PKPSubmissionService.inc.php +++ b/classes/services/PKPSubmissionService.inc.php @@ -473,12 +473,13 @@ public function getPropertyStages($submission, $stageIds = null) { $stage['status'] = __($reviewRound->getStatusKey()); // Revision files in this round. - import('lib.pkp.classes.submission.SubmissionFile'); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getRevisionsByReviewRound($reviewRound, SUBMISSION_FILE_REVIEW_REVISION); - $stage['files'] = array( - 'count' => count($submissionFiles), - ); + $stage['files'] = [ + 'count' => Services::get('submissionFile')->getCount([ + 'submissionIds' => [$submission->getId()], + 'fileStages' => [SUBMISSION_FILE_REVIEW_REVISION], + 'reviewRounds' => [$reviewRound->getId()], + ]), + ]; // See if the curent user can only recommend: $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /* @var $stageAssignmentDao StageAssignmentDAO */ @@ -495,9 +496,9 @@ public function getPropertyStages($submission, $stageIds = null) { } } else { // workaround for pkp/pkp-lib#4231, pending formal data model - $stage['files'] = array( + $stage['files'] = [ 'count' => 0 - ); + ]; } break; @@ -506,12 +507,12 @@ public function getPropertyStages($submission, $stageIds = null) { case WORKFLOW_STAGE_ID_EDITING: case WORKFLOW_STAGE_ID_PRODUCTION: import('lib.pkp.classes.submission.SubmissionFile'); // Import constants - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $fileStageIId = $stageId === WORKFLOW_STAGE_ID_EDITING ? SUBMISSION_FILE_COPYEDIT : SUBMISSION_FILE_PROOF; - $submissionFiles = $submissionFileDao->getLatestRevisions($submission->getId(), $fileStageIId); - $stage['files'] = array( - 'count' => count($submissionFiles), - ); + $stage['files'] = [ + 'count' => Services::get('submissionFile')->getCount([ + 'submissionIds' => [$submission->getId()], + 'fileStages' => [WORKFLOW_STAGE_ID_EDITING ? SUBMISSION_FILE_COPYEDIT : SUBMISSION_FILE_PROOF], + ]), + ]; break; } diff --git a/classes/services/PKPUserService.inc.php b/classes/services/PKPUserService.inc.php index d8f9a044298..a5147a45d82 100644 --- a/classes/services/PKPUserService.inc.php +++ b/classes/services/PKPUserService.inc.php @@ -194,7 +194,7 @@ public function getReviewersMax($args = []) { * Build the reviewers query object for getReviewers requests * * @see self::getQueryBuilder() - * @return UserQueryBuilder + * @return PKPUserQueryBuilder */ public function getReviewersQueryBuilder($args = []) { @@ -556,7 +556,7 @@ public function getAccessibleStageRoles($userId, $contextId, &$submission, $stag $accessibleStageRoles = array_unique($accessibleStageRoles); } - return $accessibleStageRoles; + return array_map('intval', $accessibleStageRoles); } /** diff --git a/classes/services/queryBuilders/PKPAuthorQueryBuilder.inc.php b/classes/services/queryBuilders/PKPAuthorQueryBuilder.inc.php index 8d0db4f8e94..2ef52d61113 100644 --- a/classes/services/queryBuilders/PKPAuthorQueryBuilder.inc.php +++ b/classes/services/queryBuilders/PKPAuthorQueryBuilder.inc.php @@ -120,13 +120,13 @@ public function getIds() { * @copydoc PKP\Services\QueryBuilders\Interfaces\EntityQueryBuilderInterface::getQuery() */ public function getQuery() { - $this->columns = ['*', 'p.locale AS submission_locale']; + $this->columns = ['*', 's.locale AS submission_locale']; $q = Capsule::table('authors as a'); $q->leftJoin('publications as p', 'a.publication_id', '=', 'p.publication_id'); + $q->leftJoin('submissions as s', 'p.submission_id', '=', 's.submission_id'); if (!empty($this->contextIds)) { - $q->leftJoin('submissions as s', 'p.submission_id', '=', 's.submission_id') - ->whereIn('s.context_id', $this->contextIds); + $q->whereIn('s.context_id', $this->contextIds); } if (!empty($this->familyName) || !empty($this->givenName)) { diff --git a/classes/services/queryBuilders/PKPSubmissionFileQueryBuilder.inc.php b/classes/services/queryBuilders/PKPSubmissionFileQueryBuilder.inc.php new file mode 100644 index 00000000000..0452359e7e0 --- /dev/null +++ b/classes/services/queryBuilders/PKPSubmissionFileQueryBuilder.inc.php @@ -0,0 +1,251 @@ +fileStages = is_array($fileStages) ? $fileStages : [$fileStages]; + return $this; + } + + /** + * Set genreIds filter + * + * @param array|int $genreIds + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function filterByGenreIds($genreIds) { + $this->genreIds = is_array($genreIds) ? $genreIds : [$genreIds]; + return $this; + } + + /** + * Set review rounds filter + * + * @param array|int $reviewRoundIds + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function filterByReviewRoundIds($reviewRoundIds) { + $this->reviewRoundIds = is_array($reviewRoundIds) ? $reviewRoundIds : [$reviewRoundIds]; + return $this; + } + + /** + * Set review assignments filter + * + * @param array|int $reviewIds + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function filterByReviewIds($reviewIds) { + $this->reviewIds = is_array($reviewIds) ? $reviewIds : [$reviewIds]; + return $this; + } + + /** + * Set submissionIds filter + * + * @param array|int $submissionIds + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function filterBySubmissionIds($submissionIds) { + $this->submissionIds = is_array($submissionIds) ? $submissionIds : [$submissionIds]; + return $this; + } + + /** + * Set fileIds filter + * + * @param array|int $fileIds + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function filterByFileIds($fileIds) { + $this->fileIds = is_array($fileIds) ? $fileIds : [$fileIds]; + return $this; + } + + /** + * Set assocType and assocId filters + * + * @param array|int $assocTypes One or more of the ASSOC_TYPE_ constants + * @param array|int $assocIds Match with ids for these assoc types + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function filterByAssoc($assocTypes, $assocIds = []) { + $this->assocTypes = is_array($assocTypes) ? $assocTypes : [$assocTypes]; + if (!empty($assocIds)) { + $this->assocIds = is_array($assocIds) ? $assocIds : [$assocIds]; + } + return $this; + } + + /** + * Set uploaderUserIds filter + * + * @param array|int $uploaderUserIds + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function filterByUploaderUserIds($uploaderUserIds) { + $this->uploaderUserIds = is_array($uploaderUserIds) ? $uploaderUserIds : [$uploaderUserIds]; + return $this; + } + + /** + * Whether or not to include dependent files in the results + * + * @param boolean $includeDependentFiles + * @return \PKP\Services\QueryBuilders\PKPSubmissionFileQueryBuilder + */ + public function includeDependentFiles($includeDependentFiles) { + $this->includeDependentFiles = (boolean) $includeDependentFiles; + return $this; + } + + /** + * @copydoc PKP\Services\QueryBuilders\Interfaces\EntityQueryBuilderInterface::getCount() + */ + public function getCount() { + return $this + ->getQuery() + ->select('sf.submission_file_id') + ->get() + ->count(); + } + + /** + * @copydoc PKP\Services\QueryBuilders\Interfaces\EntityQueryBuilderInterface::getIds() + */ + public function getIds() { + return $this + ->getQuery() + ->select('sf.submission_file_id') + ->pluck('sf.submission_file_id') + ->toArray(); + } + + /** + * Execute query builder + * + * @return object Query object + */ + public function getQuery() { + $this->columns = ['sf.*']; + + $q = Capsule::table('submission_files as sf'); + + if (!empty($this->submissionIds)) { + $q->whereIn('sf.submission_id', $this->submissionIds); + } + + if (!empty($this->fileStages)) { + $q->whereIn('sf.file_stage', $this->fileStages); + } + + if (!empty($this->genreIds)) { + $q->whereIn('sf.genre_id', $this->genreIds); + } + + if (!empty($this->fileIds)) { + $q->leftJoin('submission_file_revisions as sfr', 'sfr.submission_file_id', '=', 'sf.submission_file_id') + ->whereIn('sfr.file_id', $this->fileIds); + } + + if (!empty($this->reviewRoundIds)) { + $q->join('review_round_files as rr', 'rr.submission_file_id', '=', 'sf.submission_file_id') + ->whereIn('rr.review_round_id', $this->reviewRoundIds); + } + + if (!empty($this->reviewIds)) { + $q->join('review_files as rf', 'rf.submission_file_id', '=', 'sf.submission_file_id') + ->whereIn('rf.review_id', $this->reviewIds); + } + + if (!empty($this->assocTypes)) { + $q->whereIn('sf.assoc_type', $this->assocTypes); + + if (!empty($this->assocIds)) { + $q->whereIn('sf.assoc_id', $this->assocIds); + } + } + + if (!empty($this->uploaderUserIds)) { + $q->whereIn('sf.uploader_user_id', $this->uploaderUserIds); + } + + if (empty($this->includeDependentFiles) && !in_array(SUBMISSION_FILE_DEPENDENT, $this->fileStages)) { + $q->where('sf.file_stage', '!=', SUBMISSION_FILE_DEPENDENT); + } + + // Add app-specific query statements + \HookRegistry::call('SubmissionFile::getMany::queryObject', array(&$q, $this)); + + // Only return results for the latest revision + $q->select($this->columns); + + return $q; + } + + /** + * Get the file ids for each revision of a submission file + * + * @param int $submissionFileId + * @return array + */ + public function getRevisionFileIds($submissionFileId) { + return Capsule::table('submission_file_revisions') + ->where('submission_file_id', '=', $submissionFileId) + ->orderBy('revision_id', 'desc') + ->pluck('file_id') + ->toArray(); + } +} diff --git a/classes/statistics/PKPMetricsDAO.inc.php b/classes/statistics/PKPMetricsDAO.inc.php index 486ea2626e3..b3066af6f68 100644 --- a/classes/statistics/PKPMetricsDAO.inc.php +++ b/classes/statistics/PKPMetricsDAO.inc.php @@ -354,13 +354,12 @@ protected function foreignKeyLookup($assocType, $assocId) { switch($assocType) { case ASSOC_TYPE_SUBMISSION_FILE: case ASSOC_TYPE_SUBMISSION_FILE_COUNTER_OTHER: - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFile = $submissionFileDao->getLatestRevision($assocId); + $submissionFile = Services::get('submissionFile')->get($assocId); if ($submissionFile) { $isFile = true; - $submissionId = $submissionFile->getSubmissionId(); - if ($submissionFile->getAssocType() == ASSOC_TYPE_REPRESENTATION) { - $representationId = $submissionFile->getAssocId(); + $submissionId = $submissionFile->getData('submissionId'); + if ($submissionFile->getData('assocType') == ASSOC_TYPE_REPRESENTATION) { + $representationId = $submissionFile->getData('assocId'); } else { throw new Exception('Cannot load record: submission file is not associated with a representation object.'); } diff --git a/classes/submission/EditDecisionDAO.inc.php b/classes/submission/EditDecisionDAO.inc.php index 2d44ecf218d..60c3af3e264 100644 --- a/classes/submission/EditDecisionDAO.inc.php +++ b/classes/submission/EditDecisionDAO.inc.php @@ -171,16 +171,16 @@ function responseExists($decision, $submissionId) { $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ $reviewRound = $reviewRoundDao->getReviewRound($submissionId, $stageId, $round); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ import('lib.pkp.classes.submission.SubmissionFile'); // Bring the file constants. - $submissionFiles = $submissionFileDao->getRevisionsByReviewRound($reviewRound, SUBMISSION_FILE_REVIEW_REVISION); - - if (is_array($submissionFiles)) { - foreach ($submissionFiles as $file) { - if ($file->getDateUploaded() > $decision['dateDecided']) { - $sentRevisions = true; - break; - } + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'reviewRoundIds' => [$reviewRound->getId()], + 'fileStages' => [SUBMISSION_FILE_REVIEW_REVISION], + ]); + + foreach ($submissionFilesIterator as $submissionFile) { + if ($submissionFile->getData('uploadedAt') > $decision['dateDecided']) { + $sentRevisions = true; + break; } } diff --git a/classes/submission/GenreDAO.inc.php b/classes/submission/GenreDAO.inc.php index 257df82a238..996e026186f 100644 --- a/classes/submission/GenreDAO.inc.php +++ b/classes/submission/GenreDAO.inc.php @@ -191,10 +191,10 @@ function newDataObject() { */ function _fromRow($row) { $genre = $this->newDataObject(); - $genre->setId($row['genre_id']); + $genre->setId((int) $row['genre_id']); $genre->setKey($row['entry_key']); $genre->setContextId($row['context_id']); - $genre->setCategory($row['category']); + $genre->setCategory((int) $row['category']); $genre->setDependent($row['dependent']); $genre->setSupplementary($row['supplementary']); $genre->setSequence($row['seq']); diff --git a/classes/submission/PKPAuthorDAO.inc.php b/classes/submission/PKPAuthorDAO.inc.php index e65fa730ad0..f706f99ee6c 100644 --- a/classes/submission/PKPAuthorDAO.inc.php +++ b/classes/submission/PKPAuthorDAO.inc.php @@ -18,19 +18,19 @@ import('lib.pkp.classes.submission.PKPAuthor'); abstract class PKPAuthorDAO extends SchemaDAO { - /** @copydoc SchemaDao::$schemaName */ + /** @copydoc SchemaDAO::$schemaName */ public $schemaName = SCHEMA_AUTHOR; - /** @copydoc SchemaDao::$tableName */ + /** @copydoc SchemaDAO::$tableName */ public $tableName = 'authors'; - /** @copydoc SchemaDao::$settingsTableName */ + /** @copydoc SchemaDAO::$settingsTableName */ public $settingsTableName = 'author_settings'; - /** @copydoc SchemaDao::$primaryKeyColumn */ + /** @copydoc SchemaDAO::$primaryKeyColumn */ public $primaryKeyColumn = 'author_id'; - /** @copydoc SchemaDao::$primaryTableColumns */ + /** @copydoc SchemaDAO::$primaryTableColumns */ public $primaryTableColumns = [ 'id' => 'author_id', 'email' => 'email', @@ -54,7 +54,7 @@ function newDataObject() { */ public function getById($objectId) { $result = $this->retrieve( - 'SELECT a.*, p.locale AS submission_locale FROM authors a JOIN publications p ON (a.publication_id = p.publication_id) WHERE author_id = ?', + 'SELECT a.*, s.locale AS submission_locale FROM authors a JOIN publications p ON (a.publication_id = p.publication_id) JOIN submissions s ON (s.submission_id = p.submission_id) WHERE author_id = ?', (int) $objectId ); @@ -79,10 +79,11 @@ function getByPublicationId($publicationId, $sortByAuthorId = false, $useInclude if ($useIncludeInBrowse) $params[] = 1; $result = $this->retrieve( - 'SELECT DISTINCT a.*, ug.show_title, p.locale AS submission_locale + 'SELECT DISTINCT a.*, ug.show_title, s.locale AS submission_locale FROM authors a JOIN user_groups ug ON (a.user_group_id=ug.user_group_id) JOIN publications p ON (p.publication_id = a.publication_id) + JOIN submissions s ON (s.submission_id = p.submission_id) LEFT JOIN author_settings au ON (au.author_id = a.author_id) WHERE a.publication_id = ? ' . ($useIncludeInBrowse ? ' AND a.include_in_browse = ?' : '') diff --git a/classes/submission/PKPSubmission.inc.php b/classes/submission/PKPSubmission.inc.php index 19b11eedb64..aa1facf31b3 100644 --- a/classes/submission/PKPSubmission.inc.php +++ b/classes/submission/PKPSubmission.inc.php @@ -221,19 +221,39 @@ function setContextId($contextId) { } /** - * Get a piece of data for this object, localized to the current - * locale of the current publication if possible. - * @param $key string - * @param $preferredLocale string - * @param $returnLocale string Optional reference to string receiving return value's locale + * Get localized data for this object. + * + * It selects the locale in the following order: + * - $preferredLocale + * - the user's current locale + * - the submission's primary locale + * - the first locale we find data for + * + * @param string $key + * @param string $preferredLocale * @return mixed - * @deprecated 3.2.0.0 */ - function &getLocalizedData($key, $preferredLocale = null) { - $publication = $this->getCurrentPublication(); - if ($publication) { - return $publication->getLocalizedData($key, $preferredLocale); + public function getLocalizedData($key, $preferredLocale = null) { + // 1. Preferred locale + if ($preferredLocale && $this->getData($key, $preferredLocale)) { + return $this->getData($key, $preferredLocale); + } + // 2. User's current locale + if (!empty($this->getData($key, AppLocale::getLocale()))) { + return $this->getData($key, AppLocale::getLocale()); + } + // 3. Submission's primary locale + if (!empty($this->getData($key, $this->getData('locale')))) { + return $this->getData($key, $this->getData('locale')); + } + // 4. The first locale we can find data for + $data = $this->getData($key, null); + foreach ((array) $data as $value) { + if (!empty($value)) { + return $value; + } } + return null; } @@ -451,11 +471,7 @@ function getPrimaryAuthor() { * @deprecated 3.2.0.0 */ function getLocale() { - $publication = $this->getCurrentPublication(); - if (!$publication) { - return ''; - } - return $publication->getData('locale'); + return $this->getData('locale'); } /** @@ -464,10 +480,7 @@ function getLocale() { * @deprecated 3.2.0.0 */ function setLocale($locale) { - $publication = $this->getCurrentPublication(); - if ($publication) { - $publication->setData('locale', $locale); - } + $this->setData('locale', $locale); } /** diff --git a/classes/submission/PKPSubmissionDAO.inc.php b/classes/submission/PKPSubmissionDAO.inc.php index d79764a48ff..97623bf42a1 100644 --- a/classes/submission/PKPSubmissionDAO.inc.php +++ b/classes/submission/PKPSubmissionDAO.inc.php @@ -45,6 +45,7 @@ abstract class PKPSubmissionDAO extends SchemaDAO { 'dateLastActivity' => 'date_last_activity', 'dateSubmitted' => 'date_submitted', 'lastModified' => 'last_modified', + 'locale' => 'locale', 'stageId' => 'stage_id', 'status' => 'status', 'submissionProgress' => 'submission_progress', @@ -112,8 +113,6 @@ function deleteById($submissionId) { throw new Exception('Could not delete submission. No submission with the id ' . (int) $submissionId . ' was found.'); } - parent::deleteById($submissionId); - // Delete publications $publicationsIterator = Services::get('publication')->getMany(['submissionIds' => $submissionId]); $publicationDao = DAORegistry::getDAO('PublicationDAO'); /* @var $publicationDao PublicationDAO */ @@ -122,15 +121,12 @@ function deleteById($submissionId) { } // Delete submission files. - // 'deleteAllRevisionsBySubmissionId' has to be called before 'rmtree' - // because SubmissionFileDaoDelegate::deleteObjects checks the file - // and returns false if the file is not there, which makes the foreach loop in - // SubmissionFileDAO::_deleteInternally not run till the end. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->deleteAllRevisionsBySubmissionId($submissionId); - import('lib.pkp.classes.file.SubmissionFileManager'); - $submissionFileManager = new SubmissionFileManager($submission->getContextId(), $submission->getId()); - $submissionFileManager->rmtree($submissionFileManager->getBasePath()); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$submission->getId()], + ]); + foreach ($submissionFilesIterator as $submissionFile) { + Services::get('submissionFile')->delete($submissionFile); + } $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ $reviewRoundDao->deleteBySubmissionId($submissionId); @@ -167,6 +163,8 @@ function deleteById($submissionId) { $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /* @var $submissionEmailLogDao SubmissionEmailLogDAO */ $submissionEmailLogDao->deleteByAssoc(ASSOC_TYPE_SUBMISSION, $submissionId); + + parent::deleteById($submissionId); } /** diff --git a/classes/submission/PKPSubmissionFileDAO.inc.php b/classes/submission/PKPSubmissionFileDAO.inc.php new file mode 100644 index 00000000000..1702e38a2de --- /dev/null +++ b/classes/submission/PKPSubmissionFileDAO.inc.php @@ -0,0 +1,343 @@ + 'assoc_id', + 'assocType' => 'assoc_type', + 'createdAt' => 'created_at', + 'fileId' => 'file_id', + 'fileStage' => 'file_stage', + 'genreId' => 'genre_id', + 'id' => 'submission_file_id', + 'sourceSubmissionFileId' => 'source_submission_file_id', + 'submissionId' => 'submission_id', + 'updatedAt' => 'updated_at', + 'uploaderUserId' => 'uploader_user_id', + 'viewable' => 'viewable', + ]; + + /** + * Create a new DataObject of the appropriate class + * + * @return DataObject + */ + public function newDataObject() { + return new SubmissionFile(); + } + + /** + * @copydoc SchemaDAO::_fromRow() + */ + public function _fromRow($primaryRow) { + $submissionFile = parent::_fromRow($primaryRow); + + // Set the primary locale from the submission + $locale = Capsule::table('submissions as s') + ->where('s.submission_id', '=', $submissionFile->getData('submissionId')) + ->value('locale'); + $submissionFile->setData('locale', $locale); + + return $submissionFile; + } + + /** + * @copydoc SchemaDAO::insertObject + */ + public function insertObject($submissionFile) { + parent::insertObject($submissionFile); + + Capsule::table('submission_file_revisions')->insert([ + 'submission_file_id' => $submissionFile->getId(), + 'file_id' => $submissionFile->getData('fileId'), + ]); + + if (in_array($submissionFile->getData('assocType'), [ASSOC_TYPE_REVIEW_ROUND, ASSOC_TYPE_REVIEW_ASSIGNMENT])) { + if ($submissionFile->getData('assocType') === ASSOC_TYPE_REVIEW_ROUND) { + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ + $reviewRound = $reviewRoundDao->getById($submissionFile->getData('assocId')); + } elseif ($submissionFile->getData('assocType') === ASSOC_TYPE_REVIEW_ASSIGNMENT) { + $reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /* @var $reviewAssignmentDao ReviewAssignmentDAO */ + $reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getData('assocId')); + $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ + $reviewRound = $reviewRoundDao->getById($reviewAssignment->getReviewRoundId()); + } + if (!$reviewRound) { + throw new Exception('Review round not found for adding submission file.'); + } + Capsule::table('review_round_files')->insert([ + 'submission_id' => $submissionFile->getData('submissionId'), + 'review_round_id' => $reviewRound->getId(), + 'stage_id' => $reviewRound->getStageId(), + 'submission_file_id' => $submissionFile->getId(), + ]); + } + + return $submissionFile->getId(); + } + + /** + * @copydoc SchemaDAO::updateObject() + */ + public function updateObject($submissionFile) { + parent::updateObject($submissionFile); + + $hasFileId = Capsule::table('submission_file_revisions') + ->where('submission_file_id', '=', $submissionFile->getId()) + ->where('file_id', '=', $submissionFile->getData('fileId')) + ->exists(); + + if (!$hasFileId) { + Capsule::table('submission_file_revisions')->insert([ + 'submission_file_id' => $submissionFile->getId(), + 'file_id' => $submissionFile->getData('fileId'), + ]); + } + } + + /** + * @copydoc SchemaDAO::deleteById() + */ + public function deleteById($submissionFileId) { + Capsule::table('submission_file_revisions') + ->where('submission_file_id', '=', $submissionFileId) + ->delete(); + + Capsule::table('review_round_files') + ->where('submission_file_id', '=', $submissionFileId) + ->delete(); + + parent::deleteById($submissionFileId); + } + + + // + // Public methods + // + /** + * Retrieve file by public file ID + * @param $pubIdType string One of the NLM pub-id-type values or + * 'other::something' if not part of the official NLM list + * (see ). + * @param $pubId string + * @param $submissionId int optional + * @param $contextId int optional + * @return SubmissionFile|null + */ + function getByPubId($pubIdType, $pubId, $submissionId = null, $contextId = null) { + if (empty($pubId)) { + return null; + } + + $submissionFileId = Capsule::table('submission_files as sf') + ->leftJoin('submission_file_settings as sfs', 'sfs.submission_file_id', '=' , 'sf.submission_file_id') + ->where('sf.submission_id', '=', $submissionId) + ->where(function($q) use ($pubIdType, $pubId) { + $q->where('sfs.setting_name', '=', 'pub-id::' . $pubIdType); + $q->where('sfs.setting_value', '=', $pubId); + }) + ->value('sf.submission_file_id'); + + if (empty($submissionFileId)) { + return null; + } + + $submissionFile = Services::get('submissionFile')->get($submissionFileId); + + if ($submissionFile->getData('fileStage') !== SUBMISSION_FILE_PROOF) { + return $submissionFile; + } + + return null; + } + + /** + * Retrieve file by public ID or submissionFileId + * + * @param string|int $bestId Publisher id or submissionFileId + * @param int $submissionId + * @return SubmissionFile|null + */ + function getByBestId($bestId, $submissionId) { + $submissionFile = null; + if ($bestId != '') $submissionFile = $this->getByPubId('publisher-id', $bestId, $submissionId, null); + if (!isset($submissionFile)) { + $submissionFile = Services::get('submissionFile')->get($bestId); + } + if ($submissionFile && in_array($submissionFile->getData('fileStage'), [SUBMISSION_FILE_PROOF, SUBMISSION_FILE_DEPENDENT])) { + return $submissionFile; + } + return null; + } + + /** + * Assign file to a review round. + * @param $submissionFileId int The file to be assigned. + * @param $reviewRound ReviewRound + */ + function assignRevisionToReviewRound($submissionFileId, $reviewRound) { + + // Avoid duplication errors -- clear out any existing entries + $this->deleteReviewRoundAssignment($submissionFileId); + + return $this->update( + 'INSERT INTO review_round_files + (submission_id, review_round_id, stage_id, submission_file_id) + VALUES (?, ?, ?, ?)', + array( + (int)$reviewRound->getSubmissionId(), + (int)$reviewRound->getId(), + (int)$reviewRound->getStageId(), + (int)$submissionFileId + ) + ); + } + + /** + * Remove a specific file assignment from a review round. + * @param $submissionFileId int The file id. + */ + function deleteReviewRoundAssignment($submissionFileId) { + // Remove currently assigned review files. + $this->update( + 'DELETE FROM review_round_files + WHERE submission_file_id = ?', + array( + (int) $submissionFileId, + ) + ); + } + + + // + // Protected helper methods + // + + + /** + * Checks if public identifier exists (other than for the specified + * submission file ID, which is treated as an exception). + * @param $pubIdType string One of the NLM pub-id-type values or + * 'other::something' if not part of the official NLM list + * (see ). + * @param $pubId string + * @param $fileId int An ID to be excluded from the search. + * @param $contextId int + * @return boolean + */ + function pubIdExists($pubIdType, $pubId, $excludePubObjectId, $contextId) { + $result = $this->retrieve( + 'SELECT COUNT(*) + FROM submission_file_settings sfs + INNER JOIN submission_files sf ON sfs.file_id = sf.file_id + INNER JOIN submissions s ON sf.submission_id = s.submission_id + WHERE sfs.setting_name = ? AND sfs.setting_value = ? AND sfs.file_id <> ? AND s.context_id = ?', + array( + 'pub-id::'.$pubIdType, + $pubId, + (int) $excludePubObjectId, + (int) $contextId + ) + ); + $returner = $result->fields[0] ? true : false; + $result->Close(); + return $returner; + } + + /** + * @copydoc PKPPubIdPluginDAO::changePubId() + */ + function changePubId($pubObjectId, $pubIdType, $pubId) { + $idFields = array( + 'file_id', 'locale', 'setting_name' + ); + $updateArray = array( + 'file_id' => (int) $pubObjectId, + 'locale' => '', + 'setting_name' => 'pub-id::'.$pubIdType, + 'setting_type' => 'string', + 'setting_value' => (string)$pubId + ); + $this->replace('submission_file_settings', $updateArray, $idFields); + $this->flushCache(); + } + + /** + * @copydoc PKPPubIdPluginDAO::deletePubId() + */ + function deletePubId($pubObjectId, $pubIdType) { + $settingName = 'pub-id::'.$pubIdType; + $this->update( + 'DELETE FROM submission_file_settings WHERE setting_name = ? AND file_id = ?', + array( + $settingName, + (int)$pubObjectId + ) + ); + $this->flushCache(); + } + + /** + * @copydoc PKPPubIdPluginDAO::deleteAllPubIds() + */ + function deleteAllPubIds($contextId, $pubIdType) { + $settingName = 'pub-id::'.$pubIdType; + + $submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */ + $submissions = $submissionDao->getByContextId($contextId); + while ($submission = $submissions->next()) { + $submissionFileIds = Services::get('submissionFile')->getIds([ + 'submissionIds' => [$submission->getId()], + ]); + foreach ($submissionFileIds as $submissionFileId) { + $this->update( + 'DELETE FROM submission_file_settings WHERE setting_name = ? AND submission_file_id = ?', + array( + $settingName, + $submissionFileId + ) + ); + } + } + $this->flushCache(); + } + + // + // Private helper methods + // +} + + diff --git a/classes/submission/Representation.inc.php b/classes/submission/Representation.inc.php index e07553d1431..0e501cf7ce5 100644 --- a/classes/submission/Representation.inc.php +++ b/classes/submission/Representation.inc.php @@ -141,18 +141,6 @@ function getContextId() { function getDAO() { return Application::getRepresentationDAO(); } - - function getRepresentationFiles($fileStage = null) { - $publication = Services::get('publication')->get($this->getData('publicationId')); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /** @var $submissionFileDao SubmissionFileDAO */ - return $submissionFileDao->getLatestRevisionsByAssocId( - ASSOC_TYPE_REPRESENTATION, - $this->getId(), - $publication->getData('submissionId'), - $fileStage, - null - ); - } } diff --git a/classes/submission/ReviewFilesDAO.inc.php b/classes/submission/ReviewFilesDAO.inc.php index 0d1ac5d1793..3082c0ebe39 100644 --- a/classes/submission/ReviewFilesDAO.inc.php +++ b/classes/submission/ReviewFilesDAO.inc.php @@ -19,17 +19,17 @@ class ReviewFilesDAO extends DAO { /** * Grant a review file to a review. * @param $reviewId int Review assignment ID - * @param $fileId int Review file ID + * @param $submissionFileId int Submission file ID */ - function grant($reviewId, $fileId) { + function grant($reviewId, $submissionFileId) { $this->update( 'INSERT INTO review_files - (review_id, file_id) + (review_id, submission_file_id) VALUES (?, ?)', array( (int) $reviewId, - (int) $fileId + (int) $submissionFileId ) ); } @@ -63,13 +63,13 @@ function revokeByReviewId($reviewId) { /** * Check review file availability * @param $reviewId integer - * @param $fileId int + * @param $submission_file_id int * @return boolean */ - function check($reviewId, $fileId) { + function check($reviewId, $submission_file_id) { $result = $this->retrieve( - 'SELECT * FROM review_files WHERE review_id = ? AND file_id = ?', - array((int) $reviewId, (int) $fileId) + 'SELECT * FROM review_files WHERE review_id = ? AND submission_file_id = ?', + array((int) $reviewId, (int) $submission_file_id) ); $returner = $result->RecordCount(); diff --git a/classes/submission/SubmissionArtworkFile.inc.php b/classes/submission/SubmissionArtworkFile.inc.php deleted file mode 100644 index 316b8d5bb42..00000000000 --- a/classes/submission/SubmissionArtworkFile.inc.php +++ /dev/null @@ -1,223 +0,0 @@ -getData('caption'); - } - - /** - * Set artwork caption. - * @param $caption string - */ - function setCaption($caption) { - $this->setData('caption', $caption); - } - - /** - * Get the credit. - * @return string - */ - function getCredit() { - return $this->getData('credit'); - } - - /** - * Set the credit. - * @param $credit string - */ - function setCredit($credit) { - $this->setData('credit', $credit); - } - - /** - * Get the copyright owner. - * @return string - */ - function getCopyrightOwner() { - return $this->getData('copyrightOwner'); - } - - /** - * Set the copyright owner. - * @param $owner string - */ - function setCopyrightOwner($owner) { - $this->setData('copyrightOwner', $owner); - } - - /** - * Get contact details for the copyright owner. - * @return string - */ - function getCopyrightOwnerContactDetails() { - return $this->getData('copyrightOwnerContact'); - } - - /** - * Set the contact details for the copyright owner. - * @param $contactDetails string - */ - function setCopyrightOwnerContactDetails($contactDetails) { - $this->setData('copyrightOwnerContact', $contactDetails); - } - - /** - * Get the permission terms. - * @return string - */ - function getPermissionTerms() { - return $this->getData('terms'); - } - - /** - * Set the permission terms. - * @param $terms string - */ - function setPermissionTerms($terms) { - $this->setData('terms', $terms); - } - - /** - * Get the permission form file id. - * @return int - */ - function getPermissionFileId() { - return $this->getData('permissionFileId'); - } - - /** - * Set the permission form file id. - * @param $fileId int - */ - function setPermissionFileId($fileId) { - $this->setData('permissionFileId', $fileId); - } - - /** - * Get the contact author's id. - * @return int - */ - function getContactAuthor() { - return $this->getData('contactAuthor'); - } - - /** - * Set the contact author's id. - * @param $authorId int - */ - function setContactAuthor($authorId) { - $this->setData('contactAuthor', $authorId); - } - - /** - * Get the width of the image in pixels. - * @return integer - */ - function getWidth() { - if (!$this->_imageInfo) { - $this->_imageInfo = getimagesize($this->getFilePath()); - } - return $this->_imageInfo[0]; - } - - /** - * Get the height of the image in pixels. - * @return integer - */ - function getHeight() { - if (!$this->_imageInfo) { - $this->_imageInfo = getimagesize($this->getFilePath()); - } - return $this->_imageInfo[1]; - } - - /** - * Get the physical width of an image when printed - * - * Common use is to print at 300 DPI (dots per inch), but you can pass any - * any pixel density to this function to return it's printed width. For - * instance, a 300 DPI is roughly equal to 118 dpcm (dots per centimeter), - * so you'd pass $dpi = 118 to calculate the width in centimeters. - * - * @param $dpi int Dots (or pixels) per inch (or any other unit of - * measurement). - * @return integer - */ - function getPhysicalWidth($dpi) { - $width = $this->getWidth(); - if (!is_int($width) || $width <= 0) { - return 0; - } - return number_format($width/$dpi,1); - } - - /** - * Get the physical height of an image when printed - * - * @see self::getPhysicalWidth - * @param $dpi int Dots (or pixels) per inch (or any other unit of - * measurement). - * @return integer - */ - function getPhysicalHeight($dpi) { - $height = $this->getheight(); - if (!is_int($height) || $height <= 0) { - return 0; - } - return number_format($height/$dpi,1); - } - - /** - * Copy the user-facing (editable) metadata from another submission - * file. - * @param $submissionFile SubmissionFile - */ - function copyEditableMetadataFrom($submissionFile) { - if (is_a($submissionFile, 'SubmissionArtworkFile')) { - $this->setCaption($submissionFile->getCaption()); - $this->setCredit($submissionFile->getCredit()); - $this->setCopyrightOwner($submissionFile->getCopyrightOwner()); - $this->setCopyrightOwnerContactDetails($submissionFile->getCopyrightOwnerContactDetails()); - $this->setPermissionTerms($submissionFile->getPermissionTerms()); - } - - parent::copyEditableMetadataFrom($submissionFile); - } - - /** - * @copydoc SubmissionFile::getMetadataForm - */ - function getMetadataForm($stageId, $reviewRound) { - import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesArtworkMetadataForm'); - return new SubmissionFilesArtworkMetadataForm($this, $stageId, $reviewRound); - } -} - - diff --git a/classes/submission/SubmissionArtworkFileDAODelegate.inc.php b/classes/submission/SubmissionArtworkFileDAODelegate.inc.php deleted file mode 100644 index 4f88f9bb198..00000000000 --- a/classes/submission/SubmissionArtworkFileDAODelegate.inc.php +++ /dev/null @@ -1,150 +0,0 @@ -update( - 'INSERT INTO submission_artwork_files - (file_id, revision, caption, chapter_id, contact_author, copyright_owner, copyright_owner_contact, credit, permission_file_id, permission_terms) - VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', - array( - $artworkFile->getFileId(), - $artworkFile->getRevision(), - $artworkFile->getCaption(), - $artworkFile->getChapterId(), - $artworkFile->getContactAuthor(), - $artworkFile->getCopyrightOwner(), - $artworkFile->getCopyrightOwnerContactDetails(), - $artworkFile->getCredit(), - $artworkFile->getPermissionFileId(), - $artworkFile->getPermissionTerms() - ) - ); - - return $artworkFile; - } - - /** - * @see SubmissionFileDAODelegate::update() - * @param $artworkFile ArtworkFile - * @param $previousFile ArtworkFile - * @return boolean True if success. - */ - function updateObject($artworkFile, $previousFile) { - // Update the parent class table first. - if (!parent::updateObject($artworkFile, $previousFile)) return false; - - // Now update the artwork file table. - $this->update( - 'UPDATE submission_artwork_files - SET - file_id = ?, - revision = ?, - caption = ?, - chapter_id = ?, - contact_author = ?, - copyright_owner = ?, - copyright_owner_contact = ?, - credit = ?, - permission_file_id = ?, - permission_terms = ? - WHERE file_id = ? and revision = ?', - array( - (int)$artworkFile->getFileId(), - (int)$artworkFile->getRevision(), - $artworkFile->getCaption(), - is_null($artworkFile->getChapterId()) ? null : (int)$artworkFile->getChapterId(), - $artworkFile->getContactAuthor(), - $artworkFile->getCopyrightOwner(), - $artworkFile->getCopyrightOwnerContactDetails(), - $artworkFile->getCredit(), - is_null($artworkFile->getPermissionFileId()) ? null : (int)$artworkFile->getPermissionFileId(), - $artworkFile->getPermissionTerms(), - (int)$previousFile->getFileId(), - (int)$previousFile->getRevision() - ) - ); - return true; - } - - /** - * @see SubmissionFileDAODelegate::deleteObject() - */ - function deleteObject($submissionFile) { - // First delete the submission file entry. - if (!parent::deleteObject($submissionFile)) return false; - - // Delete the artwork file entry. - return $this->update( - 'DELETE FROM submission_artwork_files - WHERE file_id = ? AND revision = ?', - array( - (int)$submissionFile->getFileId(), - (int)$submissionFile->getRevision() - ) - ); - } - - /** - * @see SubmissionFileDAODelegate::fromRow() - * @return ArtworkFile - */ - function fromRow($row) { - $artworkFile = parent::fromRow($row); - $artworkFile->setCredit($row['credit']); - $artworkFile->setCaption($row['caption']); - $artworkFile->setChapterId(is_null($row['chapter_id']) ? null : (int)$row['chapter_id']); - $artworkFile->setContactAuthor($row['contact_author']); - $artworkFile->setCopyrightOwner($row['copyright_owner']); - $artworkFile->setPermissionTerms($row['permission_terms']); - $artworkFile->setPermissionFileId(is_null($row['permission_file_id']) ? null : (int)$row['permission_file_id']); - $artworkFile->setCopyrightOwnerContactDetails($row['copyright_owner_contact']); - - return $artworkFile; - } - - /** - * @copydoc SubmissionFileDAODelegate::newDataObject() - */ - function newDataObject() { - return new SubmissionArtworkFile(); - } -} - - diff --git a/classes/submission/SubmissionFile.inc.php b/classes/submission/SubmissionFile.inc.php index 2cd5a3f74c8..7908ae6d085 100644 --- a/classes/submission/SubmissionFile.inc.php +++ b/classes/submission/SubmissionFile.inc.php @@ -13,8 +13,6 @@ * @brief Submission file class. */ -import('lib.pkp.classes.file.PKPFile'); - // Define the file stage identifiers. define('SUBMISSION_FILE_SUBMISSION', 2); define('SUBMISSION_FILE_NOTE', 3); @@ -22,8 +20,6 @@ define('SUBMISSION_FILE_REVIEW_ATTACHMENT', 5); // SUBMISSION_FILE_REVIEW_REVISION defined below (FIXME: re-order before release) define('SUBMISSION_FILE_FINAL', 6); -define('SUBMISSION_FILE_FAIR_COPY', 7); -define('SUBMISSION_FILE_EDITOR', 8); define('SUBMISSION_FILE_COPYEDIT', 9); define('SUBMISSION_FILE_PROOF', 10); define('SUBMISSION_FILE_PRODUCTION_READY', 11); @@ -31,8 +27,17 @@ define('SUBMISSION_FILE_REVIEW_REVISION', 15); define('SUBMISSION_FILE_DEPENDENT', 17); define('SUBMISSION_FILE_QUERY', 18); +define('SUBMISSION_FILE_INTERNAL_REVIEW_FILE', 19); +define('SUBMISSION_FILE_INTERNAL_REVIEW_REVISION', 20); + +class SubmissionFile extends DataObject { -class SubmissionFile extends PKPFile { + /** + * @copydoc DataObject::getDAO() + */ + function getDAO() { + return DAORegistry::getDAO('SubmissionFileDAO'); + } /** * Get a piece of data for this object, localized to the current @@ -43,7 +48,7 @@ class SubmissionFile extends PKPFile { */ function &getLocalizedData($key, $preferredLocale = null) { if (is_null($preferredLocale)) $preferredLocale = AppLocale::getLocale(); - $localePrecedence = array($preferredLocale, $this->getSubmissionLocale()); + $localePrecedence = [$preferredLocale, $this->getData('locale')]; foreach ($localePrecedence as $locale) { if (empty($locale)) continue; $value =& $this->getData($key, $locale); @@ -66,55 +71,17 @@ function &getLocalizedData($key, $preferredLocale = null) { // // Getters and Setters // - /** - * Get ID of file. - * @return int - */ - function getFileId() { - // WARNING: Do not modernize getter/setters without considering - // ID clash with subclasses ArticleGalley and ArticleNote! - return $this->getData('fileId'); - } - - /** - * Set ID of file. - * @param $fileId int - */ - function setFileId($fileId) { - // WARNING: Do not modernize getter/setters without considering - // ID clash with subclasses ArticleGalley and ArticleNote! - $this->setData('fileId', $fileId); - } - - /** - * Get ID of file. - * @return int - */ - function getId() { - // WARNING: Do not modernize getter/setters without considering - // ID clash with subclasses ArticleGalley and ArticleNote! - return $this->getData('fileId'); - } - - /** - * Set ID of file. - * @param $fileId int - */ - function setId($fileId) { - // WARNING: Do not modernize getter/setters without considering - // ID clash with subclasses ArticleGalley and ArticleNote! - $this->setData('fileId', $fileId); - } /** * Get the locale of the submission. * This is not properly a property of the submission file * (e.g. it won't be persisted to the DB with the update function) * It helps solve submission locale requirement for file's multilingual metadata + * @deprecated 3.3.0.0 * @return string */ function getSubmissionLocale() { - return $this->getData('submissionLocale'); + return $this->getData('locale'); } /** @@ -122,58 +89,11 @@ function getSubmissionLocale() { * This is not properly a property of the submission file * (e.g. it won't be persisted to the DB with the update function) * It helps solve submission locale requirement for file's multilingual metadata + * @deprecated 3.3.0.0 * @param $submissionLocale string */ function setSubmissionLocale($submissionLocale) { - $this->setData('submissionLocale', $submissionLocale); - } - - /** - * Get source file ID of this file. - * @return int - */ - function getSourceFileId() { - return $this->getData('sourceFileId'); - } - - /** - * Set source file ID of this file. - * @param $sourceFileId int - */ - function setSourceFileId($sourceFileId) { - $this->setData('sourceFileId', $sourceFileId); - } - - /** - * Get source revision of this file. - * @return int - */ - function getSourceRevision() { - return $this->getData('sourceRevision'); - } - - /** - * Set source revision of this file. - * @param $sourceRevision int - */ - function setSourceRevision($sourceRevision) { - $this->setData('sourceRevision', $sourceRevision); - } - - /** - * Get associated ID of file. - * @return int - */ - function getAssocId() { - return $this->getData('assocId'); - } - - /** - * Set associated ID of file. - * @param $assocId int - */ - function setAssocId($assocId) { - $this->setData('assocId', $assocId); + $this->setData('locale', $submissionLocale); } /** @@ -232,67 +152,10 @@ function setSalesType($salesType) { $this->setData('salesType', $salesType); } - /** - * Set the name of the file - * @param $name string - * @param $locale string - */ - function setName($name, $locale) { - $this->setData('name', $name, $locale); - } - - /** - * Get the name of the file - * @param $locale string - * @return string - */ - function getName($locale) { - return $this->getData('name', $locale); - } - - /** - * Get the localized name of the file - * @return string - */ - function getLocalizedName() { - return $this->getLocalizedData('name'); - } - - /** - * Determine whether this file supports dependent content. - * @return boolean - */ - function supportsDependentFiles() { - return !in_array($this->getFileStage(), array(SUBMISSION_FILE_DEPENDENT, SUBMISSION_FILE_QUERY)) && in_array($this->getFileType(), array( - 'text/html', - 'application/xml', - 'text/xml', - )); - } - - /** - * Get the file's extension. - * @return string - */ - function getExtension() { - import('lib.pkp.classes.file.FileManager'); - $fileManager = new FileManager(); - return $fileManager->parseFileExtension($this->getOriginalFileName()); - } - - /** - * Get the file's document type (enumerated types) - * @return string - */ - function getDocumentType() { - import('lib.pkp.classes.file.FileManager'); - $fileManager = new FileManager(); - return $fileManager->getDocumentType($this->getFileType()); - } - /** * Set the genre id of this file (i.e. referring to Manuscript, Index, etc) * Foreign key into genres table + * @deprecated 3.3.0.0 * @param $genreId int */ function setGenreId($genreId) { @@ -302,20 +165,13 @@ function setGenreId($genreId) { /** * Get the genre id of this file (i.e. referring to Manuscript, Index, etc) * Foreign key into genres table + * @deprecated 3.3.0.0 * @return int */ function getGenreId() { return $this->getData('genreId'); } - /** - * Get revision number. - * @return int - */ - function getRevision() { - return $this->getData('revision'); - } - /** * Return the "best" file ID -- If a public ID is set, * use it; otherwise use the internal ID and revision. @@ -324,50 +180,12 @@ function getRevision() { function getBestId() { $publicFileId = $this->getStoredPubId('publisher-id'); if (!empty($publicFileId)) return $publicFileId; - return $this->getFileIdAndRevision(); - } - - /** - * Get the combined key of the file - * consisting of the file id and the revision. - * @return string - */ - function getFileIdAndRevision() { - $id = $this->getFileId(); - $revision = $this->getRevision(); - $idAndRevision = $id; - if ($revision) { - $idAndRevision .= '-'.$revision; - } - return $idAndRevision; - } - - /** - * Set revision number. - * @param $revision int - */ - function setRevision($revision) { - $this->setData('revision', $revision); - } - - /** - * Get ID of submission. - * @return int - */ - function getSubmissionId() { - return $this->getData('submissionId'); - } - - /** - * Set ID of submission. - * @param $submissionId int - */ - function setSubmissionId($submissionId) { - $this->setData('submissionId', $submissionId); + return $this->getId(); } /** * Get file stage of the file. + * @deprecated 3.3.0.0 * @return int SUBMISSION_FILE_... */ function getFileStage() { @@ -376,6 +194,7 @@ function getFileStage() { /** * Set file stage of the file. + * @deprecated 3.3.0.0 * @param $fileStage int SUBMISSION_FILE_... */ function setFileStage($fileStage) { @@ -384,41 +203,27 @@ function setFileStage($fileStage) { /** * Get modified date of file. + * @deprecated 3.3.0.0 * @return date */ function getDateModified() { - return $this->getData('dateModified'); + return $this->getData('updatedAt'); } /** * Set modified date of file. - * @param $dateModified date + * @deprecated 3.3.0.0 + * @param $updatedAt date */ - function setDateModified($dateModified) { - return $this->setData('dateModified', $dateModified); - } - - /** - * Get round. - * @return int - */ - - function getRound() { - return $this->getData('round'); - } - - /** - * Set round. - * @param $round int - */ - function setRound($round) { - return $this->setData('round', $round); + function setDateModified($updatedAt) { + return $this->setData('updatedAt', $updatedAt); } /** * Get viewable. + * @deprecated 3.3.0.0 * @return boolean */ function getViewable() { @@ -428,6 +233,7 @@ function getViewable() { /** * Set viewable. + * @deprecated 3.3.0.0 * @param $viewable boolean */ function setViewable($viewable) { @@ -436,6 +242,7 @@ function setViewable($viewable) { /** * Set the uploader's user id. + * @deprecated 3.3.0.0 * @param $uploaderUserId integer */ function setUploaderUserId($uploaderUserId) { @@ -444,6 +251,7 @@ function setUploaderUserId($uploaderUserId) { /** * Get the uploader's user id. + * @deprecated 3.3.0.0 * @return integer */ function getUploaderUserId() { @@ -452,6 +260,7 @@ function getUploaderUserId() { /** * Get type that is associated with this file. + * @deprecated 3.3.0.0 * @return int */ function getAssocType() { @@ -460,6 +269,7 @@ function getAssocType() { /** * Set type that is associated with this file. + * @deprecated 3.3.0.0 * @param $assocType int */ function setAssocType($assocType) { @@ -468,6 +278,7 @@ function setAssocType($assocType) { /** * Get the submission chapter id. + * @deprecated 3.3.0.0 * @return int */ function getChapterId() { @@ -476,218 +287,10 @@ function getChapterId() { /** * Set the submission chapter id. + * @deprecated 3.3.0.0 * @param $chapterId int */ function setChapterId($chapterId) { $this->setData('chapterId', $chapterId); } - - /** - * Return a context-aware file path. - */ - function getFilePath() { - // Get the context ID - $submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */ - $submission = $submissionDao->getById($this->getSubmissionId()); - if (!$submission) return null; - $contextId = $submission->getContextId(); - unset($submission); - - // Construct the file path - import('lib.pkp.classes.file.SubmissionFileManager'); - $submissionFileManager = new SubmissionFileManager($contextId, $this->getSubmissionId()); - return $submissionFileManager->getBasePath() . $this->_fileStageToPath($this->getFileStage()) . '/' . $this->getServerFileName(); - } - - /** - * Build a file name label. - * @return string - */ - function getFileLabel($locale = null) { - // Retrieve the localized file name as basis for the label. - if ($locale) { - $fileLabel = $this->getName($locale); - } else { - $fileLabel = $this->getLocalizedName(); - } - - // If we have no file name then use a default name. - if (empty($fileLabel)) $fileLabel = $this->getOriginalFileName(); - - // Add the revision number to the label if we have more than one revision. - if ($this->getRevision() > 1) $fileLabel .= ' (' . $this->getRevision() . ')'; - - return $fileLabel; - } - - - /** - * Copy the user-facing (editable) metadata from another submission - * file. - * @param $submissionFile SubmissionFile - */ - function copyEditableMetadataFrom($submissionFile) { - assert(is_a($submissionFile, 'SubmissionFile')); - $this->setName($submissionFile->getName(null), null); - $this->setChapterId($submissionFile->getChapterId()); - } - - /** - * Get the filename that should be sent to clients when downloading. - * @return string - */ - function getClientFileName() { - // Generate a human readable time stamp. - $timestamp = date('Ymd', strtotime($this->getDateUploaded())); - - $genreDao = DAORegistry::getDAO('GenreDAO'); /* @var $genreDao GenreDAO */ - $genre = $genreDao->getById($this->getGenreId()); - - // Make the file name unique across all files and file revisions. - // Also make sure that files can be ordered sensibly by file name. - return $this->getSubmissionId() . '-'. - ($genre? ($genre->getLocalizedName() . '-'):'') . - $this->getFileId() . '-' . - $this->getRevision() . '-' . - $this->getFileStage() . '-' . - $timestamp . - '.' . - $this->getExtension(); - } - - // - // Overridden public methods from PKPFile - // - /** - * @see PKPFile::getServerFileName() - * Generate the file name from identification data rather than - * retrieving it from the database. - */ - function getServerFileName() { - return $this->_generateFileName(); - } - - /** - * @see PKPFile::setFileName() - * Do not allow setting the file name of a submission file - * directly because it is generated from identification data. - */ - function setServerFileName($fileName) { - assert(false); - } - - /** - * Get submission file number of public downloads. - * @return int - */ - function getViews() { - $application = Application::get(); - return $application->getPrimaryMetricByAssoc(ASSOC_TYPE_SUBMISSION_FILE, $this->getFileId()); - } - - // - // Private helper methods - // - - /** - * Generate the unique filename for this submission file. - * @return string - */ - function _generateFileName() { - // Generate a human readable time stamp. - $timestamp = date('Ymd', strtotime($this->getDateUploaded())); - - // Make the file name unique across all files and file revisions. - // Also make sure that files can be ordered sensibly by file name. - return $this->getSubmissionId() . '-'. - $this->getGenreId() . '-' . - $this->getFileId() . '-' . - $this->getRevision() . '-' . - $this->getFileStage() . '-' . - $timestamp . - '.' . - strtolower_codesafe($this->getExtension()); - } - - /** - * Generate a user-facing name for the file - * @param $anonymous boolean Whether the user name should be excluded - * @return string - */ - function _generateName($anonymous = false) { - $genreDao = DAORegistry::getDAO('GenreDAO'); /* @var $genreDao GenreDAO */ - $genre = $genreDao->getById($this->getGenreId()); - $userDAO = DAORegistry::getDAO('UserDAO'); - $user = $userDAO->getById($this->getUploaderUserId()); - - $submissionLocale = $this->getSubmissionLocale(); - AppLocale::requireComponents(LOCALE_COMPONENT_PKP_COMMON, $submissionLocale); - - $genreName = ''; - if ($genre) { - $genreName = $genre->getName($submissionLocale) ? $genre->getName($submissionLocale) : $genre->getLocalizedName(); - } - - $localeKey = $anonymous ? 'common.file.anonymousNamingPattern' : 'common.file.namingPattern'; - return __($localeKey, - array( - 'genre' => $genreName, - 'docType' => $this->getDocumentType(), - 'originalFilename' => $this->getOriginalFilename(), - 'username' => $user->getUsername(), - ), - $submissionLocale - ); - } - - /** - * Return path associated with a file stage code. - * @param $fileStage string - * @return string - */ - function _fileStageToPath($fileStage) { - static $fileStageToPath = array( - 0 => '', // Temporary files do not use stages - SUBMISSION_FILE_SUBMISSION => 'submission', - SUBMISSION_FILE_NOTE => 'note', - SUBMISSION_FILE_REVIEW_FILE => 'submission/review', - SUBMISSION_FILE_REVIEW_ATTACHMENT => 'submission/review/attachment', - SUBMISSION_FILE_REVIEW_REVISION => 'submission/review/revision', - SUBMISSION_FILE_FINAL => 'submission/final', - SUBMISSION_FILE_FAIR_COPY => 'submission/fairCopy', - SUBMISSION_FILE_EDITOR => 'submission/editor', - SUBMISSION_FILE_COPYEDIT => 'submission/copyedit', - SUBMISSION_FILE_DEPENDENT => 'submission/proof', - SUBMISSION_FILE_PROOF => 'submission/proof', - SUBMISSION_FILE_PRODUCTION_READY => 'submission/productionReady', - SUBMISSION_FILE_ATTACHMENT => 'attachment', - SUBMISSION_FILE_QUERY => 'submission/query', - ); - - assert(isset($fileStageToPath[$fileStage])); - return $fileStageToPath[$fileStage]; - } - - // - // Public methods - // - /** - * Get the metadata form for this submission file. - * @param $stageId int FILE_STAGE_... - * @param $reviewRound ReviewRound - * @return Form - */ - function getMetadataForm($stageId, $reviewRound) { - import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesMetadataForm'); - return new SubmissionFilesMetadataForm($this, $stageId, $reviewRound); - } - - /** - * @copydoc DataObject::getDAO() - */ - function getDAO() { - return DAORegistry::getDAO('SubmissionFileDAO'); - } } - - diff --git a/classes/submission/SubmissionFileDAO.inc.php b/classes/submission/SubmissionFileDAO.inc.php deleted file mode 100644 index 23ad4410ee5..00000000000 --- a/classes/submission/SubmissionFileDAO.inc.php +++ /dev/null @@ -1,1119 +0,0 @@ -_getInternally($submissionId, $fileStage, $fileId, $revision, null, null, null, null, null, false, null); - return $this->_checkAndReturnRevision($revisions); - } - - /** - * Find file IDs by querying file settings. - * @param $settingName string - * @param $settingValue mixed - * @param $submissionId int optional - * @param $contextId int optional - * @return array The file IDs identified by setting. - */ - function getFileIdsBySetting($settingName, $settingValue, $submissionId = null, $contextId = null) { - $params = array($settingName); - - $sql = 'SELECT DISTINCT f.file_id - FROM submission_files f - INNER JOIN submissions s ON s.submission_id = f.submission_id'; - if (is_null($settingValue)) { - $sql .= ' LEFT JOIN submission_file_settings fs ON f.file_id = fs.file_id AND fs.setting_name = ? - WHERE (fs.setting_value IS NULL OR fs.setting_value = \'\')'; - } else { - $params[] = (string) $settingValue; - $sql .= ' INNER JOIN submission_file_settings fs ON f.file_id = fs.file_id - WHERE fs.setting_name = ? AND fs.setting_value = ?'; - } - - if ($submissionId) { - $params[] = (int) $submissionId; - $sql .= ' AND f.submission_id = ?'; - } - - if ($contextId) { - $params[] = (int) $contextId; - $sql .= ' AND s.context_id = ?'; - } - - $sql .= ' ORDER BY f.file_id'; - $result = $this->retrieve($sql, $params); - - $fileIds = array(); - while (!$result->EOF) { - $row = $result->getRowAssoc(false); - $fileIds[] = $row['file_id']; - $result->MoveNext(); - } - - $result->Close(); - return $fileIds; - } - - /** - * Retrieve file by public file ID - * @param $pubIdType string One of the NLM pub-id-type values or - * 'other::something' if not part of the official NLM list - * (see ). - * @param $pubId string - * @param $submissionId int optional - * @param $contextId int optional - * @return SubmissionFile|null - */ - function getByPubId($pubIdType, $pubId, $submissionId = null, $contextId = null) { - $file = null; - if (!empty($pubId)) { - $fileIds = $this->getFileIdsBySetting('pub-id::'.$pubIdType, $pubId, $submissionId, $contextId); - if (!empty($fileIds)) { - assert(count($fileIds) == 1); - $fileId = $fileIds[0]; - $file = $this->getLatestRevision($fileId, SUBMISSION_FILE_PROOF, $submissionId); - } - } - return $file; - } - - /** - * Retrieve file by public ID or, failing that, - * internal file ID and revision; public ID takes precedence. - * @param $fileId string Either public ID or fileId-revision - * @param $submissionId int - * @return SubmissionFile|null - */ - function getByBestId($fileId, $submissionId) { - $file = null; - if ($fileId != '') $file = $this->getByPubId('publisher-id', $fileId, $submissionId, null); - if (!isset($file)) { - list($fileId, $revision) = array_map(function($a) { - return (int) $a; - }, preg_split('/-/', $fileId)); - $file = $this->getRevision($fileId, $revision, null, $submissionId); - } - if ($file && $file->getFileStage() == SUBMISSION_FILE_PROOF) return $file; - if ($file && $file->getFileStage() == SUBMISSION_FILE_DEPENDENT) return $file; - return null; - } - - /** - * Retrieve the latest revision of a file. - * @param $fileId int File ID. - * @param $fileStage int (optional) further restricts the selection to - * a given file stage. - * @param $submissionId int (optional) for validation purposes only - * @return SubmissionFile|null - */ - function getLatestRevision($fileId, $fileStage = null, $submissionId = null) { - if (!$fileId) return null; - - $revisions = $this->_getInternally($submissionId, $fileStage, $fileId, null, null, null, null, null, null, true, null, null); - return $this->_checkAndReturnRevision($revisions); - } - - /** - * Retrieve a list of current revisions. - * @param $submissionId int Submission ID. - * @param $fileStage int (optional) further restricts the selection to - * a given file stage. - * @param $rangeInfo DBResultRange (optional) - * @return array|null a list of SubmissionFile instances - */ - function getLatestRevisions($submissionId, $fileStage = null, $rangeInfo = null) { - if (!$submissionId) return null; - return $this->_getInternally($submissionId, $fileStage, null, null, null, null, null, null, null, true, $rangeInfo); - } - - /** - * Retrieve all revisions of a submission file. - * @param $fileId int File ID. - * @param $fileStage int (optional) further restricts the selection to - * a given file stage. - * @param $submissionId int Optional submission ID for validation - * purposes only - * @param $rangeInfo DBResultRange (optional) - * @return array|null a list of SubmissionFile instances - */ - function getAllRevisions($fileId, $fileStage = null, $submissionId = null, $rangeInfo = null) { - if (!$fileId) return null; - return $this->_getInternally($submissionId, $fileStage, $fileId, null, null, null, null, null, null, false, $rangeInfo); - } - - /** - * Retrieve all submission files & revisions for a submission. - * @param $submissionId int Submission ID. - * @param $rangeInfo DBResultRange (optional) - * @return array a list of SubmissionFile instances - */ - function getBySubmissionId($submissionId, $rangeInfo = null) { - if (!$submissionId) return null; - return $this->_getInternally($submissionId, null, null, null, null, null, null, null, null, false, $rangeInfo); - } - - /** - * Retrieve the latest revision of all files associated - * to a certain object. - * @param $assocType int ASSOC_TYPE_... - * @param $assocId int ID corresponding to specified assocType. - * @param $fileStage int (optional) further restricts the selection to - * a given file stage. - * @param $rangeInfo DBResultRange (optional) - * @return array|null a list of SubmissionFile instances - */ - function getLatestRevisionsByAssocId($assocType, $assocId, $submissionId = null, $fileStage = null, $rangeInfo = null) { - if (!($assocType && $assocId)) return null; - return $this->_getInternally($submissionId, $fileStage, null, null, $assocType, $assocId, null, null, null, true, $rangeInfo); - } - - /** - * Retrieve all files associated to a certain object. - * @param $assocType int ASSOC_TYPE_... - * @param $assocId int ID corresponding to specified assocType. - * @param $fileStage int (optional) further restricts the selection to - * a given file stage. - * @param $rangeInfo DBResultRange (optional) - * @return array|null a list of SubmissionFile instances - */ - function getAllRevisionsByAssocId($assocType, $assocId, $fileStage = null, $rangeInfo = null) { - if (!($assocType && $assocId)) return null; - return $this->_getInternally(null, $fileStage, null, null, $assocType, $assocId, null, null, null, false, $rangeInfo); - } - - /** - * Get all file revisions assigned to the given review round. - * @param $reviewRound ReviewRound - * @param $fileStage int SUBMISSION_FILE_... - * @param $uploaderUserId int Uploader's user ID - * @return array|null A list of SubmissionFiles. - */ - function getRevisionsByReviewRound($reviewRound, $fileStage = null, - $uploaderUserId = null) { - if (!is_a($reviewRound, 'ReviewRound')) return null; - return $this->_getInternally($reviewRound->getSubmissionId(), - $fileStage, null, null, null, null, null, - $uploaderUserId, $reviewRound->getId() - ); - } - - /** - * Get the latest revisions of all files that are in the specified - * review round. - * @param $reviewRound ReviewRound - * @param $fileStage int SUBMISSION_FILE_... (Optional) - * @return array A list of SubmissionFiles. - */ - function getLatestRevisionsByReviewRound($reviewRound, $fileStage = null) { - if (!$reviewRound) return array(); - return $this->_getInternally($reviewRound->getSubmissionId(), - $fileStage, null, null, null, null, $reviewRound->getStageId(), - null, $reviewRound->getId(), true - ); - } - - /** - * Retrieve the current revision number for a file. - * @param $fileId int File ID. - * @return int|null - */ - function getLatestRevisionNumber($fileId) { - assert(!is_null($fileId)); - - // Retrieve the latest revision from the database. - $result = $this->retrieve( - 'SELECT MAX(revision) AS max_revision FROM submission_files WHERE file_id = ?', - (int) $fileId - ); - if($result->RecordCount() != 1) return null; - - $row = $result->FetchRow(); - $result->Close(); - - $latestRevision = (int)$row['max_revision']; - assert($latestRevision > 0); - return $latestRevision; - } - - /** - * Insert a new SubmissionFile. - * @param $submissionFile SubmissionFile - * @param $sourceFile string The place where the physical file - * resides right now or the file name in the case of an upload. - * The file will be copied to its canonical target location. - * @param $isUpload boolean set to true if the file has just been - * uploaded. - * @return SubmissionFile - */ - function insertObject($submissionFile, $sourceFile, $isUpload = false) { - // Make sure that the implementation of the updated file - // is compatible with its genre (upcast but no downcast). - $submissionFile = $this->_castToGenre($submissionFile); - - // Find the required target implementation and delegate. - $targetImplementation = strtolower_codesafe( - $this->_getFileImplementationForGenreId( - $submissionFile->getGenreId()) - ); - $targetDaoDelegate = $this->_getDaoDelegate($targetImplementation); - $insertedFile = $targetDaoDelegate->insertObject($submissionFile, $sourceFile, $isUpload); - - // If the updated file does not have the correct target type then we'll have - // to retrieve it again from the database to cast it to the right type (downcast). - if ($insertedFile && strtolower_codesafe(get_class($insertedFile)) != $targetImplementation) { - $insertedFile = $this->_castToDatabase($insertedFile); - } - return $insertedFile; - } - - /** - * Update an existing submission file. - * - * NB: We implement a delete + insert strategy to deal with - * various casting problems (e.g. file implementation/genre - * may change, file path may change, etc.). - * - * @param $updatedFile SubmissionFile - * @param $previousFileId integer The file id before the file - * was changed. Must only be given if the file id changed - * so that the previous file can be identified. - * @param $previousRevision integer The revision before the file - * was changed. Must only be given if the revision changed - * so that the previous file can be identified. - * @return SubmissionFile The updated file. This file may be of - * a different file implementation than the file passed into the - * method if the genre of the file didn't fit its implementation. - */ - function updateObject($updatedFile, $previousFileId = null, $previousRevision = null) { - // Make sure that the implementation of the updated file - // is compatible with its genre. - $updatedFile = $this->_castToGenre($updatedFile); - - // Complete the identifying data of the previous file if not given. - $previousFileId = (int)($previousFileId ? $previousFileId : $updatedFile->getFileId()); - $previousRevision = (int)($previousRevision ? $previousRevision : $updatedFile->getRevision()); - - // Retrieve the previous file. - $previousFile = $this->getRevision($previousFileId, $previousRevision); - assert(is_a($previousFile, 'SubmissionFile')); - - // Canonicalized the implementation of the previous file. - $previousImplementation = strtolower_codesafe(get_class($previousFile)); - - // Find the required target implementation and delegate. - $targetImplementation = strtolower_codesafe( - $this->_getFileImplementationForGenreId( - $updatedFile->getGenreId()) - ); - $targetDaoDelegate = $this->_getDaoDelegate($targetImplementation); - - // If the implementation in the database differs from the target - // implementation then we'll have to delete + insert the object - // to make sure that the database contains consistent data. - if ($previousImplementation != $targetImplementation) { - // We'll have to copy the previous file to its target - // destination so that it is not lost when we delete the - // previous file. - // When the implementation (i.e. genre) changes then the - // file locations will also change so we should not get - // a file name clash. - $previousFilePath = $previousFile->getFilePath(); - $targetFilePath = $updatedFile->getFilePath(); - - assert($previousFilePath != $targetFilePath && !file_exists($targetFilePath)); - import('lib.pkp.classes.file.FileManager'); - $fileManager = new FileManager(); - $fileManager->copyFile($previousFilePath, $targetFilePath); - - // We use the delegates directly to make sure - // that we address the right implementation in the database - // on delete and insert. - $sourceDaoDelegate = $this->_getDaoDelegate($previousImplementation); - $sourceDaoDelegate->deleteObject($previousFile); - $targetDaoDelegate->insertObject($updatedFile, $targetFilePath); - } else { - // If the implementation in the database does not change then we - // can do an efficient update. - if (!$targetDaoDelegate->updateObject($updatedFile, $previousFile)) { - return null; - } - } - - // If the updated file does not have the correct target type then we'll have - // to retrieve it again from the database to cast it to the right type. - if (strtolower_codesafe(get_class($updatedFile)) != $targetImplementation) { - $updatedFile = $this->_castToDatabase($updatedFile); - } - - return $updatedFile; - } - - /** - * Set the latest revision of a file as the latest revision - * of another file. - * @param $revisedFileId integer the revised file - * @param $newFileId integer the file that will become the - * latest revision of the revised file. - * @param $submissionId integer the submission id the two files - * must belong to. - * @param $fileStage integer the file stage the two files - * must belong to. - * @return SubmissionFile the new revision or null if something went wrong. - */ - function setAsLatestRevision($revisedFileId, $newFileId, $submissionId, $fileStage) { - $revisedFileId = (int)$revisedFileId; - $newFileId = (int)$newFileId; - $submissionId = (int)$submissionId; - $fileStage = (int)$fileStage; - - // Check whether the two files are already revisions of each other. - if ($revisedFileId == $newFileId) return null; - - // Retrieve the latest revisions of the two submission files. - $revisedFile = $this->getLatestRevision($revisedFileId, $fileStage, $submissionId); - $newFile = $this->getLatestRevision($newFileId, $fileStage, $submissionId); - if (!($revisedFile && $newFile)) return null; - - // Save identifying data of the changed file required for update. - $previousFileId = $newFile->getFileId(); - $previousRevision = $newFile->getRevision(); - - // Copy data over from the revised file to the new file. - $newFile->setFileId($revisedFileId); - $newFile->setRevision($revisedFile->getRevision()+1); - $newFile->setGenreId($revisedFile->getGenreId()); - $newFile->setAssocType($revisedFile->getAssocType()); - $newFile->setAssocId($revisedFile->getAssocId()); - - // Update the file in the database. - return $this->updateObject($newFile, $previousFileId, $previousRevision); - } - - /** - * Assign file to a review round. - * @param $fileId int The file to be assigned. - * @param $revision int The revision of the file to be assigned. - * @param $reviewRound ReviewRound - */ - function assignRevisionToReviewRound($fileId, $revision, $reviewRound) { - if (!is_numeric($fileId) || !is_numeric($revision)) fatalError('Invalid file!'); - - // Avoid duplication errors -- clear out any existing entries - $this->deleteReviewRoundAssignment($reviewRound->getSubmissionId(), $reviewRound->getStageId(), $fileId, $revision); - - return $this->update( - 'INSERT INTO review_round_files - (submission_id, review_round_id, stage_id, file_id, revision) - VALUES (?, ?, ?, ?, ?)', - array( - (int)$reviewRound->getSubmissionId(), - (int)$reviewRound->getId(), - (int)$reviewRound->getStageId(), - (int)$fileId, - (int)$revision - ) - ); - } - - /** - * Delete a specific revision of a submission file. - * @param $submissionFile SubmissionFile - * @return integer the number of deleted file revisions - */ - function deleteRevision($submissionFile) { - return $this->deleteRevisionById($submissionFile->getFileId(), $submissionFile->getRevision(), $submissionFile->getFileStage(), $submissionFile->getSubmissionId()); - } - - /** - * Delete a specific revision of a submission file by id. - * @param $fileId int File ID. - * @param $revision int File revision number. - * @param $fileStage int (optional) further restricts - * the selection to a given file stage. - * @param $submissionId int (optional) for validation - * purposes only - * @return integer the number of deleted file revisions - */ - function deleteRevisionById($fileId, $revision, $fileStage = null, $submissionId = null) { - return $this->_deleteInternally($submissionId, $fileStage, $fileId, $revision); - } - - /** - * Delete the latest revision of a submission file by id. - * @param $fileId int File ID. - * @param $fileStage int (optional) further restricts - * the selection to a given file stage. - * @param $submissionId int (optional) for validation - * purposes only - * @return integer the number of deleted file revisions - */ - function deleteLatestRevisionById($fileId, $fileStage= null, $submissionId = null) { - return $this->_deleteInternally($submissionId, $fileStage, $fileId, null, null, null, null, null, true); - } - - /** - * Delete all revisions of a file, optionally - * restricted to a given file stage. - * @param $fileId int File ID. - * @param $fileStage int (optional) further restricts - * the selection to a given file stage. - * @param $submissionId int (optional) for validation - * purposes only - * @return integer the number of deleted file revisions - */ - function deleteAllRevisionsById($fileId, $fileStage = null, $submissionId = null) { - return $this->_deleteInternally($submissionId, $fileStage, $fileId); - } - - /** - * Delete all revisions of all files of a submission, - * optionally restricted to a given file stage. - * @param $submissionId int Submission ID. - * @param $fileStage int (optional) further restricts - * the selection to a given file stage. - * @return integer the number of deleted file revisions - */ - function deleteAllRevisionsBySubmissionId($submissionId, $fileStage = null) { - return $this->_deleteInternally($submissionId, $fileStage); - } - - /** - * Retrieve all files associated to a certain object. - * @param $assocType int ASSOC_TYPE_... - * @param $assocId int ID corresponding to specified assocType. - * @param $fileStage int (optional) further restricts - * the selection to a given file stage. - * @return integer the number of deleted file revisions. - */ - function deleteAllRevisionsByAssocId($assocType, $assocId, $fileStage = null) { - return $this->_deleteInternally(null, $fileStage, null, null, $assocType, $assocId); - } - - /** - * Remove all file assignements for the given review round. - * @param $reviewRoundId int The review round ID. - */ - function deleteAllRevisionsByReviewRound($reviewRoundId) { - // Remove currently assigned review files. - return $this->update('DELETE FROM review_round_files WHERE review_round_id = ?', (int)$reviewRoundId); - } - - /** - * Remove a specific file assignment from a review round. - * @param $submissionId int The submission id of the file - * @param $stageId int The review round type. - * @param $fileId int The file id. - * @param $revision int The file revision. - */ - function deleteReviewRoundAssignment($submissionId, $stageId, $fileId, $revision) { - // Remove currently assigned review files. - $this->update( - 'DELETE FROM review_round_files - WHERE submission_id = ? AND stage_id = ? AND file_id = ? AND revision = ?', - array( - (int) $submissionId, - (int) $stageId, - (int) $fileId, - (int) $revision - ) - ); - } - - /** - * Transfer the ownership of the submission files of one user to another. - * @param $oldUserId int User ID of old user (to be deleted) - * @param $newUserId int User ID of new user (to receive assets belonging to old user) - */ - function transferOwnership($oldUserId, $newUserId) { - $submissionFiles = $this->_getInternally(null, null, null, null, null, null, null, $oldUserId); - foreach ($submissionFiles as $file) { - $daoDelegate = $this->_getDaoDelegateForObject($file); - $file->setUploaderUserId($newUserId); - $daoDelegate->updateObject($file, $file); // nothing else changes - } - } - - /** - * Construct a new data object corresponding to this DAO. - * @param $genreId integer The genre is required to identify the right - * file implementation. - * @return SubmissionFile - */ - function newDataObjectByGenreId($genreId) { - // Identify the delegate. - $daoDelegate = $this->_getDaoDelegateForGenreId($genreId); - - // Instantiate and return the object. - return $daoDelegate->newDataObject(); - } - - - // - // Abstract template methods to be implemented by subclasses. - // - /** - * Return the available delegates mapped by lower - * case class names. - * @return array a list of fully qualified class names - * indexed by the lower case class name of the file - * implementation they serve. - * NB: Be careful to order class names such that they - * can be called in the given order to delete files - * without offending foreign key constraints, i.e. - * place the sub-classes before the super-classes. - */ - function getDelegateClassNames() { - return array( - 'submissionfile' => 'lib.pkp.classes.submission.SubmissionFileDAODelegate', - 'submissionartworkfile' => 'lib.pkp.classes.submission.SubmissionArtworkFileDAODelegate', - 'supplementaryfile' => 'lib.pkp.classes.submission.SupplementaryFileDAODelegate', - ); - } - - /** - * Return the mapping of genre categories to the lower - * case class name of file implementation. - * @return array a list of lower case class names of - * file implementations. - */ - function getGenreCategoryMapping() { - return array( - GENRE_CATEGORY_DOCUMENT => 'submissionfile', - GENRE_CATEGORY_ARTWORK => 'submissionartworkfile', - GENRE_CATEGORY_SUPPLEMENTARY => 'supplementaryfile', - ); - } - - /** - * Return the basic join over all file class tables. - * @return string - */ - function baseQueryForFileSelection() { - // Build the basic query that joins the class tables. - // The DISTINCT is required to de-dupe the review_round_files join in - // SubmissionFileDAO. - return 'SELECT DISTINCT - sf.file_id AS submission_file_id, sf.revision AS submission_revision, - af.file_id AS artwork_file_id, af.revision AS artwork_revision, - suf.file_id AS supplementary_file_id, suf.revision AS supplementary_revision, - p.locale AS submission_locale, - sf.*, af.*, suf.* - FROM submission_files sf - LEFT JOIN submission_artwork_files af ON sf.file_id = af.file_id AND sf.revision = af.revision - LEFT JOIN submission_supplementary_files suf ON sf.file_id = suf.file_id AND sf.revision = suf.revision - LEFT JOIN submissions as s ON s.submission_id = sf.submission_id - LEFT JOIN publications p ON s.current_publication_id = p.publication_id '; - } - - - // - // Protected helper methods - // - /** - * Internal function to return a SubmissionFile object from a row. - * @param $row array - * @param $fileImplementation string - * @return SubmissionFile - */ - function fromRow($row, $fileImplementation = null) { - switch(true) { - case isset($row['artwork_file_id']) && is_numeric($row['artwork_file_id']): - $daoDelegate = $this->_getDaoDelegate('SubmissionArtworkFile'); - break; - case isset($row['supplementary_file_id']) && is_numeric($row['supplementary_file_id']): - $daoDelegate = $this->_getDaoDelegate('SupplementaryFile'); - break; - default: - $daoDelegate = $this->_getDaoDelegate('SubmissionFile'); - } - - // Let the DAO delegate instantiate the file implementation. - return $daoDelegate->fromRow($row); - } - - - /** - * Return all file stages. - * @return array - */ - function getAllFileStages() { - // Bring in the file stages definition. - import('lib.pkp.classes.submission.SubmissionFile'); - return array( - SUBMISSION_FILE_SUBMISSION, - SUBMISSION_FILE_NOTE, - SUBMISSION_FILE_REVIEW_FILE, - SUBMISSION_FILE_REVIEW_ATTACHMENT, - SUBMISSION_FILE_FINAL, - SUBMISSION_FILE_FAIR_COPY, - SUBMISSION_FILE_EDITOR, - SUBMISSION_FILE_COPYEDIT, - SUBMISSION_FILE_PROOF, - SUBMISSION_FILE_PRODUCTION_READY, - SUBMISSION_FILE_ATTACHMENT, - SUBMISSION_FILE_REVIEW_REVISION, - SUBMISSION_FILE_DEPENDENT, - SUBMISSION_FILE_QUERY, - ); - } - - /** - * @copydoc PKPPubIdPluginDAO::pubIdExists() - */ - function pubIdExists($pubIdType, $pubId, $excludePubObjectId, $contextId) { - $submissionFileDAODelegate = $this->_getDaoDelegate('submissionfile'); - return $submissionFileDAODelegate->pubIdExists($pubIdType, $pubId, $excludePubObjectId, $contextId); - } - - /** - * @copydoc PKPPubIdPluginDAO::changePubId() - */ - function changePubId($pubObjectId, $pubIdType, $pubId) { - $submissionFileDAODelegate = $this->_getDaoDelegate('submissionfile'); - $submissionFileDAODelegate->changePubId($pubObjectId, $pubIdType, $pubId); - } - - /** - * @copydoc PKPPubIdPluginDAO::deletePubId() - */ - function deletePubId($pubObjectId, $pubIdType) { - $submissionFileDAODelegate = $this->_getDaoDelegate('submissionfile'); - $submissionFileDAODelegate->deletePubId($pubObjectId, $pubIdType); - } - - /** - * @copydoc PKPPubIdPluginDAO::deleteAllPubIds() - */ - function deleteAllPubIds($contextId, $pubIdType) { - $submissionFileDAODelegate = $this->_getDaoDelegate('submissionfile'); - $submissionFileDAODelegate->deleteAllPubIds($contextId, $pubIdType); - } - - /** - * Get the workflow stage id associated with a submission file - * - * Maps a file stage to a workflow stage. When a file is associated with a - * review round or query, it will get the stage id from the round or query. - * - * @param $submissionFile SubmissionFile - * @return null|int One of the WORKFLOW_STAGE_... constants or null if the - * submission file is not attached to a particular stage - */ - public function getWorkflowStageId($submissionFile) { - switch ($submissionFile->getFileStage()) { - case SUBMISSION_FILE_SUBMISSION: - return WORKFLOW_STAGE_ID_SUBMISSION; - case SUBMISSION_FILE_REVIEW_FILE: - case SUBMISSION_FILE_REVIEW_ATTACHMENT: - case SUBMISSION_FILE_REVIEW_REVISION: - $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ - $reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getFileId()); - return $reviewRound->getStageId(); - case SUBMISSION_FILE_FINAL: - case SUBMISSION_FILE_COPYEDIT: - return WORKFLOW_STAGE_ID_EDITING; - case SUBMISSION_FILE_PROOF: - case SUBMISSION_FILE_PRODUCTION_READY: - case SUBMISSION_FILE_DEPENDENT: - return WORKFLOW_STAGE_ID_PRODUCTION; - case SUBMISSION_FILE_QUERY: - $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ - $note = $noteDao->getById($submissionFile->getAssocId()); - $queryDao = DAORegistry::getDAO('QueryDAO'); /* @var $queryDao QueryDAO */ - $query = $queryDao->getById($note->getAssocId()); - return $query?$query->getStageId():null; - } - } - - // - // Private helper methods - // - /** - * Map a genre to the corresponding file implementation. - * @param $genreId integer - * @return string The class name of the file implementation. - */ - private function _getFileImplementationForGenreId($genreId) { - static $genreCache = array(); - - if (!isset($genreCache[$genreId])) { - if (is_null($genreId)) { - // If no genreId is given fall back to the document category - $genreCategory = GENRE_CATEGORY_DOCUMENT; - - } else { - // We have to instantiate the genre to find out about - // its category. - $genreDao = DAORegistry::getDAO('GenreDAO'); /* @var $genreDao GenreDAO */ - $genre = $genreDao->getById($genreId); - $genreCategory = $genre->getCategory(); - } - - // Identify the file implementation. - $genreMapping = $this->getGenreCategoryMapping(); - assert(isset($genreMapping[$genreCategory])); - $genreCache[$genreId] = $genreMapping[$genreCategory]; - } - - return $genreCache[$genreId]; - } - - /** - * Instantiates an approprate SubmissionFileDAODelegate - * based on the given genre identifier. - * @param $genreId integer - * @return SubmissionFileDAODelegate - */ - private function _getDaoDelegateForGenreId($genreId) { - // Find the required file implementation. - $fileImplementation = $this->_getFileImplementationForGenreId($genreId); - - // Return the DAO delegate. - return $this->_getDaoDelegate($fileImplementation); - } - - /** - * Instantiates an appropriate SubmissionFileDAODelegate - * based on the given SubmissionFile. - * @param $object SubmissionFile - * @return SubmissionFileDAODelegate - */ - private function _getDaoDelegateForObject($object) { - return $this->_getDaoDelegate(get_class($object)); - } - - /** - * Return the requested SubmissionFileDAODelegate. - * @param $fileImplementation string the class name of - * a file implementation that the requested delegate - * should serve. - * @return SubmissionFileDAODelegate - */ - private function _getDaoDelegate($fileImplementation) { - // Normalize the file implementation name. - $fileImplementation = strtolower_codesafe($fileImplementation); - - // Did we already instantiate the requested delegate? - if (!isset($this->_delegates[$fileImplementation])) { - // Instantiate the requested delegate. - $delegateClasses = $this->getDelegateClassNames(); - assert(isset($delegateClasses[$fileImplementation])); - $delegateClass = $delegateClasses[$fileImplementation]; - $this->_delegates[$fileImplementation] = instantiate($delegateClass, 'SubmissionFileDAODelegate'); - } - - // Return the delegate. - return $this->_delegates[$fileImplementation]; - } - - /** - * Private method to retrieve submission file revisions - * according to the given filters. - * @param $submissionId int Optional submission ID. - * @param $fileStage int Optional FILE_STAGE_... - * @param $fileId int Optional file ID. - * @param $revision int Optional file revision number. - * @param $assocType int Optional ASSOC_TYPE_... - * @param $assocId int Optional ID corresponding to assocType - * @param $stageId int Optional stage ID - * @param $uploaderUserId int Optional uploader's user ID - * @param $reviewRoundId int Optional review round ID - * @param $latestOnly boolean True iff only the latest revisions should be returned. - * @param $rangeInfo DBResultRange Optional range info for returned data. - * @return array a list of SubmissionFile instances - */ - private function _getInternally($submissionId = null, $fileStage = null, $fileId = null, $revision = null, - $assocType = null, $assocId = null, $stageId = null, $uploaderUserId = null, - $reviewRoundId = null, $latestOnly = false, $rangeInfo = null) { - // Retrieve the base query. - $sql = $this->baseQueryForFileSelection(); - - // Add the revision round file join if a revision round - // filter was requested. - if ($reviewRoundId) { - $sql .= 'INNER JOIN review_round_files rrf - ON sf.submission_id = rrf.submission_id - AND sf.file_id = rrf.file_id - AND sf.revision '.($latestOnly ? '>=' : '=').' rrf.revision '; - } - - // Filter the query. - list($filterClause, $params) = $this->_buildFileSelectionFilter( - $submissionId, $fileStage, $fileId, $revision, - $assocType, $assocId, $stageId, $uploaderUserId, $reviewRoundId); - - // Did the user request all or only the latest revision? - if ($latestOnly) { - // Filter the latest revision of each file. - // NB: We have to do this in the SQL for paging to work - // correctly. We use a partial cartesian join here to - // maintain MySQL 3.23 backwards compatibility. This - // should be ok as we usually only have few revisions per - // file. - $sql .= 'LEFT JOIN submission_files sf2 ON sf.file_id = sf2.file_id AND sf.revision < sf2.revision - WHERE sf2.revision IS NULL AND '.$filterClause; - } else { - $sql .= 'WHERE '.$filterClause; - } - - // Order the query. - $sql .= ' ORDER BY sf.submission_id ASC, sf.file_stage ASC, sf.file_id ASC, sf.revision DESC'; - - // Execute the query. - if ($rangeInfo) { - $result = $this->retrieveRange($sql, $params, $rangeInfo); - } else { - $result = $this->retrieve($sql, $params); - } - - // Build the result array. - $submissionFiles = array(); - while (!$result->EOF) { - // Retrieve the next result row. - $row = $result->GetRowAssoc(false); - - // Construct a combined id from file id and revision - // that uniquely identifies the file. - $idAndRevision = $row['submission_file_id'].'-'.$row['submission_revision']; - - // Check for duplicates. - assert(!isset($submissionFiles[$idAndRevision])); - - // Instantiate the file and add it to the - // result array with a unique key. - // N.B. The subclass implementation of fromRow receives just the $row - // but calls SubmissionFileDAO::fromRow($row, $fileImplementation) as defined here. - $submissionFiles[$idAndRevision] = $this->fromRow($row); - - // Move the query cursor to the next record. - $result->MoveNext(); - } - $result->Close(); - - return $submissionFiles; - } - - /** - * Private method to delete submission file revisions - * according to the given filters. - * @param $submissionId int Optional submission ID. - * @param $fileStage int Optional FILE_STAGE_... - * @param $fileId int Optional file ID. - * @param $revision int Optional file revision number. - * @param $assocType int Optional ASSOC_TYPE_... - * @param $assocId int Optional ID corresponding to specified assocType. - * @param $stageId int Optional stage ID. - * @param $uploaderUserId int Optional uploader user ID. - * @param $latestOnly boolean True iff only the latest revision should be deleted. - * @return boolean|integer Returns boolean false if an error occurs, otherwise the number - * of deleted files. - */ - private function _deleteInternally($submissionId = null, $fileStage = null, $fileId = null, $revision = null, - $assocType = null, $assocId = null, $stageId = null, $uploaderUserId = null, - $latestOnly = false) { - - // Identify all matched files. - $deletedFiles = $this->_getInternally($submissionId, $fileStage, $fileId, $revision, - $assocType, $assocId, $stageId, $uploaderUserId, null, $latestOnly, null); - if (empty($deletedFiles)) return 0; - - foreach($deletedFiles as $deletedFile) { /* @var $deletedFile SubmissionFile */ - // Delete file in the database. - // NB: We cannot safely bulk-delete because MySQL 3.23 - // does not support multi-column IN-clauses. Same is true - // for multi-table access or subselects in the DELETE - // statement. And having a long (... AND ...) OR (...) - // clause could hit length limitations. - $daoDelegate = $this->_getDaoDelegateForObject($deletedFile); - if (!$daoDelegate->deleteObject($deletedFile)) return false; - } - - // Return the number of deleted files. - return count($deletedFiles); - } - - /** - * Build an SQL where clause to select - * submissions based on the given filter information. - * @param $submissionId int Submission ID - * @param $fileStage int File stage ID - * @param $fileId int File ID - * @param $revision int File revision number - * @param $assocType int ASSOC_TYPE_... - * @param $assocId int ID corresponding to specified assocType - * @param $stageId int Stage ID - * @param $uploaderUserId int Uploader user ID - * @param $reviewRoundId int Review round ID - * @return array an array that contains the generated SQL - * filter clause and the corresponding parameters. - */ - private function _buildFileSelectionFilter($submissionId, $fileStage, - $fileId, $revision, $assocType, $assocId, $stageId, $uploaderUserId, $reviewRoundId) { - - // Make sure that at least one entity filter has been set. - assert($submissionId>0 || (int)$uploaderUserId || (int)$fileId || (int)$assocId); - - // Both, assoc type and id, must be set (or unset) together. - assert(((int)$assocType && (int)$assocId) || !((int)$assocType || (int)$assocId)); - - // Collect the filtered columns and ids in - // an array for consistent handling. - $filters = array( - 'sf.submission_id' => $submissionId, - 'sf.file_stage' => $fileStage, - 'sf.file_id' => $fileId, - 'sf.revision' => $revision, - 'sf.assoc_type' => $assocType, - 'sf.assoc_id' => $assocId, - 'sf.uploader_user_id' => $uploaderUserId, - 'rrf.stage_id' => $stageId, - 'rrf.review_round_id' => $reviewRoundId, - ); - - // Build and return a SQL where clause and a parameter - // array. - $filterClause = ''; - $params = array(); - $conjunction = ''; - foreach($filters as $filteredColumn => $filteredId) { - if ($filteredId) { - $filterClause .= $conjunction.' '.$filteredColumn.' = ?'; - $conjunction = ' AND'; - $params[] = (int)$filteredId; - } - } - return array($filterClause, $params); - } - - /** - * Make sure that the genre of the file and its file - * implementation are compatible. - * - * NB: In the case of a downcast this means that not all data in the - * object will be saved to the database. It is the UI's responsibility - * to inform users about potential loss of data if they change to - * a genre that permits less meta-data than the prior genre! - * - * @param $submissionFile SubmissionFile - * @return SubmissionFile The same file in a compatible implementation. - */ - private function _castToGenre($submissionFile) { - // Find the required target implementation. - $targetImplementation = strtolower_codesafe( - $this->_getFileImplementationForGenreId( - $submissionFile->getGenreId()) - ); - - // If the current implementation of the updated object - // is the same as the target implementation, skip cast. - if (is_a($submissionFile, $targetImplementation)) return $submissionFile; - - // The updated file has to be upcast by manually - // instantiating the target object and copying data - // to the target. - $targetDaoDelegate = $this->_getDaoDelegate($targetImplementation); - $targetFile = $targetDaoDelegate->newDataObject(); - $targetFile = $submissionFile->upcastTo($targetFile); - return $targetFile; - } - - /** - * Make sure that a file's implementation corresponds to the way it is - * saved in the database. - * @param $submissionFile SubmissionFile - * @return SubmissionFile - */ - private function _castToDatabase($submissionFile) { - return $this->getRevision( - $submissionFile->getFileId(), - $submissionFile->getRevision() - ); - } - - /** - * Check whether the given array contains exactly - * zero or one revisions and return it. - * @param $revisions array - * @return SubmissionFile - */ - private function _checkAndReturnRevision($revisions) { - assert(count($revisions) <= 1); - if (empty($revisions)) return null; - - $revision = array_pop($revisions); - assert(is_a($revision, 'SubmissionFile')); - return $revision; - } - - /** - * Make a copy of the file to the specified file stage - * @param $context Context - * @param $submissionFile SubmissionFile - * @param $fileStage int SUBMISSION_FILE_... - * @return newFileId int - */ - function copyFile($context, $submissionFile, $fileStage){ - import('lib.pkp.classes.file.SubmissionFileManager'); - $submissionFileManager = new SubmissionFileManager($context->getId(), $submissionFile->getSubmissionId()); - $fileId = $submissionFile->getFileId(); - $revision = $submissionFile->getRevision(); - list($newFileId, $newRevision) = $submissionFileManager->copyFileToFileStage($fileId, $revision, $fileStage, null, $submissionFile->getViewable()); - return $newFileId; - } -} - - diff --git a/classes/submission/SubmissionFileDAODelegate.inc.php b/classes/submission/SubmissionFileDAODelegate.inc.php deleted file mode 100644 index 2a3a5e9f9b4..00000000000 --- a/classes/submission/SubmissionFileDAODelegate.inc.php +++ /dev/null @@ -1,447 +0,0 @@ -getFileId(); - - if (!is_numeric($submissionFile->getRevision())) { - // Set the initial revision. - $submissionFile->setRevision(1); - } - - if (!is_bool($submissionFile->getViewable())) { - // Set the viewable default. - $submissionFile->setViewable(false); - } - - $params = array( - (int)$submissionFile->getRevision(), - (int)$submissionFile->getSubmissionId(), - is_null($submissionFile->getSourceFileId()) ? null : (int)$submissionFile->getSourceFileId(), - is_null($submissionFile->getSourceRevision()) ? null : (int)$submissionFile->getSourceRevision(), - $submissionFile->getFileType(), - (int)$submissionFile->getFileSize(), - $submissionFile->getOriginalFileName(), - (int)$submissionFile->getFileStage(), - (boolean)$submissionFile->getViewable() ? 1 : 0, - is_null($submissionFile->getUploaderUserId()) ? null : (int)$submissionFile->getUploaderUserId(), - is_null($submissionFile->getAssocType()) ? null : (int)$submissionFile->getAssocType(), - is_null($submissionFile->getAssocId()) ? null : (int)$submissionFile->getAssocId(), - is_null($submissionFile->getGenreId()) ? null : (int)$submissionFile->getGenreId(), - $submissionFile->getDirectSalesPrice(), - $submissionFile->getSalesType(), - ); - - if ($fileId) { - array_unshift($params, (int) $fileId); - } - - $this->update( - sprintf('INSERT INTO submission_files - (' . ($fileId ? 'file_id, ' : '') . 'revision, submission_id, source_file_id, source_revision, file_type, file_size, original_file_name, file_stage, date_uploaded, date_modified, viewable, uploader_user_id, assoc_type, assoc_id, genre_id, direct_sales_price, sales_type) - VALUES - (' . ($fileId ? '?, ' : '') . '?, ?, ?, ?, ?, ?, ?, ?, %s, %s, ?, ?, ?, ?, ?, ?, ?)', - $this->datetimeToDB($submissionFile->getDateUploaded()), $this->datetimeToDB($submissionFile->getDateModified())), - $params - ); - - if (!$fileId) { - $submissionFile->setFileId($this->_getInsertId('submission_files', 'file_id')); - } - - $submissionLocale = $submissionFile->getSubmissionLocale(); - - $reviewStage = in_array($submissionFile->getFileStage(), array( - SUBMISSION_FILE_REVIEW_FILE, SUBMISSION_FILE_REVIEW_ATTACHMENT, SUBMISSION_FILE_REVIEW_REVISION - )); - - if ($reviewStage) { - $submissionFile->setName($submissionFile->_generateName(true), $submissionLocale); - } else { - if ($isUpload || !$submissionFile->getName($submissionLocale)) { - $submissionFile->setName($submissionFile->_generateName(), $submissionLocale); - } - } - - $this->updateLocaleFields($submissionFile); - - // Determine the final destination of the file (requires - // the file id we just generated). - $targetFilePath = $submissionFile->getFilePath(); - - // Only copy the file if it is not yet in the target position. - if ($isUpload || $sourceFile != $targetFilePath) { - // Copy the file from its current location to the target destination. - import('lib.pkp.classes.file.FileManager'); - $fileManager = new FileManager(); - if ($isUpload) { - $success = $fileManager->uploadFile($sourceFile, $targetFilePath); - } else { - assert(is_readable($sourceFile)); - $success = $fileManager->copyFile($sourceFile, $targetFilePath); - } - if (!$success) { - // If the copy/upload operation fails then remove - // the already inserted meta-data. - $this->deleteObject($submissionFile); - $nullVar = null; - return $nullVar; - } - } - assert(is_readable($targetFilePath)); - - return $submissionFile; - } - - /** - * Update a submission file. - * @param $submissionFile SubmissionFile The target state - * of the updated file. - * @param $previousFile SubmissionFile The current state - * of the updated file. - * @return boolean - */ - function updateObject($submissionFile, $previousFile) { - // Update the file in the database. - $this->update( - sprintf('UPDATE submission_files - SET - file_id = ?, - revision = ?, - submission_id = ?, - source_file_id = ?, - source_revision = ?, - file_type = ?, - file_size = ?, - original_file_name = ?, - file_stage = ?, - date_uploaded = %s, - date_modified = %s, - viewable = ?, - uploader_user_id = ?, - assoc_type = ?, - assoc_id = ?, - genre_id = ?, - direct_sales_price = ?, - sales_type = ? - WHERE file_id = ? AND revision = ?', - $this->datetimeToDB($submissionFile->getDateUploaded()), $this->datetimeToDB($submissionFile->getDateModified())), - array( - (int)$submissionFile->getFileId(), - (int)$submissionFile->getRevision(), - (int)$submissionFile->getSubmissionId(), - is_null($submissionFile->getSourceFileId()) ? null : (int)$submissionFile->getSourceFileId(), - is_null($submissionFile->getSourceRevision()) ? null : (int)$submissionFile->getSourceRevision(), - $submissionFile->getFileType(), - $submissionFile->getFileSize(), - $submissionFile->getOriginalFileName(), - $submissionFile->getFileStage(), - (boolean)$submissionFile->getViewable() ? 1 : 0, - is_null($submissionFile->getUploaderUserId()) ? null : (int)$submissionFile->getUploaderUserId(), - is_null($submissionFile->getAssocType()) ? null : (int)$submissionFile->getAssocType(), - is_null($submissionFile->getAssocId()) ? null : (int)$submissionFile->getAssocId(), - is_null($submissionFile->getGenreId()) ? null : (int)$submissionFile->getGenreId(), - $submissionFile->getDirectSalesPrice(), - $submissionFile->getSalesType(), - (int)$previousFile->getFileId(), - (int)$previousFile->getRevision(), - ) - ); - - $this->updateLocaleFields($submissionFile); - - // Update all dependent objects. - $this->_updateDependentObjects($submissionFile, $previousFile); - - // Copy the file from its current location to the target destination - // if necessary. - $previousFilePath = $previousFile->getFilePath(); - $targetFilePath = $submissionFile->getFilePath(); - if ($previousFilePath != $targetFilePath && is_file($previousFilePath)) { - // The file location changed so let's move the file on - // the file system, too. - assert(is_readable($previousFilePath)); - import('lib.pkp.classes.file.FileManager'); - $fileManager = new FileManager(); - if (!$fileManager->copyFile($previousFilePath, $targetFilePath)) return false; - if (!$fileManager->deleteByPath($previousFilePath)) return false; - } - - return file_exists($targetFilePath); - } - - /** - * Delete a submission file from the database. - * @param $submissionFile SubmissionFile - * @return boolean - */ - function deleteObject($submissionFile) { - if (!$this->update( - 'DELETE FROM submission_files - WHERE file_id = ? AND revision = ?', - array( - (int)$submissionFile->getFileId(), - (int)$submissionFile->getRevision() - ))) return false; - - // if we've removed the last revision of this file, clean up - // the settings for this file as well. - $result = $this->retrieve( - 'SELECT * FROM submission_files WHERE file_id = ?', - array((int)$submissionFile->getFileId()) - ); - - if ($result->RecordCount() == 0) { - $this->update('DELETE FROM submission_file_settings WHERE file_id = ?', - array((int) $submissionFile->getFileId())); - } - - // Delete all dependent objects. - $this->_deleteDependentObjects($submissionFile); - - // Delete the file on the file system, too. - $filePath = $submissionFile->getFilePath(); - if(!(is_file($filePath) && is_readable($filePath))) return false; - assert(is_writable(dirname($filePath))); - - import('lib.pkp.classes.file.FileManager'); - $fileManager = new FileManager(); - $fileManager->deleteByPath($filePath); - - return !file_exists($filePath); - } - - /** - * Function to return a SubmissionFile object from a row. - * @param $row array - * @return SubmissionFile - */ - function fromRow($row) { - $submissionFile = $this->newDataObject(); - $submissionFile->setFileId((int)$row['submission_file_id']); - $submissionFile->setSubmissionLocale($row['submission_locale']); - $submissionFile->setRevision((int)$row['submission_revision']); - $submissionFile->setAssocType(is_null($row['assoc_type']) ? null : (int)$row['assoc_type']); - $submissionFile->setAssocId(is_null($row['assoc_id']) ? null : (int)$row['assoc_id']); - $submissionFile->setSourceFileId(is_null($row['source_file_id']) ? null : (int)$row['source_file_id']); - $submissionFile->setSourceRevision(is_null($row['source_revision']) ? null : (int)$row['source_revision']); - $submissionFile->setSubmissionId((int)$row['submission_id']); - $submissionFile->setFileStage((int)$row['file_stage']); - $submissionFile->setOriginalFileName($row['original_file_name']); - $submissionFile->setFileType($row['file_type']); - $submissionFile->setGenreId(is_null($row['genre_id']) ? null : (int)$row['genre_id']); - $submissionFile->setFileSize((int)$row['file_size']); - $submissionFile->setUploaderUserId(is_null($row['uploader_user_id']) ? null : (int)$row['uploader_user_id']); - $submissionFile->setViewable((boolean)$row['viewable']); - $submissionFile->setDateUploaded($this->datetimeFromDB($row['date_uploaded'])); - $submissionFile->setDateModified($this->datetimeFromDB($row['date_modified'])); - $submissionFile->setDirectSalesPrice($row['direct_sales_price']); - $submissionFile->setSalesType($row['sales_type']); - - $this->getDataObjectSettings('submission_file_settings', 'file_id', $row['submission_file_id'], $submissionFile); - - return $submissionFile; - } - - /** - * Construct a new data object corresponding to this DAO. - * @return SubmissionFile - */ - function newDataObject() { - return new SubmissionFile(); - } - - - // - // Protected helper methods - // - /** - * Get the list of fields for which data is localized. - * @return array - */ - function getLocaleFieldNames() { - $localeFieldNames = parent::getLocaleFieldNames(); - $localeFieldNames[] = 'name'; - return $localeFieldNames; - } - - /** - * Get a list of additional fields that do not have - * dedicated accessors. - * @return array - */ - function getAdditionalFieldNames() { - $additionalFields = parent::getAdditionalFieldNames(); - $additionalFields[] = 'pub-id::publisher-id'; - $additionalFields[] = 'chapterId'; - return $additionalFields; - } - - /** - * Update the localized fields for this submission file. - * @param $submissionFile SubmissionFile - */ - function updateLocaleFields($submissionFile) { - // Update the locale fields. - $this->updateDataObjectSettings('submission_file_settings', $submissionFile, array( - 'file_id' => $submissionFile->getFileId() - )); - } - - /** - * Checks if public identifier exists (other than for the specified - * submission file ID, which is treated as an exception). - * @param $pubIdType string One of the NLM pub-id-type values or - * 'other::something' if not part of the official NLM list - * (see ). - * @param $pubId string - * @param $fileId int An ID to be excluded from the search. - * @param $contextId int - * @return boolean - */ - function pubIdExists($pubIdType, $pubId, $excludePubObjectId, $contextId) { - $result = $this->retrieve( - 'SELECT COUNT(*) - FROM submission_file_settings sfs - INNER JOIN submission_files sf ON sfs.file_id = sf.file_id - INNER JOIN submissions s ON sf.submission_id = s.submission_id - WHERE sfs.setting_name = ? AND sfs.setting_value = ? AND sfs.file_id <> ? AND s.context_id = ?', - array( - 'pub-id::'.$pubIdType, - $pubId, - (int) $excludePubObjectId, - (int) $contextId - ) - ); - $returner = $result->fields[0] ? true : false; - $result->Close(); - return $returner; - } - - /** - * @copydoc PKPPubIdPluginDAO::changePubId() - */ - function changePubId($pubObjectId, $pubIdType, $pubId) { - $idFields = array( - 'file_id', 'locale', 'setting_name' - ); - $updateArray = array( - 'file_id' => (int) $pubObjectId, - 'locale' => '', - 'setting_name' => 'pub-id::'.$pubIdType, - 'setting_type' => 'string', - 'setting_value' => (string)$pubId - ); - $this->replace('submission_file_settings', $updateArray, $idFields); - $this->flushCache(); - } - - /** - * @copydoc PKPPubIdPluginDAO::deletePubId() - */ - function deletePubId($pubObjectId, $pubIdType) { - $settingName = 'pub-id::'.$pubIdType; - $this->update( - 'DELETE FROM submission_file_settings WHERE setting_name = ? AND file_id = ?', - array( - $settingName, - (int)$pubObjectId - ) - ); - $this->flushCache(); - } - - /** - * @copydoc PKPPubIdPluginDAO::deleteAllPubIds() - */ - function deleteAllPubIds($contextId, $pubIdType) { - $settingName = 'pub-id::'.$pubIdType; - - $submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */ - $submissions = $submissionDao->getByContextId($contextId); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - while ($submission = $submissions->next()) { - $submissionFiles = $submissionFileDao->getBySubmissionId($submission->getId()); - foreach ($submissionFiles as $submissionFile) { - $this->update( - 'DELETE FROM submission_file_settings WHERE setting_name = ? AND file_id = ?', - array( - $settingName, - (int)$submissionFile->getFileId() - ) - ); - } - } - $this->flushCache(); - } - - // - // Private helper methods - // - /** - * Update all objects that depend on the given file. - * @param $submissionFile SubmissionFile - * @param $previousFile SubmissionFile - */ - function _updateDependentObjects($submissionFile, $previousFile) { - // If the file ids didn't change then we do not have to - // do anything. - if ( - $previousFile->getFileId() == $submissionFile->getFileId() || - $previousFile->getRevision() == $submissionFile->getRevision() - ) return; - - // Update file views that refer to this file. - $viewsDao = DAORegistry::getDAO('ViewsDAO'); /* @var $viewsDao ViewsDAO */ - $viewsDao->moveViews( - ASSOC_TYPE_SUBMISSION_FILE, - $previousFile->getFileIdAndRevision(), $submissionFile->getFileIdAndRevision() - ); - } - - /** - * Delete all objects that depend on the given file. - * @param $submissionFile SubmissionFile - */ - function _deleteDependentObjects($submissionFile) { - // Delete file views that refer to this file. - $viewsDao = DAORegistry::getDAO('ViewsDAO'); /* @var $viewsDao ViewsDAO */ - $viewsDao->deleteViews( - ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getFileIdAndRevision() - ); - } -} - - diff --git a/classes/submission/SupplementaryFile.inc.php b/classes/submission/SupplementaryFile.inc.php deleted file mode 100644 index 6bdf9a5d20c..00000000000 --- a/classes/submission/SupplementaryFile.inc.php +++ /dev/null @@ -1,245 +0,0 @@ -getLocalizedData('creator', $preferredLocale); - } - - /** - * Get creator. - * @param $locale - * @return string - */ - function getCreator($locale) { - return $this->getData('creator', $locale); - } - - /** - * Set creator. - * @param $creator string - * @param $locale - */ - function setCreator($creator, $locale) { - $this->setData('creator', $creator, $locale); - } - - /** - * Get localized subject - * @return string - */ - function getLocalizedSubject() { - return $this->getLocalizedData('subject'); - } - - /** - * Get subject. - * @param $locale string - * @return string - */ - function getSubject($locale) { - return $this->getData('subject', $locale); - } - - /** - * Set subject. - * @param $subject string - * @param $locale string - */ - function setSubject($subject, $locale) { - $this->setData('subject', $subject, $locale); - } - - /** - * Get localized description - * @return string - */ - function getLocalizedDescription() { - return $this->getLocalizedData('description'); - } - - /** - * Get file description. - * @param $locale string - * @return string - */ - function getDescription($locale) { - return $this->getData('description', $locale); - } - - /** - * Set file description. - * @param $description string - * @param $locale string - */ - function setDescription($description, $locale) { - $this->setData('description', $description, $locale); - } - - /** - * Get localized publisher - * @return string - */ - function getLocalizedPublisher() { - return $this->getLocalizedData('publisher'); - } - - /** - * Get publisher. - * @param $locale string - * @return string - */ - function getPublisher($locale) { - return $this->getData('publisher', $locale); - } - - /** - * Set publisher. - * @param $publisher string - * @param $locale string - */ - function setPublisher($publisher, $locale) { - $this->setData('publisher', $publisher, $locale); - } - - /** - * Get localized sponsor - * @return string - */ - function getLocalizedSponsor() { - return $this->getLocalizedData('sponsor'); - } - - /** - * Get sponsor. - * @param $locale string - * @return string - */ - function getSponsor($locale) { - return $this->getData('sponsor', $locale); - } - - /** - * Set sponsor. - * @param $sponsor string - * @param $locale string - */ - function setSponsor($sponsor, $locale) { - $this->setData('sponsor', $sponsor, $locale); - } - - /** - * Get date created. - * @return date - */ - function getDateCreated() { - return $this->getData('dateCreated'); - } - - /** - * Set date created. - * @param $dateCreated date - */ - function setDateCreated($dateCreated) { - $this->setData('dateCreated', $dateCreated); - } - - /** - * Get localized source - * @return string - */ - function getLocalizedSource() { - return $this->getLocalizedData('source'); - } - - /** - * Get source. - * @param $locale string - * @return string - */ - function getSource($locale) { - return $this->getData('source', $locale); - } - - /** - * Set source. - * @param $source string - * @param $locale string - */ - function setSource($source, $locale) { - $this->setData('source', $source, $locale); - } - - /** - * Get language. - * @return string - */ - function getLanguage() { - return $this->getData('language'); - } - - /** - * Set language. - * @param $language string - */ - function setLanguage($language) { - $this->setData('language', $language); - } - - /** - * Copy the user-facing (editable) metadata from another submission - * file. - * @param $submissionFile SubmissionFile - */ - function copyEditableMetadataFrom($submissionFile) { - if (is_a($submissionFile, 'SupplementaryFile')) { - $this->setCreator($submissionFile->getCreator(null), null); - $this->setSubject($submissionFile->getSubject(null), null); - $this->setDescription($submissionFile->getDescription(null), null); - $this->setPublisher($submissionFile->getPublisher(null), null); - $this->setSponsor($submissionFile->getSponsor(null), null); - $this->setDateCreated($submissionFile->getDateCreated()); - $this->setSource($submissionFile->getSource(null), null); - $this->setLanguage($submissionFile->getLanguage()); - } - - parent::copyEditableMetadataFrom($submissionFile); - } - - /** - * @copydoc SubmissionFile::getMetadataForm - */ - function getMetadataForm($stageId, $reviewRound) { - import('lib.pkp.controllers.wizard.fileUpload.form.SupplementaryFileMetadataForm'); - return new SupplementaryFileMetadataForm($this, $stageId, $reviewRound); - } -} - - diff --git a/classes/submission/SupplementaryFileDAODelegate.inc.php b/classes/submission/SupplementaryFileDAODelegate.inc.php deleted file mode 100644 index 380b42ca33f..00000000000 --- a/classes/submission/SupplementaryFileDAODelegate.inc.php +++ /dev/null @@ -1,133 +0,0 @@ -update( - 'INSERT INTO submission_supplementary_files - (file_id, revision) - VALUES - (?, ?)', - array( - (int) $supplementaryFile->getFileId(), - (int) $supplementaryFile->getRevision(), - ) - ); - - return $supplementaryFile; - } - - /** - * @see SubmissionFileDAODelegate::update() - * @param $suppFile SupplementaryFile - * @param $previousFile SupplementaryFile - * @return boolean True if success. - */ - function updateObject($suppFile, $previousFile) { - // Update the parent class table first. - if (!parent::updateObject($suppFile, $previousFile)) return false; - - // Now update the supplementary file table. - $this->update( - 'UPDATE submission_supplementary_files - SET - file_id = ?, - revision = ? - WHERE file_id = ? AND revision = ?', - array( - (int)$suppFile->getFileId(), - (int)$suppFile->getRevision(), - (int)$previousFile->getFileId(), - (int)$previousFile->getRevision() - ) - ); - return true; - } - - /** - * @copydoc SubmissionFileDAODelegate::deleteObject() - */ - function deleteObject($submissionFile) { - // First delete the submission file entry. - if (!parent::deleteObject($submissionFile)) return false; - - // Delete the supplementary file entry. - $this->update( - 'DELETE FROM submission_supplementary_files - WHERE file_id = ? AND revision = ?', - array( - (int) $submissionFile->getFileId(), - (int) $submissionFile->getRevision() - ) - ); - return true; - } - - /** - * @copydoc DAO::getLocaleFieldNames() - */ - function getLocaleFieldNames() { - return array_merge( - parent::getLocaleFieldNames(), - array( - 'creator', 'subject', 'description', 'publisher', 'sponsor', 'source', - ) - ); - } - - /** - * @copydoc DAO::getAdditionalFieldNames() - */ - function getAdditionalFieldNames() { - return array_merge( - parent::getAdditionalFieldNames(), - array( - 'dateCreated', 'language', - ) - ); - } - - /** - * @copydoc SubmissionFileDAODelegate::newDataObject() - */ - function newDataObject() { - return new SupplementaryFile(); - } - -} - - diff --git a/classes/submission/form/PKPSubmissionSubmitStep1Form.inc.php b/classes/submission/form/PKPSubmissionSubmitStep1Form.inc.php index b28369733cf..3ddec98de44 100644 --- a/classes/submission/form/PKPSubmissionSubmitStep1Form.inc.php +++ b/classes/submission/form/PKPSubmissionSubmitStep1Form.inc.php @@ -231,7 +231,9 @@ function readInputData() { * Set the submission data from the form. * @param Submission $submission */ - function setSubmissionData($submission) { } + function setSubmissionData($submission) { + $submission->setData('locale', $this->getData('locale')); + } /** * Set the publication data from the form. @@ -240,13 +242,6 @@ function setSubmissionData($submission) { } */ function setPublicationData($publication, $submission) { $publication->setData('submissionId', $submission->getId()); - $oldLocale = $publication->getData('locale'); - $publication->setData('locale', $this->getData('locale')); - $publication->setData('language', PKPString::substr($this->getData('locale'), 0, 2)); - if ($oldLocale && $oldLocale != $this->getData('locale')) { - $authorDao = DAORegistry::getDAO('AuthorDAO'); /* @var $authorDao AuthorDAO */ - $authorDao->changePublicationLocale($publication->getId(), $oldLocale, $this->getData('locale')); - } } /** @@ -337,6 +332,7 @@ function execute(...$functionArgs) { } if (isset($this->submission)) { + $oldLocale = $this->submission->getData('locale'); // Update existing submission $this->setSubmissionData($this->submission); if ($this->submission->getSubmissionProgress() <= $this->step) { @@ -354,6 +350,12 @@ function execute(...$functionArgs) { $this->setPublicationData($publication, $this->submission); $publication = Services::get('publication')->edit($publication, $publication->_data, $request); + // Update author name data when submission locale is changed + if ($oldLocale !== $this->submission->getData('locale')) { + $authorDao = DAORegistry::getDAO('AuthorDAO'); /* @var $authorDao AuthorDAO */ + $authorDao->changePublicationLocale($publication->getId(), $oldLocale, $this->getData('locale')); + } + } else { // Create new submission $this->submission = $submissionDao->newDataObject(); @@ -385,11 +387,11 @@ function execute(...$functionArgs) { $userGivenNames = $user->getGivenName(null); $userFamilyNames = $user->getFamilyName(null); if (is_null($userFamilyNames)) $userFamilyNames = array(); - if (empty($userGivenNames[$this->submission->getLocale()])) { + if (empty($userGivenNames[$this->submission->getData('locale')])) { $site = Application::get()->getRequest()->getSite(); - $userGivenNames[$this->submission->getLocale()] = $userGivenNames[$site->getPrimaryLocale()]; + $userGivenNames[$this->submission->getData('locale')] = $userGivenNames[$site->getPrimaryLocale()]; // then there should also be no family name for the submission locale - $userFamilyNames[$this->submission->getLocale()] = !empty($userFamilyNames[$site->getPrimaryLocale()]) ? $userFamilyNames[$site->getPrimaryLocale()] : ''; + $userFamilyNames[$this->submission->getData('locale')] = !empty($userFamilyNames[$site->getPrimaryLocale()]) ? $userFamilyNames[$site->getPrimaryLocale()] : ''; } $author->setGivenName($userGivenNames, null); $author->setFamilyName($userFamilyNames, null); diff --git a/classes/submission/form/PKPSubmissionSubmitStep2Form.inc.php b/classes/submission/form/PKPSubmissionSubmitStep2Form.inc.php index bcd5b5c734b..5a63ca7a799 100644 --- a/classes/submission/form/PKPSubmissionSubmitStep2Form.inc.php +++ b/classes/submission/form/PKPSubmissionSubmitStep2Form.inc.php @@ -25,6 +25,136 @@ function __construct($context, $submission) { parent::__construct($context, $submission, 2); } + /** + * @copydoc Form::fetch + */ + function fetch($request, $template = null, $display = false) { + + // SUBMISSION_FILE_ constants + import('lib.pkp.classes.submission.SubmissionFile'); + + $genres = []; + $genreResults = DAORegistry::getDAO('GenreDAO')->getEnabledByContextId($request->getContext()->getId()); + while ($genre = $genreResults->next()) { + if ($genre->getDependent()) { + continue; + } + $genres[] = $genre; + } + + $fileUploadApiUrl = ''; + $submissionFiles = []; + if ($this->submission) { + $fileUploadApiUrl = $request->getDispatcher()->url( + $request, + ROUTE_API, + $request->getContext()->getPath(), + 'submissions/' . $this->submission->getId() . '/files' + ); + $submissionFileForm = new \PKP\components\forms\submission\PKPSubmissionFileForm($fileUploadApiUrl, $genres); + + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'fileStages' => [SUBMISSION_FILE_SUBMISSION], + 'submissionIds' => [$this->submission->getId()], + ]); + if (count($submissionFilesIterator)) { + foreach ($submissionFilesIterator as $submissionFile) { + $submissionFiles[] = Services::get('submissionFile')->getSummaryProperties($submissionFile, [ + 'request' => $request, + 'submission' => $this->submission, + ]); + } + } + } + + $templateMgr = TemplateManager::getManager($request); + + $templateMgr->setState([ + 'components' => [ + 'submissionFiles' => [ + 'addFileLabel' => 'Add File', + 'apiUrl' => $fileUploadApiUrl, + 'cancelUploadLabel' => 'Cancel Upload', + 'genrePromptLabel' => __('submission.submit.genre.label'), + 'documentTypes' => [ + 'DOCUMENT_TYPE_DEFAULT' => DOCUMENT_TYPE_DEFAULT, + 'DOCUMENT_TYPE_AUDIO' => DOCUMENT_TYPE_AUDIO, + 'DOCUMENT_TYPE_EXCEL' => DOCUMENT_TYPE_EXCEL, + 'DOCUMENT_TYPE_HTML' => DOCUMENT_TYPE_HTML, + 'DOCUMENT_TYPE_IMAGE' => DOCUMENT_TYPE_IMAGE, + 'DOCUMENT_TYPE_PDF' => DOCUMENT_TYPE_PDF, + 'DOCUMENT_TYPE_WORD' => DOCUMENT_TYPE_WORD, + 'DOCUMENT_TYPE_EPUB' => DOCUMENT_TYPE_EPUB, + 'DOCUMENT_TYPE_VIDEO' => DOCUMENT_TYPE_VIDEO, + 'DOCUMENT_TYPE_ZIP' => DOCUMENT_TYPE_ZIP, + ], + 'emptyLabel' => 'Upload any files the editorial team will need to evaluate your submission.', + 'emptyAddLabel' => 'Upload Files', + 'fileStage' => SUBMISSION_FILE_SUBMISSION, + 'form' => isset($submissionFileForm) ? $submissionFileForm->getConfig() : null, + 'genres' => array_map(function($genre) { + return [ + 'id' => (int) $genre->getId(), + 'name' => $genre->getLocalizedName(), + 'isPrimary' => !$genre->getSupplementary() && !$genre->getDependent(), + ]; + }, $genres), + 'id' => 'submissionFiles', + 'items' => $submissionFiles, + 'options' => [ + 'maxFilesize' => Application::getIntMaxFileMBs(), + 'dropzoneDictDefaultMessage' => __('form.dropzone.dictDefaultMessage'), + 'dropzoneDictFallbackMessage' => __('form.dropzone.dictFallbackMessage'), + 'dropzoneDictFallbackText' => __('form.dropzone.dictFallbackText'), + 'dropzoneDictFileTooBig' => __('form.dropzone.dictFileTooBig'), + 'dropzoneDictInvalidFileType' => __('form.dropzone.dictInvalidFileType'), + 'dropzoneDictResponseError' => __('form.dropzone.dictResponseError'), + 'dropzoneDictCancelUpload' => __('form.dropzone.dictCancelUpload'), + 'dropzoneDictUploadCanceled' => __('form.dropzone.dictUploadCanceled'), + 'dropzoneDictCancelUploadConfirmation' => __('form.dropzone.dictCancelUploadConfirmation'), + 'dropzoneDictRemoveFile' => __('form.dropzone.dictRemoveFile'), + 'dropzoneDictMaxFilesExceeded' => __('form.dropzone.dictMaxFilesExceeded'), + ], + 'otherLabel' => 'Other', + 'removeConfirmLabel' => __('submission.submit.removeConfirm'), + 'stageId' => WORKFLOW_STAGE_ID_SUBMISSION, + 'title' => 'Files', + 'uploadProgressLabel' => 'Uploading {$percent}% complete', + ], + ], + ]); + + // Temporary workaround that allows state to be passed to a + // page fragment retrieved by $templateMgr->fetch(). This + // should not be done under normal circumstances! + $templateMgr->_assignState($templateMgr->_state); + + return parent::fetch($request, $template, $display); + } + + /** + * @copydoc Form::validate + */ + function validate($callHooks = true) { + + // SUBMISSION_FILE_ constants + import('lib.pkp.classes.submission.SubmissionFile'); + + // Validate that all upload files have been assigned a genreId + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'fileStages' => [SUBMISSION_FILE_SUBMISSION], + 'submissionIds' => [$this->submission->getId()], + ]); + foreach ($submissionFilesIterator as $submissionFile) { + if (!$submissionFile->getData('genreId')) { + $this->addError('files', __('submission.submit.genre.error')); + $this->addErrorField('files'); + } + } + + return parent::validate($callHooks); + } + /** * Save changes to submission. * @return int the submission ID diff --git a/classes/submission/reviewRound/ReviewRoundDAO.inc.php b/classes/submission/reviewRound/ReviewRoundDAO.inc.php index a49fc67235c..65b36d4a9df 100644 --- a/classes/submission/reviewRound/ReviewRoundDAO.inc.php +++ b/classes/submission/reviewRound/ReviewRoundDAO.inc.php @@ -155,7 +155,7 @@ function getBySubmissionFileId($submissionFileId) { 'SELECT * FROM review_rounds rr INNER JOIN review_round_files rrf ON rr.review_round_id = rrf.review_round_id - WHERE rrf.file_id = ?', + WHERE rrf.submission_file_id = ?', array((int) $submissionFileId)); $returner = null; diff --git a/classes/template/PKPTemplateManager.inc.php b/classes/template/PKPTemplateManager.inc.php index baa79bf0508..99d7d197555 100644 --- a/classes/template/PKPTemplateManager.inc.php +++ b/classes/template/PKPTemplateManager.inc.php @@ -756,6 +756,7 @@ function setupBackendPage() { 'common.confirm', 'common.delete', 'common.edit', + 'common.editItem', 'common.error', 'common.filter', 'common.filterAdd', @@ -1191,6 +1192,28 @@ function display($template = null, $cache_id = null, $compile_id = null, $parent parent::display($template, $cache_id, $compile_id, $parent); } + /** + * DO NOT USE. Assign the state data to the template + * + * This method is a temporary workaround and should not be used + * to pass state to the template. It is only used during submission + * as a temporary method to initialize a Vue.js component inside + * of a tab template. + * + * This can lead to a separate Vue instance embedded inside of the + * main app's Vue instance. The instances can not talk to each other. + * + * This should be removed when the submission wizard is updated to + * make use of the new forms powered by Vue.js. + * + * @deprecated 3.3 + * @param [type] $state + * @return void + */ + function _assignState($state) { + $this->assign('state', $this->_state); + } + /** * Clear template compile and cache directories. @@ -2146,10 +2169,8 @@ function smartyPluckFiles($params, $smarty) { // The approved list of `by` attributes // chapter Any files assigned to a chapter ID. A value of `any` will return files assigned to any chapter. A value of 0 will return files not assigned to chapter // publicationFormat Any files in a given publicationFormat ID - // component Any files of a component type by class name: SubmissionFile|SubmissionArtworkFile|SupplementaryFile - // fileExtension Any files with a file extension in all caps: PDF // genre Any files with a genre ID (file genres are configurable but typically refer to Manuscript, Bibliography, etc) - if (!in_array($params['by'], ['chapter', 'publicationFormat', 'component', 'fileExtension', 'genre'])) { + if (!in_array($params['by'], array('chapter','publicationFormat','fileExtension','genre'))) { error_log('Smarty: {pluck_files} function called without a valid `by` param. Called in ' . __FILE__ . ':' . __LINE__); $smarty->assign($params['assign'], []); return; @@ -2182,19 +2203,7 @@ function smartyPluckFiles($params, $smarty) { break; case 'publicationFormat': - if ($file->getAssocId() == $params['value']) { - $matching_files[] = $file; - } - break; - - case 'component': - if (get_class($file) == $params['value']) { - $matching_files[] = $file; - } - break; - - case 'fileExtension': - if ($file->getExtension() == $params['value']) { + if ($file->getData('assocId') == $params['value']) { $matching_files[] = $file; } break; diff --git a/classes/user/PKPUserAction.inc.php b/classes/user/PKPUserAction.inc.php index 0e957283e83..389f94719ed 100644 --- a/classes/user/PKPUserAction.inc.php +++ b/classes/user/PKPUserAction.inc.php @@ -28,8 +28,13 @@ public function mergeUsers($oldUserId, $newUserId) { HookRegistry::call('UserAction::mergeUsers', array(&$oldUserId, &$newUserId)); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->transferOwnership($oldUserId, $newUserId); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'uploaderUserIds' => [$oldUserId], + 'includeDependentFiles' => true, + ]); + foreach ($submissionFilesIterator as $submissionFile) { + Services::get('submissionFile')->edit($submissionFile, ['uploaderUserId' => $newUserId], Application::get()->getRequest()); + } $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ $notes = $noteDao->getByUserId($oldUserId); diff --git a/composer.json b/composer.json index cf50746ab41..f010fdc7b5d 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "gettext/gettext": "^4.6", "sokil/php-isocodes": "^3.0", "doctrine/dbal": "^2.10", - "guzzlehttp/guzzle": "^6.5" + "guzzlehttp/guzzle": "^6.5", + "league/flysystem": "^1.0" }, "require-dev": { "phpunit/phpunit": "~8", diff --git a/composer.lock b/composer.lock index 1b6d95ca460..e13a0e16ba7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8a9b98ab91f5d610f0c1d59dc724c6f2", + "content-hash": "978ce1d6b99fa9b5cadb112bd62165dc", "packages": [ { "name": "adodb/adodb-php", @@ -330,16 +330,16 @@ }, { "name": "doctrine/dbal", - "version": "2.10.3", + "version": "2.10.4", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "03ca23afc2ee062f5d3e32426ad37c34a4770dcf" + "reference": "47433196b6390d14409a33885ee42b6208160643" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/03ca23afc2ee062f5d3e32426ad37c34a4770dcf", - "reference": "03ca23afc2ee062f5d3e32426ad37c34a4770dcf", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/47433196b6390d14409a33885ee42b6208160643", + "reference": "47433196b6390d14409a33885ee42b6208160643", "shasum": "" }, "require": { @@ -421,7 +421,7 @@ "sqlserver", "sqlsrv" ], - "time": "2020-09-02T01:35:42+00:00" + "time": "2020-09-12T21:20:41+00:00" }, { "name": "doctrine/event-manager", @@ -1469,6 +1469,90 @@ "homepage": "https://laravel.com", "time": "2019-10-24T16:01:23+00:00" }, + { + "name": "league/flysystem", + "version": "1.0.70", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/585824702f534f8d3cf7fab7225e8466cc4b7493", + "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": ">=5.5.9" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/phpspec": "^3.4 || ^4.0 || ^5.0 || ^6.0", + "phpunit/phpunit": "^5.7.26" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "time": "2020-07-26T07:20:36+00:00" + }, { "name": "michelf/php-markdown", "version": "1.9.0", @@ -1556,16 +1640,16 @@ }, { "name": "nesbot/carbon", - "version": "2.39.1", + "version": "2.39.2", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "7af467873250583cc967a59ee9df29fabab193c1" + "reference": "326efde1bc09077a26cb77f6e2e32e13f06c27f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7af467873250583cc967a59ee9df29fabab193c1", - "reference": "7af467873250583cc967a59ee9df29fabab193c1", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/326efde1bc09077a26cb77f6e2e32e13f06c27f2", + "reference": "326efde1bc09077a26cb77f6e2e32e13f06c27f2", "shasum": "" }, "require": { @@ -1631,7 +1715,7 @@ "datetime", "time" ], - "time": "2020-09-04T13:11:37+00:00" + "time": "2020-09-10T12:16:42+00:00" }, { "name": "nikic/fast-route", @@ -2769,16 +2853,16 @@ }, { "name": "symfony/translation-contracts", - "version": "v1.1.9", + "version": "v1.1.10", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "a5db6f7707fd35d137b1398734f2d745c8616ea2" + "reference": "84180a25fad31e23bebd26ca09d89464f082cacc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/a5db6f7707fd35d137b1398734f2d745c8616ea2", - "reference": "a5db6f7707fd35d137b1398734f2d745c8616ea2", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/84180a25fad31e23bebd26ca09d89464f082cacc", + "reference": "84180a25fad31e23bebd26ca09d89464f082cacc", "shasum": "" }, "require": { @@ -2826,7 +2910,7 @@ "interoperability", "standards" ], - "time": "2020-07-06T13:19:58+00:00" + "time": "2020-09-02T16:08:58+00:00" }, { "name": "tinymce/tinymce", @@ -3687,7 +3771,6 @@ "keywords": [ "tokenizer" ], - "abandoned": true, "time": "2019-09-17T06:23:10+00:00" }, { diff --git a/controllers/api/file/FileApiHandler.inc.php b/controllers/api/file/FileApiHandler.inc.php index a33d5c70e36..f1794661bec 100644 --- a/controllers/api/file/FileApiHandler.inc.php +++ b/controllers/api/file/FileApiHandler.inc.php @@ -19,7 +19,6 @@ // Import the base handler. import('classes.handler.Handler'); import('lib.pkp.classes.core.JSONMessage'); -import('lib.pkp.classes.file.SubmissionFileManager'); import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); class FileApiHandler extends Handler { @@ -39,28 +38,29 @@ function __construct() { // Implement methods from PKPHandler // function authorize($request, &$args, $roleAssignments) { - $fileIds = $request->getUserVar('filesIdsAndRevisions'); + $submissionId = (int) $request->getUserVar('submissionId'); + $submissionFileId = (int) $request->getUserVar('submissionFileId'); + $fileStage = (int) $request->getUserVar('fileStage'); $libraryFileId = $request->getUserVar('libraryFileId'); - if (is_string($fileIds)) { - $fileIdsArray = explode(';', $fileIds); - // Remove empty entries (a trailing ";" will cause these) - $fileIdsArray = array_filter($fileIdsArray, function($a) { - return !empty($a); - }); - } - if (!empty($fileIdsArray)) { - $multipleSubmissionFileAccessPolicy = new PolicySet(COMBINING_DENY_OVERRIDES); - foreach ($fileIdsArray as $fileIdAndRevision) { - $multipleSubmissionFileAccessPolicy->addPolicy($this->_getAccessPolicy($request, $args, $roleAssignments, $fileIdAndRevision)); - } - $this->addPolicy($multipleSubmissionFileAccessPolicy); + if (!empty($submissionFileId)) { + import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); + $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_READ, $submissionFileId)); } else if (is_numeric($libraryFileId)) { import('lib.pkp.classes.security.authorization.ContextAccessPolicy'); $this->addPolicy(new ContextAccessPolicy($request, $roleAssignments)); - } else { - // IDs will be specified using the default parameters. - $this->addPolicy($this->_getAccessPolicy($request, $args, $roleAssignments)); + } else if (!empty($fileStage) && empty($submissionFileId)) { + $submissionFileIds = Services::get('submissionFile')->getIds([ + 'submissionIds' => [$submissionId], + 'fileStages' => [$fileStage], + 'includeDependentFiles' => $fileStage === SUBMISSION_FILE_DEPENDENT, + ]); + import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); + $allFilesAccessPolicy = new PolicySet(COMBINING_DENY_OVERRIDES); + foreach ($submissionFileIds as $submissionFileId) { + $allFilesAccessPolicy->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_READ, $submissionFileId)); + } + $this->addPolicy($allFilesAccessPolicy); } return parent::authorize($request, $args, $roleAssignments); @@ -76,14 +76,40 @@ function authorize($request, &$args, $roleAssignments) { */ function downloadFile($args, $request) { $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); - assert(isset($submissionFile)); // Should have been validated already - $context = $request->getContext(); - $fileManager = $this->_getFileManager($context->getId(), $submissionFile->getSubmissionId()); - if (!$fileManager->downloadById($submissionFile->getFileId(), $submissionFile->getRevision(), false, $submissionFile->getClientFileName())) { - error_log('FileApiHandler: File ' . $submissionFile->getFilePath() . ' does not exist or is not readable!'); - header('HTTP/1.0 500 Internal Server Error'); - fatalError('500 Internal Server Error'); + $fileId = $request->getUserVar('fileId') ?? $submissionFile->getData('fileId'); + $validFileIds = Services::get('submissionFile')->getRevisionFileIds($submissionFile->getId()); + if (!in_array($fileId, $validFileIds)) { + throw new Exception('File ' . $fileId . ' is not a revision of submission file ' . $submissionFile->getId()); + } + $path = Services::get('file')->getPath((int) $fileId); + if (!Services::get('file')->fs->has($path)) { + throw new Exception('File ' . $fileId . ' at ' . $path . ' does not exist or can not be read.'); } + + $filename = $request->getUserVar('filename') ?? $submissionFile->getLocalizedData('name'); + + // Enforce anonymous filenames for anonymous review assignments + $reviewAssignment = $this->getAuthorizedContextObject(ASSOC_TYPE_REVIEW_ASSIGNMENT); + if ($reviewAssignment + && $reviewAssignment->getReviewMethod() == SUBMISSION_REVIEW_METHOD_DOUBLEBLIND + && $reviewAssignment->getReviewerId() == $request->getUser()->getId()) { + AppLocale::requireComponents([LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION]); + $genreDao = DAORegistry::getDAO('GenreDAO'); /* @var $genreDao GenreDAO */ + $genre = $genreDao->getById($submissionFile->getData('genreId')); + $filename = sprintf( + '%s-%s-%d-%s-%d', + \Stringy\Stringy::create($request->getContext()->getLocalizedData('acronym'))->toLowerCase(), + \Stringy\Stringy::create(__('submission.list.reviewAssignment'))->dasherize(), + $submissionFile->getData('submissionId'), + $genre->getLocalizedName(), + $submissionFile->getId() + ); + } + + $filename = Services::get('file')->formatFilename($path, $filename); + + Services::get('file')->download($path, $filename); + die; } /** @@ -105,32 +131,34 @@ function downloadLibraryFile($args, $request) { function downloadAllFiles($args, $request) { // Retrieve the authorized objects. $submissionFiles = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILES); - $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); - // Find out the paths of all files in this grid. - $context = $request->getContext(); - $filePaths = array(); - $fileManager = $this->_getFileManager($context->getId(), $submission->getId()); - $filesDir = $fileManager->getBasePath(); + $files = []; foreach ($submissionFiles as $submissionFile) { - // Remove absolute path so the archive doesn't include it (otherwise all files are organized by absolute path) - $filePaths[str_replace($filesDir, '', $submissionFile->getFilePath())] = $submissionFile->getClientFileName(); + $path = Services::get('file')->getPath($submissionFile->getData('fileId')); + $files[$path] = Services::get('file')->formatFilename($path, $submissionFile->getLocalizedData('name')); + } + AppLocale::requireComponents([LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_APP_SUBMISSION, LOCALE_COMPONENT_PKP_EDITOR, LOCALE_COMPONENT_APP_EDITOR]); + $filename = __('submission.files'); + if (!empty($args['nameLocaleKey'])) { + $filename = __($args['nameLocaleKey']) ?? $filename; } + $filename = $args['submissionId'] . '-' . $filename; + $filename = \Stringy\Stringy::create($filename)->toLowerCase()->dasherize()->regexReplace('[^a-z0-9\-\_.]', ''); import('lib.pkp.classes.file.FileArchive'); $fileArchive = new FileArchive(); - $archivePath = $fileArchive->create($filePaths, $filesDir); + $archivePath = $fileArchive->create($files, rtrim(Config::getVar('files', 'files_dir'), '/')); if (file_exists($archivePath)) { $fileManager = new FileManager(); if ($fileArchive->zipFunctional()) { - $fileManager->downloadByPath($archivePath, 'application/x-zip', false, 'files.zip'); + $fileManager->downloadByPath($archivePath, 'application/x-zip', false, $filename . '.zip'); } else { - $fileManager->downloadByPath($archivePath, 'application/x-gtar', false, 'files.tar.gz'); + $fileManager->downloadByPath($archivePath, 'application/x-gtar', false, $filename . '.tar.gz'); } $fileManager->deleteByPath($archivePath); } else { - fatalError('Creating archive with submission files failed!'); + throw new Exception('Creating archive with submission files failed!'); } } @@ -142,19 +170,12 @@ function downloadAllFiles($args, $request) { */ function recordDownload($args, $request) { $submissionFiles = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILES); - $fileId = null; foreach ($submissionFiles as $submissionFile) { - $submissionFileManager = new SubmissionFileManager($request->getContext()->getId(), $submissionFile->getSubmissionId()); - $submissionFileManager->recordView($submissionFile); - $fileId = $submissionFile->getFileId(); + Services::get('submissionFile')->recordView($submissionFile); unset($submissionFile); } - if (count($submissionFiles) > 1) { - $fileId = null; - } - return $this->enableLinkAction($args, $request); } @@ -169,28 +190,6 @@ function recordDownload($args, $request) { function enableLinkAction($args, $request) { return DAO::getDataChangedEvent(); } - - /** - * return the application specific file manager. - * @param $contextId int the context for this manager. - * @param $submissionId int the submission id. - * @return SubmissionFileManager - */ - function _getFileManager($contextId, $submissionId) { - return new SubmissionFileManager($contextId, $submissionId); - } - - /** - * return the application specific file access policy. - * @param $request PKPRequest - * @param $args - * @param $roleAssignments array - * @param $fileIdAndRevision array optional - * @return SubmissionFileAccessPolicy - */ - function _getAccessPolicy($request, $args, $roleAssignments, $fileIdAndRevision = null) { - return new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_READ, $fileIdAndRevision); - } } diff --git a/controllers/api/file/PKPManageFileApiHandler.inc.php b/controllers/api/file/PKPManageFileApiHandler.inc.php index dc3452a275c..b6609933071 100644 --- a/controllers/api/file/PKPManageFileApiHandler.inc.php +++ b/controllers/api/file/PKPManageFileApiHandler.inc.php @@ -37,7 +37,7 @@ function __construct() { // function authorize($request, &$args, $roleAssignments) { import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); - $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_MODIFY)); + $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_MODIFY, (int) $args['submissionFileId'])); return parent::authorize($request, $args, $roleAssignments); } @@ -52,77 +52,17 @@ function authorize($request, &$args, $roleAssignments) { * @return JSONMessage JSON object */ function deleteFile($args, $request) { - if (!$request->checkCSRF()) return new JSONMessage(false); - - $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); - $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); - $stageId = $request->getUserVar('stageId'); - - assert(isset($submissionFile) && isset($submission)); // Should have been validated already - - $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ - $noteDao->deleteByAssoc(ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getFileId()); - - // Retrieve the review round so it can be updated after the file is - // deleted - if ($submissionFile->getFileStage() == SUBMISSION_FILE_REVIEW_REVISION) { - import('lib.pkp.classes.submission.reviewRound.ReviewRoundDAO'); - $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ - $reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getFileId()); + if (!$request->checkCSRF()) { + return new JSONMessage(false); } - // Detach any dependent entities to this file deletion. - $this->detachEntities($submissionFile, $submission->getId(), $stageId); - - // Delete the submission file. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - if (!$submissionFileDao->deleteRevisionById($submissionFile->getFileId(), $submissionFile->getRevision(), $submissionFile->getFileStage(), $submission->getId())) return new JSONMessage(false); - - $notificationMgr = new NotificationManager(); - switch ($submissionFile->getFileStage()) { - case SUBMISSION_FILE_REVIEW_REVISION: - // Get a list of author user IDs - $authorUserIds = array(); - $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /* @var $stageAssignmentDao StageAssignmentDAO */ - $submitterAssignments = $stageAssignmentDao->getBySubmissionAndRoleId($submission->getId(), ROLE_ID_AUTHOR); - while ($assignment = $submitterAssignments->next()) { - $authorUserIds[] = $assignment->getUserId(); - } - - // Update the notifications - $notificationMgr->updateNotification( - $request, - array(NOTIFICATION_TYPE_PENDING_INTERNAL_REVISIONS, NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS), - $authorUserIds, - ASSOC_TYPE_SUBMISSION, - $submission->getId() - ); - - // Update the ReviewRound status when revision is submitted - $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ - $reviewRoundDao->updateStatus($reviewRound); - break; - - case SUBMISSION_FILE_COPYEDIT: - $notificationMgr->updateNotification( - $request, - array(NOTIFICATION_TYPE_ASSIGN_COPYEDITOR, NOTIFICATION_TYPE_AWAITING_COPYEDITS), - null, - ASSOC_TYPE_SUBMISSION, - $submission->getId() - ); - break; - } - - $this->removeFileIndex($submission, $submissionFile); - $fileManager = $this->getFileManager($submission->getContextId(), $submission->getId()); - $fileManager->deleteById($submissionFile->getFileId(), $submissionFile->getRevision()); + Services::get('submissionFile')->delete($this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE)); $this->setupTemplate($request); $user = $request->getUser(); - if (!$request->getUserVar('suppressNotification')) NotificationManager::createTrivialNotification($user->getId(), NOTIFICATION_TYPE_SUCCESS, array('contents' => __('notification.removedFile'))); - - $this->logDeletionEvent($request, $submission, $submissionFile, $user); + if (!$request->getUserVar('suppressNotification')) { + NotificationManager::createTrivialNotification($user->getId(), NOTIFICATION_TYPE_SUCCESS, array('contents' => __('notification.removedFile'))); + } return DAO::getDataChangedEvent(); } @@ -155,9 +95,10 @@ function editMetadataTab($args, $request) { $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); $reviewRound = $this->getAuthorizedContextObject(ASSOC_TYPE_REVIEW_ROUND); $stageId = $request->getUserVar('stageId'); - $metadataForm = $submissionFile->getMetadataForm($stageId, $reviewRound); - $metadataForm->setShowButtons(true); - return new JSONMessage(true, $metadataForm->fetch($request)); + import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesMetadataForm'); + $form = new SubmissionFilesMetadataForm($submissionFile, $stageId, $reviewRound); + $form->setShowButtons(true); + return new JSONMessage(true, $form->fetch($request)); } /** @@ -172,11 +113,12 @@ function saveMetadata($args, $request) { $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); $reviewRound = $this->getAuthorizedContextObject(ASSOC_TYPE_REVIEW_ROUND); $stageId = $request->getUserVar('stageId'); - $metadataForm = $submissionFile->getMetadataForm($stageId, $reviewRound); - $metadataForm->readInputData(); - if ($metadataForm->validate()) { - $metadataForm->execute(); - $submissionFile = $metadataForm->getSubmissionFile(); + import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesMetadataForm'); + $form = new SubmissionFilesMetadataForm($submissionFile, $stageId, $reviewRound); + $form->readInputData(); + if ($form->validate()) { + $form->execute(); + $submissionFile = $form->getSubmissionFile(); // Get a list of author user IDs $authorUserIds = array(); @@ -208,18 +150,6 @@ function saveMetadata($args, $request) { } } - // Log the upload event - import('lib.pkp.classes.log.SubmissionLog'); - import('classes.log.SubmissionEventLogEntry'); - import('lib.pkp.classes.log.SubmissionFileEventLogEntry'); // constants - $user = $request->getUser(); - SubmissionLog::logEvent( - $request, $submission, - $submissionFile->getRevision()>1?SUBMISSION_LOG_FILE_REVISION_UPLOAD:SUBMISSION_LOG_FILE_UPLOAD, - $submissionFile->getRevision()>1?'submission.event.fileRevised':'submission.event.fileUploaded', - array('fileStage' => $submissionFile->getFileStage(), 'fileId' => $submissionFile->getFileId(), 'fileRevision' => $submissionFile->getRevision(), 'originalFileName' => $submissionFile->getOriginalFileName(), 'submissionId' => $submissionFile->getSubmissionId(), 'username' => $user->getUsername(), 'name' => $submissionFile->getLocalizedName()) - ); - // Inform SearchIndex of changes $articleSearchIndex = Application::getSubmissionSearchIndex(); $articleSearchIndex->submissionFilesChanged($submission); @@ -227,38 +157,10 @@ function saveMetadata($args, $request) { return DAO::getDataChangedEvent(); } else { - return new JSONMessage(true, $metadataForm->fetch($request)); + return new JSONMessage(true, $form->fetch($request)); } } - /** - * Remove the submission file index. - * @param $submission Submission - * @param $submissionFile SubmissionFile - */ - abstract function removeFileIndex($submission, $submissionFile); - - /** - * Get the submission file manager. - * @param $contextId int the context id. - * @param $submissionId int the submission id. - * @return SubmissionFileManager - */ - function getFileManager($contextId, $submissionId) { - import('lib.pkp.classes.file.SubmissionFileManager'); - return new SubmissionFileManager($contextId, $submissionId); - } - - /** - * Logs the deletion event using app-specific logging classes. - * Must be implemented by subclasses. - * @param $request PKPRequest - * @param $submission Submission - * @param $submissionFile SubmissionFile - * @param $user User - */ - abstract function logDeletionEvent($request, $submission, $submissionFile, $user); - /** * Get the list of notifications to be updated on metadata form submission. * @return array @@ -267,23 +169,6 @@ protected function getUpdateNotifications() { return array(NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS); } - /** - * Detach any dependent entities to this file upload. - * @param $submissionFile SubmissionFile - * @param $submissionId integer - * @param $stageId integer - */ - function detachEntities($submissionFile, $submissionId, $stageId) { - switch ($submissionFile->getFileStage()) { - case SUBMISSION_FILE_REVIEW_FILE: - case SUBMISSION_FILE_REVIEW_ATTACHMENT: - case SUBMISSION_FILE_REVIEW_REVISION: - // check to see if we need to remove review_round_file associations - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->deleteReviewRoundAssignment($submissionId, $stageId, $submissionFile->getFileId(), $submissionFile->getRevision()); - } - } - } diff --git a/controllers/api/file/linkAction/AddFileLinkAction.inc.php b/controllers/api/file/linkAction/AddFileLinkAction.inc.php index 6075918982f..d03d60efb47 100644 --- a/controllers/api/file/linkAction/AddFileLinkAction.inc.php +++ b/controllers/api/file/linkAction/AddFileLinkAction.inc.php @@ -37,9 +37,11 @@ class AddFileLinkAction extends BaseAddFileLinkAction { * @param $revisedFileId int Revised file ID, if any * @param $dependentFilesOnly bool whether to only include dependent * files in the Genres dropdown. + * @param $queryId int The query id. Use when the assoc details point + * to a note */ function __construct($request, $submissionId, $stageId, $uploaderRoles, - $fileStage, $assocType = null, $assocId = null, $reviewRoundId = null, $revisedFileId = null, $dependentFilesOnly = false) { + $fileStage, $assocType = null, $assocId = null, $reviewRoundId = null, $revisedFileId = null, $dependentFilesOnly = false, $queryId = null) { // Create the action arguments array. $actionArgs = array('fileStage' => $fileStage, 'reviewRoundId' => $reviewRoundId); @@ -53,6 +55,10 @@ function __construct($request, $submissionId, $stageId, $uploaderRoles, } if ($dependentFilesOnly) $actionArgs['dependentFilesOnly'] = true; + if ($queryId) { + $actionArgs['queryId'] = $queryId; + } + // Identify text labels based on the file stage. $textLabels = AddFileLinkAction::_getTextLabels($fileStage); @@ -105,10 +111,6 @@ function _getTextLabels($fileStage) { 'wizardTitle' => 'submission.upload.copyeditedVersion', 'buttonLabel' => 'submission.addFile' ), - SUBMISSION_FILE_FAIR_COPY => array( - 'wizardTitle' => 'submission.upload.fairCopy', - 'buttonLabel' => 'submission.addFile' - ), SUBMISSION_FILE_PRODUCTION_READY => array( 'wizardTitle' => 'submission.upload.productionReady', 'buttonLabel' => 'submission.addFile' diff --git a/controllers/api/file/linkAction/DownloadFileLinkAction.inc.php b/controllers/api/file/linkAction/DownloadFileLinkAction.inc.php index 77598d33ec2..ca6b3d87e35 100644 --- a/controllers/api/file/linkAction/DownloadFileLinkAction.inc.php +++ b/controllers/api/file/linkAction/DownloadFileLinkAction.inc.php @@ -25,26 +25,33 @@ class DownloadFileLinkAction extends FileLinkAction { * link to. * @param $stageId int (optional) * @param $label string (optional) Label to use instead of filename + * @param $fileId int (optional) Download a specific revision of a file + * @param $filename string (optional) The filename to use for the file */ - function __construct($request, $submissionFile, $stageId = null, $label = null) { + function __construct($request, $submissionFile, $stageId = null, $label = null, $fileId = null, $filename = null) { // Instantiate the redirect action request. $router = $request->getRouter(); import('lib.pkp.classes.linkAction.request.PostAndRedirectAction'); $this->label = $label; + $actionArgs = $this->getActionArgs($submissionFile, $stageId); + if ($fileId) { + $actionArgs['fileId'] = $fileId; + } + if ($filename) { + $actionArgs['filename'] = $filename; + } $redirectRequest = new PostAndRedirectAction( $router->url( $request, null, 'api.file.FileApiHandler', 'recordDownload', - null, $this->getActionArgs($submissionFile, $stageId)), + null, $actionArgs), $router->url( $request, null, 'api.file.FileApiHandler', 'downloadFile', - null, $this->getActionArgs($submissionFile, $stageId)) + null, $actionArgs) ); // Configure the file link action. parent::__construct( - 'downloadFile', $redirectRequest, htmlspecialchars($this->getLabel($submissionFile)), - $submissionFile->getDocumentType(), - $submissionFile->getFileId() . '-' . $submissionFile->getRevision() + 'downloadFile', $redirectRequest, htmlspecialchars($this->getLabel($submissionFile)) ); } @@ -55,7 +62,7 @@ function __construct($request, $submissionFile, $stageId = null, $label = null) */ function getLabel($submissionFile) { if ($this->label !== null) return $this->label; - return $submissionFile->getFileLabel(); + return $submissionFile->getLocalizedData('name'); } } diff --git a/controllers/api/file/linkAction/FileLinkAction.inc.php b/controllers/api/file/linkAction/FileLinkAction.inc.php index f201e2421a6..ce9ffdce8ff 100644 --- a/controllers/api/file/linkAction/FileLinkAction.inc.php +++ b/controllers/api/file/linkAction/FileLinkAction.inc.php @@ -43,9 +43,8 @@ function getActionArgs($submissionFile, $stageId = null) { // Create the action arguments array. $args = array( - 'fileId' => $submissionFile->getFileId(), - 'revision' => $submissionFile->getRevision(), - 'submissionId' => $submissionFile->getSubmissionId() + 'submissionFileId' => $submissionFile->getId(), + 'submissionId' => $submissionFile->getData('submissionId') ); if ($stageId) $args['stageId'] = $stageId; diff --git a/controllers/grid/eventLog/EventLogGridCellProvider.inc.php b/controllers/grid/eventLog/EventLogGridCellProvider.inc.php index 46270d3b27e..89cd787c10b 100644 --- a/controllers/grid/eventLog/EventLogGridCellProvider.inc.php +++ b/controllers/grid/eventLog/EventLogGridCellProvider.inc.php @@ -81,10 +81,9 @@ function getTemplateVarsFromRowColumn($row, $column) { // Maybe anonymize files submitted by reviewers if (isset($params['fileStage']) && $params['fileStage'] === SUBMISSION_FILE_REVIEW_ATTACHMENT) { assert(isset($params['fileId']) && isset($params['submissionId'])); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFile = $submissionFileDao->getLatestRevision($params['fileId']); - if ($submissionFile && $submissionFile->getAssocType() === ASSOC_TYPE_REVIEW_ASSIGNMENT) { - $reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getAssocId()); + $submissionFile = Services::get('submissionFile')->get($params['id']); + if ($submissionFile && $submissionFile->getData('assocType') === ASSOC_TYPE_REVIEW_ASSIGNMENT) { + $reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getData('assocId')); if (!$reviewAssignment || in_array($reviewAssignment->getReviewMethod(), array(SUBMISSION_REVIEW_METHOD_ANONYMOUS, SUBMISSION_REVIEW_METHOD_DOUBLEANONYMOUS))) { $userName = __('editor.review.anonymousReviewer'); } diff --git a/controllers/grid/eventLog/EventLogGridRow.inc.php b/controllers/grid/eventLog/EventLogGridRow.inc.php index cc75fb01e01..76238073476 100644 --- a/controllers/grid/eventLog/EventLogGridRow.inc.php +++ b/controllers/grid/eventLog/EventLogGridRow.inc.php @@ -53,29 +53,34 @@ function initialize($request, $template = null) { assert($logEntry != null && (is_a($logEntry, 'EventLogEntry') || is_a($logEntry, 'EmailLogEntry'))); if (is_a($logEntry, 'EventLogEntry')) { - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ $params = $logEntry->getParams(); switch ($logEntry->getEventType()) { case SUBMISSION_LOG_FILE_REVISION_UPLOAD: case SUBMISSION_LOG_FILE_UPLOAD: - $submissionFile = $submissionFileDao->getRevision($params['fileId'], $params['fileRevision']); + $submissionFileId = $params['submissionFileId']; + $fileId = $params['fileId']; + $submissionFile = Services::get('submissionFile')->get($submissionFileId); + if (!$submissionFile) { + break; + } + $filename = $params['originalFileName'] ?? $submissionFile->getLocalizedData('name'); if ($submissionFile) { $anonymousAuthor = false; - $maybeAnonymousAuthor = $this->_isCurrentUserAssignedAuthor && $submissionFile->getFileStage() === SUBMISSION_FILE_REVIEW_ATTACHMENT; - if ($maybeAnonymousAuthor && $submissionFile->getAssocType() === ASSOC_TYPE_REVIEW_ASSIGNMENT) { + $maybeAnonymousAuthor = $this->_isCurrentUserAssignedAuthor && $submissionFile->getData('fileStage') === SUBMISSION_FILE_REVIEW_ATTACHMENT; + if ($maybeAnonymousAuthor && $submissionFile->getData('assocType') === ASSOC_TYPE_REVIEW_ASSIGNMENT) { $reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /* @var $reviewAssignmentDao ReviewAssignmentDAO */ - $reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getAssocId()); + $reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getData('assocId')); if ($reviewAssignment && in_array($reviewAssignment->getReviewMethod(), array(SUBMISSION_REVIEW_METHOD_ANONYMOUS, SUBMISSION_REVIEW_METHOD_DOUBLEANONYMOUS))) { $anonymousAuthor = true; } } if (!$anonymousAuthor) { - $workflowStageId = $submissionFileDao->getWorkflowStageId($submissionFile); + $workflowStageId = Services::get('submissionFile')->getWorkflowStageId($submissionFile); // If a submission file is attached to a query that has been deleted, we cannot // determine its stage. Don't present a download link in this case. - if ($workflowStageId || $submissionFile->getFileStage() != SUBMISSION_FILE_QUERY) { - $this->addAction(new DownloadFileLinkAction($request, $submissionFile, $submissionFileDao->getWorkflowStageId($submissionFile), __('common.download'))); + if ($workflowStageId || $submissionFile->getData('fileStage') != SUBMISSION_FILE_QUERY) { + $this->addAction(new DownloadFileLinkAction($request, $submissionFile, $workflowStageId, __('common.download'), $fileId, $filename)); } } } diff --git a/controllers/grid/eventLog/SubmissionEventLogGridHandler.inc.php b/controllers/grid/eventLog/SubmissionEventLogGridHandler.inc.php index adca01083e0..ff1573f871c 100644 --- a/controllers/grid/eventLog/SubmissionEventLogGridHandler.inc.php +++ b/controllers/grid/eventLog/SubmissionEventLogGridHandler.inc.php @@ -28,6 +28,9 @@ class SubmissionEventLogGridHandler extends GridHandler { /** @var Submission */ var $_submission; + /** @var int The current workflow stage */ + var $_stageId; + /** @var boolean Is the current user assigned as an author to this submission */ var $_isCurrentUserAssignedAuthor; @@ -105,6 +108,8 @@ function initialize($request, $args = null) { $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); $this->setSubmission($submission); + $this->_stageId = (int) $args['stageId']; + // Load submission-specific translations AppLocale::requireComponents( LOCALE_COMPONENT_APP_SUBMISSION, @@ -170,6 +175,7 @@ function getRequestArgs() { return array( 'submissionId' => $submission->getId(), + 'stageId' => $this->_stageId, ); } diff --git a/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.inc.php b/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.inc.php index 9a656a66611..aa2b298746b 100644 --- a/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.inc.php +++ b/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.inc.php @@ -49,7 +49,7 @@ function setSubmissionFile($submissionFile) { */ function authorize($request, &$args, $roleAssignments) { import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); - $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_READ)); + $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_READ, (int) $args['submissionFileId'])); return parent::authorize($request, $args, $roleAssignments); } @@ -78,11 +78,11 @@ function initialize($request, $args = null) { function getRequestArgs() { $submissionFile = $this->getSubmissionFile(); - return array( - 'submissionId' => $submissionFile->getSubmissionId(), - 'fileId' => $submissionFile->getFileId(), - 'revision' => $submissionFile->getRevision(), - ); + return [ + 'submissionId' => $submissionFile->getData('submissionId'), + 'submissionFileId' => $submissionFile->getId(), + 'stageId' => $this->_stageId, + ]; } /** @@ -91,23 +91,11 @@ function getRequestArgs() { protected function loadData($request, $filter = null) { $submissionFile = $this->getSubmissionFile(); $submissionFileEventLogDao = DAORegistry::getDAO('SubmissionFileEventLogDAO'); /* @var $submissionFileEventLogDao SubmissionFileEventLogDAO */ - $eventLogEntries = $submissionFileEventLogDao->getByFileId( - $submissionFile->getFileId() + $eventLogEntries = $submissionFileEventLogDao->getById( + $submissionFile->getId() ); $eventLogEntries = $eventLogEntries->toArray(); - if ($filter['allEvents']) { - // Also include events from past versions - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - while (true) { - $submissionFile = $submissionFileDao->getRevision($submissionFile->getSourceFileId(), $submissionFile->getSourceRevision()); - if (!$submissionFile) break; - - $iterator = $submissionFileEventLogDao->getByFileId($submissionFile->getFileId()); - $eventLogEntries += $iterator->toArray(); - } - } - return $eventLogEntries; } diff --git a/controllers/grid/files/FileDateGridColumn.inc.php b/controllers/grid/files/FileDateGridColumn.inc.php index 478a787ac66..ae4836eccbd 100644 --- a/controllers/grid/files/FileDateGridColumn.inc.php +++ b/controllers/grid/files/FileDateGridColumn.inc.php @@ -49,7 +49,7 @@ function getTemplateVarsFromRow($row) { $submissionFileData = $row->getData(); $submissionFile = $submissionFileData['submissionFile']; assert(is_a($submissionFile, 'SubmissionFile')); - $mtimestamp = strtotime($submissionFile->getDateModified()); + $mtimestamp = strtotime($submissionFile->getData('updatedAt')); $dateFormatLong = \Application::get()->getRequest()->getContext()->getLocalizedDateFormatLong(); $date = strftime($dateFormatLong, $mtimestamp); // File age diff --git a/controllers/grid/files/FileNameGridColumn.inc.php b/controllers/grid/files/FileNameGridColumn.inc.php index d277f52a772..2e0932cc1ab 100644 --- a/controllers/grid/files/FileNameGridColumn.inc.php +++ b/controllers/grid/files/FileNameGridColumn.inc.php @@ -58,9 +58,8 @@ function getTemplateVarsFromRow($row) { $submissionFileData = $row->getData(); $submissionFile = $submissionFileData['submissionFile']; assert(is_a($submissionFile, 'SubmissionFile')); - $id = $submissionFile->getFileId() . '-' . $submissionFile->getRevision(); - $fileExtension = strtolower($submissionFile->getExtension()); - return array('label' => '' . $id . ''); + $fileExtension = pathinfo(Services::get('file')->getPath($submissionFile->getData('fileId')), PATHINFO_EXTENSION); + return array('label' => '' . $submissionFile->getId() . ''); } diff --git a/controllers/grid/files/SelectableSubmissionFileListCategoryGridHandler.inc.php b/controllers/grid/files/SelectableSubmissionFileListCategoryGridHandler.inc.php index 0464dcc21d8..51a8b056f65 100644 --- a/controllers/grid/files/SelectableSubmissionFileListCategoryGridHandler.inc.php +++ b/controllers/grid/files/SelectableSubmissionFileListCategoryGridHandler.inc.php @@ -190,7 +190,11 @@ function initialize($request, $args = null) { if ($capabilities->canDownloadAll() && $this->hasGridDataElements($request)) { $submission = $this->getSubmission(); $stageId = $this->getStageId(); - $linkParams = array('submissionId' => $submission->getId(), 'stageId' => $stageId); + $linkParams = [ + 'nameLocaleKey' => $this->getTitle(), + 'submissionId' => $submission->getId(), + 'stageId' => $stageId, + ]; $files = $this->getFilesToDownload($request); $this->addAction($capabilities->getDownloadAllAction($request, $files, $linkParams), GRID_ACTION_POSITION_BELOW); diff --git a/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.inc.php b/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.inc.php index b0b5f319f5e..ac89d85c2cf 100644 --- a/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.inc.php +++ b/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.inc.php @@ -86,7 +86,6 @@ function loadData($filter = array()) { * @copydoc CategoryGridDataProvider::loadCategoryData() */ function loadCategoryData($request, $categoryDataElement, $filter = null, $reviewRound = null) { - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ $dataProvider = $this->getDataProvider(); $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); $stageId = $categoryDataElement; @@ -99,22 +98,30 @@ function loadCategoryData($request, $categoryDataElement, $filter = null, $revie $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ $reviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $stageId); } - $stageSubmissionFiles = $submissionFileDao->getLatestRevisionsByReviewRound($reviewRound, $fileStage); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$submission->getId()], + 'reviewRoundIds' => [$reviewRound->getId()], + 'fileStages' => (array) $fileStage, + ]); + $stageSubmissionFiles = iterator_to_array($submissionFilesIterator); } else { // Filter the passed workflow stage files. if (!$this->_submissionFiles) { - $this->_submissionFiles = $submissionFileDao->getLatestRevisions($submission->getId()); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$submission->getId()], + ]); + $this->_submissionFiles = iterator_to_array($submissionFilesIterator); } $submissionFiles = $this->_submissionFiles; $stageSubmissionFiles = array(); foreach ($submissionFiles as $key => $submissionFile) { - if (in_array($submissionFile->getFileStage(), (array) $fileStage)) { + if (in_array($submissionFile->getData('fileStage'), (array) $fileStage)) { $stageSubmissionFiles[$key] = $submissionFile; - } elseif ($submissionFile->getFileStage() == SUBMISSION_FILE_QUERY) { + } elseif ($submissionFile->getData('fileStage') == SUBMISSION_FILE_QUERY) { // Determine the stage from the query. - if ($submissionFile->getAssocType()!=ASSOC_TYPE_NOTE) break; + if ($submissionFile->getData('assocType')!=ASSOC_TYPE_NOTE) break; $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ - $note = $noteDao->getById($submissionFile->getAssocId()); + $note = $noteDao->getById($submissionFile->getData('assocId')); assert($note && $note->getAssocType()==ASSOC_TYPE_QUERY); $queryDao = DAORegistry::getDAO('QueryDAO'); /* @var $queryDao QueryDAO */ $query = $queryDao->getById($note->getAssocId()); diff --git a/controllers/grid/files/SubmissionFilesGridDataProvider.inc.php b/controllers/grid/files/SubmissionFilesGridDataProvider.inc.php index a2435820275..9efead7d302 100644 --- a/controllers/grid/files/SubmissionFilesGridDataProvider.inc.php +++ b/controllers/grid/files/SubmissionFilesGridDataProvider.inc.php @@ -85,11 +85,11 @@ function getFileStage() { * @copydoc GridDataProvider::loadData() */ function loadData($filter = array()) { - // Retrieve all submission files for the given file stage. - $submission = $this->getSubmission(); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getLatestRevisions($submission->getId(), $this->getFileStage(), null); - return $this->prepareSubmissionFileData($submissionFiles, $this->_viewableOnly, $filter); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$this->getSubmission()->getId()], + 'fileStages' => [$this->getFileStage()], + ]); + return $this->prepareSubmissionFileData(iterator_to_array($submissionFilesIterator), $this->_viewableOnly, $filter); } // @@ -134,7 +134,7 @@ protected function applyFilter($revisions, $filter) { if (!empty($filter['search'])) switch ($filter['column']) { case 'name': foreach ($revisions as $key => $submissionFile) { - if (!stristr($submissionFile->getName(AppLocale::getLocale()), $filter['search'])) { + if (!stristr($submissionFile->getData('name', AppLocale::getLocale()), $filter['search'])) { unset($revisions[$key]); } } @@ -160,10 +160,9 @@ function prepareSubmissionFileData($revisions, $viewableOnly = false, $filter = foreach ($revisions as $revision) { if ($viewableOnly && !$revision->getViewable()) continue; - $submissionFileData[$revision->getFileId()] = array( + $submissionFileData[$revision->getId()] = array( 'submissionFile' => $revision ); - unset($revision); } return $submissionFileData; } diff --git a/controllers/grid/files/SubmissionFilesGridHandler.inc.php b/controllers/grid/files/SubmissionFilesGridHandler.inc.php index 428b87c1462..1a213086e4a 100644 --- a/controllers/grid/files/SubmissionFilesGridHandler.inc.php +++ b/controllers/grid/files/SubmissionFilesGridHandler.inc.php @@ -142,7 +142,12 @@ function initialize($request, $args = null) { // Test whether an archive tool is available for the export to work, if so, add 'download all' grid action if ($capabilities->canDownloadAll() && $this->hasGridDataElements($request)) { $stageId = $this->getStageId(); - $linkParams = array('submissionId' => $submission->getId(), 'stageId' => $stageId); + $linkParams = [ + 'nameLocaleKey' => $this->getTitle(), + 'fileStage' => $this->getDataProvider()->getFileStage(), + 'submissionId' => $submission->getId(), + 'stageId' => $stageId, + ]; $files = $this->getFilesToDownload($request); $this->addAction($capabilities->getDownloadAllAction($request, $files, $linkParams), GRID_ACTION_POSITION_BELOW); diff --git a/controllers/grid/files/attachment/ReviewerReviewAttachmentGridDataProvider.inc.php b/controllers/grid/files/attachment/ReviewerReviewAttachmentGridDataProvider.inc.php index ed82b0656da..ba929a313bd 100644 --- a/controllers/grid/files/attachment/ReviewerReviewAttachmentGridDataProvider.inc.php +++ b/controllers/grid/files/attachment/ReviewerReviewAttachmentGridDataProvider.inc.php @@ -79,12 +79,12 @@ function getRequestArgs() { * @copydoc GridDataProvider::loadData() */ function loadData($filter = array()) { - // Get all review files assigned to this submission. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getLatestRevisionsByAssocId( - ASSOC_TYPE_REVIEW_ASSIGNMENT, $this->_getReviewId(), $this->getSubmission()->getId(), $this->getFileStage() - ); - return $this->prepareSubmissionFileData($submissionFiles, false, $filter); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$this->getSubmission()->getId()], + 'assocTypes' => [ASSOC_TYPE_REVIEW_ASSIGNMENT], + 'assocIds' => [$this->_getReviewId()] + ]); + return $this->prepareSubmissionFileData(iterator_to_array($submissionFilesIterator), false, $filter); } // diff --git a/controllers/grid/files/dependent/DependentFilesGridDataProvider.inc.php b/controllers/grid/files/dependent/DependentFilesGridDataProvider.inc.php index 5b443fcbc67..db4112568fe 100644 --- a/controllers/grid/files/dependent/DependentFilesGridDataProvider.inc.php +++ b/controllers/grid/files/dependent/DependentFilesGridDataProvider.inc.php @@ -41,9 +41,14 @@ function __construct($assocId) { function loadData($filter = array()) { // Retrieve all dependent files for the given file stage and original submission file id (i.e. the main galley/production file) $submission = $this->getSubmission(); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getLatestRevisionsByAssocId(ASSOC_TYPE_SUBMISSION_FILE, $this->getAssocId(), $submission->getId(), $this->getFileStage()); - return $this->prepareSubmissionFileData($submissionFiles, $this->_viewableOnly, $filter); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'assocTypes' => [ASSOC_TYPE_SUBMISSION_FILE], + 'assocIds' => [$this->getAssocId()], + 'submissionIds' => [$submission->getId()], + 'fileStages' => [$this->getFileStage()], + 'includeDependentFiles' => true, + ]); + return $this->prepareSubmissionFileData(iterator_to_array($submissionFilesIterator), $this->_viewableOnly, $filter); } /** diff --git a/controllers/grid/files/dependent/DependentFilesGridHandler.inc.php b/controllers/grid/files/dependent/DependentFilesGridHandler.inc.php index 38a67025cc2..7dd62999985 100644 --- a/controllers/grid/files/dependent/DependentFilesGridHandler.inc.php +++ b/controllers/grid/files/dependent/DependentFilesGridHandler.inc.php @@ -24,10 +24,10 @@ class DependentFilesGridHandler extends FileListGridHandler { function __construct() { // import app-specific grid data provider for access policies. $request = Application::get()->getRequest(); - $fileId = $request->getUserVar('fileId'); // authorized in authorize() method. + $submissionFileId = $request->getUserVar('submissionFileId'); // authorized in authorize() method. import('lib.pkp.controllers.grid.files.dependent.DependentFilesGridDataProvider'); parent::__construct( - new DependentFilesGridDataProvider($fileId), + new DependentFilesGridDataProvider($submissionFileId), $request->getUserVar('stageId'), FILE_GRID_ADD|FILE_GRID_DELETE|FILE_GRID_VIEW_NOTES|FILE_GRID_EDIT ); @@ -45,7 +45,7 @@ function __construct() { */ function authorize($request, &$args, $roleAssignments) { import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); - $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_MODIFY)); + $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_MODIFY, (int) $args['submissionFileId'])); return parent::authorize($request, $args, $roleAssignments); } @@ -57,7 +57,7 @@ function getRequestArgs() { $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); return array_merge( parent::getRequestArgs(), - array('fileId' => $submissionFile->getFileId()) + array('submissionFileId' => $submissionFile->getId()) ); } } diff --git a/controllers/grid/files/fileList/linkAction/DownloadAllLinkAction.inc.php b/controllers/grid/files/fileList/linkAction/DownloadAllLinkAction.inc.php index f0efef926b3..0b97b71865c 100644 --- a/controllers/grid/files/fileList/linkAction/DownloadAllLinkAction.inc.php +++ b/controllers/grid/files/fileList/linkAction/DownloadAllLinkAction.inc.php @@ -26,11 +26,9 @@ class DownloadAllLinkAction extends LinkAction { * @param $actionArgs array * @param $files array Files to be downloaded. */ - function __construct($request, $actionArgs, $files) { + function __construct($request, $actionArgs) { // Instantiate the redirect action request. $router = $request->getRouter(); - $filesIdsAndRevisions = $this->_getFilesIdsAndRevisions($files); - $actionArgs['filesIdsAndRevisions'] = $filesIdsAndRevisions; import('lib.pkp.classes.linkAction.request.PostAndRedirectAction'); $redirectRequest = new PostAndRedirectAction($router->url($request, null, 'api.file.FileApiHandler', 'recordDownload', null, $actionArgs), $router->url($request, null, 'api.file.FileApiHandler', 'downloadAllFiles', null, $actionArgs)); @@ -38,28 +36,6 @@ function __construct($request, $actionArgs, $files) { // Configure the link action. parent::__construct('downloadAll', $redirectRequest, __('submission.files.downloadAll'), 'getPackage'); } - - - // - // Private helper methods. - // - /** - * Return an string with all files ids and revisions. - * @param $files array The files that will be downloaded. - * @return string - */ - function _getFilesIdsAndRevisions($files) { - $filesIdsAndRevisions = null; - foreach ($files as $fileData) { - $file =& $fileData['submissionFile']; - $fileId = $file->getFileId(); - $revision = $file->getRevision(); - $filesIdsAndRevisions .= $fileId . '-' . $revision . ';'; - unset($file); - } - - return $filesIdsAndRevisions; - } } diff --git a/controllers/grid/files/form/ManageSubmissionFilesForm.inc.php b/controllers/grid/files/form/ManageSubmissionFilesForm.inc.php index e2e9e3cce26..ce6d9b8429a 100644 --- a/controllers/grid/files/form/ManageSubmissionFilesForm.inc.php +++ b/controllers/grid/files/form/ManageSubmissionFilesForm.inc.php @@ -70,29 +70,32 @@ function readInputData() { * @param $fileStage int SUBMISSION_FILE_... */ function execute($stageSubmissionFiles, $fileStage = null) { + $request = Application::get()->getRequest(); $selectedFiles = (array)$this->getData('selectedFiles'); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getLatestRevisions($this->getSubmissionId()); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$this->getSubmissionId()], + ]); - foreach ($submissionFiles as $submissionFile) { + foreach ($submissionFilesIterator as $submissionFile) { // Get the viewable flag value. $isViewable = in_array( - $submissionFile->getFileId(), + $submissionFile->getId(), $selectedFiles ); // If this is a submission file that's already in this listing... if ($this->fileExistsInStage($submissionFile, $stageSubmissionFiles, $fileStage)) { // ...update the "viewable" flag accordingly. - if ($isViewable != $submissionFile->getViewable()) { - $submissionFile->setViewable($isViewable); - $submissionFileDao->updateObject($submissionFile); + if ($isViewable != $submissionFile->getData('viewable')) { + $submissionFile = Services::get('submissionFile')->edit( + $submissionFile, + ['viewable' => $isViewable], + $request + ); } } elseif ($isViewable) { // Import a file from a different workflow area - $request = Application::get()->getRequest(); - $context = $request->getContext(); - $submissionFile = $this->importFile($context, $submissionFile, $fileStage); + $submissionFile = $this->importFile($submissionFile, $fileStage); } } } @@ -104,8 +107,8 @@ function execute($stageSubmissionFiles, $fileStage = null) { * @param $fileStage int FILE_STAGE_... */ protected function fileExistsInStage($submissionFile, $stageSubmissionFiles, $fileStage) { - if (!isset($stageSubmissionFiles[$submissionFile->getFileId()])) return false; - foreach ($stageSubmissionFiles[$submissionFile->getFileId()] as $stageFile) { + if (!isset($stageSubmissionFiles[$submissionFile->getId()])) return false; + foreach ($stageSubmissionFiles[$submissionFile->getId()] as $stageFile) { if ($stageFile->getFileStage() == $submissionFile->getFileStage() && $stageFile->getFileStage() == $fileStage) return true; } return false; @@ -113,20 +116,16 @@ protected function fileExistsInStage($submissionFile, $stageSubmissionFiles, $fi /** * Make a copy of the file to the specified file stage. - * @param $context Context * @param $submissionFile SubmissionFile * @param $fileStage int SUBMISSION_FILE_... * @return SubmissionFile Resultant new submission file */ - protected function importFile($context, $submissionFile, $fileStage) { - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - import('lib.pkp.classes.file.SubmissionFileManager'); - $submissionFileManager = new SubmissionFileManager($context->getId(), $submissionFile->getSubmissionId()); - // Split the file into file id and file revision. - $fileId = $submissionFile->getFileId(); - $revision = $submissionFile->getRevision(); - list($newFileId, $newRevision) = $submissionFileManager->copyFileToFileStage($fileId, $revision, $fileStage, null, true); - return $submissionFileDao->getRevision($newFileId, $newRevision); + protected function importFile($submissionFile, $fileStage) { + $newSubmissionFile = clone $submissionFile; + $newSubmissionFile->setData('fileStage', $fileStage); + $newSubmissionFile->setData('sourceSubmissionFileId', $submissionFile->getId()); + $newSubmissionFile = Services::get('submissionFile')->add($newSubmissionFile, Application::get()->getRequest()); + return $newSubmissionFile; } } diff --git a/controllers/grid/files/productionReady/ProductionReadyFilesGridHandler.inc.php b/controllers/grid/files/productionReady/ProductionReadyFilesGridHandler.inc.php index 65e86cb99b6..b109436f0a7 100644 --- a/controllers/grid/files/productionReady/ProductionReadyFilesGridHandler.inc.php +++ b/controllers/grid/files/productionReady/ProductionReadyFilesGridHandler.inc.php @@ -40,13 +40,7 @@ function __construct() { 'deleteFile', ) ); - } - /** - * @copydoc FileListGridHandler::initialize() - */ - function initialize($request, $args = null) { - parent::initialize($request, $args); $this->setTitle('editor.submission.production.productionReadyFiles'); } } diff --git a/controllers/grid/files/proof/form/ManageProofFilesForm.inc.php b/controllers/grid/files/proof/form/ManageProofFilesForm.inc.php index d304948ab88..b9ea2223caf 100644 --- a/controllers/grid/files/proof/form/ManageProofFilesForm.inc.php +++ b/controllers/grid/files/proof/form/ManageProofFilesForm.inc.php @@ -57,20 +57,13 @@ protected function fileExistsInStage($submissionFile, $stageSubmissionFiles, $fi /** * @copydoc ManageSubmissionFilesForm::importFile() */ - protected function importFile($context, $submissionFile, $fileStage) { - $newSubmissionFile = parent::importFile($context, $submissionFile, $fileStage); + protected function importFile($submissionFile, $fileStage) { + $newSubmissionFile = clone $submissionFile; + $newSubmissionFile->setData('assocType', ASSOC_TYPE_REPRESENTATION); + $newSubmissionFile->setData('assocId', $this->_representationId); + $newSubmissionFile->setData('viewable', false); // Not approved by default - $representationDao = Application::getRepresentationDAO(); - $representation = $representationDao->getById($this->_representationId); - - $newSubmissionFile->setAssocType(ASSOC_TYPE_REPRESENTATION); - $newSubmissionFile->setAssocId($representation->getId()); - $newSubmissionFile->setFileStage(SUBMISSION_FILE_PROOF); - $newSubmissionFile->setViewable(false); // Not approved by default - - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->updateObject($newSubmissionFile); - return $newSubmissionFile; + return parent::importFile($newSubmissionFile, SUBMISSION_FILE_PROOF); } } diff --git a/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.inc.php b/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.inc.php index 49b1d2a06cc..529224b1179 100644 --- a/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.inc.php +++ b/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.inc.php @@ -65,7 +65,7 @@ function isDataElementInCategorySelected($categoryDataId, &$gridDataElement) { // Passed the checks above. If it's part of the current query, mark selected. $query = $this->getAuthorizedContextObject(ASSOC_TYPE_QUERY); $headNote = $query->getHeadNote(); - return ($submissionFile->getAssocType() == ASSOC_TYPE_NOTE && $submissionFile->getAssocId() == $headNote->getId()); + return ($submissionFile->getData('assocType') == ASSOC_TYPE_NOTE && $submissionFile->getData('assocId') == $headNote->getId()); } // diff --git a/controllers/grid/files/query/QueryNoteFilesGridDataProvider.inc.php b/controllers/grid/files/query/QueryNoteFilesGridDataProvider.inc.php index 098cf1612b9..c35beaadaf5 100644 --- a/controllers/grid/files/query/QueryNoteFilesGridDataProvider.inc.php +++ b/controllers/grid/files/query/QueryNoteFilesGridDataProvider.inc.php @@ -66,12 +66,17 @@ function loadData($filter = array()) { $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ $note = $noteDao->getById($this->_noteId); if ($note->getAssocType() != ASSOC_TYPE_QUERY || $note->getAssocId() != $query->getId()) { - fatalError('Invalid note ID specified!'); + throw new Exception('Invalid note ID specified!'); } - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getLatestRevisionsByAssocId(ASSOC_TYPE_NOTE, $this->_noteId, $submission->getId(), $this->getFileStage()); - return $this->prepareSubmissionFileData($submissionFiles, $this->_viewableOnly, $filter); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'assocTypes' => [ASSOC_TYPE_NOTE], + 'assocIds' => [$this->_noteId], + 'submissionIds' => [$submission->getId()], + 'fileStages' => [(int) $this->getFileStage()], + ]); + + return $this->prepareSubmissionFileData(iterator_to_array($submissionFilesIterator), $this->_viewableOnly, $filter); } /** @@ -100,9 +105,17 @@ function getAddFileAction($request) { $query = $this->getAuthorizedContextObject(ASSOC_TYPE_QUERY); import('lib.pkp.controllers.api.file.linkAction.AddFileLinkAction'); return new AddFileLinkAction( - $request, $submission->getId(), $this->getStageId(), - $this->getUploaderRoles(), $this->getFileStage(), - ASSOC_TYPE_NOTE, $this->_noteId + $request, + $submission->getId(), + $this->getStageId(), + $this->getUploaderRoles(), + $this->getFileStage(), + ASSOC_TYPE_NOTE, + $this->_noteId, + null, + null, + null, + $query->getId() ); } } diff --git a/controllers/grid/files/query/form/ManageQueryNoteFilesForm.inc.php b/controllers/grid/files/query/form/ManageQueryNoteFilesForm.inc.php index 90397eb3599..ae3d549e1fe 100644 --- a/controllers/grid/files/query/form/ManageQueryNoteFilesForm.inc.php +++ b/controllers/grid/files/query/form/ManageQueryNoteFilesForm.inc.php @@ -62,15 +62,15 @@ function execute($stageSubmissionFiles, $fileStage = null) { } /** - * @copydoc ManageSubmissionFilesForm::fileExistsInStage + * @copydoc ManageSubmissionFilesForm::fileExistsInStage */ protected function fileExistsInStage($submissionFile, $stageSubmissionFiles, $fileStage) { if (!parent::fileExistsInStage($submissionFile, $stageSubmissionFiles, $fileStage)) return false; - foreach ($stageSubmissionFiles[$submissionFile->getFileId()] as $stageFile) { + foreach ($stageSubmissionFiles[$submissionFile->getId()] as $stageFile) { if ( $stageFile->getFileStage() == $submissionFile->getFileStage() && $stageFile->getFileStage() == $fileStage && - ($stageFile->getAssocType() != ASSOC_TYPE_NOTE || $stageFile->getAssocId() == $this->_noteId) + ($stageFile->getData('assocType') != ASSOC_TYPE_NOTE || $stageFile->getData('assocId') == $this->_noteId) ) return true; } return false; @@ -79,13 +79,12 @@ protected function fileExistsInStage($submissionFile, $stageSubmissionFiles, $fi /** * @copydoc ManageSubmissionFilesForm::importFile() */ - protected function importFile($context, $submissionFile, $fileStage) { - $submissionFile = parent::importFile($context, $submissionFile, $fileStage); - $submissionFile->setAssocType(ASSOC_TYPE_NOTE); - $submissionFile->setAssocId($this->_noteId); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->updateObject($submissionFile); - return $submissionFile; + protected function importFile($submissionFile, $fileStage) { + $newSubmissionFile = clone $submissionFile; + $newSubmissionFile->setData('assocType', ASSOC_TYPE_NOTE); + $newSubmissionFile->setData('assocId', $this->_noteId); + + return parent::importFile($newSubmissionFile, $fileStage); } } diff --git a/controllers/grid/files/review/LimitReviewFilesGridHandler.inc.php b/controllers/grid/files/review/LimitReviewFilesGridHandler.inc.php index ba4cbca61da..b58fb9e2d1c 100644 --- a/controllers/grid/files/review/LimitReviewFilesGridHandler.inc.php +++ b/controllers/grid/files/review/LimitReviewFilesGridHandler.inc.php @@ -69,7 +69,7 @@ function isDataElementSelected($gridDataElement) { // A review assignment was specified in the request; preset the // checkboxes to the currently available set of files. $reviewFilesDao = DAORegistry::getDAO('ReviewFilesDAO'); /* @var $reviewFilesDao ReviewFilesDAO */ - return $reviewFilesDao->check($reviewAssignment->getId(), $submissionFile->getFileId()); + return $reviewFilesDao->check($reviewAssignment->getId(), $submissionFile->getId()); } else { // No review assignment specified; default to all files available. return true; diff --git a/controllers/grid/files/review/ReviewGridDataProvider.inc.php b/controllers/grid/files/review/ReviewGridDataProvider.inc.php index 2750de2f4fd..af835cbd44f 100644 --- a/controllers/grid/files/review/ReviewGridDataProvider.inc.php +++ b/controllers/grid/files/review/ReviewGridDataProvider.inc.php @@ -62,10 +62,15 @@ function getRequestArgs() { */ function loadData($filter = array()) { // Get all review files assigned to this submission. - $reviewRound = $this->getReviewRound(); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getLatestRevisionsByReviewRound($reviewRound, $this->_showAll?null:$this->getFileStage()); - return $this->prepareSubmissionFileData($submissionFiles, $this->_viewableOnly, $filter); + $params = [ + 'submissionId' => [$this->getSubmission()->getId()], + 'reviewRoundIds' => [$this->getReviewRound()->getId()], + ]; + if (!$this->_showAll) { + $params['fileStages'] = [(int) $this->getFileStage()]; + } + $submissionFilesIterator = Services::get('submissionFile')->getMany($params); + return $this->prepareSubmissionFileData(iterator_to_array($submissionFilesIterator), $this->_viewableOnly, $filter); } // diff --git a/controllers/grid/files/review/ReviewRevisionsGridDataProvider.inc.php b/controllers/grid/files/review/ReviewRevisionsGridDataProvider.inc.php index 753e0d6e136..07bcede1ec8 100644 --- a/controllers/grid/files/review/ReviewRevisionsGridDataProvider.inc.php +++ b/controllers/grid/files/review/ReviewRevisionsGridDataProvider.inc.php @@ -35,10 +35,12 @@ function __construct() { function loadData($filter = array()) { // Grab the files that are new (incoming) revisions // of those currently assigned to the review round. - $reviewRound = $this->getReviewRound(); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getLatestRevisionsByReviewRound($reviewRound, $this->getFileStage()); - return $this->prepareSubmissionFileData($submissionFiles, false, $filter); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$this->getSubmission()->getId()], + 'fileStages' => $this->getFileStage(), + 'reviewRoundIds' => [$this->getReviewRound()->getId()], + ]); + return $this->prepareSubmissionFileData(iterator_to_array($submissionFilesIterator), false, $filter); } diff --git a/controllers/grid/files/review/ReviewerReviewFilesGridDataProvider.inc.php b/controllers/grid/files/review/ReviewerReviewFilesGridDataProvider.inc.php index 4fb098bb4c1..0f8847626da 100644 --- a/controllers/grid/files/review/ReviewerReviewFilesGridDataProvider.inc.php +++ b/controllers/grid/files/review/ReviewerReviewFilesGridDataProvider.inc.php @@ -62,10 +62,10 @@ function loadData($filter = array()) { $submissionFileData = parent::loadData(); $reviewFilesDao = DAORegistry::getDAO('ReviewFilesDAO'); /* @var $reviewFilesDao ReviewFilesDAO */ $reviewAssignment = $this->getAuthorizedContextObject(ASSOC_TYPE_REVIEW_ASSIGNMENT); - foreach ($submissionFileData as $fileId => $fileData) { - if (!$reviewFilesDao->check($reviewAssignment->getId(), $fileId)) { + foreach ($submissionFileData as $submissionFileId => $fileData) { + if (!$reviewFilesDao->check($reviewAssignment->getId(), $submissionFileId)) { // Not permitted; remove from list. - unset($submissionFileData[$fileId]); + unset($submissionFileData[$submissionFileId]); } } return $submissionFileData; diff --git a/controllers/grid/files/review/form/ManageReviewFilesForm.inc.php b/controllers/grid/files/review/form/ManageReviewFilesForm.inc.php index cbf24942de4..e5032778326 100644 --- a/controllers/grid/files/review/form/ManageReviewFilesForm.inc.php +++ b/controllers/grid/files/review/form/ManageReviewFilesForm.inc.php @@ -91,11 +91,12 @@ function execute($stageSubmissionFiles, $fileStage = null) { /** * @copydoc ManageSubmissionFilesForm::importFile() */ - protected function importFile($context, $submissionFile, $fileStage) { - $newSubmissionFile = parent::importFile($context, $submissionFile, $fileStage); - + protected function importFile($submissionFile, $fileStage) { + $newSubmissionFile = parent::importFile($submissionFile, $fileStage); $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->assignRevisionToReviewRound($newSubmissionFile->getFileId(), $newSubmissionFile->getRevision(), $this->getReviewRound()); + $submissionFileDao->assignRevisionToReviewRound($newSubmissionFile->getId(), $this->getReviewRound()); + + return $newSubmissionFile; } } diff --git a/controllers/grid/notifications/NotificationsGridCellProvider.inc.php b/controllers/grid/notifications/NotificationsGridCellProvider.inc.php index bd3676933be..8599762cdc5 100644 --- a/controllers/grid/notifications/NotificationsGridCellProvider.inc.php +++ b/controllers/grid/notifications/NotificationsGridCellProvider.inc.php @@ -146,14 +146,12 @@ function _getTitle($notification) { if (!isset($submissionId) && isset($fileId)) { assert(is_numeric($fileId)); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFile = $submissionFileDao->getLatestRevision($fileId); + $submissionFile = Services::get('submissionFile')->get($fileId); assert(is_a($submissionFile, 'SubmissionFile')); - $submissionId = $submissionFile->getSubmissionId(); + $submissionId = $submissionFile->getData('submissionId'); } assert(is_numeric($submissionId)); - $submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */ - $submission = $submissionDao->getById($submissionId); + $submission = Services::get('submission')->get($submissionId); assert(is_a($submission, 'Submission')); return $submission->getLocalizedTitle(); diff --git a/controllers/grid/queries/QueryNotesGridCellProvider.inc.php b/controllers/grid/queries/QueryNotesGridCellProvider.inc.php index 721ff890550..25e5cd639cb 100644 --- a/controllers/grid/queries/QueryNotesGridCellProvider.inc.php +++ b/controllers/grid/queries/QueryNotesGridCellProvider.inc.php @@ -59,14 +59,14 @@ function getTemplateVarsFromRowColumn($row, $column) { function getCellActions($request, $row, $column, $position = GRID_ACTION_POSITION_DEFAULT) { switch ($column->getId()) { case 'contents': - $element = $row->getData(); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - import('lib.pkp.classes.submission.SubmissionFile'); - $submissionFiles = $submissionFileDao->getLatestRevisionsByAssocId( - ASSOC_TYPE_NOTE, $element->getId(), - $this->_submission->getId(), - SUBMISSION_FILE_QUERY - ); + import('lib.pkp.classes.submission.SubmissionFile'); // SUBMISSION_FILE_ + $submissionFiles = Services::get('submissionFile')->getMany([ + 'assocTypes' => [ASSOC_TYPE_NOTE], + 'assocIds' => [$row->getData()->getId()], + 'submissionIds' => [$this->_submission->getId()], + 'fileStages' => [SUBMISSION_FILE_QUERY], + ]); + import('lib.pkp.controllers.api.file.linkAction.DownloadFileLinkAction'); $actions = array(); foreach ($submissionFiles as $submissionFile) { diff --git a/controllers/grid/users/reviewer/form/EditReviewForm.inc.php b/controllers/grid/users/reviewer/form/EditReviewForm.inc.php index c320925e6ef..e910543d2c3 100644 --- a/controllers/grid/users/reviewer/form/EditReviewForm.inc.php +++ b/controllers/grid/users/reviewer/form/EditReviewForm.inc.php @@ -111,18 +111,22 @@ function execute(...$functionArgs) { $request = Application::get()->getRequest(); $context = $request->getContext(); - // Get the list of available files for this review. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - import('lib.pkp.classes.submission.SubmissionFile'); // File constants - $submissionFiles = $submissionFileDao->getLatestRevisionsByReviewRound($this->_reviewRound, SUBMISSION_FILE_REVIEW_FILE); - $selectedFiles = (array) $this->getData('selectedFiles'); - // Revoke all, then grant selected. $reviewFilesDao = DAORegistry::getDAO('ReviewFilesDAO'); /* @var $reviewFilesDao ReviewFilesDAO */ $reviewFilesDao->revokeByReviewId($this->_reviewAssignment->getId()); - foreach ($submissionFiles as $submissionFile) { - if (in_array($submissionFile->getFileId(), $selectedFiles)) { - $reviewFilesDao->grant($this->_reviewAssignment->getId(), $submissionFile->getFileId()); + + import('lib.pkp.classes.submission.SubmissionFile'); // SUBMISSION_FILE_... constants + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$this->_reviewAssignment->getSubmissionId()], + 'reviewRoundIds' => [$this->_reviewRound->getId()], + 'fileStages' => [SUBMISSION_FILE_REVIEW_FILE], + ]); + $selectedFiles = array_map(function($id) { + return (int) $id; + }, (array) $this->getData('selectedFiles')); + foreach ($submissionFilesIterator as $submissionFile) { + if (in_array($submissionFile->getId(), $selectedFiles)) { + $reviewFilesDao->grant($this->_reviewAssignment->getId(), $submissionFile->getId()); } } diff --git a/controllers/grid/users/reviewer/form/ReviewerForm.inc.php b/controllers/grid/users/reviewer/form/ReviewerForm.inc.php index 7af330f680d..78b1dd9d402 100644 --- a/controllers/grid/users/reviewer/form/ReviewerForm.inc.php +++ b/controllers/grid/users/reviewer/form/ReviewerForm.inc.php @@ -359,14 +359,19 @@ function execute(...$functionParams) { $reviewAssignmentDao->updateObject($reviewAssignment); // Grant access for this review to all selected files. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - import('lib.pkp.classes.submission.SubmissionFile'); // File constants - $submissionFiles = $submissionFileDao->getLatestRevisionsByReviewRound($currentReviewRound, SUBMISSION_FILE_REVIEW_FILE); - $selectedFiles = (array) $this->getData('selectedFiles'); + import('lib.pkp.classes.submission.SubmissionFile'); // SUBMISSION_FILE_ + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$submission->getId()], + 'reviewRoundIds' => [$currentReviewRound->getId()], + 'fileStages' => [SUBMISSION_FILE_REVIEW_FILE], + ]); + $selectedFiles = array_map(function($id) { + return (int) $id; + }, (array) $this->getData('selectedFiles')); $reviewFilesDao = DAORegistry::getDAO('ReviewFilesDAO'); /* @var $reviewFilesDao ReviewFilesDAO */ - foreach ($submissionFiles as $submissionFile) { - if (in_array($submissionFile->getFileId(), $selectedFiles)) { - $reviewFilesDao->grant($reviewAssignment->getId(), $submissionFile->getFileId()); + foreach ($submissionFilesIterator as $submissionFile) { + if (in_array($submissionFile->getId(), $selectedFiles)) { + $reviewFilesDao->grant($reviewAssignment->getId(), $submissionFile->getId()); } } diff --git a/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.inc.php b/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.inc.php index 8eeb9ee030d..a251c82ec81 100644 --- a/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.inc.php +++ b/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.inc.php @@ -42,9 +42,8 @@ function __construct($itemId, $itemType, $stageId, $template = null) { if($itemType == ASSOC_TYPE_SUBMISSION) { $this->_submissionId = $itemId; } else { - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFile = $submissionFileDao->getLatestRevision($itemId); - $this->_submissionId = $submissionFile->getSubmissionId(); + $submissionFile = Services::get('submissionFile')->get($itemId); + $this->_submissionId = $submissionFile->getData('submissionId'); } // Some other forms (e.g. the Add Participant form) subclass this form and diff --git a/controllers/informationCenter/FileInformationCenterHandler.inc.php b/controllers/informationCenter/FileInformationCenterHandler.inc.php index 9dd3fc52df6..378c9457995 100644 --- a/controllers/informationCenter/FileInformationCenterHandler.inc.php +++ b/controllers/informationCenter/FileInformationCenterHandler.inc.php @@ -57,12 +57,12 @@ function initialize($request) { parent::initialize($request); $this->_stageId = $this->getAuthorizedContextObject(ASSOC_TYPE_WORKFLOW_STAGE); - - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $this->submissionFile = $submissionFileDao->getLatestRevision($request->getUserVar('fileId')); + $this->submissionFile = Services::get('submissionFile')->get($request->getUserVar('submissionFileId')); // Ensure data integrity. - if (!$this->_submission || !$this->submissionFile || $this->_submission->getId() != $this->submissionFile->getSubmissionId()) fatalError('Unknown or invalid submission or submission file!'); + if (!$this->_submission || !$this->submissionFile || $this->_submission->getId() != $this->submissionFile->getData('submissionId')) { + throw new Exception('Unknown or invalid submission or submission file!'); + }; } /** @@ -75,9 +75,6 @@ function viewInformationCenter($args, $request) { // Assign variables to the template manager and display $templateMgr = TemplateManager::getManager($request); - $fileName = (($s = $this->submissionFile->getLocalizedName()) != '') ? $s : __('common.untitled'); - if (($i = $this->submissionFile->getRevision()) > 1) $fileName .= " ($i)"; // Add revision number to label - if (empty($fileName)) $fileName = __('common.untitled'); $templateMgr->assign('removeHistoryTab', (int) $request->getUserVar('removeHistoryTab')); return parent::viewInformationCenter($args, $request); @@ -93,7 +90,7 @@ function viewNotes($args, $request) { $this->setupTemplate($request); import('lib.pkp.controllers.informationCenter.form.NewFileNoteForm'); - $notesForm = new NewFileNoteForm($this->submissionFile->getFileId()); + $notesForm = new NewFileNoteForm($this->submissionFile->getId()); $notesForm->initData(); $templateMgr = TemplateManager::getManager($request); @@ -116,22 +113,13 @@ function _listPastNotes($args, $request) { $noteDao = DAORegistry::getDAO('NoteDAO'); /* @var $noteDao NoteDAO */ $submissionFile = $this->submissionFile; - $notes = array(); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - while (true) { - $submissionFile = $submissionFileDao->getRevision($submissionFile->getSourceFileId(), $submissionFile->getSourceRevision()); - if (!$submissionFile) break; - - $iterator = $noteDao->getByAssoc($this->_getAssocType(), $submissionFile->getFileId()); - $notes += $iterator->toArray(); - } + $notes = $noteDao->getByAssoc($this->_getAssocType(), $submissionFile->getData('sourceSubmissionFileId'))->toArray(); import('lib.pkp.classes.core.ArrayItemIterator'); $templateMgr->assign('notes', new ArrayItemIterator($notes)); $user = $request->getUser(); $templateMgr->assign(array( 'currentUserId' => $user->getId(), - 'notesListId' => 'pastNotesList', 'notesDeletable' => false, )); @@ -148,7 +136,7 @@ function saveNote($args, $request) { $this->setupTemplate($request); import('lib.pkp.controllers.informationCenter.form.NewFileNoteForm'); - $notesForm = new NewFileNoteForm($this->submissionFile->getFileId()); + $notesForm = new NewFileNoteForm($this->submissionFile->getId()); $notesForm->readInputData(); if ($notesForm->validate()) { @@ -199,7 +187,7 @@ function _getLinkParams() { return array_merge( parent::_getLinkParams(), array( - 'fileId' => $this->submissionFile->getFileId(), + 'submissionFileId' => $this->submissionFile->getId(), 'stageId' => $this->_stageId, ) ); @@ -210,7 +198,7 @@ function _getLinkParams() { * @return int */ function _getAssocId() { - return $this->submissionFile->getFileId(); + return $this->submissionFile->getId(); } /** @@ -230,7 +218,7 @@ function setupTemplate($request) { // Get the latest history item to display in the header $submissionEventLogDao = DAORegistry::getDAO('SubmissionFileEventLogDAO'); /* @var $submissionEventLogDao SubmissionFileEventLogDAO */ - $fileEvents = $submissionEventLogDao->getByFileId($this->submissionFile->getFileId()); + $fileEvents = $submissionEventLogDao->getById($this->submissionFile->getId()); $lastEvent = $fileEvents->next(); if(isset($lastEvent)) { $templateMgr->assign('lastEvent', $lastEvent); diff --git a/controllers/informationCenter/linkAction/FileInfoCenterLinkAction.inc.php b/controllers/informationCenter/linkAction/FileInfoCenterLinkAction.inc.php index f242fa2a8c2..6f1d9f4e271 100644 --- a/controllers/informationCenter/linkAction/FileInfoCenterLinkAction.inc.php +++ b/controllers/informationCenter/linkAction/FileInfoCenterLinkAction.inc.php @@ -45,7 +45,7 @@ function getModal($request, $submissionFile, $stageId) { import('lib.pkp.classes.linkAction.request.AjaxModal'); $router = $request->getRouter(); - $title = (isset($submissionFile)) ? implode(': ', array(__('informationCenter.informationCenter'), htmlspecialchars($submissionFile->getLocalizedName()))) : __('informationCenter.informationCenter'); + $title = (isset($submissionFile)) ? implode(': ', array(__('informationCenter.informationCenter'), htmlspecialchars($submissionFile->getLocalizedData('name')))) : __('informationCenter.informationCenter'); $ajaxModal = new AjaxModal( $router->url( diff --git a/controllers/modals/editorDecision/form/EditorDecisionWithEmailForm.inc.php b/controllers/modals/editorDecision/form/EditorDecisionWithEmailForm.inc.php index 87ed8f35f94..753e5d0ad64 100644 --- a/controllers/modals/editorDecision/form/EditorDecisionWithEmailForm.inc.php +++ b/controllers/modals/editorDecision/form/EditorDecisionWithEmailForm.inc.php @@ -230,16 +230,16 @@ function _sendReviewMailToAuthor($submission, $emailKey, $request) { $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ $selectedAttachments = $this->getData('selectedAttachments'); if(is_array($selectedAttachments)) { - foreach ($selectedAttachments as $fileId) { + foreach ($selectedAttachments as $submissionFileId) { // Retrieve the submission file. - $submissionFile = $submissionFileDao->getLatestRevision($fileId); + $submissionFile = Services::get('submissionFile')->get($submissionFileId); assert(is_a($submissionFile, 'SubmissionFile')); // Check the association information. - if($submissionFile->getAssocType() == ASSOC_TYPE_REVIEW_ASSIGNMENT) { + if($submissionFile->getData('assocType') == ASSOC_TYPE_REVIEW_ASSIGNMENT) { // The review attachment has been uploaded by a reviewer. - $reviewAssignmentId = $submissionFile->getAssocId(); + $reviewAssignmentId = $submissionFile->getData('assocId'); assert(is_numeric($reviewAssignmentId)); } else { // The review attachment has been uploaded by the editor. @@ -252,9 +252,10 @@ function _sendReviewMailToAuthor($submission, $emailKey, $request) { assert(!is_null($reviewIndex)); // Add the attachment to the email. + $path = rtrim(Config::getVar('files', 'files_dir'), '/') . '/' . Services::get('file')->getPath($submissionFile->getData('fileId')); $email->addAttachment( - $submissionFile->getFilePath(), - PKPString::enumerateAlphabetically($reviewIndex).'-'.$submissionFile->getOriginalFileName() + $path, + PKPString::enumerateAlphabetically($reviewIndex).'-'.$submissionFile->getLocalizedData('name') ); // Update submission file to set viewable as true, so author diff --git a/controllers/modals/editorDecision/form/PromoteForm.inc.php b/controllers/modals/editorDecision/form/PromoteForm.inc.php index 39d1757e187..1a509749b17 100644 --- a/controllers/modals/editorDecision/form/PromoteForm.inc.php +++ b/controllers/modals/editorDecision/form/PromoteForm.inc.php @@ -92,9 +92,10 @@ function execute(...$functionParams) { $editorAction = new EditorAction(); $editorAction->recordDecision($request, $submission, $decision, $actionLabels, $reviewRound); + // Bring in the SUBMISSION_FILE_* constants. + import('lib.pkp.classes.submission.SubmissionFile'); + // Identify email key and status of round. - import('lib.pkp.classes.file.SubmissionFileManager'); - $submissionFileManager = new SubmissionFileManager($submission->getContextId(), $submission->getId()); switch ($decision) { case SUBMISSION_EDITOR_DECISION_ACCEPT: $emailKey = 'EDITOR_DECISION_ACCEPT'; @@ -105,18 +106,17 @@ function execute(...$functionParams) { // Move to the editing stage. $editorAction->incrementWorkflowStage($submission, WORKFLOW_STAGE_ID_EDITING, $request); - // Bring in the SUBMISSION_FILE_* constants. - import('lib.pkp.classes.submission.SubmissionFile'); - // Bring in the Manager (we need it). - import('lib.pkp.classes.file.SubmissionFileManager'); - - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ $selectedFiles = $this->getData('selectedFiles'); if(is_array($selectedFiles)) { - foreach ($selectedFiles as $fileId) { - $revisionNumber = $submissionFileDao->getLatestRevisionNumber($fileId); - $submissionFileManager->copyFileToFileStage($fileId, $revisionNumber, SUBMISSION_FILE_FINAL, null, true); + foreach ($selectedFiles as $submissionFileId) { + $submissionFile = Services::get('submissionFile')->get($submissionFileId); + $newSubmissionFile = clone $submissionFile; + $newSubmissionFile->setData('fileStage', SUBMISSION_FILE_FINAL); + $newSubmissionFile->setData('sourceSubmissionFileId', $submissionFile->getId()); + $newSubmissionFile->setData('assocType', null); + $newSubmissionFile->setData('assocId', null); + $newSubmissionFile = Services::get('submissionFile')->add($newSubmissionFile, Application::get()->getRequest()); } } @@ -148,24 +148,24 @@ function execute(...$functionParams) { // Bring in the SUBMISSION_FILE_* constants. import('lib.pkp.classes.submission.SubmissionFile'); - // Bring in the Manager (we need it). - import('lib.pkp.classes.file.SubmissionFileManager'); - - // Move the revisions to the next stage - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ $selectedFiles = $this->getData('selectedFiles'); if(is_array($selectedFiles)) { - foreach ($selectedFiles as $fileId) { - $revisionNumber = $submissionFileDao->getLatestRevisionNumber($fileId); - $submissionFileManager->copyFileToFileStage($fileId, $revisionNumber, SUBMISSION_FILE_PRODUCTION_READY); + foreach ($selectedFiles as $submissionFileId) { + $submissionFile = Services::get('submissionFile')->get($submissionFileId); + $newSubmissionFile = clone $submissionFile; + $newSubmissionFile->setData('fileStage', SUBMISSION_FILE_PRODUCTION_READY); + $newSubmissionFile->setData('sourceSubmissionFileId', $submissionFile->getId()); + $newSubmissionFile->setData('assocType', null); + $newSubmissionFile->setData('assocId', null); + $newSubmissionFile = Services::get('submissionFile')->add($newSubmissionFile, Application::get()->getRequest()); } } // Send email to the author. $this->_sendReviewMailToAuthor($submission, $emailKey, $request); break; default: - fatalError('Unsupported decision!'); + throw new Exception('Unsupported decision!'); } if ($this->getData('requestPayment')) { diff --git a/controllers/tab/pubIds/form/PKPPublicIdentifiersForm.inc.php b/controllers/tab/pubIds/form/PKPPublicIdentifiersForm.inc.php index 6770df864bd..2a1af2bbc82 100644 --- a/controllers/tab/pubIds/form/PKPPublicIdentifiersForm.inc.php +++ b/controllers/tab/pubIds/form/PKPPublicIdentifiersForm.inc.php @@ -155,7 +155,7 @@ function validate($callHooks = true) { $publisherId = $this->getData('publisherId'); $pubObjectId = $pubObject->getId(); if ($assocType == ASSOC_TYPE_SUBMISSION_FILE) { - $pubObjectId = $pubObject->getFileId(); + $pubObjectId = $pubObject->getId(); } $contextDao = Application::getContextDAO(); if ($publisherId) { diff --git a/controllers/wizard/fileUpload/PKPFileUploadWizardHandler.inc.php b/controllers/wizard/fileUpload/PKPFileUploadWizardHandler.inc.php index 5d67a7ac4b0..6bf3ff36fc1 100644 --- a/controllers/wizard/fileUpload/PKPFileUploadWizardHandler.inc.php +++ b/controllers/wizard/fileUpload/PKPFileUploadWizardHandler.inc.php @@ -42,6 +42,9 @@ class PKPFileUploadWizardHandler extends Handler { /** @var integer */ var $_assocId; + /** @var integer */ + var $_queryId; + /** * Constructor @@ -52,7 +55,7 @@ function __construct() { array(ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_AUTHOR, ROLE_ID_REVIEWER, ROLE_ID_ASSISTANT), array( 'startWizard', 'displayFileUploadForm', - 'uploadFile', 'confirmRevision', + 'uploadFile', 'editMetadata', 'finishFileSubmission' ) @@ -63,6 +66,115 @@ function __construct() { // // Implement template methods from PKPHandler // + function authorize($request, &$args, $roleAssignments) { + // We validate file stage outside a policy because + // we don't need to validate in another places. + $fileStage = (int) $request->getUserVar('fileStage'); + if ($fileStage) { + $fileStages = Services::get('submissionFile')->getFileStages(); + if (!in_array($fileStage, $fileStages)) { + return false; + } + } + + // Validate file ids. We have two cases where we might have a file id. + // CASE 1: user is uploading a revision to a file, the revised file id + // will need validation. + $revisedFileId = (int)$request->getUserVar('revisedFileId'); + // CASE 2: user already have uploaded a file (and it's editing the metadata), + // we will need to validate the uploaded file id. + $submissionFileId = (int)$request->getUserVar('submissionFileId'); + // Get the right one to validate. + $submissionFileIdToValidate = null; + if ($revisedFileId && !$submissionFileId) { + $submissionFileIdToValidate = $revisedFileId; + } else if ($submissionFileId && !$revisedFileId) { + $submissionFileIdToValidate = $submissionFileId; + } else if ($revisedFileId && $submissionFileId) { + // Those two cases will not happen at the same time. + return false; + } + + // Allow access to modify a specific file + if ($submissionFileIdToValidate) { + import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); + $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_MODIFY, $submissionFileIdToValidate)); + + // Allow uploading to review attachments + } elseif ($fileStage === SUBMISSION_FILE_REVIEW_ATTACHMENT) { + $assocType = (int) $request->getUserVar('assocType'); + $assocId = (int) $request->getUserVar('assocId'); + $stageId = (int) $request->getUserVar('stageId'); + if (empty($assocType) || $assocType !== ASSOC_TYPE_REVIEW_ASSIGNMENT || empty($assocId)) { + return false; + } + + $stageId = (int) $request->getUserVar('stageId'); + import('lib.pkp.classes.security.authorization.ReviewStageAccessPolicy'); + $this->addPolicy(new ReviewStageAccessPolicy($request, $args, $roleAssignments, 'submissionId', $stageId)); + import('lib.pkp.classes.security.authorization.internal.ReviewRoundRequiredPolicy'); + $this->addPolicy(new ReviewRoundRequiredPolicy($request, $args)); + import('lib.pkp.classes.security.authorization.ReviewAssignmentFileWritePolicy'); + $this->addPolicy(new ReviewAssignmentFileWritePolicy($request, $assocId)); + + // Allow uploading to a note + } elseif ($fileStage === SUBMISSION_FILE_QUERY) { + $assocType = (int) $request->getUserVar('assocType'); + $assocId = (int) $request->getUserVar('assocId'); + $stageId = (int) $request->getUserVar('stageId'); + if (empty($assocType) || $assocType !== ASSOC_TYPE_NOTE || empty($assocId)) { + return false; + } + + import('lib.pkp.classes.security.authorization.QueryAccessPolicy'); + $this->addPolicy(new QueryAccessPolicy($request, $args, $roleAssignments, $stageId)); + import('lib.pkp.classes.security.authorization.NoteAccessPolicy'); + $this->addPolicy(new NoteAccessPolicy($request, $assocId, NOTE_ACCESS_WRITE)); + + // Allow uploading a dependent file to another file + } elseif ($fileStage === SUBMISSION_FILE_DEPENDENT) { + $assocType = (int) $request->getUserVar('assocType'); + $assocId = (int) $request->getUserVar('assocId'); + if (empty($assocType) || $assocType !== ASSOC_TYPE_SUBMISSION_FILE || empty($assocId)) { + return false; + } + + import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); + $this->addPolicy(new SubmissionFileAccessPolicy($request, $args, $roleAssignments, SUBMISSION_FILE_ACCESS_MODIFY, $assocId)); + + // Allow uploading to other file stages in the workflow + } else { + $stageId = (int) $request->getUserVar('stageId'); + $assocType = (int) $request->getUserVar('assocType'); + $assocId = (int) $request->getUserVar('assocId'); + import('lib.pkp.classes.security.authorization.WorkflowStageAccessPolicy'); + $this->addPolicy(new WorkflowStageAccessPolicy($request, $args, $roleAssignments, 'submissionId', $stageId)); + + AppLocale::requireComponents(LOCALE_COMPONENT_PKP_API, LOCALE_COMPONENT_APP_API); + import('lib.pkp.classes.security.authorization.SubmissionFileAccessPolicy'); // SUBMISSION_FILE_ACCESS_MODIFY + import('lib.pkp.classes.security.authorization.internal.SubmissionFileStageAccessPolicy'); + $this->addPolicy(new SubmissionFileStageAccessPolicy($fileStage, SUBMISSION_FILE_ACCESS_MODIFY, 'api.submissionFiles.403.unauthorizedFileStageIdWrite')); + + // Additional checks before uploading to a review file stage + if (in_array($fileStage, [SUBMISSION_FILE_REVIEW_REVISION, SUBMISSION_FILE_REVIEW_FILE]) + || $assocType === ASSOC_TYPE_REVIEW_ROUND) { + import('lib.pkp.classes.security.authorization.internal.ReviewRoundRequiredPolicy'); + $this->addPolicy(new ReviewRoundRequiredPolicy($request, $args)); + } + + // Additional checks before uploading to a representation + if ($fileStage === SUBMISSION_FILE_PROOF || $assocType === ASSOC_TYPE_REPRESENTATION) { + if (empty($assocType) || $assocType !== ASSOC_TYPE_REPRESENTATION || empty($assocId)) { + return false; + } + import('lib.pkp.classes.security.authorization.internal.RepresentationUploadAccessPolicy'); + $this->addPolicy(new RepresentationUploadAccessPolicy($request, $args, $assocId)); + } + } + + return parent::authorize($request, $args, $roleAssignments); + } + /** * @copydoc PKPHandler::initialize() */ @@ -88,6 +200,7 @@ function initialize($request) { $reviewRound = $this->getReviewRound(); $this->_assocType = $request->getUserVar('assocType') ? (int)$request->getUserVar('assocType') : null; $this->_assocId = $request->getUserVar('assocId') ? (int)$request->getUserVar('assocId') : null; + $this->_queryId = $request->getUserVar('queryId') ? (int) $request->getUserVar('queryId') : null; // The revised file will be non-null if we revise a single existing file. if ($this->getRevisionOnly() && $request->getUserVar('revisedFileId')) { @@ -207,6 +320,7 @@ function startWizard($args, $request) { 'assocType' => $this->getAssocType(), 'assocId' => $this->getAssocId(), 'dependentFilesOnly' => $request->getUserVar('dependentFilesOnly'), + 'queryId' => $this->_queryId, )); return $templateMgr->fetchJson('controllers/wizard/fileUpload/fileUploadWizard.tpl'); } @@ -224,7 +338,7 @@ function displayFileUploadForm($args, $request) { $fileForm = new SubmissionFilesUploadForm( $request, $submission->getId(), $this->getStageId(), $this->getUploaderRoles(), $this->getFileStage(), $this->getRevisionOnly(), $this->getReviewRound(), $this->getRevisedFileId(), - $this->getAssocType(), $this->getAssocId() + $this->getAssocType(), $this->getAssocId(), $this->_queryId ); $fileForm->initData(); @@ -244,7 +358,7 @@ function uploadFile($args, $request) { import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesUploadForm'); $uploadForm = new SubmissionFilesUploadForm( $request, $submission->getId(), $this->getStageId(), null, $this->getFileStage(), - $this->getRevisionOnly(), $this->getReviewRound(), null, $this->getAssocType(), $this->getAssocId() + $this->getRevisionOnly(), $this->getReviewRound(), null, $this->getAssocType(), $this->getAssocId(), $this->_queryId ); $uploadForm->readInputData(); @@ -258,177 +372,14 @@ function uploadFile($args, $request) { return new JSONMessage(false, __('common.uploadFailed')); } - $this->_attachEntities($uploadedFile); - // Retrieve file info to be used in a JSON response. $uploadedFileInfo = $this->_getUploadedFileInfo($uploadedFile); $reviewRound = $this->getReviewRound(); - // If no revised file id was given then try out whether - // the user maybe accidentally didn't identify this file as a revision. - if (!$uploadForm->getRevisedFileId()) { - $user = $request->getUser(); - $revisionSubmissionFilesSelection = $uploadForm->getRevisionSubmissionFilesSelection($user, $uploadedFile); - $revisedFileId = $this->_checkForRevision($uploadedFile, $revisionSubmissionFilesSelection); - if ($revisedFileId) { - // Instantiate the revision confirmation form. - import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesUploadConfirmationForm'); - $confirmationForm = new SubmissionFilesUploadConfirmationForm($request, $submission->getId(), $this->getStageId(), $this->getFileStage(), $reviewRound, $revisedFileId, $this->getAssocType(), $this->getAssocId(), $uploadedFile); - $confirmationForm->initData(); - - // Render the revision confirmation form. - return new JSONMessage(true, $confirmationForm->fetch($request), '0', $uploadedFileInfo); - } - } - // Advance to the next step (i.e. meta-data editing). return new JSONMessage(true, '', '0', $uploadedFileInfo); } - /** - * Attach any dependent entities to a new file upload. - * @param $submissionFile SubmissionFile - */ - protected function _attachEntities($submissionFile) { - switch ($submissionFile->getFileStage()) { - case SUBMISSION_FILE_ATTACHMENT: - // If this attachment was created in the review stage, add it to - // the review round. - if ($reviewRound = $this->getReviewRound()) { - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->assignRevisionToReviewRound($submissionFile->getFileId(), $submissionFile->getRevision(), $reviewRound); - } - break; - case SUBMISSION_FILE_REVIEW_FILE: - case SUBMISSION_FILE_REVIEW_ATTACHMENT: - case SUBMISSION_FILE_REVIEW_REVISION: - // Add the uploaded review file to the review round. - $reviewRound = $this->getReviewRound(); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->assignRevisionToReviewRound($submissionFile->getFileId(), $submissionFile->getRevision(), $reviewRound); - - if ($submissionFile->getFileStage() == SUBMISSION_FILE_REVIEW_REVISION) { - // Get a list of author user IDs - $authorUserIds = array(); - $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /* @var $stageAssignmentDao StageAssignmentDAO */ - $submitterAssignments = $stageAssignmentDao->getBySubmissionAndRoleId($reviewRound->getSubmissionId(), ROLE_ID_AUTHOR); - while ($assignment = $submitterAssignments->next()) { - $authorUserIds[] = $assignment->getUserId(); - } - - // Update the task notifications - $notificationMgr = new NotificationManager(); - $notificationMgr->updateNotification( - Application::get()->getRequest(), - array(NOTIFICATION_TYPE_PENDING_INTERNAL_REVISIONS, NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS), - $authorUserIds, - ASSOC_TYPE_SUBMISSION, - $reviewRound->getSubmissionId() - ); - - // Update the ReviewRound status when revision is submitted - import('lib.pkp.classes.submission.reviewRound.ReviewRoundDAO'); - $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */ - $reviewRoundDao->updateStatus($reviewRound); - - // Notify editors about the revision upload - $submission = $this->getSubmission(); - $request = Application::get()->getRequest(); - $router = $request->getRouter(); - $dispatcher = $router->getDispatcher(); - $context = $request->getContext(); - $uploader = $request->getUser(); - // If the file is uploaded by an author - if (in_array($uploader->getId(), $authorUserIds)) { - - // Fetch the latest notification email timestamp if any - import('lib.pkp.classes.log.SubmissionEmailLogEntry'); // Import email event constants - $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /* @var $submissionEmailLogDao SubmissionEmailLogDAO */ - $submissionEmails = $submissionEmailLogDao->getByEventType($submission->getId(), SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION); - $lastNotification = null; - $sentDates = array(); - if ($submissionEmails){ - while ($email = $submissionEmails->next()) { - if ($email->getDateSent()){ - $sentDates[] = $email->getDateSent(); - } - } - if (!empty($sentDates)){ - $lastNotification = max(array_map('strtotime', $sentDates)); - } - } - - import('lib.pkp.classes.mail.SubmissionMailTemplate'); - $mail = new SubmissionMailTemplate($submission, 'REVISED_VERSION_NOTIFY'); - $mail->setEventType(SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION); - $mail->setReplyTo($context->getData('contactEmail'), $context->getData('contactName')); - // Get editors assigned to the submission, consider also the recommendOnly editors - $userDao = DAORegistry::getDAO('UserDAO'); /* @var $userDao UserDAO */ - $editorsStageAssignments = $stageAssignmentDao->getEditorsAssignedToStage($submission->getId(), $this->getStageId()); - foreach ($editorsStageAssignments as $editorsStageAssignment) { - $editor = $userDao->getById($editorsStageAssignment->getUserId()); - // If no prior notification exists OR if editor has logged in after the last revision upload OR the last upload and notification was sent more than a day ago, send a new notification - if (is_null($lastNotification) || strtotime($editor->getDateLastLogin()) > $lastNotification || strtotime('-1 day') > $lastNotification){ - $mail->addRecipient($editor->getEmail(), $editor->getFullName()); - } - } - // Get uploader name - $submissionUrl = $dispatcher->url($request, ROUTE_PAGE, null, 'workflow', 'index', array($submission->getId(), $this->getStageId())); - $mail->assignParams(array( - 'authorName' => $uploader->getFullName(), - 'editorialContactSignature' => $context->getData('contactName'), - 'submissionUrl' => $submissionUrl, - )); - - if ($mail->getRecipients()){ - if (!$mail->send($request)) { - import('classes.notification.NotificationManager'); - $notificationMgr = new NotificationManager(); - $notificationMgr->createTrivialNotification($request->getUser()->getId(), NOTIFICATION_TYPE_ERROR, array('contents' => __('email.compose.error'))); - } - } - - } - } - break; - } - } - - /** - * Confirm that the uploaded file is a revision of an - * earlier uploaded file. - * @param $args array - * @param $request Request - * @return JSONMessage JSON object - */ - function confirmRevision($args, $request) { - // Instantiate the revision confirmation form. - $submission = $this->getSubmission(); - import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesUploadConfirmationForm'); - // FIXME?: need assocType and assocId? Not sure if they would be used, so not adding now. - $reviewRound = $this->getReviewRound(); - $confirmationForm = new SubmissionFilesUploadConfirmationForm( - $request, $submission->getId(), $this->getStageId(), $this->getFileStage(), $reviewRound - ); - $confirmationForm->readInputData(); - - // Validate the form and revise the file. - if ($confirmationForm->validate()) { - if (is_a($uploadedFile = $confirmationForm->execute(), 'SubmissionFile')) { - - $this->_attachEntities($uploadedFile); - - // Go to the meta-data editing step. - return new JSONMessage(true, '', '0', $this->_getUploadedFileInfo($uploadedFile)); - } else { - - return new JSONMessage(false, __('common.uploadFailed')); - } - } else { - return new JSONMessage(true, $confirmationForm->fetch($request)); - } - } - /** * Edit the metadata of the latest revision of * the requested submission file. @@ -437,9 +388,11 @@ function confirmRevision($args, $request) { * @return JSONMessage JSON object */ function editMetadata($args, $request) { - $metadataForm = $this->_getMetadataForm($request); - $metadataForm->initData(); - return new JSONMessage(true, $metadataForm->fetch($request)); + $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); + import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesMetadataForm'); + $form = new SubmissionFilesMetadataForm($submissionFile, $this->getStageId(), $this->getReviewRound()); + $form->initData(); + return new JSONMessage(true, $form->fetch($request)); } /** @@ -468,58 +421,6 @@ function finishFileSubmission($args, $request) { // // Private helper methods // - /** - * Retrieve the requested meta-data form. - * @param $request Request - * @return SubmissionFilesMetadataForm - */ - function _getMetadataForm($request) { - $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); - return $submissionFile->getMetadataForm($this->getStageId(), $this->getReviewRound()); - } - - /** - * Check if the uploaded file has a similar name to an existing - * file which would then be a candidate for a revised file. - * @param $uploadedFile SubmissionFile - * @param $submissionFiles array a list of submission files to - * check the uploaded file against. - * @return integer the if of the possibly revised file or null - * if no matches were found. - */ - function &_checkForRevision(&$uploadedFile, &$submissionFiles) { - // Get the file name. - $uploadedFileName = $uploadedFile->getOriginalFileName(); - - // Start with the minimal required similarity. - $minPercentage = Config::getVar('files', 'filename_revision_match', 70); - - // Find out whether one of the files belonging to the current - // file stage matches the given file name. - $possibleRevisedFileId = null; - $matchedPercentage = 0; - foreach ((array) $submissionFiles as $submissionFile) { /* @var $submissionFile SubmissionFile */ - // Test whether the current submission file is similar - // to the uploaded file. (Transliterate to ASCII -- the - // similar_text function can't handle UTF-8.) - similar_text( - $a = Stringy\Stringy::create($uploadedFileName)->toAscii(), - $b = Stringy\Stringy::create($submissionFile->getOriginalFileName())->toAscii(), - $matchedPercentage - ); - if($matchedPercentage > $minPercentage && !$this->_onlyNumbersDiffer($a, $b)) { - // We found a file that might be a possible revision. - $possibleRevisedFileId = $submissionFile->getFileId(); - - // Reset the min percentage to this comparison's precentage - // so that only better matches will be considered from now on. - $minPercentage = $matchedPercentage; - } - } - - // Return the id of the file that we found similar. - return $possibleRevisedFileId; - } /** * Helper function: check if the only difference between $a and $b @@ -557,11 +458,9 @@ function _onlyNumbersDiffer($a, $b) { function _getUploadedFileInfo($uploadedFile) { return array( 'uploadedFile' => array( - 'fileId' => $uploadedFile->getFileId(), - 'revision' => $uploadedFile->getRevision(), - 'name' => $uploadedFile->getLocalizedName(), - 'fileLabel' => $uploadedFile->getFileLabel(), - 'type' => $uploadedFile->getDocumentType(), + 'id' => $uploadedFile->getId(), + 'fileId' => $uploadedFile->getData('fileId'), + 'name' => $uploadedFile->getLocalizedData('name'), 'genreId' => $uploadedFile->getGenreId(), ) ); diff --git a/controllers/wizard/fileUpload/form/PKPSubmissionFilesUploadBaseForm.inc.php b/controllers/wizard/fileUpload/form/PKPSubmissionFilesUploadBaseForm.inc.php index c782007ac9d..75c9c49967b 100644 --- a/controllers/wizard/fileUpload/form/PKPSubmissionFilesUploadBaseForm.inc.php +++ b/controllers/wizard/fileUpload/form/PKPSubmissionFilesUploadBaseForm.inc.php @@ -38,9 +38,12 @@ class PKPSubmissionFilesUploadBaseForm extends Form { * @param $revisionOnly boolean * @param $reviewRound ReviewRound * @param $revisedFileId integer + * @param $assocType integer + * @param $assocId integer + * @param $queryId integer */ function __construct($request, $template, $submissionId, $stageId, $fileStage, - $revisionOnly = false, $reviewRound = null, $revisedFileId = null, $assocType = null, $assocId = null) { + $revisionOnly = false, $reviewRound = null, $revisedFileId = null, $assocType = null, $assocId = null, $queryId = null) { // Check the incoming parameters. if ( !is_numeric($submissionId) || $submissionId <= 0 || @@ -76,6 +79,7 @@ function __construct($request, $template, $submissionId, $stageId, $fileStage, $this->setData('reviewRoundId', $reviewRound?$reviewRound->getId():null); $this->setData('assocType', $assocType ? (int)$assocType : null); $this->setData('assocId', $assocId ? (int)$assocId : null); + $this->setData('queryId', $queryId ? (int) $queryId : null); // Add validators. $this->addCheck(new FormValidatorPost($this)); @@ -135,9 +139,13 @@ function getSubmissionFiles() { $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ if ($this->getStageId() == WORKFLOW_STAGE_ID_INTERNAL_REVIEW || $this->getStageId() == WORKFLOW_STAGE_ID_EXTERNAL_REVIEW) { // If we have a review stage id then we also expect a review round. - if (!$this->getData('fileStage') == SUBMISSION_FILE_QUERY && !is_a($this->getReviewRound(), 'ReviewRound')) assert(false); + if (!$this->getData('fileStage') == SUBMISSION_FILE_QUERY && !is_a($this->getReviewRound(), 'ReviewRound')) { + throw new Exception('Can not get request submission files for a review stage without specifying a review round.'); + } // Can only upload submission files, review files, review attachments, dependent files, or query attachments. - if (!in_array($this->getData('fileStage'), array(SUBMISSION_FILE_SUBMISSION, SUBMISSION_FILE_REVIEW_FILE, SUBMISSION_FILE_REVIEW_ATTACHMENT, SUBMISSION_FILE_REVIEW_REVISION, SUBMISSION_FILE_QUERY, SUBMISSION_FILE_DEPENDENT, SUBMISSION_FILE_ATTACHMENT))) fatalError('Invalid file stage!'); + if (!in_array($this->getData('fileStage'), array(SUBMISSION_FILE_SUBMISSION, SUBMISSION_FILE_REVIEW_FILE, SUBMISSION_FILE_REVIEW_ATTACHMENT, SUBMISSION_FILE_REVIEW_REVISION, SUBMISSION_FILE_QUERY, SUBMISSION_FILE_DEPENDENT, SUBMISSION_FILE_ATTACHMENT))) { + throw new Exception('The file stage is not valid for the review stage.'); + } // Hide the revision selector for review // attachments to make it easier for reviewers @@ -146,22 +154,25 @@ function getSubmissionFiles() { $this->_submissionFiles = array(); } elseif ($reviewRound) { // Retrieve the submission files for the given review round. - $this->_submissionFiles = $submissionFileDao->getRevisionsByReviewRound($reviewRound); + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'reviewRoundIds' => [(int) $reviewRound], + 'submissionIds' => [(int) $this->getData('submissionId')], + ]); + $this->_submissionFiles = iterator_to_array($submissionFilesIterator); } else { // No review round, e.g. for dependent or query files $this->_submissionFiles = array(); } } else { - // Retrieve the submission files for the given file stage. - if (!$this->getAssocType() || $this->getAssocType() == ASSOC_TYPE_SUBMISSION) { - $this->_submissionFiles = $submissionFileDao->getLatestRevisions( - $this->getData('submissionId'), $this->getData('fileStage')); - } else { - $this->_submissionFiles = $submissionFileDao->getLatestRevisionsByAssocId( - $this->getAssocType(), $this->getAssocId(), $this->getData('submissionId'), $this->getData('fileStage') - ); + $params = [ + 'fileStages' => [(int) $this->getData('fileStage')], + 'submissionIds' => [(int) $this->getData('submissionId')], + ]; + if ($this->getAssocType() && $this->getAssocType() != ASSOC_TYPE_SUBMISSION) { + $params['assocTypes'] = [$this->getAssocType()]; + $params['assocIds'] = [$this->getAssocId()]; } - + $this->_submissionFiles = iterator_to_array(Services::get('submissionFile')->getMany($params)); } } @@ -180,10 +191,10 @@ function getRevisionSubmissionFilesSelection($user, $uploadedFile = null) { $submissionFiles = array(); foreach ($allSubmissionFiles as $submissionFile) { // The uploaded file must be excluded from the list of revisable files. - if ($uploadedFile && $uploadedFile->getFileId() == $submissionFile->getFileId()) continue; + if ($uploadedFile && $uploadedFile->getId() == $submissionFile->getId()) continue; if ( ($submissionFile->getFileStage() == SUBMISSION_FILE_REVIEW_ATTACHMENT || $submissionFile->getFileStage() == SUBMISSION_FILE_REVIEW_FILE) && - $stageAssignmentDao->getBySubmissionAndRoleId($submissionFile->getSubmissionId(), ROLE_ID_AUTHOR, $this->getStageId(), $user->getId()) + $stageAssignmentDao->getBySubmissionAndRoleId($submissionFile->getData('submissionId'), ROLE_ID_AUTHOR, $this->getStageId(), $user->getId()) ) { // Authors are not permitted to revise reviewer documents. continue; @@ -235,9 +246,8 @@ function fetch($request, $template = null, $display = false) { foreach ((array) $submissionFiles as $submissionFile) { // Is this the revised file? - if ($revisedFileId && $revisedFileId == $submissionFile->getFileId()) { + if ($revisedFileId && $revisedFileId == $submissionFile->getId()) { // This is the revised submission file, so pass its data on to the form. - $this->setData('revisedFileName', $submissionFile->getOriginalFileName()); $this->setData('genreId', $submissionFile->getGenreId()); $foundRevisedFile = true; } @@ -245,16 +255,10 @@ function fetch($request, $template = null, $display = false) { // Create an entry in the list of existing files which // the user can select from in case he chooses to upload // a revision. - $fileName = $submissionFile->getLocalizedName() != '' ? $submissionFile->getLocalizedName() : __('common.untitled'); - if ($submissionFile->getRevision() > 1) $fileName .= ' (' . $submissionFile->getRevision() . ')'; - - // If we are about to add a revision of a revision, remove the original one from the list of possible file choices. - if (array_key_exists($submissionFile->getSourceFileId(), $submissionFileOptions)) { - unset($submissionFileOptions[$submissionFile->getSourceFileId()]); - } + $fileName = $submissionFile->getLocalizedData('name') != '' ? $submissionFile->getLocalizedData('name') : __('common.untitled'); - $submissionFileOptions[$submissionFile->getFileId()] = $fileName; - $currentSubmissionFileGenres[$submissionFile->getFileId()] = $submissionFile->getGenreId(); + $submissionFileOptions[$submissionFile->getId()] = $fileName; + $currentSubmissionFileGenres[$submissionFile->getId()] = $submissionFile->getGenreId(); $lastSubmissionFile = $submissionFile; } @@ -262,8 +266,7 @@ function fetch($request, $template = null, $display = false) { // If there is only one option for a file to review, and user must revise, do not show the selector. if (count($submissionFileOptions) == 1 && $this->getData('revisionOnly')) { // There was only one option, use the last added submission file - $this->setData('revisedFileId', $lastSubmissionFile->getFileId()); - $this->setData('revisedFileName', $lastSubmissionFile->getOriginalFileName()); + $this->setData('revisedFileId', $lastSubmissionFile->getId()); $this->setData('genreId', $lastSubmissionFile->getGenreId()); } diff --git a/controllers/wizard/fileUpload/form/SubmissionFilesArtworkMetadataForm.inc.php b/controllers/wizard/fileUpload/form/SubmissionFilesArtworkMetadataForm.inc.php deleted file mode 100644 index 1e4ea16f3ba..00000000000 --- a/controllers/wizard/fileUpload/form/SubmissionFilesArtworkMetadataForm.inc.php +++ /dev/null @@ -1,73 +0,0 @@ -readUserVars(array( - 'artworkCaption', 'artworkCredit', 'artworkCopyrightOwner', - 'artworkCopyrightOwnerContact', 'artworkPermissionTerms' - )); - parent::readInputData(); - } - - /** - * @copydoc Form::execute() - */ - function execute(...$functionArgs) { - // - // FIXME: Should caption, credit, or any other fields be - // localized? - // FIXME: How to upload a permissions file? - // FIXME: How to select a contact author from the submission - // author list? - // - - // Update the sumbission file by reference. - $submissionFile = $this->getSubmissionFile(); - $submissionFile->setCaption($this->getData('artworkCaption')); - $submissionFile->setCredit($this->getData('artworkCredit')); - $submissionFile->setCopyrightOwner($this->getData('artworkCopyrightOwner')); - $submissionFile->setCopyrightOwnerContactDetails($this->getData('artworkCopyrightOwnerContact')); - $submissionFile->setPermissionTerms($this->getData('artworkPermissionTerms')); - - // Persist the submission file. - parent::execute(...$functionArgs); - } -} - - diff --git a/controllers/wizard/fileUpload/form/SubmissionFilesMetadataForm.inc.php b/controllers/wizard/fileUpload/form/SubmissionFilesMetadataForm.inc.php index e7de4f51e95..a4f5b622703 100644 --- a/controllers/wizard/fileUpload/form/SubmissionFilesMetadataForm.inc.php +++ b/controllers/wizard/fileUpload/form/SubmissionFilesMetadataForm.inc.php @@ -36,7 +36,7 @@ class SubmissionFilesMetadataForm extends Form { function __construct($submissionFile, $stageId, $reviewRound = null, $template = null) { if ($template === null) $template = 'controllers/wizard/fileUpload/form/submissionFileMetadataForm.tpl'; parent::__construct($template); - AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION); + AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_PKP_GRID); // Initialize the object. $this->_submissionFile = $submissionFile; @@ -45,7 +45,7 @@ function __construct($submissionFile, $stageId, $reviewRound = null, $template = $this->_reviewRound = $reviewRound; } - $submissionLocale = $submissionFile->getSubmissionLocale(); + $submissionLocale = $submissionFile->getData('locale'); $this->setDefaultFormLocale($submissionLocale); // Add validation checks. @@ -113,7 +113,11 @@ function getLocaleFieldNames() { * @copydoc Form::readInputData() */ function readInputData() { - $this->readUserVars(array('name', 'showButtons')); + $this->readUserVars(array('name', 'showButtons', + 'artworkCaption', 'artworkCredit', 'artworkCopyrightOwner', + 'artworkCopyrightOwnerContact', 'artworkPermissionTerms', + 'creator', 'subject', 'description', 'publisher', 'sponsor', 'source', 'language', 'dateCreated', + )); } /** @@ -122,10 +126,15 @@ function readInputData() { function fetch($request, $template = null, $display = false) { $templateMgr = TemplateManager::getManager($request); $reviewRound = $this->getReviewRound(); + $filepath = Services::get('file')->getPath($this->getSubmissionFile()->getData('fileId')); + $genre = DAORegistry::getDAO('GenreDAO')->getById($this->getSubmissionFile()->getData('genreId'), $request->getContext()->getId()); + $templateMgr->assign(array( 'submissionFile' => $this->getSubmissionFile(), 'stageId' => $this->getStageId(), - 'reviewRoundId' => $reviewRound?$reviewRound->getId():null + 'reviewRoundId' => $reviewRound?$reviewRound->getId():null, + 'supportsDependentFiles' => Services::get('submissionFile')->supportsDependentFiles($this->getSubmissionFile(), $filepath), + 'genre' => $genre, )); return parent::fetch($request, $template, $display); } @@ -134,14 +143,36 @@ function fetch($request, $template = null, $display = false) { * @copydoc Form::execute() */ function execute(...$functionParams) { - parent::execute(...$functionParams); - // Update the submission file with data from the form. - $submissionFile = $this->getSubmissionFile(); - $submissionFile->setName($this->getData('name'), null); // Localized - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFileDao->updateObject($submissionFile); + $props = [ + 'name' => $this->getData('name'), + ]; + + // Artwork metadata + $props = array_merge($props, [ + 'caption' => $this->getData('artworkCaption'), + 'credit' => $this->getData('artworkCredit'), + 'copyrightOwner' => $this->getData('artworkCopyrightOwner'), + 'terms' => $this->getData('artworkPermissionTerms'), + ]); + + // Supplementary file metadata + $props = array_merge($props, [ + 'subject' => $this->getData('subject'), + 'creator' => $this->getData('creator'), + 'description' => $this->getData('description'), + 'publisher' => $this->getData('publisher'), + 'sponsor' => $this->getData('sponsor'), + 'source' => $this->getData('source'), + 'language' => $this->getData('language'), + 'dateCreated' => $this->getData('dateCreated'), + ]); + + $this->_submissionFile = Services::get('submissionFile')->edit($this->getSubmissionFile(), $props, Application::get()->getRequest()); + + parent::execute(...$functionParams); } + } diff --git a/controllers/wizard/fileUpload/form/SubmissionFilesUploadConfirmationForm.inc.php b/controllers/wizard/fileUpload/form/SubmissionFilesUploadConfirmationForm.inc.php deleted file mode 100644 index d4d1803c99f..00000000000 --- a/controllers/wizard/fileUpload/form/SubmissionFilesUploadConfirmationForm.inc.php +++ /dev/null @@ -1,90 +0,0 @@ -setData('uploadedFile', $uploadedFile); - } - } - - - // - // Implement template methods from Form - // - /** - * @copydoc Form::readInputData() - */ - function readInputData() { - $this->readUserVars(array('uploadedFileId')); - return parent::readInputData(); - } - - /** - * Save the submission file upload confirmation form. - * @see Form::execute() - * @return SubmissionFile if successful, otherwise null - */ - function execute(...$functionArgs) { - // Retrieve the file ids of the revised and the uploaded files. - $revisedFileId = $this->getRevisedFileId(); - $uploadedFileId = (int)$this->getData('uploadedFileId'); - if ($revisedFileId == $uploadedFileId) fatalError('The revised file id and the uploaded file id cannot be the same!'); - - parent::execute(...$functionArgs); - - // Assign the new file as the latest revision of the old file. - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionId = $this->getData('submissionId'); - $fileStage = $this->getData('fileStage'); - $newFileLatestRevision = $submissionFileDao->getLatestRevision($uploadedFileId, $fileStage, $submissionId); - // detach the latest attached revision, because the file returned here will then be attached - import('controllers.api.file.ManageFileApiHandler'); - $mangeFileApiHandler = new ManageFileApiHandler(); - $mangeFileApiHandler->detachEntities($newFileLatestRevision, $newFileLatestRevision->getSubmissionId(), $this->getStageId()); - if ($revisedFileId) { - // The file was revised; update revision information - return $submissionFileDao->setAsLatestRevision($revisedFileId, $uploadedFileId, $submissionId, $fileStage); - } else { - // This is a new upload, not a revision; don't do anything. - return $newFileLatestRevision; - } - } -} - - diff --git a/controllers/wizard/fileUpload/form/SubmissionFilesUploadForm.inc.php b/controllers/wizard/fileUpload/form/SubmissionFilesUploadForm.inc.php index 18b0a676f68..43e4343a71c 100644 --- a/controllers/wizard/fileUpload/form/SubmissionFilesUploadForm.inc.php +++ b/controllers/wizard/fileUpload/form/SubmissionFilesUploadForm.inc.php @@ -33,9 +33,12 @@ class SubmissionFilesUploadForm extends PKPSubmissionFilesUploadBaseForm { * @param $stageId integer * @param $reviewRound ReviewRound * @param $revisedFileId integer + * @param $assocType integer + * @param $assocId integer + * @param $queryId integer */ function __construct($request, $submissionId, $stageId, $uploaderRoles, $fileStage, - $revisionOnly = false, $reviewRound = null, $revisedFileId = null, $assocType = null, $assocId = null) { + $revisionOnly = false, $reviewRound = null, $revisedFileId = null, $assocType = null, $assocId = null, $queryId = null) { // Initialize class. assert(is_null($uploaderRoles) || (is_array($uploaderRoles) && count($uploaderRoles) >= 1)); @@ -45,7 +48,7 @@ function __construct($request, $submissionId, $stageId, $uploaderRoles, $fileSta parent::__construct( $request, 'controllers/wizard/fileUpload/form/fileUploadForm.tpl', - $submissionId, $stageId, $fileStage, $revisionOnly, $reviewRound, $revisedFileId, $assocType, $assocId + $submissionId, $stageId, $fileStage, $revisionOnly, $reviewRound, $revisedFileId, $assocType, $assocId, $queryId ); // Disable the genre selector for review file attachments @@ -128,58 +131,59 @@ function fetch($request, $template = null, $display = false) { * @return SubmissionFile if successful, otherwise null */ function execute(...$functionParams) { - // Identify the file genre and category. - $revisedFileId = $this->getRevisedFileId(); - if ($revisedFileId) { - // The file genre and category will be copied over from the revised file. - $fileGenre = null; - } else { - // This is a new file so we need the file genre and category from the form. - $fileGenre = $this->getData('genreId') ? (int)$this->getData('genreId') : null; - } // Identify the uploading user. $request = Application::get()->getRequest(); $user = $request->getUser(); assert(is_a($user, 'User')); - $assocType = $this->getData('assocType') ? (int) $this->getData('assocType') : null; - $assocId = $this->getData('assocId') ? (int) $this->getData('assocId') : null; - $fileStage = $this->getData('fileStage'); - // Upload the file. - import('lib.pkp.classes.file.SubmissionFileManager'); - $submissionFileManager = new SubmissionFileManager( - $request->getContext()->getId(), - $this->getData('submissionId') - ); - $submissionFile = $submissionFileManager->uploadSubmissionFile( - 'uploadedFile', $fileStage, $user->getId(), - $revisedFileId, $fileGenre, $assocType, $assocId + import('lib.pkp.classes.file.FileManager'); + $fileManager = new FileManager(); + $extension = $fileManager->parseFileExtension($_FILES['uploadedFile']['name']); + + $submissionDir = Services::get('submissionFile')->getSubmissionDir($request->getContext()->getId(), $this->getData('submissionId')); + $fileId = Services::get('file')->add( + $_FILES['uploadedFile']['tmp_name'], + $submissionDir . '/' . uniqid() . '.' . $extension ); + + if ($this->getRevisedFileId()) { + $submissionFile = Services::get('submissionFile')->get($this->getRevisedFileId()); + $submissionFile = Services::get('submissionFile')->edit( + $submissionFile, + [ + 'fileId' => $fileId, + 'name' => [ + $request->getContext()->getPrimaryLocale() => $_FILES['uploadedFile']['name'], + ], + 'uploaderUserId' => $user->getId(), + ], + $request + ); + } else { + $submissionFile = DAORegistry::getDao('SubmissionFileDAO')->newDataObject(); + $submissionFile->setData('fileId', $fileId); + $submissionFile->setData('fileStage', $this->getData('fileStage')); + $submissionFile->setData('name', $_FILES['uploadedFile']['name'], $request->getContext()->getPrimaryLocale()); + $submissionFile->setData('submissionId', $this->getData('submissionId')); + $submissionFile->setData('uploaderUserId', $user->getId()); + $submissionFile->setData('assocType', $this->getData('assocType') ? (int) $this->getData('assocType') : null); + $submissionFile->setData('assocId', $this->getData('assocId') ? (int) $this->getData('assocId') : null); + $submissionFile->setData('genreId', (int) $this->getData('genreId')); + + if ($this->getReviewRound() && $this->getReviewRound()->getId() && empty($submissionFile->getData('assocType'))) { + $submissionFile->setData('assocType', ASSOC_TYPE_REVIEW_ROUND); + $submissionFile->setData('assocId', $this->getReviewRound()->getId()); + } + + $submissionFile = Services::get('submissionFile')->add($submissionFile, $request); + } + if (!$submissionFile) return null; - // Log the event. - import('lib.pkp.classes.log.SubmissionFileLog'); - import('lib.pkp.classes.log.SubmissionFileEventLogEntry'); // constants - SubmissionFileLog::logEvent( - $request, - $submissionFile, - $revisedFileId?SUBMISSION_LOG_FILE_REVISION_UPLOAD:SUBMISSION_LOG_FILE_UPLOAD, // assocId - $revisedFileId?'submission.event.revisionUploaded':'submission.event.fileUploaded', - array( - 'fileStage' => $fileStage, - 'revisedFileId' => $revisedFileId, - 'fileId' => $submissionFile->getFileId(), - 'fileRevision' => $submissionFile->getRevision(), - 'originalFileName' => $submissionFile->getOriginalFileName(), - 'submissionId' => $this->getData('submissionId'), - 'username' => $user->getUsername() - ) - ); - $hookResult = parent::execute($submissionFile, ...$functionParams); - if ($hookResult) { + if ($hookResult) { return $hookResult; } diff --git a/controllers/wizard/fileUpload/form/SupplementaryFileMetadataForm.inc.php b/controllers/wizard/fileUpload/form/SupplementaryFileMetadataForm.inc.php deleted file mode 100644 index df5be5711f6..00000000000 --- a/controllers/wizard/fileUpload/form/SupplementaryFileMetadataForm.inc.php +++ /dev/null @@ -1,67 +0,0 @@ -readUserVars(array( - 'creator', 'subject', 'description', 'publisher', 'sponsor', 'source', 'language', 'dateCreated', - )); - parent::readInputData(); - } - - /** - * @copydoc Form::execute() - */ - function execute(...$functionArgs) { - // Update the submission file from form data. - $submissionFile = $this->getSubmissionFile(); - $submissionFile->setSubject($this->getData('subject'), null); // Localized - $submissionFile->setCreator($this->getData('creator'), null); // Localized - $submissionFile->setDescription($this->getData('description'), null); // Localized - $submissionFile->setPublisher($this->getData('publisher'), null); // Localized - $submissionFile->setSponsor($this->getData('sponsor'), null); // Localized - $submissionFile->setSource($this->getData('source'), null); // Localized - $submissionFile->setLanguage($this->getData('language')); - $submissionFile->setDateCreated($this->getData('dateCreated')); - - // Persist the submission file. - parent::execute(...$functionArgs); - } -} - - diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 5b5c3c36f09..1b0fa4805d4 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -132,42 +132,18 @@ Cypress.Commands.add('createSubmission', (data, context) => { cy.get('button.submitFormButton').click(); // === Submission Step 2 === + cy.get('button:contains("Add File")'); // File uploads - var firstFile = true; data.files.forEach(file => { - if (!firstFile) cy.get('a[id^="component-grid-"][id*="-add"]').click(); - cy.wait(2000); // Avoid occasional failure due to form init taking time - cy.get('div.pkp_modal_panel').then($modalDiv => { - cy.wait(3000); - // PPS has a "create galley" modal that needs attention first - if ($modalDiv.find('div.header:contains("Create New Galley")').length) { - cy.get('div.pkp_modal_panel input[id^="label-"]').type('PDF', {delay: 0}); - cy.get('div.pkp_modal_panel button:contains("Save")').click(); - cy.wait(2000); // Avoid occasional failure due to form init taking time - } - }); - cy.get('select[id=genreId]').select(file.genre); cy.fixture(file.file, 'base64').then(fileContent => { cy.get('input[type=file]').upload( {fileContent, 'fileName': file.fileName, 'mimeType': 'application/pdf', 'encoding': 'base64'} ); + cy.get('button:contains("Article Text")').click(); + cy.get('span:contains("Article Text")'); }); - cy.get('button').contains('Continue').click(); - cy.wait(2000); - for (const field in file.metadata) { - cy.get('input[id^="' + Cypress.$.escapeSelector(field) + '"]:visible,textarea[id^="' + Cypress.$.escapeSelector(field) + '"]').type(file.metadata[field], {delay: 0}); - cy.get('input[id^="language"').click({force: true}); // Close multilingual and datepicker pop-overs - } - cy.get('button').contains('Continue').click(); - cy.get('button').contains('Complete').click(); - firstFile = false; }); - if (firstFile) { - // There were no files; close the automatically-opened upload form. - cy.get('a.pkpModalCloseButton').click(); - } - // Save the ID to the data object cy.location('search') .then(search => { @@ -246,7 +222,7 @@ Cypress.Commands.add('createSubmission', (data, context) => { cy.get('form[id=submitStep4Form]').find('button').contains('Finish Submission').click(); cy.get('button.pkpModalConfirmButton').click(); cy.waitJQuery(); - cy.get('h2').contains('Submission complete'); + cy.get('h2:contains("Submission complete")'); }); Cypress.Commands.add('findSubmissionAsEditor', (username, password, familyName, context) => { diff --git a/js/controllers/form/FormHandler.js b/js/controllers/form/FormHandler.js index 821358e2842..d0056bf5624 100755 --- a/js/controllers/form/FormHandler.js +++ b/js/controllers/form/FormHandler.js @@ -135,10 +135,6 @@ this.trigger('formInvalid'); } - // Initialize editable toggles - $('.pkpEditableToggle', $form) - .click(this.callbackWrapper(this.toggleEditableControl)); - this.initializeTinyMCE(); // bind a handler to make sure tinyMCE fields are populated. @@ -403,42 +399,6 @@ }; - /** - * Toggle editable display controls - * - * Editable display controls pair a visual display of data with an editable - * set of fields for that data. This function will toggle between the - * display and edit views. - * - * To use this feature, assign an outer element a `pkp-editable` data - * attribute and corresponding data attributes to the child display and - * input views. - * - *
- *
- * - *
- *
- * - *
- *
- * - * @param {HTMLElement} toggle The HTML element this event was fired on - * @param {Event} event The event which fired this function - */ - $.pkp.controllers.form.FormHandler.prototype.toggleEditableControl = - function(toggle, event) { - event.preventDefault(); - var control = $(toggle).parents('[data-pkp-editable="true"]'); - - if (!control.length) { - return; - } - - control.toggleClass('isEditing'); - }; - - // // Private Methods // diff --git a/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js b/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js index 1b0d3ac6ba9..9b50e0c67b8 100644 --- a/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js +++ b/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js @@ -130,7 +130,7 @@ throw new Error('Unsupported tab index.'); } - newUrl = newUrl + '&fileId=' + this.uploadedFile_.fileId; + newUrl = newUrl + '&submissionFileId=' + this.uploadedFile_.id; ui.newTab.find('.ui-tabs-anchor').attr('href', newUrl); } @@ -241,6 +241,8 @@ // If the user presses cancel after uploading a file then delete the file. if (this.uploadedFile_) { this.uploadedFile_.csrfToken = this.csrfToken_; + // Autorization policy expects to find the submissionFileId para + this.uploadedFile_.submissionFileId = this.uploadedFile_.id; $.post(this.deleteUrl_, this.uploadedFile_, $.pkp.classes.Helper.curry(this.wizardCancelSuccess, this, wizardElement, event), 'json'); diff --git a/js/pages/submission/SubmissionStep2FormHandler.js b/js/pages/submission/SubmissionStep2FormHandler.js index 48a122eb61b..a59052c9bc4 100644 --- a/js/pages/submission/SubmissionStep2FormHandler.js +++ b/js/pages/submission/SubmissionStep2FormHandler.js @@ -29,32 +29,56 @@ function($form, options) { this.parent($form, options); - - this.bind('urlInDivLoaded', this.showFileUploadWizard_); }; $.pkp.classes.Helper.inherits( $.pkp.pages.submission.SubmissionStep2FormHandler, $.pkp.controllers.form.AjaxFormHandler); + /** + * Public callback used to prevent buttons in the submission files + * list panel from submitting the form. + * + * This is a temporary workaround because buttons in the list panel + * can cause the form to submit without firing the `submit` event + * in jQuery. + * + * This method is bound to the form component's onsubmit attribute + * to ensure that all actions which trigger the form submission + * are checked before it is submitted. + * + * There are still some cases that this doesn't catch and those + * are routed through this.submitForm() below. + * + * @param {Object} event + */ + $.pkp.pages.submission.SubmissionStep2FormHandler.prototype.checkSubmit = + function(event) { + event.preventDefault(); + event.stopPropagation(); + return false; + }; - // - // Private methods. - // /** - * When the files grid is shown on step 2, click the 'add files' - * link action. - * @private - * @param {HTMLElement} sourceElement The element that - * issued the event. - * @param {Event} event The triggering event. - * @param {?string} data additional event data. + * Check if the form submission was made by clicking on one of the + * form submit buttons before submitting it. + * + * This is a workaround for embedding the submission files list panel + * into the form. See docs at this.checkSubmit() + * + * @param {Object} validator The validator plug-in. + * @param {HTMLElement} formElement The wrapped HTML form. */ - $.pkp.pages.submission.SubmissionStep2FormHandler. - prototype.showFileUploadWizard_ = function(sourceElement, event, data) { + $.pkp.pages.submission.SubmissionStep2FormHandler.prototype.submitForm = + function(validator, formElement) { + var submitButton = formElement.querySelector('[id^="submitFormButton"]'), + cancelButton = formElement.querySelector('[id^="cancelFormButton"]'); - // OJS and OMP use addFile; PPS uses addGalley - $('#' + data).find('[id*="-addFile-button-"], [id*="-addGalley-button-"]') - .click(); + if (validator.submitButton.id == submitButton.id || + validator.submitButton.id == cancelButton.id) { + this.parent('submitForm', validator, formElement); + } else { + this.hideSpinner(); + } }; diff --git a/locale/ar_IQ/api.po b/locale/ar_IQ/api.po index b7494e833da..6925f62d5e5 100644 --- a/locale/ar_IQ/api.po +++ b/locale/ar_IQ/api.po @@ -101,16 +101,16 @@ msgstr "تعذر تنفيذ طلبك لأنه يفتقر إلى معلومات msgid "api.submissions.400.invalidIssueIdentifiers" msgstr "ما تطلبه من رقم المجلد، العدد، أو السنة، غير صحيح." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "تعذر العثور على أي ملف لرفعه مع الطلب." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "تعذر رفع واحد أو أكثر من الملفات." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "يتعذر رفع الملفات التي يزيد حجمها عن {$maxSize}." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "تعذر رفع الملف بسبب خطأ في إعدادات الملقم. لطفاً، بادر إلى مراسلة مشرف النظام." msgid "api.themes.404.themeUnavailable" diff --git a/locale/ar_IQ/manager.po b/locale/ar_IQ/manager.po index d2b38155637..bdada075f64 100644 --- a/locale/ar_IQ/manager.po +++ b/locale/ar_IQ/manager.po @@ -659,9 +659,6 @@ msgstr "حدث خطأ ما عند حذف هذه الفقرة." msgid "manager.setup.favicon" msgstr "الصورة المصغرة" -msgid "manager.setup.groupType" -msgstr "التجميع حسب نوع الملف" - msgid "manager.setup.homepageImage" msgstr "الصفحة الرئيسية" @@ -1195,12 +1192,6 @@ msgstr "حذف هذا المكون" msgid "grid.action.restoreGenres" msgstr "استعادة الإعدادات الإفتراضية لهذا المكون" -msgid "manager.setup.genres.dependent" -msgstr "إعتبر الملفات من هذا النوع ملفات مستقلة (أي، لا يتم إدراجها مع المحتوى المنشور)" - -msgid "manager.setup.genres.supplementary" -msgstr "إعتبر الملفات من هذا النوع ملفات تكميلية (أي، ليست ملفات طلب نشر أساسية)" - msgid "manager.setup.genres.key" msgstr "المفتاح" diff --git a/locale/ar_IQ/submission.po b/locale/ar_IQ/submission.po index a9abe54f520..128977410c7 100644 --- a/locale/ar_IQ/submission.po +++ b/locale/ar_IQ/submission.po @@ -420,9 +420,6 @@ msgstr "مقبول" msgid "submission.acknowledge" msgstr "إقرار" -msgid "submission.addSuppFile" -msgstr "أضف ملفاً تكميلياً" - msgid "submission.ask" msgstr "إسأل" @@ -570,8 +567,9 @@ msgstr "حذف المعلومة من سجل الوقائع" msgid "submission.event.submissionSubmitted" msgstr "طلب النشر الأساسي إكتمل." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "التنقيح \"{$name}\" (رمز الملف {$fileId}-{$fileRevision}) قد رُفع إلى الموقع." +msgstr "التنقيح \"{$name}\" (رمز الملف {$submissionFileId}) قد رُفع إلى الموقع." msgid "submission.event.general.metadataUpdated" msgstr "البيانات الوصفية للمؤلَّف تم تحديثها" @@ -729,9 +727,6 @@ msgstr "هل أنت متأكد من رغبتك في حذف هذا اللوح ا msgid "submission.layout.confirmDeleteGalleyImage" msgstr "هل أنت متأكد من رغبتك في إزالة هذه الصورة من اللوح الطباعي؟" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "هل أنت متأكد من رغبتك في حذف هذا الملف التكميلي نهائياً؟" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "حذف ملف الأنماط هذا." @@ -795,15 +790,9 @@ msgstr "نسخة التصميم" msgid "submission.layout.newGalley" msgstr "إنشاء لوح طباعي جديد" -msgid "submission.layout.newSupplementaryFile" -msgstr "رفع ملف تكميلي جديد" - msgid "submission.layout.noStyleFile" msgstr "لم تتم إضافة ملف أنماط إلى هذا اللوح الطباعي." -msgid "submission.layout.supplementaryFiles" -msgstr "تحكيم الملف التكميلي" - msgid "submission.layout.viewingGalley" msgstr "معاينة اللوح الطباعي للمؤلَّف" @@ -1038,9 +1027,6 @@ msgstr "إرفع ملف مناقشة" msgid "submission.summary" msgstr "الموجز" -msgid "submission.supplementaryFiles" -msgstr "الملفات التكميلية" - msgid "submission.supportingAgencies" msgstr "الوكالات الداعمة" @@ -1221,9 +1207,6 @@ msgstr "رفع الملف" msgid "submission.upload.copyeditedVersion" msgstr "إرفع ملفاً مدققاً" -msgid "submission.upload.fairCopy" -msgstr "رفع نسخة مصفاة" - msgid "submission.upload.uploadFiles" msgstr "رفع الملفات" diff --git a/locale/ca_ES/api.po b/locale/ca_ES/api.po index 1db4fa20654..5790eccc7d9 100644 --- a/locale/ca_ES/api.po +++ b/locale/ca_ES/api.po @@ -67,18 +67,18 @@ msgstr "" "El tema actiu, {$themePluginPath}, no està habilitat i podria no instal·lar-" "se." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "No s'ha pogut carregar l'arxiu a causa d'un error de configuració del " "servidor. Contacteu amb l'administrador/a de sistemes." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Els arxius més grans que {$maxSize} no es poden carregar." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Un o més arxius no es poden carregar." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "No s'ha trobat cap arxiu per carregar en aquesta sol·licitud." msgid "api.submissions.unknownError" diff --git a/locale/ca_ES/manager.po b/locale/ca_ES/manager.po index efaebaf54ac..1e8ab548b36 100644 --- a/locale/ca_ES/manager.po +++ b/locale/ca_ES/manager.po @@ -595,9 +595,6 @@ msgstr "Conflicte d’interessos" msgid "manager.setup.favicon" msgstr "Icona de web" -msgid "manager.setup.groupType" -msgstr "Classificació dels tipus de fitxers" - msgid "manager.setup.reviewOptions.numWeeksPerResponse" msgstr "Nombre de setmanes per acceptar o rebutjar una sol·licitud de revisió." @@ -1037,9 +1034,6 @@ msgstr "Elimina aquest gènere" msgid "grid.action.restoreGenres" msgstr "Restableix els gèneres a la configuració per defecte" -msgid "manager.setup.genres.dependent" -msgstr "Marca els fitxers d'aquest tipus com a fitxers dependents (p. ex., tramesa no principal)" - msgid "manager.settings.wizard" msgstr "Assistent de configuració" @@ -1887,11 +1881,6 @@ msgstr "Un identificador simbòlic curt per a aquest gènere." msgid "manager.setup.genres.key" msgstr "Contrasenya" -msgid "manager.setup.genres.supplementary" -msgstr "" -"Marcar els arxius d'aquest tipus com a arxius addicionals (p. ex.: contingut " -"de tramesa no primari)" - msgid "settings.libraryFiles.public.selectLibraryFiles" msgstr "Seleccioneu els arxius de biblioteca per adjuntar" diff --git a/locale/ca_ES/submission.po b/locale/ca_ES/submission.po index d2bce5fab53..df787344080 100644 --- a/locale/ca_ES/submission.po +++ b/locale/ca_ES/submission.po @@ -365,9 +365,6 @@ msgstr "Acceptada" msgid "submission.acknowledge" msgstr "Rebuda" -msgid "submission.addSuppFile" -msgstr "Afegeix un fitxer addicional" - msgid "submission.ask" msgstr "Sol·licita" @@ -506,8 +503,9 @@ msgstr "Suprimeix l’entrada del registre" msgid "submission.event.submissionSubmitted" msgstr "La tramesa inicial s'ha completat." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "S'ha carregat la revisió «{$name}» (amb l'identificador de fitxer {$fileId}-{$fileRevision})." +msgstr "S'ha carregat la revisió «{$name}» (amb l'identificador de fitxer {$submissionFileId})." msgid "submission.event.general.metadataUpdated" msgstr "Metadades de tramesa actualitzades" @@ -620,9 +618,6 @@ msgstr "Esteu segur/a que voleu suprimir aquestes galerades de manera permanent? msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Esteu segur/a que voleu suprimir aquesta imatge de les galerades?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Esteu segur/a que voleu suprimir aquest fitxer addicional de manera permanent?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Suprimeix aquestes galerades." @@ -686,15 +681,9 @@ msgstr "Versió maquetada" msgid "submission.layout.newGalley" msgstr "Crea galerades noves" -msgid "submission.layout.newSupplementaryFile" -msgstr "Penja un fitxer addicional nou" - msgid "submission.layout.noStyleFile" msgstr "No s’ha afegit cap fitxer de full d’estil a aquestes galerades." -msgid "submission.layout.supplementaryFiles" -msgstr "Revisió del fitxer addicional" - msgid "submission.layout.viewingGalley" msgstr "Visualitza les galerades de la tramesa" @@ -932,9 +921,6 @@ msgstr "Carrega fitxers que aquest fitxer de producció necessita per mostrar-lo msgid "submission.summary" msgstr "Resum" -msgid "submission.supplementaryFiles" -msgstr "Fitxers addicionals" - msgid "submission.supportingAgencies" msgstr "Agències de suport" @@ -1099,9 +1085,6 @@ msgstr "Puja un fitxer" msgid "submission.upload.copyeditedVersion" msgstr "Carrega un fitxer corregit" -msgid "submission.upload.fairCopy" -msgstr "Carrega una còpia en net" - msgid "submission.upload.uploadFiles" msgstr "Carrega fitxers" diff --git a/locale/cs_CZ/manager.po b/locale/cs_CZ/manager.po index 2f61ea62b91..734ecc541d8 100644 --- a/locale/cs_CZ/manager.po +++ b/locale/cs_CZ/manager.po @@ -878,12 +878,6 @@ msgstr "Nastavení distribuce" msgid "manager.reviewForms.confirmActivate" msgstr "Jste si jisti, že chcete povolit tento formulář recenzenta? Jakmile bude přiřazen k jakékoliv recenzi, nebude možné ho zakázat." -msgid "manager.setup.genres.dependent" -msgstr "Označte soubory tohoto typu jako závislé soubory (např. nezobrazované s publikovaným obsahem)" - -msgid "manager.setup.genres.supplementary" -msgstr "Označit soubory tohoto typu jako doplňkové soubory (např. ty, co neobsahují primární obsah článku)" - msgid "manager.settings.wizard" msgstr "Průvodce nastavením" @@ -1055,9 +1049,6 @@ msgstr "Nikdy nepřipomínat" msgid "manager.setup.reviewOptions.noteOnModification" msgstr "Výchozí hodnoty lze upravit pro každou recenzi během redakčního procesu." -msgid "manager.setup.groupType" -msgstr "Seskupení podle typu souboru" - msgid "manager.setup.homepageImage" msgstr "Obrázek na domácí stránku" diff --git a/locale/cs_CZ/submission.po b/locale/cs_CZ/submission.po index 0924be82f48..8cbdda7d902 100644 --- a/locale/cs_CZ/submission.po +++ b/locale/cs_CZ/submission.po @@ -26,9 +26,6 @@ msgstr "Přijata" msgid "submission.acknowledge" msgstr "Poděkovat" -msgid "submission.addSuppFile" -msgstr "Přidat doplňkový soubor" - msgid "submission.ask" msgstr "Vyžádáno" @@ -317,9 +314,6 @@ msgstr "Jste si jisti, že chcete trvale smazat tuto sazebnici?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Jste si jisti, že chcete odstranit tento obrázek ze sazebnice?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Jste si jisti, že chcete trvale smazat tento doplňkový soubor?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Smazat tento stylesheet." @@ -377,15 +371,9 @@ msgstr "Verze typografa" msgid "submission.layout.newGalley" msgstr "Vytvořit novou sazebnici" -msgid "submission.layout.newSupplementaryFile" -msgstr "Nahrát nový doplňkový soubor" - msgid "submission.layout.noStyleFile" msgstr "K této sazebnici nebyl přidán žádný stylesheet." -msgid "submission.layout.supplementaryFiles" -msgstr "Přehled doplňkových souborů" - msgid "submission.layout.viewingGalley" msgstr "Prohlížení sazebnice příspěvků" @@ -551,9 +539,6 @@ msgstr "Odesílatel" msgid "submission.summary" msgstr "Souhrn" -msgid "submission.supplementaryFiles" -msgstr "Doplňkové soubory" - msgid "submission.supportingAgencies" msgstr "Podporující instituce (granty)" @@ -998,8 +983,9 @@ msgstr "Úvodní odeslání zakončeno." msgid "submission.event.fileUploaded" msgstr "Soubor \"{$originalFileName}\" byl nahrán pro příspěvek {$submissionId} uživatelem {$username}." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Oprava \"{$name}\" (file ID {$fileId}-{$fileRevision}) byla nahrána." +msgstr "Oprava \"{$name}\" (file ID {$submissionFileId}) byla nahrána." msgid "submission.event.participantAdded" msgstr "Uživatel {$name} ({$username}) byl přiřazen k příspěvku jako {$userGroupName}." @@ -1130,9 +1116,6 @@ msgstr "Nahrát soubor" msgid "submission.upload.copyeditedVersion" msgstr "Nahrát soubor redakčních úprav" -msgid "submission.upload.fairCopy" -msgstr "Nahrát férovou kopii (Fair Copy)" - msgid "submission.upload.uploadFiles" msgstr "Nahrát soubory" diff --git a/locale/da_DK/api.po b/locale/da_DK/api.po index 59251fd0b96..20bb1d02bf4 100644 --- a/locale/da_DK/api.po +++ b/locale/da_DK/api.po @@ -64,18 +64,18 @@ msgstr "" "Det aktive tema, {$themePluginPath}, er ikke aktiveret og er muligvis ikke " "installeret." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "Filen kunne ikke uploades på grund af en serverkonfigurationsfejl. Kontakt " "systemadministratoren." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Filer, der er større end {$maxSize}, kan ikke uploades." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "En eller flere filer kunne ikke uploades." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "" "Ingen af de filer, der skal uploades, blev fundet på baggrund af anmodningen." diff --git a/locale/da_DK/manager.po b/locale/da_DK/manager.po index 94964f2c6f9..e2ccd8212e8 100644 --- a/locale/da_DK/manager.po +++ b/locale/da_DK/manager.po @@ -655,15 +655,6 @@ msgstr "E-mails" msgid "grid.action.addGenre" msgstr "Tilføj materialetype" -msgid "manager.setup.genres.dependent" -msgstr "Registrer disse filtyper som underordnede filer (f.eks. hvis de ikke skal indgå som en del af det publicerede materiale)" - -msgid "manager.setup.genres.supplementary" -msgstr "Registrer disse filtyper som supplerende filer (f.eks. når det ikke er en del af det primære indsendelsesmateriale - artikeltekst)" - -msgid "manager.setup.groupType" -msgstr "Gruppering af filtyper" - msgid "manager.setup.authorGuidelines" msgstr "Retningslinjer for forfattere" diff --git a/locale/da_DK/submission.po b/locale/da_DK/submission.po index 3f98003fd20..980ec8b61bb 100644 --- a/locale/da_DK/submission.po +++ b/locale/da_DK/submission.po @@ -26,9 +26,6 @@ msgstr "Accepteret" msgid "submission.acknowledge" msgstr "Bekræft" -msgid "submission.addSuppFile" -msgstr "Tilføj en supplerende fil" - msgid "submission.ask" msgstr "Spørg" @@ -213,9 +210,6 @@ msgstr "Er du sikker på, at du vil slette denne publiceringsfil permanent?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Er du sikker på, at du vil fjerne dette billede fra publiceringsfilen?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Er du sikker på, at du vil slette denne supplerende fil permanent?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Slet dette stylesheet." @@ -273,15 +267,9 @@ msgstr "Layout-version" msgid "submission.layout.newGalley" msgstr "Opret ny publicerings-version" -msgid "submission.layout.newSupplementaryFile" -msgstr "Overfør ny supplerende fil" - msgid "submission.layout.noStyleFile" msgstr "Der er ikke føjet en stylesheet-fil til denne publiceringsversion." -msgid "submission.layout.supplementaryFiles" -msgstr "Bedømmelse af supplerende fil" - msgid "submission.layout.viewingGalley" msgstr "Visning af manuskript (publiceringsversion)" @@ -444,9 +432,6 @@ msgstr "Indsender" msgid "submission.summary" msgstr "Resumé" -msgid "submission.supplementaryFiles" -msgstr "Supplerende filer" - msgid "submission.titleAndAbstract" msgstr "Titel og resumé" @@ -1105,8 +1090,9 @@ msgstr "Copyright-år" msgid "submission.event.participantAdded" msgstr "\"{$name}\" ({$username}) blev tilknyttet denne indsendelse som {$userGroupName}." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Revision \"{$name}\" (file ID {$fileId}-{$fileRevision}) blev uploadet." +msgstr "Revision \"{$name}\" (file ID {$submissionFileId}) blev uploadet." msgid "submission.event.lastRevisionDeleted" msgstr "En fil \"{$title}\" fik seneste revision fjernet af {$username}." @@ -1268,9 +1254,6 @@ msgstr "Upload en biblioteksfil" msgid "submission.upload.libraryCategory" msgstr "Kategori for indsendelsesdokument" -msgid "submission.upload.fairCopy" -msgstr "Upload kopi af renskrift" - msgid "submission.upload.uploadFiles" msgstr "Upload filer" diff --git a/locale/de_DE/api.po b/locale/de_DE/api.po index a50269a7c90..b1b563fb984 100644 --- a/locale/de_DE/api.po +++ b/locale/de_DE/api.po @@ -69,18 +69,18 @@ msgstr "" "Das aktive Theme, {$themePluginPath}, ist nicht angeschalten oder nicht " "installiert." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "Die Datei konnte aufgrund eines Fehlers in der Serverkonfiguration nicht " "hochgeladen werden. Bitte kontaktieren Sie den Systemadministrator." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Dateien größer als {$maxSize} können nicht hochgeladen werden." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Eine oder mehrere Dateien konnten nicht hochgeladen werden." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "Mit dieser Anfrage konnte keine hochzuladende Datei gefunden werden." msgid "api.submissions.404.siteWideEndpoint" diff --git a/locale/de_DE/manager.po b/locale/de_DE/manager.po index ec260eaeac7..98aea2db9a3 100644 --- a/locale/de_DE/manager.po +++ b/locale/de_DE/manager.po @@ -618,9 +618,6 @@ msgstr "Es gab einen Fehler beim Löschen dieses Eintrags." msgid "manager.setup.favicon" msgstr "Favicon" -msgid "manager.setup.groupType" -msgstr "Dateitypgruppierung" - msgid "manager.setup.homepageImage" msgstr "Startseitengrafik" @@ -1110,12 +1107,6 @@ msgstr "Dieses Genre entfernen" msgid "grid.action.restoreGenres" msgstr "Genres auf Standard zurücksetzen" -msgid "manager.setup.genres.dependent" -msgstr "Dateien dieses Typs als abhängige Dateien markieren (z.B. nicht Primärveröffentlichungen)" - -msgid "manager.setup.genres.supplementary" -msgstr "Dateien dieses Typs als Zusatzdateien markieren (z.B. kein primärer Beitragsinhalt)" - msgid "manager.setup.genres.key" msgstr "Schlüssel" diff --git a/locale/de_DE/submission.po b/locale/de_DE/submission.po index 12de8c25bcf..9d1a96bacdf 100644 --- a/locale/de_DE/submission.po +++ b/locale/de_DE/submission.po @@ -407,9 +407,6 @@ msgstr "Angenommen" msgid "submission.acknowledge" msgstr "Bestätigen" -msgid "submission.addSuppFile" -msgstr "Zusatzdatei hinzufügen" - msgid "submission.ask" msgstr "Fragen" @@ -555,8 +552,9 @@ msgstr "Eintrag löschen" msgid "submission.event.submissionSubmitted" msgstr "Ersteinreichung abgeschlossen." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Revisionsdatei \"{$name}\" (Datei-ID {$fileId}-{$fileRevision}) wurde hochgeladen." +msgstr "Revisionsdatei \"{$name}\" (Datei-ID {$submissionFileId}) wurde hochgeladen." msgid "submission.event.general.metadataUpdated" msgstr "Metadaten des Beitrags aktualisiert" @@ -690,9 +688,6 @@ msgstr "Sind Sie sicher, dass Sie die Fahnen endgültig löschen wollen?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Sind Sie sicher, dass Sie das Bild aus der Fahne entfernen wollen?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Sind Sie sicher, dass Sie diese Zusatzdatei endgültig löschen wollen?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Diese Formatvorlage löschen." @@ -756,15 +751,9 @@ msgstr "Layoutversion" msgid "submission.layout.newGalley" msgstr "Neue Fahne anlegen" -msgid "submission.layout.newSupplementaryFile" -msgstr "Neue Zusatzdatei hochladen" - msgid "submission.layout.noStyleFile" msgstr "Dieser Fahne wurde keine Formatvorlage hinzugefügt." -msgid "submission.layout.supplementaryFiles" -msgstr "Begutachtung der Zusatzdatei" - msgid "submission.layout.viewingGalley" msgstr "Fahne anzeigen" @@ -999,9 +988,6 @@ msgstr "Eine Diskussionsdatei hochladen" msgid "submission.summary" msgstr "Zusammenfassung" -msgid "submission.supplementaryFiles" -msgstr "Zusätzliche Dateien" - msgid "submission.supportingAgencies" msgstr "Unterstützung" @@ -1184,9 +1170,6 @@ msgstr "Datei hochladen" msgid "submission.upload.copyeditedVersion" msgstr "Lektorierte Datei hochladen" -msgid "submission.upload.fairCopy" -msgstr "Reinschrift hochladen" - msgid "submission.upload.uploadFiles" msgstr "Dateien hochladen" diff --git a/locale/el_GR/manager.po b/locale/el_GR/manager.po index 0eed8042e7b..88eb1002245 100644 --- a/locale/el_GR/manager.po +++ b/locale/el_GR/manager.po @@ -678,9 +678,6 @@ msgstr "Μεταφορτώστε μια εικόνα που θα εμφανίζ msgid "manager.setup.homepageImage" msgstr "Εικόνα Αρχικής Σελίδας" -msgid "manager.setup.groupType" -msgstr "Ομαδοποίηση τύπων αρχείων" - msgid "manager.setup.favicon" msgstr "Εικονίδιο ιστοτόπου" @@ -1044,16 +1041,6 @@ msgstr "Ένα προαιρετικό σύντομο συμβολικό αναγ msgid "manager.setup.genres.key" msgstr "Κλειδί" -msgid "manager.setup.genres.supplementary" -msgstr "" -"Σήμανση αρχείων αυτού του τύπου ως συμπληρωματικά αρχεία (π.χ. όχι πρωτεύον " -"περιεχόμενο υποβολής)" - -msgid "manager.setup.genres.dependent" -msgstr "" -"Σήμανση αρχείων αυτού του τύπου ως εξαρτώμενα αρχεία (π.χ. να μην " -"παρατίθενται με δημοσιευμένο περιεχόμενο)" - msgid "grid.action.restoreGenres" msgstr "Επαναφέρετε τα στοιχεία στις προεπιλεγμένες ρυθμίσεις" diff --git a/locale/el_GR/submission.po b/locale/el_GR/submission.po index 632437419d8..51bea68fcc4 100644 --- a/locale/el_GR/submission.po +++ b/locale/el_GR/submission.po @@ -26,9 +26,6 @@ msgstr "Δεκτό" msgid "submission.acknowledge" msgstr "Ελήφθη" -msgid "submission.addSuppFile" -msgstr "Πρόσθεση Συμπληρωματικού Αρχείου" - msgid "submission.ask" msgstr "Ερώτημα" @@ -221,9 +218,6 @@ msgstr "Είστε βέβαιοι ότι επιθυμείτε να διαγρά msgid "submission.layout.confirmDeleteGalleyImage" msgstr " Είστε βέβαιοι ότι επιθυμείτε να καταργήσετε τη συγκεκριμένη εικόνα από το Τυπογραφικό Δοκίμιο;" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr " Είστε βέβαιοι ότι επιθυμείτε να διαγράψετε οριστικά το συγκεκριμένο συμπληρωματικό αρχείο;" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Διαγραφή του συγκεκριμένου StyleSheet." @@ -281,15 +275,9 @@ msgstr "Έκδοση Σελιδοποίησης" msgid "submission.layout.newGalley" msgstr "Δημιουργία νέου Τυπογραφικού Δοκιμίου" -msgid "submission.layout.newSupplementaryFile" -msgstr "Φόρτωση νέου συμπληρωματικού αρχείου" - msgid "submission.layout.noStyleFile" msgstr "Δεν έχει γίνει προσθήκη Stylesheet στο συγκεκριμένο Τυπογραφικό Δοκίμιο." -msgid "submission.layout.supplementaryFiles" -msgstr "Αξιολόγηση συμπληρωματικού αρχείου " - msgid "submission.layout.viewingGalley" msgstr "Προβολή Υποβληθέντων Τυπογραφικών Δοκιμίων" @@ -452,9 +440,6 @@ msgstr "Υποβολέας" msgid "submission.summary" msgstr "Σύνοψη" -msgid "submission.supplementaryFiles" -msgstr "Συμπληρωματικά Αρχεία" - msgid "submission.titleAndAbstract" msgstr "Τίτλος και Περίληψη" @@ -1903,10 +1888,11 @@ msgstr "Προβολή μηνύματος" msgid "submission.event.subjectPrefix" msgstr "Εστάλη ένα μήνυμα:" +#, fuzzy msgid "submission.event.fileRevised" msgstr "" "Μεταφορτώθηκε η αναθεώρηση \"{$name}\" (ID αρχείου " -"{$fileId}-{$fileRevision})." +"{$submissionFileId})." msgid "submission.event.submissionSubmitted" msgstr "Η πρωτότυπη Υποβολή ολοκληρώθηκε." diff --git a/locale/en_US/api.po b/locale/en_US/api.po index 31dd1b228db..906fdbcd9e2 100644 --- a/locale/en_US/api.po +++ b/locale/en_US/api.po @@ -29,6 +29,18 @@ msgstr "The announcement you requested was not found." msgid "api.emailTemplates.404.templateNotFound" msgstr "The email template you requested was not found." +msgid "api.files.400.noUpload" +msgstr "No file to be uploaded could be found with the request." + +msgid "api.files.400.uploadFailed" +msgstr "One or more files could not be uploaded." + +msgid "api.files.400.fileSize" +msgstr "Files larger than {$maxSize} can not be uploaded." + +msgid "api.files.400.config" +msgstr "File could not be uploaded because of a server configuration error. Please contact the system administrator." + msgid "api.publication.403.alreadyPublished" msgstr "The publication you want to publish is already published." @@ -62,6 +74,38 @@ msgstr "You do not have enough space in your user directory. The file you are up msgid "api.publicFiles.500.badFilesDir" msgstr "The public files directory was not found or files can not be saved to it. Please contact your administrator to resolve this issue." +msgid "api.submissionFiles.400.assocTypeAndIdRequired" +msgstr "You can not modify the file association without passing an assocType and assocId." + +msgid "api.submissionFiles.400.noFileStageId" + +msgid "api.submissionFiles.400.badDependentFileAssocType" +msgstr "A file from this file stage can not be a dependent file." + +msgid "api.submissionFiles.400.badNoteAssocType" +msgstr "You can not associated a file from this file stage a discussion note." + +msgid "api.submissionFiles.400.badReviewAssignmentAssocType" +msgstr "You can not associated a file from this file stage with a review assignment." + +msgid "api.submissionFiles.400.badReviewRoundAssocType" +msgstr "You can not associated a file from this file stage with a review round." + +msgid "api.submissionFiles.400.missingReviewRoundAssocType" +msgstr "You must provide a valid review round in order to upload to this file stage by including assocType and assocId properties." + +msgid "api.submissionFiles.400.reviewRoundSubmissionNotMatch" +msgstr "The review round for this file is not part of the specified submission stage." + +msgid "api.submissionFiles.403.unauthorizedFileStageId" +msgstr "You are not authorized to access files from this stage." + +msgid "api.submissionFiles.403.unauthorizedFileStageIdWrite" +msgstr "You are not authorized to add or edit files from this stage." + +msgid "api.submissionFiles.403.unauthorizedReviewRound" +msgstr "You are not authorized to access files from this review round." + msgid "api.submissions.403.userCantEdit" msgstr "You are not allowed to edit this publication." @@ -104,17 +148,20 @@ msgstr "Your request could not be fulfilled because it is missing required infor msgid "api.submissions.400.invalidIssueIdentifiers" msgstr "The requested volume, number or year is not valid." -msgid "api.temporaryFiles.400.noUpload" -msgstr "No file to be uploaded could be found with the request." +msgid "api.submissions.files.403.cantChangeSubmission" +msgstr "You can not change the submission that this file is assigned to." -msgid "api.temporaryFiles.409.uploadFailed" -msgstr "One or more files could not be uploaded." +msgid "api.submissions.files.400.submissionsDidNotMatch" +msgstr "The file that you requested is not part of this submission." -msgid "api.temporaryFiles.400.fileSize" -msgstr "Files larger than {$maxSize} can not be uploaded." +msgid "api.submissions.files.400.sameFileStage" +msgstr "The file is already added to this stage." -msgid "api.temporaryFiles.400.config" -msgstr "File could not be uploaded because of a server configuration error. Please contact the system administrator." +msgid "api.submissions.files.400.invalidFileStage" +msgstr "The file stage is not valid." + +msgid "api.submissions.files.400.noParams" +msgstr "No changes could be found in the request to edit this file." msgid "api.themes.404.themeUnavailable" msgstr "The active theme, {$themePluginPath}, is not enabled and may not be installed." diff --git a/locale/en_US/common.po b/locale/en_US/common.po index 4d1155d6c70..20825209f56 100644 --- a/locale/en_US/common.po +++ b/locale/en_US/common.po @@ -354,6 +354,9 @@ msgstr "Download PDF" msgid "common.edit" msgstr "Edit" +msgid "common.editItem" +msgstr "Edit {$name}" + msgid "common.enable" msgstr "Enable" @@ -1697,6 +1700,18 @@ msgstr "Invalid submission." msgid "user.authorization.invalidRepresentation" msgstr "Invalid representation." +msgid "user.authorization.invalidReviewRound" +msgstr "Invalid review round." + +msgid "user.authorization.invalidQuery" +msgstr "Invalid discussion." + +msgid "user.authorization.unauthorizedNote" +msgstr "You do not have access to this discussion message." + +msgid "user.authorization.unauthorizedReviewAssignment" +msgstr "You do not have access to this review assignment." + msgid "user.authorization.authorRoleMissing" msgstr "You do not currently have sufficient privileges to view the submission. Please edit your profile to ensure that you have been granted the appropriate roles under \"Register As\"." diff --git a/locale/en_US/manager.po b/locale/en_US/manager.po index 70c7b0c602c..7fd81e146ee 100644 --- a/locale/en_US/manager.po +++ b/locale/en_US/manager.po @@ -673,9 +673,6 @@ msgstr "There was an error when deleting this item." msgid "manager.setup.favicon" msgstr "Favicon" -msgid "manager.setup.groupType" -msgstr "File type grouping" - msgid "manager.setup.homepageImage" msgstr "Homepage Image" @@ -1240,11 +1237,14 @@ msgstr "Delete this Component" msgid "grid.action.restoreGenres" msgstr "Restore the components to the default settings" +msgid "manager.setup.genres.label" +msgstr "File Type" + msgid "manager.setup.genres.dependent" -msgstr "Mark files of this type as dependent files (e.g. not to be listed with published content)" +msgstr "These are dependent files, such as images displayed by a HTML file, and will not be displayed with published content." msgid "manager.setup.genres.supplementary" -msgstr "Mark files of this type as supplementary files (e.g. not primary submission content)" +msgstr "These are supplementary files, such as data sets and research materials, and will be displayed separately from the main publication files." msgid "manager.setup.genres.key" msgstr "Key" @@ -1258,6 +1258,12 @@ msgstr "The key already exists." msgid "manager.setup.genres.key.alphaNumeric" msgstr "The key can contain only alphanumeric characters, underscores, and hyphens, and must begin and end with an alphanumeric character." +msgid "manager.setup.genres.metatadata" +msgstr "File Metadata" + +msgid "manager.setup.genres.metatadata.description" +msgstr "Select the type of metadata that these files may receive. Document should be selected for the main publication files, such as the downloadable PDF, so that these files inherit their metadata from the publication. Otherwise, choose Supplementary Content for most file types. Artwork is appropriate for files which require distinct credit, caption and licensing metadata." + msgid "manager.settings.wizard" msgstr "Settings Wizard" @@ -1616,6 +1622,9 @@ msgstr "The revision \"{$revisionId}\" for submission file \"{$fileId}\" would c msgid "plugins.importexport.native.error.submissionFileImportFailed" msgstr "The submission file could not be imported" +msgid "plugins.importexport.native.error.submissionFileSkipped" +msgstr "The submission file {$id} was skipped because it is attached to a record that will not be imported, such as a review assignment or discussion." + msgid "plugins.importexport.user.importExportErrors" msgstr "Import/Export errors:" diff --git a/locale/en_US/submission.po b/locale/en_US/submission.po index dbb9527cc61..78eded5b8f1 100644 --- a/locale/en_US/submission.po +++ b/locale/en_US/submission.po @@ -62,6 +62,18 @@ msgstr "Acknowledge the copyright statement" msgid "submission.submit.copyrightNoticeAgree" msgstr "Yes, I agree to abide by the terms of the copyright statement." +msgid "submission.submit.genre.label" +msgstr "What kind of file is this?" + +msgid "submission.submit.genre.description" +msgstr "Choose the option that best describes this file." + +msgid "submission.submit.genre.error" +msgstr "Please select the kind of file for each upload." + +msgid "submission.submit.removeConfirm" +msgstr "Are you sure you want to remove this file?" + msgid "submission.submit.whatNext" msgstr "What Happens Next?" @@ -419,9 +431,6 @@ msgstr "Accepted" msgid "submission.acknowledge" msgstr "Acknowledge" -msgid "submission.addSuppFile" -msgstr "Add a Supplementary File" - msgid "submission.ask" msgstr "Ask" @@ -570,7 +579,7 @@ msgid "submission.event.submissionSubmitted" msgstr "Initial submission completed." msgid "submission.event.fileRevised" -msgstr "Revision \"{$name}\" (file ID {$fileId}-{$fileRevision}) was uploaded." +msgstr "Revision \"{$name}\" was uploaded for file {$submissionFileId}." msgid "submission.event.general.metadataUpdated" msgstr "Submission metadata updated" @@ -635,6 +644,9 @@ msgstr "Files" msgid "submission.files.downloadAll" msgstr "Download All Files" +msgid "submission.file.notAllowedUploaderUserId" +msgstr "A record of the file uploader is saved when the file is created and can not be changed." + msgid "submission.galley" msgstr "Galley" @@ -728,9 +740,6 @@ msgstr "Are you sure you want to permanently delete this galley?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Are you sure you want to remove this image from the galley?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Are you sure you want to permanently delete this supplementary file?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Delete this style sheet." @@ -794,15 +803,9 @@ msgstr "Layout Version" msgid "submission.layout.newGalley" msgstr "Create New Galley" -msgid "submission.layout.newSupplementaryFile" -msgstr "Upload New Supplementary File" - msgid "submission.layout.noStyleFile" msgstr "No style sheet file has been added to this galley." -msgid "submission.layout.supplementaryFiles" -msgstr "Supplementary File Review" - msgid "submission.layout.viewingGalley" msgstr "Viewing Submission Galley" @@ -1037,9 +1040,6 @@ msgstr "Upload a Discussion File" msgid "submission.summary" msgstr "Summary" -msgid "submission.supplementaryFiles" -msgstr "Supplementary Files" - msgid "submission.supportingAgencies" msgstr "Supporting Agencies" @@ -1220,9 +1220,6 @@ msgstr "Upload File" msgid "submission.upload.copyeditedVersion" msgstr "Upload Copyedited File" -msgid "submission.upload.fairCopy" -msgstr "Upload Fair Copy" - msgid "submission.upload.uploadFiles" msgstr "Upload Files" @@ -1418,6 +1415,9 @@ msgstr "Draft Files" msgid "editor.submission.addAuditorError" msgstr "There was an error trying to assign the auditor. Please Try again." +msgid "submission.event.fileEdited" +msgstr "The metadata for file \"{$originalFileName}\" was edited by {$username}." + msgid "submission.event.fileUploaded" msgstr "A file \"{$originalFileName}\" was uploaded for submission {$submissionId} by {$username}." diff --git a/locale/es_ES/api.po b/locale/es_ES/api.po index 9eed0f12a68..df11451de4a 100644 --- a/locale/es_ES/api.po +++ b/locale/es_ES/api.po @@ -22,18 +22,18 @@ msgstr "" "El tema activo, {$themePluginPath}, no está habilitado y podría no " "instalarse." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "No se pudo cargar el archivo debido a un error de configuración del " "servidor. Contacte con el administrador/a de sistemas." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Los archivos más grandes que {$maxSize} no se pueden cargar." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Uno o más archivos no se han podido cargar." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "" "No se ha podido encontrar ningún archivo que cargar para esta solicitud." diff --git a/locale/es_ES/manager.po b/locale/es_ES/manager.po index cca383bf173..7524b7a18e6 100644 --- a/locale/es_ES/manager.po +++ b/locale/es_ES/manager.po @@ -588,9 +588,6 @@ msgstr "Conflicto de intereses" msgid "manager.setup.competingInterestsDescription" msgstr "Se pedirá a los revisores/as que cumplan con la política de divulgación de conflictos de intereses que especifique a continuación." -msgid "manager.setup.groupType" -msgstr "Agrupación de tipos de archivo" - msgid "manager.setup.reviewOptions.numWeeksPerResponse" msgstr "Número de semanas para aceptar o rechazar una petición de revisión." @@ -931,9 +928,6 @@ msgstr "Eliminar este género" msgid "grid.action.restoreGenres" msgstr "Restaurar todos los géneros a la configuración por defecto" -msgid "manager.setup.genres.dependent" -msgstr "Marcar los archivos de este tipo como archivos dependientes" - msgid "manager.settings.wizard" msgstr "Asistente de ajustes" @@ -1140,11 +1134,6 @@ msgstr "Actualizado" msgid "manager.plugins.noCompatibleVersion.short" msgstr "No disponible" -msgid "manager.setup.genres.supplementary" -msgstr "" -"Marcar los ficheros de este tipo como ficheros complementarios (p. ej.: " -"contenido de envío no primario)" - msgid "manager.reviewForms" msgstr "Formularios de revisión" diff --git a/locale/es_ES/submission.po b/locale/es_ES/submission.po index 509ac627a93..92d7ed89774 100644 --- a/locale/es_ES/submission.po +++ b/locale/es_ES/submission.po @@ -359,9 +359,6 @@ msgstr "Aceptado" msgid "submission.acknowledge" msgstr "Recibido" -msgid "submission.addSuppFile" -msgstr "Añadir archivo complementario" - msgid "submission.ask" msgstr "Preguntar" @@ -476,8 +473,9 @@ msgstr "Borrar entrada de registro" msgid "submission.event.submissionSubmitted" msgstr "Envío inicial completado." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Se cargó la revisión \"{$name}\" (Id. de archivo {$fileId}-{$fileRevision})." +msgstr "Se cargó la revisión \"{$name}\" (Id. de archivo {$submissionFileId})." msgid "submission.event.general.metadataUpdated" msgstr "Metadatos de envío actualizados" @@ -593,9 +591,6 @@ msgstr "¿Seguro que quieres eliminar esta galerada de forma permanente?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "¿Seguro que quieres eliminar esta imagen de la galerada?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "¿Seguro que quieres eliminar este archivo adicional de forma permanente?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Eliminar esta hoja de estilo." @@ -659,15 +654,9 @@ msgstr "Versión maquetada" msgid "submission.layout.newGalley" msgstr "Crear nueva galerada" -msgid "submission.layout.newSupplementaryFile" -msgstr "Subir nuevo archivo complementario" - msgid "submission.layout.noStyleFile" msgstr "No se ha añadido ningún archivo de hoja de estilo a esta galerada." -msgid "submission.layout.supplementaryFiles" -msgstr "Revisión de archivo complementario" - msgid "submission.layout.viewingGalley" msgstr "Ver galerada de envío" @@ -893,9 +882,6 @@ msgstr "Subir un archivo dependiente" msgid "submission.summary" msgstr "Resumen" -msgid "submission.supplementaryFiles" -msgstr "Archivos complementarios" - msgid "submission.supportingAgencies" msgstr "Agencias de apoyo" @@ -1045,9 +1031,6 @@ msgstr "Subir archivo" msgid "submission.upload.copyeditedVersion" msgstr "Subir archivo corregido" -msgid "submission.upload.fairCopy" -msgstr "Subir copia en limpio" - msgid "submission.upload.uploadFiles" msgstr "Subir archivos" diff --git a/locale/eu_ES/submission.po b/locale/eu_ES/submission.po index 4f5a8285984..d3a866a2c35 100644 --- a/locale/eu_ES/submission.po +++ b/locale/eu_ES/submission.po @@ -23,9 +23,6 @@ msgstr "Onartua" msgid "submission.acknowledge" msgstr "Hartu-agiria" -msgid "submission.addSuppFile" -msgstr "Gehitu Fitxategi osagarria" - msgid "submission.ask" msgstr "Galdetu" @@ -206,9 +203,6 @@ msgstr "Ziur zaude galerada hau betiko ezabatu nahi duzula?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Ziur zaude irudi hau kendu nahi duzula galeradatik?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Ziur zaude fitxategi osagarri hau betiko ezabatu nahi duzula?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Ezabatu estilo-orri hau." @@ -266,15 +260,9 @@ msgstr "Maketazio-bertsioa" msgid "submission.layout.newGalley" msgstr "Sortu galerada berria" -msgid "submission.layout.newSupplementaryFile" -msgstr "Kargatu Fitxategi osagarri berria" - msgid "submission.layout.noStyleFile" msgstr "Galerada honi ez zaio estilo-orriaren fitxategirik gehitu." -msgid "submission.layout.supplementaryFiles" -msgstr "Fitxategi osagarriaren berrikustea" - msgid "submission.layout.viewingGalley" msgstr "Bidalketaren galerada ikusi" @@ -437,9 +425,6 @@ msgstr "Bidaltzailea" msgid "submission.summary" msgstr "Sumarioa" -msgid "submission.supplementaryFiles" -msgstr "Fitxategi osagarriak" - msgid "submission.titleAndAbstract" msgstr "Titulua eta laburpena" diff --git a/locale/fa_IR/manager.po b/locale/fa_IR/manager.po index b2051febb7b..eeac5ee3143 100644 --- a/locale/fa_IR/manager.po +++ b/locale/fa_IR/manager.po @@ -689,9 +689,6 @@ msgstr "سرویس‌های استخراج ارجاعات" msgid "manager.setup.filter.pleaseSelect" msgstr "لطفاً انتخاب کنید ..." -msgid "manager.setup.groupType" -msgstr "دسته‌بندی نوع فایل" - msgid "manager.setup.homepageImage" msgstr "تصویر صفحه‌ی اصلی" @@ -1169,12 +1166,6 @@ msgstr "برگرداندن اجزاء به تنظیمات پیش‌فرض" msgid "manager.setup.genres.sortable" msgstr "اجازه به فایل‌های این نوع برای دسته‌بندی برحسب فصل" -msgid "manager.setup.genres.dependent" -msgstr "نشانه‌گذاری این نوع از فایل‌ها به عنوان فایل‌های مستقل (به این معنی که به همراه محتوای منتشر شده لیست نمی‌شود)" - -msgid "manager.setup.genres.supplementary" -msgstr "نشانه‌گذاری این نوع از فایل‌ها به عنوان فایل‌های مکمل (به این معنی که محتوای ثبت شده اصلی نمی‌باشد)" - msgid "manager.settings.wizard" msgstr "تنظیمات مرحله به مرحله" diff --git a/locale/fa_IR/submission.po b/locale/fa_IR/submission.po index 7d764aaaf87..8aa5008d3b2 100644 --- a/locale/fa_IR/submission.po +++ b/locale/fa_IR/submission.po @@ -401,9 +401,6 @@ msgstr "پذیرفته‌شده" msgid "submission.acknowledge" msgstr "تشکر" -msgid "submission.addSuppFile" -msgstr "افزودن فایل مطالب تکمیلی" - msgid "submission.ask" msgstr "درخواست" @@ -749,8 +746,9 @@ msgstr "حذف سابقه" msgid "submission.event.submissionSubmitted" msgstr "مقاله اراسل شد." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "اصلاحات \"{$name}\" (با شناسه فایل {$fileId}-{$fileRevision}) آپلود شد." +msgstr "اصلاحات \"{$name}\" (با شناسه فایل {$submissionFileId}) آپلود شد." msgid "submission.event.general.metadataUpdated" msgstr "فراداده ارسال شده به روز رسانی شد" @@ -884,9 +882,6 @@ msgstr "آیا میخواهید برای همیشه این کلیشه چاپی msgid "submission.layout.confirmDeleteGalleyImage" msgstr "آیا میخواهید این تصویر را از این کلیشه چاپی حذف کنید؟" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "آیا میخواهید برای همیشه این فایل مکمل را حذف کنید؟" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "حذف این استایل شیت" @@ -950,15 +945,9 @@ msgstr "نسخه صفحه‌آرا" msgid "submission.layout.newGalley" msgstr "ایجاد کلیشه چاپی جدید" -msgid "submission.layout.newSupplementaryFile" -msgstr "آپلود فایل مکمل جدید" - msgid "submission.layout.noStyleFile" msgstr "هیچ فایل استایل شیتی برای این کلیشه چاپی تعیین نشده است" -msgid "submission.layout.supplementaryFiles" -msgstr "بررسی فایل های مکمل" - msgid "submission.layout.viewingGalley" msgstr "مشاهده کلیشه چاپی ارسال شده" @@ -1193,9 +1182,6 @@ msgstr "آپلود فایل گفتگو" msgid "submission.summary" msgstr "خلاصه" -msgid "submission.supplementaryFiles" -msgstr "فایل های مکمل" - msgid "submission.supportingAgencies" msgstr "آژانس های پشتیبانی کننده" @@ -1346,9 +1332,6 @@ msgstr "آپلود فایل" msgid "submission.upload.copyeditedVersion" msgstr "آپلود فایل ویراستاری شده" -msgid "submission.upload.fairCopy" -msgstr "آپلود کپی منصفانه" - msgid "submission.upload.uploadFiles" msgstr "آپلود فایل‌ها" diff --git a/locale/fi_FI/manager.po b/locale/fi_FI/manager.po index 84f478a79d4..a33407415bd 100644 --- a/locale/fi_FI/manager.po +++ b/locale/fi_FI/manager.po @@ -608,9 +608,6 @@ msgstr "Tätä kohdetta poistaessa tapahtui virhe." msgid "manager.setup.favicon" msgstr "Favicon" -msgid "manager.setup.groupType" -msgstr "Tiedostotyyppien ryhmittely" - msgid "manager.setup.homepageImage" msgstr "Etusivun kuva" @@ -1072,12 +1069,6 @@ msgstr "Poista tämä osa" msgid "grid.action.restoreGenres" msgstr "Palauta osien oletusasetukset" -msgid "manager.setup.genres.dependent" -msgstr "Merkitse tämäntyyppiset tiedostot riippuviksi tiedostoiksi (esim. ei luetteloida julkaistun sisällön kanssa)" - -msgid "manager.setup.genres.supplementary" -msgstr "Merkitse tämäntyyppiset tiedostot liitetiedostoiksi (esim. ei ensisijainen käsikirjoituksen sisältö)" - msgid "manager.settings.wizard" msgstr "Ohjattu asetusten luonti" diff --git a/locale/fi_FI/submission.po b/locale/fi_FI/submission.po index 9f9d60dd29c..9104ceeaa62 100644 --- a/locale/fi_FI/submission.po +++ b/locale/fi_FI/submission.po @@ -385,9 +385,6 @@ msgstr "Hyväksytty" msgid "submission.acknowledge" msgstr "Kuittaa vastaanotetuksi" -msgid "submission.addSuppFile" -msgstr "Lisää liitetiedosto" - msgid "submission.ask" msgstr "Kysy" @@ -511,8 +508,9 @@ msgstr "Poista lokimerkintä" msgid "submission.event.submissionSubmitted" msgstr "Käsikirjoituksen ensimmäinen lähetys suoritettu." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Korjaus \"{$name}\" (tiedoston tunniste {$fileId}-{$fileRevision}) ladattiin." +msgstr "Korjaus \"{$name}\" (tiedoston tunniste {$submissionFileId}) ladattiin." msgid "submission.event.general.metadataUpdated" msgstr "Käsikirjoituksen metatiedot päivitetty" @@ -646,9 +644,6 @@ msgstr "Haluatko varmasti poistaa tämän julkaistavan tiedoston pysyvästi?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Haluatko varmasti poistaa tämän kuvan julkaistavasta tiedostosta?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Haluatko varmasti poistaa tämän liitetiedoston pysyvästi?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Poista tämä tyylitiedosto." @@ -712,15 +707,9 @@ msgstr "Taittoversio" msgid "submission.layout.newGalley" msgstr "Luo uusi julkaistava tiedosto" -msgid "submission.layout.newSupplementaryFile" -msgstr "Lataa uusi liitetiedosto" - msgid "submission.layout.noStyleFile" msgstr "Tyylitiedostoa ei ole lisätty tähän julkaistavaan tiedostoon." -msgid "submission.layout.supplementaryFiles" -msgstr "Liitetiedoston arviointi" - msgid "submission.layout.viewingGalley" msgstr "Julkaistavan tiedoston katselu" @@ -955,9 +944,6 @@ msgstr "Lataa keskustelutiedosto" msgid "submission.summary" msgstr "Yhteenveto" -msgid "submission.supplementaryFiles" -msgstr "Liitetiedostot" - msgid "submission.supportingAgencies" msgstr "Kannattajajärjestöt" @@ -1108,9 +1094,6 @@ msgstr "Lataa tiedosto" msgid "submission.upload.copyeditedVersion" msgstr "Lataa teknisesti toimitettu tiedosto" -msgid "submission.upload.fairCopy" -msgstr "Lataa puhtaaksikirjoitettu teksti" - msgid "submission.upload.uploadFiles" msgstr "Lataa tiedostot" diff --git a/locale/fr_CA/api.po b/locale/fr_CA/api.po index 5f4288faccb..878a73d4633 100644 --- a/locale/fr_CA/api.po +++ b/locale/fr_CA/api.po @@ -85,16 +85,16 @@ msgstr "La demande n'a pu être remplie par manque d'information." msgid "api.submissions.400.invalidIssueIdentifiers" msgstr "Le volume, numéro ou année requis-e n'est pas valide." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "Aucun fichier à téléverser n'a pu être trouvé avec la requête." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Un ou plusieurs fichiers n'ont pu être téléversés." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Les fichiers plus gros que {$maxSize} ne peuvent pas être téléversés." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "Le fichier n'a pas pu être téléversé en raison d'une erreur de configuration du serveur. Veuillez contacter l'administrateur-trice du système." msgid "api.themes.404.themeUnavailable" diff --git a/locale/fr_CA/manager.po b/locale/fr_CA/manager.po index 4ff01f2f0ca..90b408069bd 100644 --- a/locale/fr_CA/manager.po +++ b/locale/fr_CA/manager.po @@ -668,9 +668,6 @@ msgstr "Il s'est produit une erreur lors de la suppression de cet élément." msgid "manager.setup.favicon" msgstr "Favicône" -msgid "manager.setup.groupType" -msgstr "Groupement par type de fichier" - msgid "manager.setup.homepageImage" msgstr "Image de la page d'accueil" @@ -1187,12 +1184,6 @@ msgstr "Supprimer cet élément" msgid "grid.action.restoreGenres" msgstr "Restaurer les paramètres par défaut des éléments" -msgid "manager.setup.genres.dependent" -msgstr "Marquer les fichiers de ce type en tant que fichiers reliés (ne sont pas des soumissions principales)" - -msgid "manager.setup.genres.supplementary" -msgstr "Marquer les fichiers de ce type en tant que fichiers supplémentaires (ne font pas partie du contenu principal de la soumission)" - msgid "manager.setup.genres.key" msgstr "Clé" diff --git a/locale/fr_CA/submission.po b/locale/fr_CA/submission.po index 7ba17d33b55..8f92b876a51 100644 --- a/locale/fr_CA/submission.po +++ b/locale/fr_CA/submission.po @@ -409,9 +409,6 @@ msgstr "Accepté-e" msgid "submission.acknowledge" msgstr "Envoyer un accusé de réception" -msgid "submission.addSuppFile" -msgstr "Ajouter un fichier supplémentaire" - msgid "submission.ask" msgstr "Demander" @@ -556,8 +553,9 @@ msgstr "Supprimer l'entrée du registre" msgid "submission.event.submissionSubmitted" msgstr "Soumission initiale complétée." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "La révision « {$name} » (# Fichier {$fileId}-{$fileRevision}) a été téléversée." +msgstr "La révision « {$name} » (# Fichier {$submissionFileId}) a été téléversée." msgid "submission.event.general.metadataUpdated" msgstr "Métadonnées de la soumission mises à jour" @@ -691,9 +689,6 @@ msgstr "Êtes-vous certain-e de vouloir supprimer définitivement ces épreuves msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Êtes-vous certain-e de vouloir supprimer cette image des épreuves ?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Êtes-vous certain-e de vouloir supprimer définitivement ce fichier supplémentaire ?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Supprimer cette feuille de style." @@ -757,15 +752,9 @@ msgstr "Version de mise en page" msgid "submission.layout.newGalley" msgstr "Créer de nouvelles épreuves" -msgid "submission.layout.newSupplementaryFile" -msgstr "Téléverser un nouveau fichier supplémentaire" - msgid "submission.layout.noStyleFile" msgstr "Aucun fichier de feuille de style n'a été ajouté à ces épreuves." -msgid "submission.layout.supplementaryFiles" -msgstr "Révision du fichier supplémentaire" - msgid "submission.layout.viewingGalley" msgstr "Affichage les épreuves de la soumission" @@ -1000,9 +989,6 @@ msgstr "Téléverser un fichier de discussion" msgid "submission.summary" msgstr "Sommaire" -msgid "submission.supplementaryFiles" -msgstr "Fichiers supplémentaires" - msgid "submission.supportingAgencies" msgstr "Organismes subventionnaires" @@ -1189,9 +1175,6 @@ msgstr "Téléverser le fichier" msgid "submission.upload.copyeditedVersion" msgstr "Téléverser le fichier corrigé" -msgid "submission.upload.fairCopy" -msgstr "Téléverser la copie au net" - msgid "submission.upload.uploadFiles" msgstr "Téléverser les fichiers" diff --git a/locale/fr_FR/manager.po b/locale/fr_FR/manager.po index bb79575c65e..c631dd08aa6 100644 --- a/locale/fr_FR/manager.po +++ b/locale/fr_FR/manager.po @@ -396,9 +396,6 @@ msgstr "Outils de lecture" msgid "manager.roles" msgstr "Rôles" -msgid "manager.setup.groupType" -msgstr "Groupement par type de fichier" - msgid "manager.setup.homeTitleImageInvalid" msgstr "Format d'image de l'en-tête de la page d'accueil invalide. Les formats acceptés sont .gif, .jpg, ou .png." @@ -976,11 +973,6 @@ msgstr "Vous devez donner un nom à ce fichier bibliothèque." msgid "settings.libraryFiles.typeRequired" msgstr "Un type de fichier est nécessaire pour ce fichier bibliothèque." -msgid "manager.setup.genres.dependent" -msgstr "" -"Marquer les fichiers de ce type comme fichiers dépendants (par exemple à ne " -"pas lister avec le contenu publié)" - msgid "manager.workflow" msgstr "Flux des travaux" @@ -1238,9 +1230,6 @@ msgstr "Officiel" msgid "manager.plugins.pluginGallery.certifications.reviewed" msgstr "Révisé" -msgid "manager.setup.genres.supplementary" -msgstr "Marquer les fichiers de ce type en tant que fichiers supplémentaires (par exemple, ne font pas partie du contenu principal de la soumission)" - msgid "manager.reviewForms.confirmActivate" msgstr "" "Voulez-vous vraiment activer ce formulaire d'évaluation ? Une fois qu'il est " diff --git a/locale/fr_FR/submission.po b/locale/fr_FR/submission.po index 7edc365a3a1..5b25eb4ae11 100644 --- a/locale/fr_FR/submission.po +++ b/locale/fr_FR/submission.po @@ -38,9 +38,6 @@ msgstr "Accepté" msgid "submission.acknowledge" msgstr "Envoyer un accusé de réception" -msgid "submission.addSuppFile" -msgstr "Ajouter un fichier supplémentaire" - msgid "submission.ask" msgstr "Demander" @@ -244,11 +241,6 @@ msgstr "Êtes-vous certain de vouloir supprimer définitivement cette épreuve  msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Êtes-vous certain de vouloir supprimer cette image de l'épreuve ?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "" -"Êtes-vous certain de vouloir supprimer définitivement ce fichier " -"supplémentaire ?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Supprimer cette feuille de style." @@ -306,15 +298,9 @@ msgstr "Version de mise en page" msgid "submission.layout.newGalley" msgstr "Créer une nouvelle épreuve" -msgid "submission.layout.newSupplementaryFile" -msgstr "Télécharger un nouveau fichier supplémentaire" - msgid "submission.layout.noStyleFile" msgstr "Aucun fichier de feuille de style n'a été ajouté à cette épreuve." -msgid "submission.layout.supplementaryFiles" -msgstr "Révision du fichier supplémentaire" - msgid "submission.layout.viewingGalley" msgstr "Voir l'épreuve de la submission" @@ -483,9 +469,6 @@ msgstr "Soumissionnaire" msgid "submission.summary" msgstr "Résumé" -msgid "submission.supplementaryFiles" -msgstr "Fichiers supplémentaires" - msgid "submission.titleAndAbstract" msgstr "Titre et résumé" @@ -1161,8 +1144,9 @@ msgstr "Exemple de couverture" msgid "submission.event.submissionSubmitted" msgstr "Soumission initiale complétée." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "La révision \"{$name}\" (file ID {$fileId}-{$fileRevision}) a été téléchargée." +msgstr "La révision \"{$name}\" (file ID {$submissionFileId}) a été téléchargée." msgid "submission.files.downloadAll" msgstr "Télécharger tous les fichiers" @@ -1319,9 +1303,6 @@ msgstr "Transférer le fichier" msgid "submission.upload.copyeditedVersion" msgstr "Transférer le fichier corrigé" -msgid "submission.upload.fairCopy" -msgstr "Transférer la copie au net" - msgid "submission.upload.uploadFiles" msgstr "Transférer les fichiers" diff --git a/locale/hr_HR/submission.po b/locale/hr_HR/submission.po index 3f0704f99b1..c22a52e4486 100644 --- a/locale/hr_HR/submission.po +++ b/locale/hr_HR/submission.po @@ -23,9 +23,6 @@ msgstr "Prihvaćena" msgid "submission.acknowledge" msgstr "Potvrđeno" -msgid "submission.addSuppFile" -msgstr "Dodavanje dopunske datoteke" - msgid "submission.ask" msgstr "Upit" @@ -194,9 +191,6 @@ msgstr "Jeste li sigurni da želite trajno izbrisati ovaj prijelom?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Jeste li sigurni da želite ukloniti ovu sliku iz prijeloma?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Jeste li sigurni da želite trajno izbrisati ovu dopunsku datoteku?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Obrišite ovu stilsku tablicu." @@ -254,15 +248,9 @@ msgstr "Verzija za prijelom" msgid "submission.layout.newGalley" msgstr "Kreirajte novu prijelom" -msgid "submission.layout.newSupplementaryFile" -msgstr "Postavite novu dopunsku datoteku" - msgid "submission.layout.noStyleFile" msgstr "Stilska tablica datoteke nije dodana ovom prijelomu." -msgid "submission.layout.supplementaryFiles" -msgstr "Recenzija dopunske datoteke" - msgid "submission.layout.viewingGalley" msgstr "Pregled prijave za prijelom" @@ -416,9 +404,6 @@ msgstr "Recenzija prijave" msgid "submission.submitter" msgstr "Podnositelj" -msgid "submission.supplementaryFiles" -msgstr "Dopunske datoteke" - msgid "submission.titleAndAbstract" msgstr "Naslov i sažetak" diff --git a/locale/hu_HU/api.po b/locale/hu_HU/api.po index 982a1806179..04478271a0d 100644 --- a/locale/hu_HU/api.po +++ b/locale/hu_HU/api.po @@ -67,18 +67,18 @@ msgstr "" "Az aktív téma, {$themePluginPath}, nincs engedélyezve és lehet, hogy nincs " "telepítve sem." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "A fájlt nem lehetett feltölteni szerverkonfigurációs hiba miatt. Vegye fel a " "kapcsolatot a rendszergazdával." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "A(z) {$maxSize}-nél/nél nagyobb fájlokat nem lehet feltölteni." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Egy vagy több fájlt nem lehetett feltölteni." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "A kéréssel egyetlen feltölthető fájl sem található." msgid "api.submissions.404.siteWideEndpoint" diff --git a/locale/hu_HU/manager.po b/locale/hu_HU/manager.po index bfef08322c3..8be7a42a451 100644 --- a/locale/hu_HU/manager.po +++ b/locale/hu_HU/manager.po @@ -763,9 +763,6 @@ msgstr "Hivatkozás kicsomagoló szolgáltatások" msgid "manager.setup.filter.pleaseSelect" msgstr "Kérem, válasszon..." -msgid "manager.setup.groupType" -msgstr "Fájl típus csoportosítás" - msgid "manager.setup.homeHeaderImageInvalid" msgstr "Érvénytelen honlap fejléc logó képformátum vagy a feltöltés sikertelen. Az elfogadott formátumok .gif, .jpg, vagy .png." @@ -1288,12 +1285,6 @@ msgstr "Komponensek visszaállítása az alapértelmezett beállításokra" msgid "manager.setup.genres.sortable" msgstr "Allow files of this type to be sorted by chapter" -msgid "manager.setup.genres.dependent" -msgstr "Ehhez a típushoz tartozó fájlokat ne független anyagnak jelölje (tipikusan ne kerüljön megjelenítésre a publikált anyaggal)" - -msgid "manager.setup.genres.supplementary" -msgstr "Ehhez a típushoz tartozó fájlokat kiegészítő anyagnak jelölje (tipikusan nem az elsődleges, fő tartalom)" - msgid "manager.settings.wizard" msgstr "Beállítás varázsló" diff --git a/locale/hu_HU/submission.po b/locale/hu_HU/submission.po index 556521d3064..8061b77103f 100644 --- a/locale/hu_HU/submission.po +++ b/locale/hu_HU/submission.po @@ -392,9 +392,6 @@ msgstr "Elfogadott" msgid "submission.acknowledge" msgstr "Visszajelzés, köszönet" -msgid "submission.addSuppFile" -msgstr "Kiegészítő fájl hozzáadása" - msgid "submission.ask" msgstr "Kérdez" @@ -753,8 +750,9 @@ msgstr "Eredeti fájl beküldése kész." msgid "submission.event.fileUploaded" msgstr "\"{$originalFileName}\" fájlt feltöltötte a {$username} felhasználó {$submissionId} azonosítóval." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Lektori vélemény \"{$name}\" (file ID {$fileId}-{$fileRevision}) feltöltve." +msgstr "Lektori vélemény \"{$name}\" (file ID {$submissionFileId}) feltöltve." msgid "submission.event.general.metadataUpdated" msgstr "Beküldés metaadatai frissítve" @@ -894,9 +892,6 @@ msgstr "Biztos benne, hogy véglegesen törölni szeretné ezt a formátumot?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Biztos benne, hogy törölni szeretné ezt a képet a tördelt változatból?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Biztos benne, hogy véglegesen törölni szeretné ezt a kiegészítő fájlt?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Ennek a stíluslapnak a törlése." @@ -960,15 +955,9 @@ msgstr "Elrendezés verzió" msgid "submission.layout.newGalley" msgstr "Újformátum létrehozása" -msgid "submission.layout.newSupplementaryFile" -msgstr "Új kiegészítő fájl feltöltése" - msgid "submission.layout.noStyleFile" msgstr "Nincs stíluslap fájl hozzárendelve ehhez a formátumhoz." -msgid "submission.layout.supplementaryFiles" -msgstr "Kiegészítő fájl véleményezése" - msgid "submission.layout.viewingGalley" msgstr "Benyújtott anyagok megtekintése" @@ -1203,9 +1192,6 @@ msgstr "Párbeszéd fájl feltöltése" msgid "submission.summary" msgstr "Összegzés" -msgid "submission.supplementaryFiles" -msgstr "Kiegészítő fájlok" - msgid "submission.supportingAgencies" msgstr "Támogató ügynökségek" @@ -1356,9 +1342,6 @@ msgstr "Fájl feltöltése" msgid "submission.upload.copyeditedVersion" msgstr "Megszerkesztett fájl feltöltése" -msgid "submission.upload.fairCopy" -msgstr "Pontos másolat feltöltése" - msgid "submission.upload.uploadFiles" msgstr "Fájlok feltöltése" diff --git a/locale/id_ID/api.po b/locale/id_ID/api.po index bf42068acc8..7de443b6238 100644 --- a/locale/id_ID/api.po +++ b/locale/id_ID/api.po @@ -21,18 +21,18 @@ msgid "api.themes.404.themeUnavailable" msgstr "" "Tema aktif, {$themePluginPath}, tidak diaktifkan dan mungkin belum terinstal." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "File tidak dapat diunggah karena konfigurasi server terjadi error. Hubungi " "pihak admin sistem." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "File lebih besar dari {$maxSize} tidak bisa diunggah." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Satu file atau lebih tidak dapat diunggah." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "Tidak ada file yang akan diunggah ditemukan sesuai permintaan." msgid "api.submissions.400.invalidIssueIdentifiers" diff --git a/locale/id_ID/manager.po b/locale/id_ID/manager.po index efde4552650..e95de117978 100644 --- a/locale/id_ID/manager.po +++ b/locale/id_ID/manager.po @@ -969,16 +969,6 @@ msgstr "Identifier simbolik ringkas opsional untuk genre ini." msgid "manager.setup.genres.key" msgstr "Key" -msgid "manager.setup.genres.supplementary" -msgstr "" -"Tandai jenis file ini sebagai file pelengkap (misalnya, bukan isi naskah " -"utama)" - -msgid "manager.setup.genres.dependent" -msgstr "" -"Tandai jenis file ini sebagai file dependen (misalnya, tidak ditampilkan " -"pada konten yang diterbitkan)" - msgid "grid.action.restoreGenres" msgstr "Pulihkan komponen ke pengaturan awal" @@ -1516,9 +1506,6 @@ msgstr "Unggah gambar untu ditampilkan pada halaman beranda." msgid "manager.setup.homepageImage" msgstr "Gambar Beranda" -msgid "manager.setup.groupType" -msgstr "Pengelompokan jenis file" - msgid "manager.setup.favicon" msgstr "Favicon" diff --git a/locale/id_ID/submission.po b/locale/id_ID/submission.po index d99a88f7752..64b45c295ec 100644 --- a/locale/id_ID/submission.po +++ b/locale/id_ID/submission.po @@ -319,9 +319,6 @@ msgstr "Diterima" msgid "submission.acknowledge" msgstr "Diketahui" -msgid "submission.addSuppFile" -msgstr "Tambah file tambahan" - msgid "submission.ask" msgstr "Tanya" @@ -505,9 +502,6 @@ msgstr "Apakah Anda yakin ingin menghapus galley ini secara permanen?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Apakah Anda yakin ingin memindahkan gambar ini dari galley?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Apakah Anda yakin ingin menghapus file tambahan ini secara permanen?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Hapus stylesheet ini." @@ -565,15 +559,9 @@ msgstr "Versi Layout" msgid "submission.layout.newGalley" msgstr "Buat Galley Baru" -msgid "submission.layout.newSupplementaryFile" -msgstr "upload File Tambahan Baru" - msgid "submission.layout.noStyleFile" msgstr "Tidak ada file stylesheet yang telah ditambahkan ke galley ini." -msgid "submission.layout.supplementaryFiles" -msgstr "Review File Tambahan" - msgid "submission.layout.viewingGalley" msgstr "Melihat Galley Naskah" @@ -739,9 +727,6 @@ msgstr "Naskah" msgid "submission.summary" msgstr "Ringkasan" -msgid "submission.supplementaryFiles" -msgstr "File Tambahan" - msgid "submission.supportingAgencies" msgstr "Agen Pendukung" @@ -1744,9 +1729,6 @@ msgstr "" msgid "submission.upload.uploadFiles" msgstr "Unggah File" -msgid "submission.upload.fairCopy" -msgstr "Unggah Fair Copy" - msgid "submission.upload.copyeditedVersion" msgstr "Unggah File Hasil Copyedit" @@ -2045,8 +2027,9 @@ msgstr "Buka Email" msgid "submission.event.subjectPrefix" msgstr "Email telah dikirim:" +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Revisi \"{$name}\" (file ID {$fileId}-{$fileRevision}) telah diunggah." +msgstr "Revisi \"{$name}\" (file ID {$submissionFileId}) telah diunggah." msgid "submission.event.submissionSubmitted" msgstr "Pengiriman Naskah Awal Selesai." diff --git a/locale/it_IT/api.po b/locale/it_IT/api.po index a0fef53d88a..a2070a8d505 100644 --- a/locale/it_IT/api.po +++ b/locale/it_IT/api.po @@ -46,18 +46,18 @@ msgstr "" "Il tema attivo, {$themePluginPath}, non è abilitato e non può essere " "installato." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "Non è stato possibile importare il file per un errore di configurazione del " "server. Contattare l'amministratore di sistema." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Non è possibile importare file di dimensioni superiori a {$maxSize}." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Non è stato possibile importare uno o più files." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "Non è stato trovato alcun file da importare per questa richiesta." msgid "api.submissions.404.siteWideEndpoint" diff --git a/locale/it_IT/manager.po b/locale/it_IT/manager.po index 73da1c65d6f..c74186f28ac 100644 --- a/locale/it_IT/manager.po +++ b/locale/it_IT/manager.po @@ -932,9 +932,6 @@ msgstr "Dichiarazione di copyright" msgid "manager.setup.authorCopyrightNoticeAgree" msgstr "Richiedi agli autori di condividere la dichiarazione di copyright come parte del processo di invio di una proposta." -msgid "manager.setup.groupType" -msgstr "Insieme di tipo file" - msgid "manager.setup.homepageImage" msgstr "Immagine della homepage" @@ -1251,12 +1248,6 @@ msgstr "Cancella questo componente" msgid "grid.action.restoreGenres" msgstr "Riporto il componente alle configurazioni di default" -msgid "manager.setup.genres.dependent" -msgstr "Segna i file di questo tipo come file dipendenti (ad esempio non verranno elencati nei contenuti pubblicati)" - -msgid "manager.setup.genres.supplementary" -msgstr "Segna i file di questo tipo come file supplementati (ad esempio non sono il contenuto primario di una proposta)" - msgid "manager.settings.wizard" msgstr "Configurazione guidata" diff --git a/locale/it_IT/submission.po b/locale/it_IT/submission.po index 175b6b4f500..9a21606268a 100644 --- a/locale/it_IT/submission.po +++ b/locale/it_IT/submission.po @@ -23,9 +23,6 @@ msgstr "Accetta" msgid "submission.acknowledge" msgstr "Ringraziamenti" -msgid "submission.addSuppFile" -msgstr "Aggiungi un file supplementare" - msgid "submission.ask" msgstr "Richiesta" @@ -206,9 +203,6 @@ msgstr "Sei sicuro di eliminare definitivamente questa gabbia?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Sei sicuro di rimuovere questa immagine dalla gabbia?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Sei sicuro di eliminare definitivamente questo file aggiuntivo?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Elimina questo foglio di stile." @@ -266,15 +260,9 @@ msgstr "Versione di Layout" msgid "submission.layout.newGalley" msgstr "Crea una nuova gabbia" -msgid "submission.layout.newSupplementaryFile" -msgstr "Invia un nuovo file aggiuntivo" - msgid "submission.layout.noStyleFile" msgstr "Nessun foglio di stile è stato associato a questa gabbia." -msgid "submission.layout.supplementaryFiles" -msgstr "REvisione dei file supplementari" - msgid "submission.layout.viewingGalley" msgstr "Vedi la gabbia dell'articolo" @@ -437,9 +425,6 @@ msgstr "Proponente" msgid "submission.summary" msgstr "Riepilogo" -msgid "submission.supplementaryFiles" -msgstr "File supplementari" - msgid "submission.titleAndAbstract" msgstr "Titolo e abstract" @@ -946,8 +931,9 @@ msgstr "Invio iniziale completato." msgid "submission.event.fileUploaded" msgstr "Un file \"{$originalFileName}\" è stato caricato per la proposta {$submissionId} da parte di {$username}." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "La revisione \"{$name}\" (file ID {$fileId}-{$fileRevision}) è stata caricata." +msgstr "La revisione \"{$name}\" (file ID {$submissionFileId}) è stata caricata." msgid "submission.event.subjectPrefix" msgstr "Una email è stata mandata:" @@ -1157,9 +1143,6 @@ msgstr "Carica files" msgid "submission.upload.copyeditedVersion" msgstr "Carica file (versione dopo il copyediting)" -msgid "submission.upload.fairCopy" -msgstr "Carica copia di cortesia" - msgid "submission.upload.uploadFiles" msgstr "Carica files" diff --git a/locale/ku_IQ/api.po b/locale/ku_IQ/api.po index 552f21d5409..71abc97bc3a 100644 --- a/locale/ku_IQ/api.po +++ b/locale/ku_IQ/api.po @@ -20,18 +20,18 @@ msgstr "زمانی {$locale} زمانێکی پاڵپشتیکراو نییە." msgid "api.themes.404.themeUnavailable" msgstr "ڕوکاری ،{$themePluginPath}، چالاک نەکراوە و لەوانەیە دانەبەزێندرابێت." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "فایلەکە بەهۆی کێشەی سێرڤەرەوە بار نەکرا. تکایە پەیوەندی بە بەڕێوەبەری " "پەڕەکەوە بکە." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "ئەگەر فایلێک قەبارەی لە {$maxSize} گەورەتر بێت، بار ناکرێت." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "یەک یان چەند فایلێک ناتواندرێت بار بکرێت." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "هیچ فایلێکی بارکراو بۆ ئەم داواکارییە نەدۆزرایەوە." msgid "api.submissions.unknownError" diff --git a/locale/ku_IQ/submission.po b/locale/ku_IQ/submission.po index 37be0844ded..aee450f6ee6 100644 --- a/locale/ku_IQ/submission.po +++ b/locale/ku_IQ/submission.po @@ -218,9 +218,6 @@ msgstr "هیچ فایلێک بۆ هەڵسەنگاندن بەردەست نییە. msgid "submission.upload.uploadFiles" msgstr "بارکردنی فایلەکان" -msgid "submission.upload.fairCopy" -msgstr "بارکردنی نوسخەی پیشانگا" - msgid "submission.upload.copyeditedVersion" msgstr "بارکردنی فایلی وردبینیکراو" @@ -399,9 +396,6 @@ msgstr "ئاژانسەکانی ئێستا" msgid "submission.supportingAgencies" msgstr "ئاژانسە پاڵپشتەکان" -msgid "submission.supplementaryFiles" -msgstr "فایلە پاشکۆکان" - msgid "submission.summary" msgstr "پوختە" @@ -644,15 +638,9 @@ msgstr "بینینی پێشوەختە" msgid "submission.layout.viewingGalley" msgstr "پیشاندانی تابلۆی پێشکەشکراو" -msgid "submission.layout.supplementaryFiles" -msgstr "هەڵسەنگاندنی فایلی پاشکۆ" - msgid "submission.layout.noStyleFile" msgstr "هیچ فایلێکی شێواز بۆ ئەم تابلۆیە زیاد نەکراوە." -msgid "submission.layout.newSupplementaryFile" -msgstr "بارکردنی فایلی پاشکۆ" - msgid "submission.layout.newGalley" msgstr "تابلۆیەکی نوێ دروست بکە" @@ -716,9 +704,6 @@ msgstr "گۆڕانکاری لە نەخشەسازیی تابلۆ" msgid "submission.layout.deleteGalleyStylesheet" msgstr "ئەم شێوازە بسڕەوە." -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "ئایا تۆ لە سڕینەوەی فایلی پاشکۆ دڵنیایت؟" - msgid "submission.layout.confirmDeleteGalleyImage" msgstr "ئایا تۆ دڵنیایت لە سڕینەوەی ئەم وێنەیە لە تابلۆی ئەم ژمارەیە؟" @@ -884,9 +869,10 @@ msgstr "فایلی پێشکەشکراو نوێ کرایەوە" msgid "submission.event.general.metadataUpdated" msgstr "زانیاریی ناسێنەری پێشکەشکراوەکە نوێ کرایەوە" +#, fuzzy msgid "submission.event.fileRevised" msgstr "" -"پێداچونەوەی\"{$name}\" (ناسنامەی فایل {$fileId}-{$fileRevision}) بار کرا." +"پێداچونەوەی\"{$name}\" (ناسنامەی فایل {$submissionFileId}) بار کرا." msgid "submission.event.submissionSubmitted" msgstr "پێشکەشکردنی سەرەتایی تەواو کرا." @@ -1051,9 +1037,6 @@ msgstr "فایلی هاوپێچ" msgid "submission.ask" msgstr "بپرسە" -msgid "submission.addSuppFile" -msgstr "فایلێکی پاشکۆ زیاد بکە" - msgid "submission.acknowledge" msgstr "بڕیار" diff --git a/locale/nb_NO/manager.po b/locale/nb_NO/manager.po index 98777b7b73b..d0ea5390c17 100644 --- a/locale/nb_NO/manager.po +++ b/locale/nb_NO/manager.po @@ -878,12 +878,6 @@ msgstr "Slett denne komponenten" msgid "grid.action.restoreGenres" msgstr "Gjenopprett komponentene til standardoppsettet" -msgid "manager.setup.genres.dependent" -msgstr "Marker filer av denne typen som sekundærfiler (dvs. skal ikke bli vist sammen med publisert innhold)" - -msgid "manager.setup.genres.supplementary" -msgstr "Marker filer av denne typen som tilleggsfiler (dvs. ikke hovedmanuskript)" - msgid "manager.settings.wizard" msgstr "Veiviser for instillinger" @@ -1139,9 +1133,6 @@ msgstr "Programutvidelse allerede installert, men kan bli oppdatert til en nyere msgid "plugins.categories.metadata.description" msgstr "Programutvidelser for metadata implementerer ytterligere metadata-standarder" -msgid "manager.setup.groupType" -msgstr "Gruppering av filtyper" - msgid "manager.setup.homepageImage" msgstr "Hjemmesidebilde" diff --git a/locale/nb_NO/submission.po b/locale/nb_NO/submission.po index d5950ab6faa..78631556642 100644 --- a/locale/nb_NO/submission.po +++ b/locale/nb_NO/submission.po @@ -20,9 +20,6 @@ msgstr "Akseptert" msgid "submission.acknowledge" msgstr "Bekreft" -msgid "submission.addSuppFile" -msgstr "Legg til tilleggsfil" - msgid "submission.ask" msgstr "Spør" @@ -203,9 +200,6 @@ msgstr "Er du sikker på at du vil slette dette oppsettet for alltid?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Er du sikker på at du vil fjerne dette bilde fra oppsettet?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Er du sikker på at du vil slette denne tilleggsfilen for alltid?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Slett dette stilarket." @@ -263,15 +257,9 @@ msgstr "Layoutversjon" msgid "submission.layout.newGalley" msgstr "Opprett nytt oppsett" -msgid "submission.layout.newSupplementaryFile" -msgstr "Last opp ny tilleggsfil" - msgid "submission.layout.noStyleFile" msgstr "Ingen stylesheetfil er lagt til dette oppsettet." -msgid "submission.layout.supplementaryFiles" -msgstr "Tilleggsfil fagvurdering" - msgid "submission.layout.viewingGalley" msgstr "Vis manuskriptoppsett" @@ -434,9 +422,6 @@ msgstr "Innsender" msgid "submission.summary" msgstr "Oppsummering" -msgid "submission.supplementaryFiles" -msgstr "Tilleggsfiler" - msgid "submission.titleAndAbstract" msgstr "Tittel og sammendrag" diff --git a/locale/nl_NL/manager.po b/locale/nl_NL/manager.po index bf7b449c826..779d7495b31 100644 --- a/locale/nl_NL/manager.po +++ b/locale/nl_NL/manager.po @@ -619,9 +619,6 @@ msgstr "Er is een fout opgetreden bij het verwijderen van dit onderdeel." msgid "manager.setup.favicon" msgstr "Favicon" -msgid "manager.setup.groupType" -msgstr "Groepering per bestandstype" - msgid "manager.setup.homepageImage" msgstr "Afbeelding voor homepage" @@ -1142,12 +1139,6 @@ msgstr "Verwijder dit onderdeel" msgid "grid.action.restoreGenres" msgstr "Herstel de onderdelen naar de standaardwaarden" -msgid "manager.setup.genres.dependent" -msgstr "Markeer bestanden van dit type als secundaire bestanden (zodat ze niet worden opgenomen bij de uiteindelijke publicatie)" - -msgid "manager.setup.genres.supplementary" -msgstr "Markeer bestanden van dit type als toegevoegde bestanden (d.w.z. geen primaire inzendingen)" - msgid "manager.setup.genres.key" msgstr "Sleutel" diff --git a/locale/nl_NL/submission.po b/locale/nl_NL/submission.po index 7ec75cd7514..91c1f5ff337 100644 --- a/locale/nl_NL/submission.po +++ b/locale/nl_NL/submission.po @@ -401,9 +401,6 @@ msgstr "Geaccepteerd" msgid "submission.acknowledge" msgstr "Erkenning" -msgid "submission.addSuppFile" -msgstr "Voeg een aanvullend bestand toe" - msgid "submission.ask" msgstr "Vraag" @@ -545,8 +542,9 @@ msgstr "Verwijder uit logboek" msgid "submission.event.submissionSubmitted" msgstr "Initiële inzending voltooid." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Revisie \"{$name}\" (bestands-ID {$fileId}-{$fileRevision}) werd opgeladen." +msgstr "Revisie \"{$name}\" (bestands-ID {$submissionFileId}) werd opgeladen." msgid "submission.event.general.metadataUpdated" msgstr "Metadata van de inzending veranderd" @@ -683,9 +681,6 @@ msgstr "Weet u zeker dat u deze proefdruk wilt verwijderen?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Weet u zeker dat u deze afbeelding wilt verwijderen uit de proeven?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Weet u zeker dat u dit aanvullend bestand wilt verwijderen?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Verwijder deze stylesheet." @@ -749,15 +744,9 @@ msgstr "Opmaakversie" msgid "submission.layout.newGalley" msgstr "Nieuwe proefdruk maken" -msgid "submission.layout.newSupplementaryFile" -msgstr "Nieuw aanvullend bestand laden" - msgid "submission.layout.noStyleFile" msgstr "Er is geen stylesheet bestand aan deze proefdruk gekoppeld." -msgid "submission.layout.supplementaryFiles" -msgstr "Beoordeling aanvullende bestanden" - msgid "submission.layout.viewingGalley" msgstr "Inzendingsproeven bekijken" @@ -992,9 +981,6 @@ msgstr "Laad een discussie bestand op" msgid "submission.summary" msgstr "Samenvatting" -msgid "submission.supplementaryFiles" -msgstr "Aanvullende bestanden" - msgid "submission.supportingAgencies" msgstr "Ondersteunende organisatie" @@ -1172,9 +1158,6 @@ msgstr "Bestand opladen" msgid "submission.upload.copyeditedVersion" msgstr "Geredigeerd bestand opladen" -msgid "submission.upload.fairCopy" -msgstr "Netversie opladen" - msgid "submission.upload.uploadFiles" msgstr "Bestanden opladen" diff --git a/locale/pl_PL/manager.po b/locale/pl_PL/manager.po index 4af75c0c600..b59b5e87eb0 100644 --- a/locale/pl_PL/manager.po +++ b/locale/pl_PL/manager.po @@ -722,9 +722,6 @@ msgstr "Recenzenci zostaną poproszeni o potwierdzenie, że nie występuje konfl msgid "manager.setup.favicon" msgstr "Nieprawidłowy format favicony. Akceptowane formaty to ICO, PNG oraz GIF." -msgid "manager.setup.groupType" -msgstr "Grupowanie plików" - msgid "manager.setup.homepageImage" msgstr "Obraz dla strony głównej" @@ -1134,12 +1131,6 @@ msgstr "Przywróć elementy do ich ustawień domyślnych" msgid "manager.setup.genres.sortable" msgstr "Włącz sortowanie plików wg rozdziału" -msgid "manager.setup.genres.dependent" -msgstr "Oznacz pliki tego typu jako zależne (np. aby nie były widoczne razem z opublikowaną treścią)" - -msgid "manager.setup.genres.supplementary" -msgstr "Oznacz pliki tego typu jako dodatkowe (np. nie będące główną zawartością zgłoszenia)" - msgid "manager.settings.wizard" msgstr "Kreator ustawień" diff --git a/locale/pl_PL/submission.po b/locale/pl_PL/submission.po index be94512c58e..5f7fbdcccd4 100644 --- a/locale/pl_PL/submission.po +++ b/locale/pl_PL/submission.po @@ -314,9 +314,6 @@ msgstr "Zaakceptowany" msgid "submission.acknowledge" msgstr "Potwierdzenie" -msgid "submission.addSuppFile" -msgstr "Dodaj plik pomocniczy" - msgid "submission.ask" msgstr "Zapytaj" @@ -503,9 +500,6 @@ msgstr "Czy na pewno chcesz usunąć szablon pliku do publikacji?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Czy na pewno chcesz usunąć obraz pliku do publikacji?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Czy na pewno chcesz usunąć plik pomocniczy?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Usuń styl." @@ -563,15 +557,9 @@ msgstr "Wersja szablonu" msgid "submission.layout.newGalley" msgstr "Utwórz wersję do publikacji" -msgid "submission.layout.newSupplementaryFile" -msgstr "Prześlij plik pomocniczy" - msgid "submission.layout.noStyleFile" msgstr "Szablon stylów nie został dodany." -msgid "submission.layout.supplementaryFiles" -msgstr "Recenzja pliku pomocniczego" - msgid "submission.layout.viewingGalley" msgstr "Przeglądanie plików do publikacji" @@ -737,9 +725,6 @@ msgstr "Przesyłający" msgid "submission.summary" msgstr "Podsumowanie" -msgid "submission.supplementaryFiles" -msgstr "Pliki dodatkowe" - msgid "submission.supportingAgencies" msgstr "Instytucje finansujące" @@ -902,8 +887,9 @@ msgstr "Zakończono wstępne zgłoszenie." msgid "submission.event.fileUploaded" msgstr "Plik \"{$originalFileName}\" został przesłany do zgłoszenia {$submissionId} przez {$username}." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Wersja \"{$name}\" (ID {$fileId}-{$fileRevision}) została przesłana." +msgstr "Wersja \"{$name}\" (ID {$submissionFileId}) została przesłana." msgid "submission.event.participantAdded" msgstr "{$name} ({$username}) został przypisany do zgłoszenia jako {$userGroupName}." @@ -1109,9 +1095,6 @@ msgstr "Prześlij plik" msgid "submission.upload.copyeditedVersion" msgstr "Prześlij plik do korekty" -msgid "submission.upload.fairCopy" -msgstr "Prześlij wersję po wszystkich zmianach (czystopis)" - msgid "submission.upload.uploadFiles" msgstr "Prześlij pliki" diff --git a/locale/pt_BR/api.po b/locale/pt_BR/api.po index 4bdb5efeb17..71726a7aa65 100644 --- a/locale/pt_BR/api.po +++ b/locale/pt_BR/api.po @@ -123,18 +123,18 @@ msgstr "" "O tema ativo, {$themePluginPath}, não está habilitado e pode não ter sido " "instalado." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "O arquivo não pôde ser enviado por causa de um erro de configuração no " "servidor. Por favor, entre em contato com o administrador do sistema." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Arquivos maiores do que {$maxSize} não podem ser enviados." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Um ou mais arquivos não foram enviados." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "Nenhum arquivo a ser carregado foi encontrado com a solicitação." msgid "api.submissions.404.siteWideEndpoint" diff --git a/locale/pt_BR/manager.po b/locale/pt_BR/manager.po index 440a6494032..b66f57e9614 100644 --- a/locale/pt_BR/manager.po +++ b/locale/pt_BR/manager.po @@ -645,9 +645,6 @@ msgstr "Restringir resultados por país, região e / ou cidade." msgid "manager.setup.competingInterests" msgstr "Conflito de Interesses" -msgid "manager.setup.groupType" -msgstr "Agrupamento por tipo de arquivo" - msgid "manager.setup.reviewOptions.numWeeksPerResponse" msgstr "Número de semanas para aceitar ou recusar um convite de avaliação." @@ -1117,15 +1114,9 @@ msgstr "Informe a URL da página da licença, se disponível." msgid "manager.setup.metadata.submission" msgstr "Formulário de Submissão" -msgid "manager.setup.genres.dependent" -msgstr "Marque os arquivos deste tipo como arquivos dependentes (por exemplo, não submissões primárias)" - msgid "manager.reviewerSearch.change" msgstr "Mudar" -msgid "manager.setup.genres.supplementary" -msgstr "Marcar arquivos deste tipo como arquivos suplementares (por exemplo conteúdos que não sejam primários à submissão)" - msgid "manager.reviewForms.confirmActivate" msgstr "Ter certeza que deseja ativar este formulário de avaliação? Uma vez designado a um avaliador você não poderá mais desativa-lo." diff --git a/locale/pt_BR/submission.po b/locale/pt_BR/submission.po index 245d192f603..cfea572880d 100644 --- a/locale/pt_BR/submission.po +++ b/locale/pt_BR/submission.po @@ -372,9 +372,6 @@ msgstr "Aceito" msgid "submission.acknowledge" msgstr "Acusar recebimento" -msgid "submission.addSuppFile" -msgstr "Incluir documento suplementar" - msgid "submission.ask" msgstr "Solicitada em" @@ -579,9 +576,6 @@ msgstr "Deseja realmente excluir permanentemente este documento?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Deseja realmente remover esta imagem?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Deseja realmente excluir permanentemente este documento suplementar?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Excluir folha de estilo." @@ -639,15 +633,9 @@ msgstr "para Layout" msgid "submission.layout.newGalley" msgstr "Criar nova composição" -msgid "submission.layout.newSupplementaryFile" -msgstr "Transferir novo documento suplementar" - msgid "submission.layout.noStyleFile" msgstr "Nenhuma folha de estilo incluída." -msgid "submission.layout.supplementaryFiles" -msgstr "Avaliação de documento suplementar" - msgid "submission.layout.viewingGalley" msgstr "Visualizando a composição" @@ -813,9 +801,6 @@ msgstr "Submetido por" msgid "submission.summary" msgstr "Resumo" -msgid "submission.supplementaryFiles" -msgstr "Documentos suplementares" - msgid "submission.titleAndAbstract" msgstr "Título e Resumo" @@ -1178,9 +1163,10 @@ msgstr "Submissão inicial concluída." msgid "submission.event.fileUploaded" msgstr "Um arquivo \"{$originalFileName}\" foi carregado para a submissão {$submissionId} por {$username}." +#, fuzzy msgid "submission.event.fileRevised" msgstr "" -"Avaliação \"{$name}\" (ID do arquivo {$fileId}-{$fileRevision}) foi " +"Avaliação \"{$name}\" (ID do arquivo {$submissionFileId}) foi " "carregada." msgid "submission.files.downloadAll" @@ -1368,9 +1354,6 @@ msgstr "Enviar Arquivo" msgid "submission.upload.copyeditedVersion" msgstr "Enviar Arquivo da Edição de Texto" -msgid "submission.upload.fairCopy" -msgstr "Enviar Cópia \"Fair\"" - msgid "submission.upload.uploadFiles" msgstr "Enviar Arquivos" diff --git a/locale/pt_PT/manager.po b/locale/pt_PT/manager.po index bfdff968b25..979af65a17b 100644 --- a/locale/pt_PT/manager.po +++ b/locale/pt_PT/manager.po @@ -844,9 +844,6 @@ msgstr "Interesses Concorrentes" msgid "manager.setup.favicon" msgstr "Favicon" -msgid "manager.setup.groupType" -msgstr "Agrupamento tipo de ficheiro" - msgid "manager.setup.homepageImage" msgstr "Image Homepage" @@ -1243,12 +1240,6 @@ msgstr "Eliminar este Componente" msgid "grid.action.restoreGenres" msgstr "Restaurar os componentes para as configurações padrão" -msgid "manager.setup.genres.dependent" -msgstr "Marcar ficheiros deste tipo como ficheiros dependentes (por exemplo, não para serem listados com conteúdo publicado)" - -msgid "manager.setup.genres.supplementary" -msgstr "Marcar ficheiros deste tipo como ficheiros suplementares (por exemplo, não conteúdo de submissão principal)" - msgid "manager.settings.wizard" msgstr "Assistente de configurações" diff --git a/locale/pt_PT/submission.po b/locale/pt_PT/submission.po index 993b8c909ed..cc55996a539 100644 --- a/locale/pt_PT/submission.po +++ b/locale/pt_PT/submission.po @@ -26,9 +26,6 @@ msgstr "Aceite" msgid "submission.acknowledge" msgstr "Acusar Recebimento" -msgid "submission.addSuppFile" -msgstr "Adicionar Documento Suplementar" - msgid "submission.ask" msgstr "Solicitada em" @@ -212,9 +209,6 @@ msgstr "Deseja realmente eliminar permanentemente este documento?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Deseja realmente remover esta imagem?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Deseja realmente eliminar permanentemente estes documentos suplementares?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Eliminar folha de estilo." @@ -272,15 +266,9 @@ msgstr "para Layout" msgid "submission.layout.newGalley" msgstr "Criar nova edição" -msgid "submission.layout.newSupplementaryFile" -msgstr "Transferir novo documento suplementar" - msgid "submission.layout.noStyleFile" msgstr "Nenhuma folha de estilo incluída." -msgid "submission.layout.supplementaryFiles" -msgstr "Revisão de documento suplementar" - msgid "submission.layout.viewingGalley" msgstr "Visualizando a edição" @@ -443,9 +431,6 @@ msgstr "Submetido por" msgid "submission.summary" msgstr "Resumo" -msgid "submission.supplementaryFiles" -msgstr "Documentos suplementares" - msgid "submission.titleAndAbstract" msgstr "Título e Resumo" @@ -906,8 +891,9 @@ msgstr "Submissão inicial concluída." msgid "submission.event.fileUploaded" msgstr "Um ficheiro \"{$originalFileName}\" foi submetido {$submissionId} por {$username}." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Revisão \"{$name}\" (file ID {$fileId}-{$fileRevision}) foi carregada." +msgstr "Revisão \"{$name}\" (file ID {$submissionFileId}) foi carregada." msgid "submission.event.participantAdded" msgstr "{$name} ({$username}) foi designado para esta submissão como {$userGroupName}." @@ -1131,9 +1117,6 @@ msgstr "Upload Ficheiro" msgid "submission.upload.copyeditedVersion" msgstr "Upload Ficheiro Edição Cópia" -msgid "submission.upload.fairCopy" -msgstr "Upload Cópia Finalizada" - msgid "submission.upload.uploadFiles" msgstr "Upload Ficheiros" diff --git a/locale/ro_RO/submission.po b/locale/ro_RO/submission.po index b8e0e797ba4..199f0ff6f20 100644 --- a/locale/ro_RO/submission.po +++ b/locale/ro_RO/submission.po @@ -976,9 +976,10 @@ msgstr "Înregistrarea manuscrisului a fost actualizată" msgid "submission.event.general.metadataUpdated" msgstr "Metadatele înregistrării manuscrisului au fost încărcate" +#, fuzzy msgid "submission.event.fileRevised" msgstr "" -"Revizia \"{$name}\" (file ID {$fileId}-{$fileRevision}) a fost încărcată." +"Revizia \"{$name}\" (file ID {$submissionFileId}) a fost încărcată." msgid "submission.event.submissionSubmitted" msgstr "Înregistrarea inițială de manuscris a fost încheiată." @@ -1308,9 +1309,6 @@ msgstr "" msgid "submission.upload.uploadFiles" msgstr "Încărcați fișiere" -msgid "submission.upload.fairCopy" -msgstr "Încărcați copia corectă" - msgid "submission.upload.copyeditedVersion" msgstr "Încărcare fișier manuscris editat" diff --git a/locale/ru_RU/api.po b/locale/ru_RU/api.po index c7c2031c9cb..0c65f05d293 100644 --- a/locale/ru_RU/api.po +++ b/locale/ru_RU/api.po @@ -84,16 +84,16 @@ msgstr "Ваш запрос не может быть выполнен, так к msgid "api.submissions.400.invalidIssueIdentifiers" msgstr "Неправильно указан запрашиваемый том, номер или год." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "Не найдены файлы для загрузки на сервер, указанные в запросе." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Один или несколько файлов не могут быть загружены на сервер." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "Файлы больше чем {$maxSize} нельзя загрузить на сервер." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "Файл не удается загрузить на сервер из-за ошибки конфигурации сервера. Пожалуйста, свяжитесь с администратором системы." msgid "api.themes.404.themeUnavailable" diff --git a/locale/ru_RU/manager.po b/locale/ru_RU/manager.po index c48dbebfa40..e4da3f53136 100644 --- a/locale/ru_RU/manager.po +++ b/locale/ru_RU/manager.po @@ -671,9 +671,6 @@ msgstr "Во время удаления этого объекта возник msgid "manager.setup.favicon" msgstr "Favicon" -msgid "manager.setup.groupType" -msgstr "Группировка типов файлов" - msgid "manager.setup.homepageImage" msgstr "Изображение на главной странице" @@ -1230,12 +1227,6 @@ msgstr "Удалить этот компонент" msgid "grid.action.restoreGenres" msgstr "Вернуть компоненты к настройкам по умолчанию" -msgid "manager.setup.genres.dependent" -msgstr "Отметить файлы этого типа как зависимые файлы (например, не показываемые с опубликованным контентом)" - -msgid "manager.setup.genres.supplementary" -msgstr "Отметить файлы этого типа как дополнительные файлы (например, не относящиеся к основному контенту материала)" - msgid "manager.setup.genres.key" msgstr "Ключ" diff --git a/locale/ru_RU/submission.po b/locale/ru_RU/submission.po index 67efebe00fb..9a8b0a26f97 100644 --- a/locale/ru_RU/submission.po +++ b/locale/ru_RU/submission.po @@ -420,9 +420,6 @@ msgstr "Принята" msgid "submission.acknowledge" msgstr "Подтвердить" -msgid "submission.addSuppFile" -msgstr "Добавить дополнительный файл" - msgid "submission.ask" msgstr "Запрос" @@ -570,8 +567,9 @@ msgstr "Удалить запись журнала" msgid "submission.event.submissionSubmitted" msgstr "Первоначальная отправка материала завершена." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Версия «{$name}» (ID файла {$fileId}-{$fileRevision}) была загружена." +msgstr "Версия «{$name}» (ID файла {$submissionFileId}) была загружена." msgid "submission.event.general.metadataUpdated" msgstr "Метаданные материала обновлены" @@ -729,9 +727,6 @@ msgstr "Вы уверены, что хотите навсегда удалить msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Вы уверены, что хотите удалить это изображение из гранки?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Вы уверены, что хотите навсегда удалить этот дополнительный файл?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Удалить эту таблицу стилей." @@ -795,15 +790,9 @@ msgstr "Версия для верстки" msgid "submission.layout.newGalley" msgstr "Создать новую гранку" -msgid "submission.layout.newSupplementaryFile" -msgstr "Загрузить новый дополнительный файл" - msgid "submission.layout.noStyleFile" msgstr "Файл таблицы стилей не был добавлен к этой гранке." -msgid "submission.layout.supplementaryFiles" -msgstr "Рецензия на дополнительный файл" - msgid "submission.layout.viewingGalley" msgstr "Просмотр гранки материала" @@ -1038,9 +1027,6 @@ msgstr "Загрузить файл обсуждения" msgid "submission.summary" msgstr "Сводка" -msgid "submission.supplementaryFiles" -msgstr "Дополнительные файлы" - msgid "submission.supportingAgencies" msgstr "Поддерживающие организации" @@ -1221,9 +1207,6 @@ msgstr "Загрузить файл" msgid "submission.upload.copyeditedVersion" msgstr "Загрузить файл после литературного редактирования" -msgid "submission.upload.fairCopy" -msgstr "Загрузить окончательный вариант" - msgid "submission.upload.uploadFiles" msgstr "Загрузить файлы" diff --git a/locale/sl_SI/manager.po b/locale/sl_SI/manager.po index b5febe4a3cf..639978e9a38 100644 --- a/locale/sl_SI/manager.po +++ b/locale/sl_SI/manager.po @@ -618,9 +618,6 @@ msgstr "Med brisanjem je prišlo do napake." msgid "manager.setup.favicon" msgstr "Napačen format favikona. Dovoljeni formati so .ico, .png in .gif" -msgid "manager.setup.groupType" -msgstr "Grupiranje po tipu datoteke" - msgid "manager.setup.homeHeaderImageInvalid" msgstr "Napačen format slike za logotip glave osnovne strani ali pa je prišlo do napake pri nalaganju. Dovoljeni formati so .gif, .jpg, or .png." @@ -1124,12 +1121,6 @@ msgstr "Odstrani to komponento" msgid "grid.action.restoreGenres" msgstr "Povrni privzete nastavitve komponent" -msgid "manager.setup.genres.dependent" -msgstr "Označi datoteke tega tipa kot odvisne datoteke (potem ne bodo naštete med objavljenimi vsebinami)" - -msgid "manager.setup.genres.supplementary" -msgstr "Ozači datoteke tega tipa kot dodatne datoteke (torej ne predstavljajo primane vsebine prispevka)" - msgid "manager.setup.genres.key" msgstr "Ključ" diff --git a/locale/sl_SI/submission.po b/locale/sl_SI/submission.po index c7a0889a1cf..94ecea99fde 100644 --- a/locale/sl_SI/submission.po +++ b/locale/sl_SI/submission.po @@ -402,9 +402,6 @@ msgstr "Sprejeto" msgid "submission.acknowledge" msgstr "Potrdi" -msgid "submission.addSuppFile" -msgstr "Dodaj dodatno datoteko" - msgid "submission.ask" msgstr "Zaprošena" @@ -549,8 +546,9 @@ msgstr "začetna oddaja prispevka je končana." msgid "submission.event.fileUploaded" msgstr "{$username} je naložil datoteko \"{$originalFileName}\" za prispevek {$submissionId}." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Datoteka \"{$name}\" (ID datoteke {$fileId}-{$fileRevision}) je bila posodobljena." +msgstr "Datoteka \"{$name}\" (ID datoteke {$submissionFileId}) je bila posodobljena." msgid "submission.event.general.metadataUpdated" msgstr "Metapodatki prispevka so posodobljeni" @@ -684,9 +682,6 @@ msgstr "Ste prepričani, da želite dokončno zbrisati ta prelom?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Ste prepričani, da želite zbrisati to sliko iz preloma?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Ste prepričani, da želite dokončno zbrisati to dodatno datoteko?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Zbriši to stilsko tablico." @@ -750,15 +745,9 @@ msgstr "Verzija preloma" msgid "submission.layout.newGalley" msgstr "Ustvari nov prelom" -msgid "submission.layout.newSupplementaryFile" -msgstr "Oddaj novo dodatno datoteko" - msgid "submission.layout.noStyleFile" msgstr "Temu prelomu ni bila dodana nobena tablica stila." -msgid "submission.layout.supplementaryFiles" -msgstr "Recenzija dodatne datoteke" - msgid "submission.layout.viewingGalley" msgstr "Pregled preloma prispevka" @@ -993,9 +982,6 @@ msgstr "Naloži datoteko za diskusijo" msgid "submission.summary" msgstr "Izvleček" -msgid "submission.supplementaryFiles" -msgstr "Dodatne datoteke" - msgid "submission.supportingAgencies" msgstr "Podporne ustanove" @@ -1182,9 +1168,6 @@ msgstr "Naloži datoteko" msgid "submission.upload.copyeditedVersion" msgstr "Naloži lektorirano datoteko" -msgid "submission.upload.fairCopy" -msgstr "Naloži Fair kopijo" - msgid "submission.upload.uploadFiles" msgstr "Naloži datoteke" diff --git a/locale/sr_RS@cyrillic/manager.po b/locale/sr_RS@cyrillic/manager.po index aaf5833e1d3..ac5e0a32969 100644 --- a/locale/sr_RS@cyrillic/manager.po +++ b/locale/sr_RS@cyrillic/manager.po @@ -668,9 +668,6 @@ msgstr "Напомена о ауторском праву" msgid "manager.setup.copyrightNotice.description" msgstr "Захтевај од аутора да прихвате \"Напомену о ауторским правим\" као део процеса предаје рада." -msgid "manager.setup.groupType" -msgstr "Груписање докумената по типу" - msgid "manager.setup.homepageImage.description" msgstr "Доставите слику која ће бити видљива на почетној страници." @@ -1154,12 +1151,6 @@ msgstr "Морате одабрати барем једну улогу да је msgid "manager.reviewForms.confirmActivate" msgstr "Да ли сте сигурни да желите да активирате овај рецензентски образац? Кад му доделите рецензију нећете моћи да га деактивирате." -msgid "manager.setup.genres.dependent" -msgstr "Означите фајлове овог типа као зависне фајлове (они неће бити излистани са објављеним садржајем)" - -msgid "manager.setup.genres.supplementary" -msgstr "Означите фајлове овог типа као допунске фајлове (не примарно као садржај пријављеног чланка)" - msgid "settings.libraryFiles.fileRequired" msgstr "Фајл библиотека је неопходан. Проверите да ли сте одабрали и на сервер поставили фајл." diff --git a/locale/sr_RS@cyrillic/submission.po b/locale/sr_RS@cyrillic/submission.po index e0cd40dad1e..3357fc988c2 100644 --- a/locale/sr_RS@cyrillic/submission.po +++ b/locale/sr_RS@cyrillic/submission.po @@ -314,9 +314,6 @@ msgstr "Прихваћено" msgid "submission.acknowledge" msgstr "Потврђено" -msgid "submission.addSuppFile" -msgstr "Додај додатни фајл" - msgid "submission.ask" msgstr "Захтевај" @@ -811,9 +808,6 @@ msgstr "Достави документ дискусије" msgid "submission.summary" msgstr "Резиме" -msgid "submission.supplementaryFiles" -msgstr "Додатни документи" - msgid "submission.supportingAgencies" msgstr "Установе подршке" @@ -940,6 +934,7 @@ msgstr "Повратак на лог дешавања" msgid "submission.event.fileUploaded" msgstr "{$username} је доставио документ \"{$originalFileName}\" уз предају {$submissionId}." +#, fuzzy msgid "submission.event.fileRevised" msgstr "Ревизија \"{$name}\" (file ID {$fileId}-{$revision}) је достављена." @@ -1162,9 +1157,6 @@ msgstr "Проширени метаподаци" msgid "submission.upload.userGroupDescription" msgstr "Ако пријављујете Уређени волумен/свеску морате поставити на сервер свако поглавље посебно у вашој улози уредника волумена/свеске." -msgid "submission.upload.fairCopy" -msgstr "Постави на сервер исправну копију" - msgid "submission.upload.response.description" msgstr "Након рецензије додељеног фајла, забележите ниже коментаре на фајл и/или поставите копију са белешкама фајла (или други фајл)." @@ -1273,9 +1265,6 @@ msgstr "Да ли сте сигурни да желите да трајно ук msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Да ли сте сигурни да желите да уклоните ову слику из пробне копије?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Да ли сте сигурни да желите трајно обристаи овај допунски фајл?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Обриши ову страницу стила." @@ -1333,15 +1322,9 @@ msgstr "Верзија изгледа" msgid "submission.layout.newGalley" msgstr "Креирај нову пробну копију" -msgid "submission.layout.newSupplementaryFile" -msgstr "Постави нови допунски фајл" - msgid "submission.layout.noStyleFile" msgstr "Нема странице стила за ову пробну копију." -msgid "submission.layout.supplementaryFiles" -msgstr "Рецензија допунског фајла" - msgid "submission.layout.viewingGalley" msgstr "Преглед пробне копије чланка" diff --git a/locale/sr_RS@latin/manager.po b/locale/sr_RS@latin/manager.po index b509d280b64..6bb19fd12b1 100644 --- a/locale/sr_RS@latin/manager.po +++ b/locale/sr_RS@latin/manager.po @@ -668,9 +668,6 @@ msgstr "Napomena o autorskom pravu" msgid "manager.setup.copyrightNotice.description" msgstr "Zahtevaj od autora da prihvate \"Napomenu o autorskim pravim\" kao deo procesa predaje rada." -msgid "manager.setup.groupType" -msgstr "Grupisanje dokumenata po tipu" - msgid "manager.setup.homepageImage.description" msgstr "Dostavite sliku koja će biti vidljiva na početnoj stranici." @@ -1154,12 +1151,6 @@ msgstr "Morate odabrati barem jednu ulogu da je dodate ovom korisniku." msgid "manager.reviewForms.confirmActivate" msgstr "Da li ste sigurni da želite da aktivirate ovaj recenzentski obrazac? Kad mu dodelite recenziju nećete moći da ga deaktivirate." -msgid "manager.setup.genres.dependent" -msgstr "Označite fajlove ovog tipa kao zavisne fajlove (oni neće biti izlistani sa objavljenim sadržajem)" - -msgid "manager.setup.genres.supplementary" -msgstr "Označite fajlove ovog tipa kao dopunske fajlove (ne primarno kao sadržaj prijavljenog članka)" - msgid "settings.libraryFiles.fileRequired" msgstr "Fajl biblioteka je neophodan. Proverite da li ste odabrali i na server postavili fajl." diff --git a/locale/sr_RS@latin/submission.po b/locale/sr_RS@latin/submission.po index 2e49a9402f0..6becafa9a30 100644 --- a/locale/sr_RS@latin/submission.po +++ b/locale/sr_RS@latin/submission.po @@ -314,9 +314,6 @@ msgstr "Prihvaćeno" msgid "submission.acknowledge" msgstr "Potvrđeno" -msgid "submission.addSuppFile" -msgstr "Dodaj dodatni fajl" - msgid "submission.ask" msgstr "Zahtevaj" @@ -809,9 +806,6 @@ msgstr "Dostavi dokument diskusije" msgid "submission.summary" msgstr "Rezime" -msgid "submission.supplementaryFiles" -msgstr "Dodatni dokumenti" - msgid "submission.supportingAgencies" msgstr "Ustanove podrške" @@ -938,6 +932,7 @@ msgstr "Povratak na log dešavanja" msgid "submission.event.fileUploaded" msgstr "{$username} je dostavio dokument \"{$originalFileName}\" uz predaju {$submissionId}." +#, fuzzy msgid "submission.event.fileRevised" msgstr "Revizija \"{$name}\" (file ID {$fileId}-{$revision}) je dostavljena." @@ -1160,9 +1155,6 @@ msgstr "Prošireni metapodaci" msgid "submission.upload.userGroupDescription" msgstr "Ako prijavljujete Uređeni volumen/svesku morate postaviti na server svako poglavlje posebno u vašoj ulozi urednika volumena/sveske." -msgid "submission.upload.fairCopy" -msgstr "Postavi na server ispravnu kopiju" - msgid "submission.upload.response.description" msgstr "Nakon recenzije dodeljenog fajla, zabeležite niže komentare na fajl i/ili postavite kopiju sa beleškama fajla (ili drugi fajl)." @@ -1271,9 +1263,6 @@ msgstr "Da li ste sigurni da želite da trajno uklonite ovu probnu kopiju?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Da li ste sigurni da želite da uklonite ovu sliku iz probne kopije?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Da li ste sigurni da želite trajno obristai ovaj dopunski fajl?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Obriši ovu stranicu stila." @@ -1331,15 +1320,9 @@ msgstr "Verzija izgleda" msgid "submission.layout.newGalley" msgstr "Kreiraj novu probnu kopiju" -msgid "submission.layout.newSupplementaryFile" -msgstr "Postavi novi dopunski fajl" - msgid "submission.layout.noStyleFile" msgstr "Nema stranice stila za ovu probnu kopiju." -msgid "submission.layout.supplementaryFiles" -msgstr "Recenzija dopunskog fajla" - msgid "submission.layout.viewingGalley" msgstr "Pregled probne kopije članka" diff --git a/locale/sv_SE/manager.po b/locale/sv_SE/manager.po index 9adbcd9f89a..97f55d0c67b 100644 --- a/locale/sv_SE/manager.po +++ b/locale/sv_SE/manager.po @@ -613,9 +613,6 @@ msgstr "Ett fel uppstod vid borttagning av detta objekt." msgid "manager.setup.favicon" msgstr "Adressikon (eng. \"favicon\")" -msgid "manager.setup.groupType" -msgstr "Gruppering av filtyper" - msgid "manager.setup.homepageImage" msgstr "Bild på förstasidan" @@ -1103,12 +1100,6 @@ msgstr "Ta bort kompontenten" msgid "grid.action.restoreGenres" msgstr "Återställ komponenterna till defaultinställningar" -msgid "manager.setup.genres.dependent" -msgstr "Markera filer av den här typen som underordnade (t.ex. för att inte listas med publicerat innehåll)" - -msgid "manager.setup.genres.supplementary" -msgstr "Markera filer av den här typen som kompletterande filer (t.ex. inte primärt bidrag)" - msgid "manager.setup.genres.key" msgstr "Kod" diff --git a/locale/sv_SE/submission.po b/locale/sv_SE/submission.po index 6e4bce03ecf..c55e46f7f7a 100644 --- a/locale/sv_SE/submission.po +++ b/locale/sv_SE/submission.po @@ -404,9 +404,6 @@ msgstr "Accepterad" msgid "submission.acknowledge" msgstr "Bekräfta" -msgid "submission.addSuppFile" -msgstr "Bifoga tilläggsfil" - msgid "submission.ask" msgstr "Fråga" @@ -548,8 +545,9 @@ msgstr "Ta bort händelse i logg" msgid "submission.event.submissionSubmitted" msgstr "Bidraget har skickats in." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Bearbetningen \"{$name}\" (fil-ID {$fileId}-{$fileRevision}) laddas upp." +msgstr "Bearbetningen \"{$name}\" (fil-ID {$submissionFileId}) laddas upp." msgid "submission.event.general.metadataUpdated" msgstr "Bidragets metadata uppdaterade" @@ -683,9 +681,6 @@ msgstr "Är du säker på att du vill ta bort den här publiceringsversionen per msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Är du säker på att du vill ta bort den här bilden från publiceringsversionen?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Är du säker på att du vill ta bort den här tilläggsfilen permanent?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Ta bort den här stilmallen." @@ -749,15 +744,9 @@ msgstr "Layoutversion" msgid "submission.layout.newGalley" msgstr "Skapa ny publiceringsversion" -msgid "submission.layout.newSupplementaryFile" -msgstr "Ladda upp tilläggsfil" - msgid "submission.layout.noStyleFile" msgstr "Ingen stilmall i separat fil tillagd för den här publiceringsversionen." -msgid "submission.layout.supplementaryFiles" -msgstr "Granskning av tilläggsfiler" - msgid "submission.layout.viewingGalley" msgstr "Visar bidragets publiceringsversion" @@ -992,9 +981,6 @@ msgstr "Ladda upp en diskussionsfil" msgid "submission.summary" msgstr "Sammanfattning" -msgid "submission.supplementaryFiles" -msgstr "Tilläggsfiler" - msgid "submission.supportingAgencies" msgstr "Stödorganisationer" @@ -1172,9 +1158,6 @@ msgstr "Ladda upp fil" msgid "submission.upload.copyeditedVersion" msgstr "Ladda upp manusredigerad fil" -msgid "submission.upload.fairCopy" -msgstr "Ladda upp justerad kopia" - msgid "submission.upload.uploadFiles" msgstr "Ladda upp filer" diff --git a/locale/tr_TR/api.po b/locale/tr_TR/api.po index 1fb088126f2..de2c0b4571a 100644 --- a/locale/tr_TR/api.po +++ b/locale/tr_TR/api.po @@ -20,18 +20,18 @@ msgstr "{$locale} dili desteklenmiyor." msgid "api.themes.404.themeUnavailable" msgstr "Aktif tema {$themePluginPath}, etkin değil veya yüklenmemiş olabilir." -msgid "api.temporaryFiles.400.config" +msgid "api.files.400.config" msgstr "" "Sunucu yapılandırma hatası nedeniyle dosya yüklenemedi. Lütfen sistem " "yöneticisine başvurun." -msgid "api.temporaryFiles.400.fileSize" +msgid "api.files.400.fileSize" msgstr "{$MaxSize} boyutundan daha büyük dosyalar yüklenemez." -msgid "api.temporaryFiles.409.uploadFailed" +msgid "api.files.400.uploadFailed" msgstr "Bir veya daha fazla dosya yüklenemedi." -msgid "api.temporaryFiles.400.noUpload" +msgid "api.files.400.noUpload" msgstr "Sorguyla yüklenecek bir dosya bulunamadı." msgid "api.submissions.400.invalidIssueIdentifiers" diff --git a/locale/tr_TR/manager.po b/locale/tr_TR/manager.po index 376732c689f..920d58c485b 100644 --- a/locale/tr_TR/manager.po +++ b/locale/tr_TR/manager.po @@ -1122,9 +1122,6 @@ msgstr "Hakemlerden, aşağıda belirttiğiniz çıkar çatışması politikası msgid "manager.setup.favicon" msgstr "Favori ikon" -msgid "manager.setup.groupType" -msgstr "Dosya türü gruplaması" - msgid "settings.roles.roleIdRequired" msgstr "Bir rol izin düzeyi tanımlamanız gerekir." @@ -1146,12 +1143,6 @@ msgstr "Bu bileşeni sil" msgid "grid.action.restoreGenres" msgstr "Bileşenleri varsayılan ayarlara geri yükle" -msgid "manager.setup.genres.dependent" -msgstr "Bu türdeki dosyaları bağımlı dosyalar olarak işaretleyin (ör. Yayınlanan içeriğe sahip olmamak üzere)" - -msgid "manager.setup.genres.supplementary" -msgstr "Bu tür dosyaları tamamlayıcı dosyalar olarak işaretleyin (ör. Birincil gönderi içeriği olayacak şekilde)" - msgid "manager.setup.genres.key" msgstr "Anahtar" diff --git a/locale/tr_TR/submission.po b/locale/tr_TR/submission.po index 5053565ee46..b24ff936836 100644 --- a/locale/tr_TR/submission.po +++ b/locale/tr_TR/submission.po @@ -317,9 +317,6 @@ msgstr "Kabul edildi" msgid "submission.acknowledge" msgstr "Onaylama" -msgid "submission.addSuppFile" -msgstr "Ek bir dosya yükle" - msgid "submission.ask" msgstr "İstek" @@ -503,9 +500,6 @@ msgstr "Bu dizgiyi tamamen silmek istediğinizden emin misiniz?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Dizgiden bu resmi çıkarmak istediğinize emin misiniz?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Bu ek dosyayı tamamen silmek istediğinizden emin misiniz?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Bu stil sayfasını sil." @@ -563,15 +557,9 @@ msgstr "Mizanpaj Sürümü" msgid "submission.layout.newGalley" msgstr "Yeni Dizgi Oluştur" -msgid "submission.layout.newSupplementaryFile" -msgstr "Yeni Ek Dosya Yükle" - msgid "submission.layout.noStyleFile" msgstr "Bu dizgiye stil sayfa dosyası eklenmedi." -msgid "submission.layout.supplementaryFiles" -msgstr "Ek Dosya Değerlendirme" - msgid "submission.layout.viewingGalley" msgstr "Başvuru Dizgisini Görüntüleme" @@ -737,9 +725,6 @@ msgstr "Gönderici" msgid "submission.summary" msgstr "Özet" -msgid "submission.supplementaryFiles" -msgstr "Ek Dosyalar" - msgid "submission.supportingAgencies" msgstr "Destekleyen Kurumlar" @@ -938,8 +923,9 @@ msgstr "\"{$name}\" ({$username}) bu gönderi için {$userGroupName}'den kaldır msgid "submission.event.submissionSubmitted" msgstr "İlk gönderi tamamlandı." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "\"{$name}\" adlı revizyon (file ID {$fileId}-{$fileRevision}) olarak yüklendi." +msgstr "\"{$name}\" adlı revizyon (file ID {$submissionFileId}) olarak yüklendi." msgid "submission.event.subjectPrefix" msgstr "Bir e-posta gönderildi:" @@ -1172,9 +1158,6 @@ msgstr "Dosya Yükle" msgid "submission.upload.copyeditedVersion" msgstr "Düzenlenmiş Kopya Dosyası Yükle" -msgid "submission.upload.fairCopy" -msgstr "Adil Kopyayı Yükle" - msgid "submission.upload.uploadFiles" msgstr "Dosyaları Yükle" diff --git a/locale/uk_UA/manager.po b/locale/uk_UA/manager.po index e6636d36473..8015a1c8ed9 100644 --- a/locale/uk_UA/manager.po +++ b/locale/uk_UA/manager.po @@ -632,9 +632,6 @@ msgstr "Під час видалення цього об'єкту виникла msgid "manager.setup.favicon" msgstr "Favicon" -msgid "manager.setup.groupType" -msgstr "Групування типів файлів" - msgid "manager.setup.homeHeaderImageInvalid" msgstr "Завантаження невдале або неправильний формат зображення для логотипу заголовку домашньої сторінки. Прийнятними форматами є .gif, .jpg або .png." @@ -1199,14 +1196,6 @@ msgstr "Видалити цей компонент" msgid "grid.action.restoreGenres" msgstr "Відновити компоненти з усталеними налаштуваннями" -msgid "manager.setup.genres.dependent" -msgstr "" -"Позначити файли цього типу як залежні файли (наприклад, як такі, що не " -"будуть відображатися з опублікованим вмістом)" - -msgid "manager.setup.genres.supplementary" -msgstr "Позначити файли цього типу як додаткові файли (наприклад, як такі, що не стосуються основного вмісту подання)" - msgid "manager.setup.genres.key" msgstr "Ключ" diff --git a/locale/uk_UA/submission.po b/locale/uk_UA/submission.po index de6fca543d5..fdde12566b7 100644 --- a/locale/uk_UA/submission.po +++ b/locale/uk_UA/submission.po @@ -390,9 +390,6 @@ msgstr "Прийнято" msgid "submission.acknowledge" msgstr "Подяка" -msgid "submission.addSuppFile" -msgstr "Додати супровідний файл" - msgid "submission.ask" msgstr "Запитати" @@ -534,8 +531,9 @@ msgstr "Видалити запис журналу" msgid "submission.event.submissionSubmitted" msgstr "Початкове надсилання подання завершено." +#, fuzzy msgid "submission.event.fileRevised" -msgstr "Версія \"{$name}\" (file ID {$fileId}-{$fileRevision}) була завантажена." +msgstr "Версія \"{$name}\" (file ID {$submissionFileId}) була завантажена." msgid "submission.event.general.metadataUpdated" msgstr "Метадані подання оновлені" @@ -672,9 +670,6 @@ msgstr "Ви впевнені, що хочете назавжди видалит msgid "submission.layout.confirmDeleteGalleyImage" msgstr "Ви впевнені, що хочете видалити це зображення з гранки?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "Ви впевнені, що хочете назавжди видалити цей супровідний файл?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "Видалити цю таблицю стилів." @@ -738,15 +733,9 @@ msgstr "Версія верстки" msgid "submission.layout.newGalley" msgstr "Створити нову гранку" -msgid "submission.layout.newSupplementaryFile" -msgstr "Завантажити новий супровідний файл" - msgid "submission.layout.noStyleFile" msgstr "Для цієї гранки не було додано файлу таблиці стилів." -msgid "submission.layout.supplementaryFiles" -msgstr "Перегляд супровідного файлу" - msgid "submission.layout.viewingGalley" msgstr "Перегляд гранки подання" @@ -981,9 +970,6 @@ msgstr "Завантажити файл обговорення" msgid "submission.summary" msgstr "Резюме" -msgid "submission.supplementaryFiles" -msgstr "Супровідні файли" - msgid "submission.supportingAgencies" msgstr "Спонсори" @@ -1164,9 +1150,6 @@ msgstr "Завантажити файл" msgid "submission.upload.copyeditedVersion" msgstr "Завантажити файл після літературного редагування" -msgid "submission.upload.fairCopy" -msgstr "Завантажити остаточний файл" - msgid "submission.upload.uploadFiles" msgstr "Завантажити файли" diff --git a/locale/vi_VN/submission.po b/locale/vi_VN/submission.po index b6498296dd3..9107107f629 100644 --- a/locale/vi_VN/submission.po +++ b/locale/vi_VN/submission.po @@ -263,9 +263,10 @@ msgstr "Đã cập nhật tệp bài gửi" msgid "submission.event.general.metadataUpdated" msgstr "Đã cập nhật siêu dữ liệu bài gửi" +#, fuzzy msgid "submission.event.fileRevised" msgstr "" -"Bản sửa đổi \"{$name}\" (tệp với ID {$fileId}-{$fileRevision}) đã được tải " +"Bản sửa đổi \"{$name}\" (tệp với ID {$submissionFileId}) đã được tải " "lên." msgid "submission.event.submissionSubmitted" @@ -1537,9 +1538,6 @@ msgstr "" msgid "submission.upload.uploadFiles" msgstr "Tải các tập tin" -msgid "submission.upload.fairCopy" -msgstr "Tải lên bản đã chỉnh sửa lần cuối" - msgid "submission.upload.copyeditedVersion" msgstr "Tải liên tệp đã biên tập" diff --git a/locale/zh_CN/submission.po b/locale/zh_CN/submission.po index 1ab22f9fc03..b89e495dbb3 100644 --- a/locale/zh_CN/submission.po +++ b/locale/zh_CN/submission.po @@ -23,9 +23,6 @@ msgstr "已经接受的" msgid "submission.acknowledge" msgstr "致谢" -msgid "submission.addSuppFile" -msgstr "添加一个附件" - msgid "submission.ask" msgstr "Ask" @@ -206,9 +203,6 @@ msgstr "您确认将永久删除这个样稿?" msgid "submission.layout.confirmDeleteGalleyImage" msgstr "您确认将从样稿里删除这个图片?" -msgid "submission.layout.confirmDeleteSupplementaryFile" -msgstr "您确认将永久删除这个附件文件?" - msgid "submission.layout.deleteGalleyStylesheet" msgstr "删除这个样式." @@ -266,15 +260,9 @@ msgstr "排版版本" msgid "submission.layout.newGalley" msgstr "创建新的样稿" -msgid "submission.layout.newSupplementaryFile" -msgstr "上传新的附件" - msgid "submission.layout.noStyleFile" msgstr "没有样式表文件被添加到这个样稿里." -msgid "submission.layout.supplementaryFiles" -msgstr "附件审查" - msgid "submission.layout.viewingGalley" msgstr "预览投稿样稿" @@ -437,9 +425,6 @@ msgstr "提交人" msgid "submission.summary" msgstr "摘要" -msgid "submission.supplementaryFiles" -msgstr "附件文件" - msgid "submission.titleAndAbstract" msgstr "标题和摘要" diff --git a/pages/management/PKPToolsHandler.inc.php b/pages/management/PKPToolsHandler.inc.php index c9afeea2e84..af2a1737cfe 100644 --- a/pages/management/PKPToolsHandler.inc.php +++ b/pages/management/PKPToolsHandler.inc.php @@ -405,10 +405,9 @@ protected function getObjectTitle($assocId, $assocType) { if (!$section) break; return $section->getLocalizedTitle(); case ASSOC_TYPE_SUBMISSION_FILE: - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFile = $submissionFileDao->getLatestRevision($assocId); + $submissionFile = Services::get('submissionFile')->get($assocId); if (!$submissionFile) break; - return $submissionFile->getFileLabel(); + return $submissionFile->getLocalizedData('name'); } return __('manager.statistics.reports.objectNotFound'); diff --git a/plugins/generic/usageEvent/PKPUsageEventPlugin.inc.php b/plugins/generic/usageEvent/PKPUsageEventPlugin.inc.php index 457f86bbdc1..06ba55210d4 100644 --- a/plugins/generic/usageEvent/PKPUsageEventPlugin.inc.php +++ b/plugins/generic/usageEvent/PKPUsageEventPlugin.inc.php @@ -181,10 +181,14 @@ protected function buildUsageEvent($hookName, $args) { // HTML pages with no file downloads. $docSize = 0; $mimeType = 'text/html'; - } else { - // Files. + } elseif (is_a($pubObject, 'IssueGalley')) { $docSize = (int)$pubObject->getFileSize(); $mimeType = $pubObject->getFileType(); + } else { + // Files. + $path = Services::get('file')->getPath($pubObject->getData('fileId')); + $docSize = Services::get('file')->fs->getSize($path); + $mimeType = Services::get('file')->fs->getMimetype($path); } $canonicalUrl = $router->url( diff --git a/plugins/importexport/native/PKPNativeImportExportDeployment.inc.php b/plugins/importexport/native/PKPNativeImportExportDeployment.inc.php index 86ff71eb066..de58bed41f9 100644 --- a/plugins/importexport/native/PKPNativeImportExportDeployment.inc.php +++ b/plugins/importexport/native/PKPNativeImportExportDeployment.inc.php @@ -76,8 +76,6 @@ function getStageNameStageIdMapping() { 'review_file' => SUBMISSION_FILE_REVIEW_FILE, 'review_attachment' => SUBMISSION_FILE_REVIEW_ATTACHMENT, 'final' => SUBMISSION_FILE_FINAL, - 'fair_copy' => SUBMISSION_FILE_FAIR_COPY, - 'editor' => SUBMISSION_FILE_EDITOR, 'copyedit' => SUBMISSION_FILE_COPYEDIT, 'proof' => SUBMISSION_FILE_PROOF, 'production_ready' => SUBMISSION_FILE_PRODUCTION_READY, diff --git a/plugins/importexport/native/filter/NativeXmlPKPPublicationFilter.inc.php b/plugins/importexport/native/filter/NativeXmlPKPPublicationFilter.inc.php index f8ce4655642..b10b7ebb381 100644 --- a/plugins/importexport/native/filter/NativeXmlPKPPublicationFilter.inc.php +++ b/plugins/importexport/native/filter/NativeXmlPKPPublicationFilter.inc.php @@ -76,8 +76,9 @@ function handleElement($node) { $publication = $this->populateObject($publication, $node); $publicationLocale = $node->getAttribute('locale'); - if (empty($publicationLocale)) + if (empty($publicationLocale)) { $publicationLocale = $context->getPrimaryLocale(); + } $publication->setData('locale', $publicationLocale); $publication->setData('version', $node->getAttribute('version')); @@ -140,7 +141,7 @@ function handleChildElement($n, $publication) { $controlledVocabulary[] = $nc->textContent; } } - + $controlledVocabulariesValues = array(); $controlledVocabulariesValues[$locale] = $controlledVocabulary; diff --git a/plugins/importexport/native/filter/NativeXmlSubmissionFileFilter.inc.php b/plugins/importexport/native/filter/NativeXmlSubmissionFileFilter.inc.php index 4a12e3895ac..79478e4eac2 100644 --- a/plugins/importexport/native/filter/NativeXmlSubmissionFileFilter.inc.php +++ b/plugins/importexport/native/filter/NativeXmlSubmissionFileFilter.inc.php @@ -12,6 +12,7 @@ * * @brief Base class that converts a Native XML document to a submission file */ +use Illuminate\Database\Capsule\Manager as Capsule; import('lib.pkp.plugins.importexport.native.filter.NativeImportFilter'); @@ -58,75 +59,24 @@ function getClassName() { /** * Handle a submission file element * @param $node DOMElement - * @return array Array of SubmissionFile objects + * @return SubmissionFile|null Null if skipping this file */ function handleElement($node) { $deployment = $this->getDeployment(); + $submission = $deployment->getSubmission(); + $context = $deployment->getContext(); $stageName = $node->getAttribute('stage'); - $fileId = $node->getAttribute('id'); + $submissionFileId = $node->getAttribute('id'); $stageNameIdMapping = $deployment->getStageNameStageIdMapping(); assert(isset($stageNameIdMapping[$stageName])); $stageId = $stageNameIdMapping[$stageName]; - - $submissionFiles = array(); - // Handle metadata in subelements - for ($n = $node->firstChild; $n !== null; $n=$n->nextSibling) { - if (is_a($n, 'DOMElement')) { - $this->handleChildElement($n, $stageId, $fileId, $submissionFiles); - } - } - - return $submissionFiles; - } - - /** - * Handle a child node of the submission file element; add new files, if - * any, to $submissionFiles - * @param $node DOMElement - * @param $stageId int SUBMISSION_FILE_... - * @param $fileId int File id - * @param $submissionFiles array - */ - function handleChildElement($node, $stageId, $fileId, &$submissionFiles) { - $deployment = $this->getDeployment(); - $submission = $deployment->getSubmission(); - switch ($node->tagName) { - case 'revision': - $submissionFile = $this->handleRevisionElement($node, $stageId, $fileId); - if ($submissionFile) $submissionFiles[] = $submissionFile; - break; - default: - $deployment->addWarning(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownElement', array('param' => $node->tagName))); - } - } - - /** - * Handle a revision element - * @param $node DOMElement - * @param $stageId int SUBMISSION_FILE_... - * @param $fileId int File id - */ - function handleRevisionElement($node, $stageId, $fileId) { - static $genresByContextId = array(); - - $deployment = $this->getDeployment(); - $submission = $deployment->getSubmission(); - $context = $deployment->getContext(); - + $request = Application::get()->getRequest(); $errorOccured = false; - $revisionId = $node->getAttribute('number'); - - $source = $node->getAttribute('source'); - $sourceFileAndRevision = null; - if ($source) { - $sourceFileAndRevision = explode('-', $source); - } - $genreId = null; $genreName = $node->getAttribute('genre'); + // Build a cached list of genres by context ID by name if ($genreName) { - // Build a cached list of genres by context ID by name if (!isset($genresByContextId[$context->getId()])) { $genreDao = DAORegistry::getDAO('GenreDAO'); /* @var $genreDao GenreDAO */ $genres = $genreDao->getByContextId($context->getId()); @@ -145,35 +95,8 @@ function handleRevisionElement($node, $stageId, $fileId) { } } - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFile = $submissionFileDao->newDataObjectByGenreId($genreId); - $submissionFile->setSubmissionId($submission->getId()); - $submissionFile->setSubmissionLocale($submission->getLocale()); - $submissionFile->setGenreId($genreId); - $submissionFile->setFileStage($stageId); - $submissionFile->setDateUploaded(Core::getCurrentDate()); - $submissionFile->setDateModified(Core::getCurrentDate()); - if ($node->getAttribute('available') == 'true') $submissionFile->setViewable(true); - - $submissionFile->setOriginalFileName($filename = $node->getAttribute('filename')); - for ($n = $node->firstChild; $n !== null; $n=$n->nextSibling) { - if (is_a($n, 'DOMElement')) { - $filename = $this->handleRevisionChildElement($n, $submission, $submissionFile); - } - } - if (!$filename) { - // $this->handleRevisionChildElement() failed to provide any file (error message should have been handled in called method) - $errorOccured = true; - } else { - clearstatcache(true, $filename); - if (!file_exists($filename) || !filesize($filename)) { - // $this->handleRevisionChildElement() failed to provide a real file - $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.native.error.submissionFileImportFailed')); - $errorOccured = true; - } - } - $uploaderUsername = $node->getAttribute('uploader'); + $uploaderUserId = null; if (!$uploaderUsername) { $user = $deployment->getUser(); } else { @@ -181,161 +104,221 @@ function handleRevisionElement($node, $stageId, $fileId) { $userDao = DAORegistry::getDAO('UserDAO'); /* @var $userDao UserDAO */ $user = $userDao->getByUsername($uploaderUsername); } - if ($user) { - $submissionFile->setUploaderUserId($user->getId()); - } else { - $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownUploader', array('param' => $uploaderUsername))); - $errorOccured = true; - } + $uploaderUserId = $user + ? (int) $user->getId() + : Application::get()->getRequest()->getUser()->getId(); - $fileSize = $node->getAttribute('filesize'); - $fileSizeOnDisk = filesize($filename); - if ($fileSize) { - if ($fileSize != $fileSizeOnDisk) { - $deployment->addWarning(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.filesizeMismatch', array('expected' => $fileSize, 'actual' => $fileSizeOnDisk))); - } + $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ + $submissionFile = $submissionFileDao->newDataObject(); + $submissionFile->setData('submissionId', $submission->getId()); + $submissionFile->setData('locale', $submission->getLocale()); + $submissionFile->setData('fileStage', $stageId); + $submissionFile->setData('createdAt', Core::getCurrentDate()); + $submissionFile->setData('updatedAt', Core::getCurrentDate()); + $submissionFile->setData('dateCreated', $node->getAttribute('date_created')); + $submissionFile->setData('language', $node->getAttribute('language')); + + if ($caption = $node->getAttribute('caption')) { + $submissionFile->setData('caption', $caption); } - else { - $fileSize = $fileSizeOnDisk; + if ($copyrightOwner = $node->getAttribute('copyright_owner')) { + $submissionFile->setData('copyrightOwner', $copyrightOwner); + } + if ($credit = $node->getAttribute('credit')) { + $submissionFile->setData('credit', $credit); + } + if ($directSalesPrice = $node->getAttribute('direct_sales_price')) { + $submissionFile->setData('directSalesPrice', $directSalesPrice); + } + if ($genreId) { + $submissionFile->setData('genreId', $genreId); + } + if ($salesType = $node->getAttribute('sales_type')) { + $submissionFile->setData('salesType', $salesType); + } + if ($sourceSubmissionFileId = $node->getAttribute('source_submission_file_id')) { + $submissionFile->setData('sourceSubmissionFileId', $sourceSubmissionFileId); + } + if ($terms = $node->getAttribute('terms')) { + $submissionFile->setData('terms', $terms); + } + if ($uploaderUserId) { + $submissionFile->setData('uploaderUserId', $uploaderUserId); + } + if ($node->getAttribute('viewable') == 'true') { + $submissionFile->setViewable(true); } - $submissionFile->setFileSize($fileSize); - - $fileType = $node->getAttribute('filetype'); - $submissionFile->setFileType($fileType); - $submissionFile->setRevision($revisionId); + // Handle metadata in subelements + $allRevisionIds = []; + for ($childNode = $node->firstChild; $childNode !== null; $childNode=$childNode->nextSibling) { + if (is_a($childNode, 'DOMElement')) { + switch ($childNode->tagName) { + case 'creator': + case 'description': + case 'name': + case 'publisher': + case 'source': + case 'sponsor': + case 'subject': + list($locale, $value) = $this->parseLocalizedContent($childNode); + $submissionFile->setData($childNode->tagName, $value, $locale); + case 'submission_file_ref': + if ($submissionFile->getData('fileStage') == SUBMISSION_FILE_DEPENDENT) { + $oldAssocId = $node->getAttribute('id'); + $newAssocId = $deployment->getSubmissionFileDBId($oldAssocId); + if ($newAssocId) { + $submissionFile->setData('assocType', ASSOC_TYPE_SUBMISSION_FILE); + $submissionFile->setData('assocId', $newAssocId); + } + } + break; + case 'file': + // File has already been imported so update file id + if ($deployment->getFileDBId($childNode->getAttribute('id'))) { + $newFileId = $deployment->getFileDBId($childNode->getAttribute('id')); + } else { + $newFileId = $this->handleRevisionElement($childNode); + } + if ($newFileId) { + $allRevisionIds[] = $newFileId; + } + // If this is the current file revision, set the submission file id + if ($childNode->getAttribute('id') == $node->getAttribute('fileId')) { + $submissionFile->setData('fileId', $newFileId); + } - if ($sourceFileAndRevision) { - // the source file revision should already be processed, so get the new source file ID - $sourceFileId = $deployment->getFileDBId($sourceFileAndRevision[0], $sourceFileAndRevision[1]); - if ($sourceFileId) { - $submissionFile->setSourceFileId($sourceFileId); - $submissionFile->setSourceRevision($sourceFileAndRevision[1]); + break; + default: + $deployment->addWarning(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownElement', array('param' => $node->tagName))); + } } } - // if the same file is already inserted, take its DB file ID - $DBId = $deployment->getFileDBId($fileId); - if ($DBId) { - $submissionFile->setFileId($DBId); - $DBRevision = $deployment->getFileDBId($fileId, $revisionId); - // If both the file id and the revision id is duplicated, we cannot insert the record - if ($DBRevision) { - $errorOccured = true; - $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.duplicateRevisionForSubmission', array('fileId' => $fileId, 'revisionId' => $revisionId))); - } + if ($errorOccured) { + return null; } - if ($errorOccured) { - // if error occured, the file cannot be inserted into DB, becase - // genre, uploader and user group are required (e.g. at name generation). - $submissionFile = null; + // Add and edit the submission file revisions one-by-one so that a useful activity + // log is built and past revisions can be accessed + if (count($allRevisionIds) < 2) { + $submissionFile = Services::get('submissionFile')->add($submissionFile, $request); } else { - $insertedSubmissionFile = $submissionFileDao->insertObject($submissionFile, $filename, false); - $deployment->setFileDBId($fileId, $revisionId, $insertedSubmissionFile->getFileId()); + $currentFileId = $submissionFile->getData('fileId'); + foreach ($allRevisionIds as $i => $fileId) { + if ($fileId === $currentFileId) { + continue; + } + if ($i === 0) { + $submissionFile->setData('fileId', $fileId); + $submissionFile = Services::get('submissionFile')->add($submissionFile, $request); + } else { + $submissionFile = Services::get('submissionFile')->edit($submissionFile, ['fileId' => $fileId], $request); + } + } + $submissionFile = Services::get('submissionFile')->edit($submissionFile, ['fileId' => $currentFileId], $request); } - import('lib.pkp.classes.file.FileManager'); - $fileManager = new FileManager(); - $fileManager->deleteByPath($filename); + $deployment->setSubmissionFileDBId($node->getAttribute('id'), $submissionFile->getId()); + return $submissionFile; } /** - * Handle a child of the revision element + * Handle a revision element * @param $node DOMElement - * @param $submission Submission - * @param $submissionFile SubmissionFile - * @return string Filename for temporary file, if one was created + * @return int|null The new file id if successful */ - function handleRevisionChildElement($node, $submission, $submissionFile) { + function handleRevisionElement($node) { $deployment = $this->getDeployment(); - $context = $deployment->getContext(); $submission = $deployment->getSubmission(); - switch ($node->tagName) { - case 'submission_file_ref': - if ($submissionFile->getFileStage() == SUBMISSION_FILE_DEPENDENT) { - $fileId = $node->getAttribute('id'); - $revisionId = $node->getAttribute('revision'); - $dbFileId = $deployment->getFileDBId($fileId, $revisionId); - if ($dbFileId) { - $submissionFile->setAssocType(ASSOC_TYPE_SUBMISSION_FILE); - $submissionFile->setAssocId($dbFileId); - } - } - break; - case 'id': - $this->parseIdentifier($node, $submissionFile); - break; - case 'name': - $locale = $node->getAttribute('locale'); - if (empty($locale)) $locale = $context->getPrimaryLocale(); - $submissionFile->setName($node->textContent, $locale); - $submissionFile->setSubmissionLocale($locale); - break; - case 'href': - $submissionFile->setFileType($node->getAttribute('mime_type')); - import('lib.pkp.classes.file.TemporaryFileManager'); - $temporaryFileManager = new TemporaryFileManager(); - $temporaryFilename = tempnam($temporaryFileManager->getBasePath(), 'src'); - $filesrc = $node->getAttribute('src'); - $errorFlag = false; - if (preg_match('|\w+://.+|', $filesrc)) { - // process as a URL - $client = Application::get()->getHttpClient(); - $response = $client->request('GET', $filesrc); - file_put_contents($temporaryFilename, $response->getBody()); - if (!filesize($temporaryFilename)) { - $errorFlag = true; - } - } elseif (substr($filesrc, 0, 1) === '/') { - // local file (absolute path) - if (!copy($filesrc, $temporaryFilename)) { - $errorFlag = true; - } - } elseif (is_readable($deployment->getImportPath() . '/' . $filesrc)) { - // local file (relative path) - $filesrc = $deployment->getImportPath() . '/' . $filesrc; - if(!copy($filesrc, $temporaryFilename)) { - $errorFlag = true; - } - } else { - // unhandled file path - $errorFlag = true; - } - if ($errorFlag) { - $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.temporaryFileFailed', array('dest' => $temporaryFilename, 'source' => $filesrc))); - $fileManager = new FileManager(); - $fileManager->deleteByPath($temporaryFilename); - $temporaryFilename = ''; - } - return $temporaryFilename; - break; - case 'embed': - $submissionFile->setFileType($node->getAttribute('mime_type')); - import('lib.pkp.classes.file.TemporaryFileManager'); - $temporaryFileManager = new TemporaryFileManager(); - $temporaryFilename = tempnam($temporaryFileManager->getBasePath(), 'embed'); - if (($e = $node->getAttribute('encoding')) != 'base64') { - $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownEncoding', array('param' => $e))); - } else { - $content = base64_decode($node->textContent, true); - $errorFlag = false; - if (!$content) { - $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.encodingError', array('param' => $e))); - $errorFlag = true; - } elseif (!file_put_contents($temporaryFilename, $content)) { - $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.temporaryFileFailed', array('dest' => $temporaryFilename, 'source' => 'embed'))); - $errorFlag = true; - } - if ($errorFlag) { - $fileManager = new FileManager(); - $fileManager->deleteByPath($temporaryFilename); - $temporaryFilename = ''; - } + + for ($childNode = $node->firstChild; $childNode !== null; $childNode=$childNode->nextSibling) { + if (is_a($childNode, 'DOMElement')) { + switch ($childNode->tagName) { + case 'href': + import('lib.pkp.classes.file.TemporaryFileManager'); + $temporaryFileManager = new TemporaryFileManager(); + $temporaryFilename = tempnam($temporaryFileManager->getBasePath(), 'src'); + $filesrc = $childNode->getAttribute('src'); + $errorFlag = false; + if (preg_match('|\w+://.+|', $filesrc)) { + // process as a URL + $client = Application::get()->getHttpClient(); + $response = $client->request('GET', $filesrc); + file_put_contents($temporaryFilename, $response->getBody()); + if (!filesize($temporaryFilename)) { + $errorFlag = true; + } + } elseif (substr($filesrc, 0, 1) === '/') { + // local file (absolute path) + if (!copy($filesrc, $temporaryFilename)) { + $errorFlag = true; + } + } elseif (is_readable($deployment->getImportPath() . '/' . $filesrc)) { + // local file (relative path) + $filesrc = $deployment->getImportPath() . '/' . $filesrc; + if(!copy($filesrc, $temporaryFilename)) { + $errorFlag = true; + } + } else { + // unhandled file path + $errorFlag = true; + } + if ($errorFlag) { + $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.temporaryFileFailed', array('dest' => $temporaryFilename, 'source' => $filesrc))); + $fileManager = new FileManager(); + $fileManager->deleteByPath($temporaryFilename); + $temporaryFilename = ''; + } + break; + case 'embed': + import('lib.pkp.classes.file.TemporaryFileManager'); + $temporaryFileManager = new TemporaryFileManager(); + $temporaryFilename = tempnam($temporaryFileManager->getBasePath(), 'embed'); + if (($e = $childNode->getAttribute('encoding')) != 'base64') { + $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownEncoding', array('param' => $e))); + } else { + $content = base64_decode($childNode->textContent, true); + $errorFlag = false; + if (!$content) { + $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.encodingError', array('param' => $e))); + $errorFlag = true; + } elseif (!file_put_contents($temporaryFilename, $content)) { + $deployment->addError(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.temporaryFileFailed', array('dest' => $temporaryFilename, 'source' => 'embed'))); + $errorFlag = true; + } + if ($errorFlag) { + $fileManager = new FileManager(); + $fileManager->deleteByPath($temporaryFilename); + $temporaryFilename = ''; + } + } + break; } - return $temporaryFilename; - break; + } + } + + if ($temporaryFilename) { + $fileSizeOnDisk = filesize($temporaryFilename); + $expectedFileSize = $node->getAttribute('filesize'); + if ($fileSizeOnDisk != $expectedFileSize) { + $deployment->addWarning(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.filesizeMismatch', array('expected' => $expectedFileSize, 'actual' => $fileSizeOnDisk))); + } else { + clearstatcache(true, $temporaryFilename); + import('lib.pkp.classes.file.FileManager'); + $fileManager = new FileManager(); + $submissionDir = Services::get('submissionFile')->getSubmissionDir($submission->getData('contextId'), $submission->getId()); + $newFileId = Services::get('file')->add( + $temporaryFilename, + $submissionDir . '/' . uniqid() . '.' . $node->getAttribute('extension') + ); + $deployment->setFileDBId($node->getAttribute('id'), $newFileId); + } + } + + if ($newFileId) { + return $newFileId; } } diff --git a/plugins/importexport/native/filter/NativeXmlSubmissionFilter.inc.php b/plugins/importexport/native/filter/NativeXmlSubmissionFilter.inc.php index 9d0709a11dc..ee94e4cff0d 100644 --- a/plugins/importexport/native/filter/NativeXmlSubmissionFilter.inc.php +++ b/plugins/importexport/native/filter/NativeXmlSubmissionFilter.inc.php @@ -90,8 +90,8 @@ function handleElement($node) { $this->handleChildElement($n, $submission); } } - - $submission = Services::get('submission')->edit($submission, array(), Application::get()->getRequest()); + + $submission = Services::get('submission')->get($submission->getId()); return $submission; } @@ -127,12 +127,7 @@ function handleChildElement($n, $submission) { $this->parseSubmissionFile($n, $submission); break; case 'publication': - $publication = $this->parsePublication($n, $submission); - - $publications = $submission->getData('publications'); - $publications[] = $publication[0]; - $submission->setData('publications', $publications); - + $this->parsePublication($n, $submission); break; default: $deployment = $this->getDeployment(); diff --git a/plugins/importexport/native/filter/PKPPublicationNativeXmlFilter.inc.php b/plugins/importexport/native/filter/PKPPublicationNativeXmlFilter.inc.php index cb5ae385b58..7a6a1c964b1 100644 --- a/plugins/importexport/native/filter/PKPPublicationNativeXmlFilter.inc.php +++ b/plugins/importexport/native/filter/PKPPublicationNativeXmlFilter.inc.php @@ -74,7 +74,7 @@ function createEntityNode($doc, $entity) { $entityNode = $doc->createElementNS($deployment->getNamespace(), 'publication'); $this->addIdentifiers($doc, $entityNode, $entity); - + $entityNode->setAttribute('locale', $entity->getData('locale')); $entityNode->setAttribute('version', $entity->getData('version') ?: 1); $entityNode->setAttribute('status', $entity->getData('status')); @@ -83,12 +83,12 @@ function createEntityNode($doc, $entity) { $isPublished = $entity->getData('status') === STATUS_PUBLISHED; $isPublished ? $entityNode->setAttribute('seq', (int) $entity->getData('seq')) : $entityNode->setAttribute('seq', '0'); - + $entityLanguages = $entity->getData('languages'); if ($entityLanguages) { $entityNode->setAttribute('language', $entityLanguages); } - + if ($datePublished = $entity->getData('datePublished')) { $entityNode->setAttribute('date_published', strftime('%Y-%m-%d', strtotime($datePublished))); } @@ -164,13 +164,13 @@ function addMetadata($doc, $entityNode, $entity) { $this->createLocalizedNodes($doc, $entityNode, 'type', $entity->getData('type')); $this->createLocalizedNodes($doc, $entityNode, 'source', $entity->getData('source')); $this->createLocalizedNodes($doc, $entityNode, 'rights', $entity->getData('rights')); - + if ($entity->getData('licenseUrl')) { $entityNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'licenseUrl', htmlspecialchars($entity->getData('licenseUrl')))); } $this->createLocalizedNodes($doc, $entityNode, 'copyrightHolder', $entity->getData('copyrightHolder')); - + if ($entity->getData('copyrightYear')) { $entityNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'copyrightYear', intval($entity->getData('copyrightYear')))); } diff --git a/plugins/importexport/native/filter/RepresentationNativeXmlFilter.inc.php b/plugins/importexport/native/filter/RepresentationNativeXmlFilter.inc.php index f7a5217e127..fe3874b9363 100644 --- a/plugins/importexport/native/filter/RepresentationNativeXmlFilter.inc.php +++ b/plugins/importexport/native/filter/RepresentationNativeXmlFilter.inc.php @@ -94,8 +94,7 @@ function createRepresentationNode($doc, $representation) { // Add files foreach ($this->getFiles($representation) as $submissionFile) { $fileRefNode = $doc->createElementNS($deployment->getNamespace(), 'submission_file_ref'); - $fileRefNode->setAttribute('id', $submissionFile->getFileId()); - $fileRefNode->setAttribute('revision', $submissionFile->getRevision()); + $fileRefNode->setAttribute('id', $submissionFile->getId()); $representationNode->appendChild($fileRefNode); } } diff --git a/plugins/importexport/native/filter/SubmissionFileNativeXmlFilter.inc.php b/plugins/importexport/native/filter/SubmissionFileNativeXmlFilter.inc.php index bb5dd52d86f..7ba381560da 100644 --- a/plugins/importexport/native/filter/SubmissionFileNativeXmlFilter.inc.php +++ b/plugins/importexport/native/filter/SubmissionFileNativeXmlFilter.inc.php @@ -70,81 +70,107 @@ function &process(&$submissionFile) { function createSubmissionFileNode($doc, $submissionFile) { $deployment = $this->getDeployment(); $context = $deployment->getContext(); + $stageToName = array_flip($deployment->getStageNameStageIdMapping()); + $genreDao = DAORegistry::getDAO('GenreDAO'); /* @var $genreDao GenreDAO */ + $genre = $genreDao->getById($submissionFile->getData('genreId')); + $userDao = DAORegistry::getDAO('UserDAO'); /* @var $userDao UserDAO */ + $uploaderUser = $userDao->getById($submissionFile->getData('uploaderUserId')); // Create the submission_file node and set metadata $submissionFileNode = $doc->createElementNS($deployment->getNamespace(), $this->getSubmissionFileElementName()); - - $stageToName = array_flip($deployment->getStageNameStageIdMapping()); + $submissionFileNode->setAttribute('id', $submissionFile->getId()); + $submissionFileNode->setAttribute('created_at', strftime('%Y-%m-%d', strtotime($submissionFile->getData('createdAt')))); + $submissionFileNode->setAttribute('date_created', $submissionFile->getData('dateCreated')); + $submissionFileNode->setAttribute('fileId', $submissionFile->getData('fileId')); $submissionFileNode->setAttribute('stage', $stageToName[$submissionFile->getFileStage()]); - $submissionFileNode->setAttribute('id', $submissionFile->getFileId()); - - // Create the revision node and set metadata - $revisionNode = $doc->createElementNS($deployment->getNamespace(), 'revision'); - $revisionNode->setAttribute('number', $submissionFile->getRevision()); - if ($sourceFileId = $submissionFile->getSourceFileId()) { - $revisionNode->setAttribute('source', $sourceFileId . '-' . $submissionFile->getSourceRevision()); + $submissionFileNode->setAttribute('updated_at', strftime('%Y-%m-%d', strtotime($submissionFile->getData('updatedAt')))); + $submissionFileNode->setAttribute('viewable', $submissionFile->getViewable()?'true':'false'); + if ($caption = $submissionFile->getData('caption')) { + $submissionFileNode->setAttribute('caption', $caption); + } + if ($copyrightOwner = $submissionFile->getData('copyrightOwner')) { + $submissionFileNode->setAttribute('copyright_owner', $copyrightOwner); + } + if ($credit = $submissionFile->getData('credit')) { + $submissionFileNode->setAttribute('credit', $credit); + } + if ($directSalesPrice = $submissionFile->getData('directSalesPrice')) { + $submissionFileNode->setAttribute('direct_sales_price', $directSalesPrice); } - - $genreDao = DAORegistry::getDAO('GenreDAO'); /* @var $genreDao GenreDAO */ - $genre = $genreDao->getById($submissionFile->getGenreId()); if ($genre) { - $revisionNode->setAttribute('genre', $genre->getName($context->getPrimaryLocale())); + $submissionFileNode->setAttribute('genre', $genre->getName($context->getPrimaryLocale())); } - - $revisionNode->setAttribute('filename', $submissionFile->getOriginalFileName()); - $revisionNode->setAttribute('viewable', $submissionFile->getViewable()?'true':'false'); - $revisionNode->setAttribute('date_uploaded', strftime('%Y-%m-%d', strtotime($submissionFile->getDateUploaded()))); - $revisionNode->setAttribute('date_modified', strftime('%Y-%m-%d', strtotime($submissionFile->getDateModified()))); - if ($submissionFile->getDirectSalesPrice() !== null) { - $revisionNode->setAttribute('direct_sales_price', $submissionFile->getDirectSalesPrice()); + if ($language = $submissionFile->getData('language')) { + $submissionFileNode->setAttribute('language', $language); } - $revisionNode->setAttribute('filesize', $submissionFile->getFileSize()); - $revisionNode->setAttribute('filetype', $submissionFile->getFileType()); - - $userDao = DAORegistry::getDAO('UserDAO'); /* @var $userDao UserDAO */ - $uploaderUser = $userDao->getById($submissionFile->getUploaderUserId()); - assert(isset($uploaderUser)); - $revisionNode->setAttribute('uploader', $uploaderUser->getUsername()); - - $this->addIdentifiers($doc, $revisionNode, $submissionFile); - $this->createLocalizedNodes($doc, $revisionNode, 'name', $submissionFile->getName(null)); - - // if it is a dependent file, add submission_file_ref element - if ($submissionFile->getFileStage() == SUBMISSION_FILE_DEPENDENT && $submissionFile->getAssocType() == ASSOC_TYPE_SUBMISSION_FILE) { + if ($salesType = $submissionFile->getData('salesType')) { + $submissionFileNode->setAttribute('sales_type', $salesType); + } + if ($sourceSubmissionFileId = $submissionFile->getData('sourceSubmissionFileId')) { + $submissionFileNode->setAttribute('source_submission_file_id', $sourceSubmissionFileId); + } + if ($terms = $submissionFile->getData('terms')) { + $submissionFileNode->setAttribute('terms', $terms); + } + if ($uploaderUser) { + $submissionFileNode->setAttribute('uploader', $uploaderUser->getUsername()); + } + $this->createLocalizedNodes($doc, $submissionFileNode, 'creator', $submissionFile->getData('creator')); + $this->createLocalizedNodes($doc, $submissionFileNode, 'description', $submissionFile->getData('description')); + $this->createLocalizedNodes($doc, $submissionFileNode, 'name', $submissionFile->getData('name')); + $this->createLocalizedNodes($doc, $submissionFileNode, 'publisher', $submissionFile->getData('publisher')); + $this->createLocalizedNodes($doc, $submissionFileNode, 'source', $submissionFile->getData('source')); + $this->createLocalizedNodes($doc, $submissionFileNode, 'sponsor', $submissionFile->getData('sponsor')); + $this->createLocalizedNodes($doc, $submissionFileNode, 'subject', $submissionFile->getData('subject')); + + // If it is a dependent file, add submission_file_ref element + if ($submissionFile->getData('fileStage') == SUBMISSION_FILE_DEPENDENT && $submissionFile->getData('assocType') == ASSOC_TYPE_SUBMISSION_FILE) { $fileRefNode = $doc->createElementNS($deployment->getNamespace(), 'submission_file_ref'); - $fileRefNode->setAttribute('id', $submissionFile->getAssocId()); - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $latestRevision = $submissionFileDao->getLatestRevisionNumber($submissionFile->getAssocId()); - $fileRefNode->setAttribute('revision', $latestRevision); - $revisionNode->appendChild($fileRefNode); + $fileRefNode->setAttribute('id', $submissionFile->getData('assocId')); + $submissionFileNode->appendChild($fileRefNode); } - $submissionFileNode->appendChild($revisionNode); - // Embed the file contents - - if (array_key_exists('no-embed', $this->opts)) { - $hrefNode = $doc->createElementNS($deployment->getNamespace(), 'href'); - if (array_key_exists('use-file-urls', $this->opts)) { - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); - $stageId = $submissionFileDao->getWorkflowStageId($submissionFile); - $app = Application::getApplication(); - $dispatcher = $app->getDispatcher(); - $params = ["fileId" => $submissionFile->getFileId(), - "revision" => $submissionFile->getRevision(), - "submissionId" => $submissionFile->getSubmissionId(), - "stageId" => $stageId]; - $url = $dispatcher->url($app->getRequest(), ROUTE_COMPONENT, $context->getPath(), "api.file.FileApiHandler", "downloadFile", null, $params); - $hrefNode->setAttribute('src', $url); + // Add pub-id plugins + $this->addIdentifiers($doc, $submissionFileNode, $submissionFile); + + // Create the revision nodes + $fileIds = Services::get('submissionFile')->getRevisionFileIds($submissionFile->getId()); + foreach ($fileIds as $fileId) { + $path = Services::get('file')->getPath($fileId); + $localPath = rtrim(Config::getVar('files', 'files_dir'), '/') . '/' . $path; + $revisionNode = $doc->createElementNS($deployment->getNamespace(), 'file'); + $revisionNode->setAttribute('id', $fileId); + $revisionNode->setAttribute('filesize', filesize($localPath)); + $revisionNode->setAttribute('extension', pathinfo($path, PATHINFO_EXTENSION)); + + if (array_key_exists('no-embed', $this->opts)) { + $hrefNode = $doc->createElementNS($deployment->getNamespace(), 'href'); + if (array_key_exists('use-file-urls', $this->opts)) { + $stageId = Services::get('submissionFile')->getWorkflowStageId($submissionFile); + $dispatcher = Application::get()->getDispatcher(); + $request = Application::get()->getRequest(); + $params = [ + "submissionFileId" => $submissionFile->getId(), + "submissionId" => $submissionFile->getData('submissionId'), + "stageId" => $stageId, + ]; + $url = $dispatcher->url($request, ROUTE_COMPONENT, $context->getPath(), "api.file.FileApiHandler", "downloadFile", null, $params); + $hrefNode->setAttribute('src', $url); + } else { + $hrefNode->setAttribute('src', $path); + } + $hrefNode->setAttribute('mime_type', Services::get('file')->fs->getMimetype($path)); + $revisionNode->appendChild($hrefNode); } else { - $hrefNode->setAttribute('src', $submissionFile->getFilePath()); + $embedNode = $doc->createElementNS($deployment->getNamespace(), 'embed', base64_encode(file_get_contents($localPath))); + $embedNode->setAttribute('encoding', 'base64'); + $revisionNode->appendChild($embedNode); } - $hrefNode->setAttribute('mime_type', $submissionFile->getFileType()); - $revisionNode->appendChild($hrefNode); - } else { - $embedNode = $doc->createElementNS($deployment->getNamespace(), 'embed', base64_encode(file_get_contents($submissionFile->getFilePath()))); - $embedNode->setAttribute('encoding', 'base64'); - $revisionNode->appendChild($embedNode); + + $submissionFileNode->appendChild($revisionNode); } + + return $submissionFileNode; } diff --git a/plugins/importexport/native/filter/SubmissionNativeXmlFilter.inc.php b/plugins/importexport/native/filter/SubmissionNativeXmlFilter.inc.php index 18840df834c..c0091a1d2df 100644 --- a/plugins/importexport/native/filter/SubmissionNativeXmlFilter.inc.php +++ b/plugins/importexport/native/filter/SubmissionNativeXmlFilter.inc.php @@ -156,46 +156,29 @@ function addControlledVocabulary($doc, $submissionNode, $controlledVocabulariesN */ function addFiles($doc, $submissionNode, $submission) { $filterDao = DAORegistry::getDAO('FilterDAO'); /* @var $filterDao FilterDAO */ - $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */ - $submissionFiles = $submissionFileDao->getBySubmissionId($submission->getId()); - - // Submission files will come back from the file export filter - // with one revision each, wrapped in a submission_file node: - // - // ... - // - // Reformat them into groups by submission_file, i.e.: - // - // ... - // ... - // - $submissionFileNodesByFileId = array(); - foreach ($submissionFiles as $submissionFile) { + $submissionFilesIterator = Services::get('submissionFile')->getMany([ + 'submissionIds' => [$submission->getId()], + 'includeDependentFiles' => true, + ]); + + foreach ($submissionFilesIterator as $submissionFile) { + + // Skip files attached to objects that are not included in the export, + // such as files uploaded to discussions and files uploaded by reviewers + if (in_array($submissionFile->getData('fileStage'), [SUBMISSION_FILE_QUERY, SUBMISSION_FILE_NOTE, SUBMISSION_FILE_REVIEW_ATTACHMENT])) { + $this->getDeployment()->addWarning(ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.native.error.submissionFileSkipped', array('id' => $submissionFile->getId()))); + continue; + } + $nativeExportFilters = $filterDao->getObjectsByGroup(get_class($submissionFile) . '=>native-xml'); assert(count($nativeExportFilters)==1); // Assert only a single serialization filter $exportFilter = array_shift($nativeExportFilters); $exportFilter->setDeployment($this->getDeployment()); $exportFilter->setOpts($this->opts); - $submissionFileDoc = $exportFilter->execute($submissionFile); - $fileId = $submissionFileDoc->documentElement->getAttribute('id'); - if (!isset($submissionFileNodesByFileId[$fileId])) { - $clone = $doc->importNode($submissionFileDoc->documentElement, true); - $submissionNode->appendChild($clone); - $submissionFileNodesByFileId[$fileId] = $clone; - } else { - $submissionFileNode = $submissionFileNodesByFileId[$fileId]; - // Look for a element - $revisionNode = null; - foreach ($submissionFileDoc->documentElement->childNodes as $childNode) { - if (!is_a($childNode, 'DOMElement')) continue; - if ($childNode->tagName == 'revision') $revisionNode = $childNode; - } - assert(is_a($revisionNode, 'DOMElement')); - $clone = $doc->importNode($revisionNode, true); - $firstRevisionChild = $submissionFileNode->firstChild; - $submissionFileNode->insertBefore($clone, $firstRevisionChild); - } + $submissionFileDoc = $exportFilter->execute($submissionFile, true); + $clone = $doc->importNode($submissionFileDoc->documentElement, true); + $submissionNode->appendChild($clone); } } diff --git a/plugins/importexport/native/pkp-native.xsd b/plugins/importexport/native/pkp-native.xsd index 30aa0691270..ca98004701c 100644 --- a/plugins/importexport/native/pkp-native.xsd +++ b/plugins/importexport/native/pkp-native.xsd @@ -85,30 +85,37 @@ - + + + + + + + + + - - - - - - - - - - - - + - + - - + + + + + + + + + + + + @@ -130,12 +137,15 @@ + + + + - - +