21 use \DAOResultFactory;
23 use \Illuminate\Database\Capsule\Manager as Capsule;
25 use \PKP\Services\interfaces\EntityPropertyInterface;
26 use \PKP\Services\interfaces\EntityReadInterface;
27 use \PKP\Services\interfaces\EntityWriteInterface;
28 use \APP\Services\QueryBuilders\SubmissionQueryBuilder;
30 define(
'STAGE_STATUS_SUBMISSION_UNASSIGNED', 1);
32 abstract class PKPSubmissionService implements EntityPropertyInterface, EntityReadInterface, EntityWriteInterface {
37 public function get($submissionId) {
39 return $submissionDao->getById($submissionId);
50 $qb = new \PKP\Services\QueryBuilders\PKPPublicationQueryBuilder();
51 $firstResult = $qb->getQueryByUrlPath($urlPath, $contextId)->first();
57 return $this->
get($firstResult->submission_id);
93 if (isset($args[
'count'])) {
94 import(
'lib.pkp.classes.db.DBResultRange');
95 $range = new \DBResultRange($args[
'count'],
null, isset($args[
'offset']) ? $args[
'offset'] : 0);
99 if (isset($args[
'count'])) unset($args[
'count']);
100 if (isset($args[
'offset'])) unset($args[
'offset']);
103 $result = $submissionDao->retrieveRange($submissionListQO->toSql(), $submissionListQO->getBindings(), $range);
106 return $queryResults->toIterator();
114 if (isset($args[
'count'])) unset($args[
'count']);
115 if (isset($args[
'offset'])) unset($args[
'offset']);
125 $defaultArgs = array(
126 'contextId' => CONTEXT_ID_NONE,
127 'orderBy' =>
'dateSubmitted',
128 'orderDirection' =>
'DESC',
132 'searchPhrase' =>
null,
133 'isIncomplete' =>
false,
134 'isOverdue' =>
false,
135 'daysInactive' =>
null,
138 $args = array_merge($defaultArgs, $args);
142 ->filterByContext($args[
'contextId'])
143 ->orderBy($args[
'orderBy'], $args[
'orderDirection'])
144 ->assignedTo($args[
'assignedTo'])
145 ->filterByStatus($args[
'status'])
146 ->filterByStageIds($args[
'stageIds'])
147 ->filterByIncomplete($args[
'isIncomplete'])
148 ->filterByOverdue($args[
'isOverdue'])
149 ->filterByDaysInactive($args[
'daysInactive'])
150 ->filterByCategories(isset($args[
'categoryIds'])?$args[
'categoryIds']:
null)
151 ->searchPhrase($args[
'searchPhrase']);
153 if (isset($args[
'count'])) {
154 $submissionListQB->limitTo($args[
'count']);
157 if (isset($args[
'offset'])) {
158 $submissionListQB->offsetBy($args[
'count']);
163 return $submissionListQB;
172 $request = $args[
'request'];
173 $dispatcher = $request->getDispatcher();
176 if (array_intersect([
'_href',
'urlAuthorWorkflow',
'urlEditorialWorkflow'], $props)) {
177 $submissionContext = $request->getContext();
178 if (!$submissionContext || $submissionContext->getId() != $submission->getData(
'contextId')) {
179 $submissionContext =
Services::get(
'context')->get($submission->getData(
'contextId'));
183 foreach ($props as $prop) {
186 $values[$prop] = $dispatcher->url(
189 $submissionContext->getData(
'urlPath'),
190 'submissions/' . $submission->getId()
194 $values[$prop] = array_map(
195 function($publication) use ($args, $submission, $submissionContext) {
199 'submission' => $submission,
200 'context' => $submissionContext,
202 ->getLastReviewRoundReviewAssignmentByReviewer(
203 $submission->getId(),
204 $args[
'request']->getUser()->getId()
208 $submission->getData(
'publications')
211 case 'reviewAssignments':
221 $values[$prop] = __($submission->getStatusKey());
223 case 'urlAuthorWorkflow':
224 $values[$prop] = $dispatcher->url(
227 $submissionContext->getData(
'urlPath'),
233 case 'urlEditorialWorkflow':
234 $values[$prop] = $dispatcher->url(
237 $submissionContext->getData(
'urlPath'),
247 $values[$prop] = $submission->getData($prop);
252 $values =
Services::get(
'schema')->addMissingMultilingualValues(
SCHEMA_SUBMISSION, $values, $request->getContext()->getSupportedSubmissionLocales());
254 \HookRegistry::call(
'Submission::getProperties::values', array(&$values, $submission, $props, $args));
290 '_href',
'contextId',
'currentPublicationId',
'dateLastActivity',
'dateSubmitted',
'id',
291 'lastModified',
'publications',
'reviewAssignments',
'reviewRounds',
'stageId',
'stages',
'status',
292 'statusLabel',
'submissionProgress',
'urlAuthorWorkflow',
'urlEditorialWorkflow',
'urlWorkflow',
'urlPublished',
295 \HookRegistry::call(
'Submission::getBackendListProperties::properties', array(&$props, $submission, $args));
311 foreach($reviewAssignments as $reviewAssignment) {
314 if ($reviewAssignment->getDeclined()) {
320 $due = is_null($reviewAssignment->getDateDue()) ? null : strftime($dateFormatShort, strtotime($reviewAssignment->getDateDue()));
321 $responseDue = is_null($reviewAssignment->getDateResponseDue()) ? null : strftime($dateFormatShort, strtotime($reviewAssignment->getDateResponseDue()));
324 'id' => (
int) $reviewAssignment->getId(),
325 'isCurrentUserAssigned' => $currentUser->getId() == (
int) $reviewAssignment->getReviewerId(),
326 'statusId' => (
int) $reviewAssignment->getStatus(),
327 'status' => __($reviewAssignment->getStatusKey()),
329 'responseDue' => $responseDue,
330 'round' => (
int) $reviewAssignment->getRound(),
331 'roundId' => (
int) $reviewAssignment->getReviewRoundId(),
350 foreach ($reviewRounds as $reviewRound) {
352 'id' => $reviewRound->getId(),
353 'round' => $reviewRound->getRound(),
354 'stageId' => $reviewRound->getStageId(),
355 'statusId' => $reviewRound->determineStatus(),
356 'status' => __($reviewRound->getStatusKey()),
390 if (is_null($stageIds)) {
392 } elseif (is_int($stageIds)) {
393 $stageIds = array($stageIds);
398 $contextId = $context ? $context->getId() : CONTEXT_ID_NONE;
401 foreach ($stageIds as $stageId) {
403 import(
'lib.pkp.classes.workflow.WorkflowStageDAO');
406 'id' => (
int) $stageId,
407 'label' => __($workflowStageDao->getTranslationKeyFromId($stageId)),
408 'isActiveStage' => $submission->getStageId() == $stageId,
412 $stage[
'queries'] = array();
414 import(
'lib.pkp.classes.query.QueryDAO');
416 $queries = $queryDao->getByAssoc(
417 ASSOC_TYPE_SUBMISSION,
418 $submission->getId(),
420 $request->getUser()->getId()
423 while ($query = $queries->next()) {
424 $stage[
'queries'][] = array(
425 'id' => (
int) $query->getId(),
426 'assocType' => (
int) $query->getAssocType(),
427 'assocId' => (
int) $query->getAssocId(),
428 'stageId' => $stageId,
429 'seq' => (
int) $query->getSequence(),
430 'closed' => (
bool) $query->getIsClosed(),
434 $currentUserAssignedRoles = array();
437 $stageAssignmentsResult = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId($submission->getId(), $currentUser->getId(), $stageId);
439 while ($stageAssignment = $stageAssignmentsResult->next()) {
440 $userGroup = $userGroupDao->getById($stageAssignment->getUserGroupId(), $contextId);
441 $currentUserAssignedRoles[] = (int) $userGroup->getRoleId();
444 $stage[
'currentUserAssignedRoles'] = array_values(array_unique($currentUserAssignedRoles));
449 case WORKFLOW_STAGE_ID_SUBMISSION:
450 import(
'lib.pkp.classes.stageAssignment.StageAssignmentDAO');
452 $assignedEditors = $stageAssignmentDao->editorAssignedToStage($submission->getId(), $stageId);
453 if (!$assignedEditors) {
455 $stage[
'status'] = __(
'submissions.queuedUnassigned');
459 $stage[
'files'] = array(
464 case WORKFLOW_STAGE_ID_INTERNAL_REVIEW:
465 case WORKFLOW_STAGE_ID_EXTERNAL_REVIEW:
466 import(
'lib.pkp.classes.submission.reviewRound.ReviewRoundDAO');
468 $reviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $stageId);
470 $stage[
'statusId'] = $reviewRound->determineStatus();
471 $stage[
'status'] = __($reviewRound->getStatusKey());
474 import(
'lib.pkp.classes.submission.SubmissionFile');
476 $submissionFiles = $submissionFileDao->getRevisionsByReviewRound($reviewRound, SUBMISSION_FILE_REVIEW_REVISION);
477 $stage[
'files'] = array(
478 'count' => count($submissionFiles),
483 $user = $request->getUser();
484 $editorsStageAssignments = $stageAssignmentDao->getEditorsAssignedToStage($submission->getId(), $stageId);
487 $stage[
'currentUserCanRecommendOnly'] =
false;
488 foreach ($editorsStageAssignments as $editorsStageAssignment) {
489 if ($editorsStageAssignment->getUserId() == $user->getId() && $editorsStageAssignment->getRecommendOnly()) {
490 $stage[
'currentUserCanRecommendOnly'] =
true;
496 $stage[
'files'] = array(
504 case WORKFLOW_STAGE_ID_EDITING:
505 case WORKFLOW_STAGE_ID_PRODUCTION:
506 import(
'lib.pkp.classes.submission.SubmissionFile');
508 $fileStageIId = $stageId === WORKFLOW_STAGE_ID_EDITING ? SUBMISSION_FILE_COPYEDIT : SUBMISSION_FILE_PROOF;
509 $submissionFiles = $submissionFileDao->getLatestRevisions($submission->getId(), $fileStageIId);
510 $stage[
'files'] = array(
511 'count' => count($submissionFiles),
539 if (is_null($userId)) {
540 $user = $request->getUser();
543 $user = $userDao->getById($userId);
546 if (is_null($user)) {
550 $submissionContext = $request->getContext();
552 if (!$submissionContext || $submissionContext->getId() != $submission->getData(
'contextId')) {
553 $submissionContext =
Services::get(
'context')->get($submission->getData(
'contextId'));
556 $dispatcher = $request->getDispatcher();
561 $authorUserGroupIds = $userGroupDao->getUserGroupIdsByRoleId(ROLE_ID_AUTHOR);
562 $stageAssignmentsFactory = $stageAssignmentDao->getBySubmissionAndStageId($submission->getId(),
null,
null, $user->getId());
564 $authorDashboard =
false;
565 while ($stageAssignment = $stageAssignmentsFactory->next()) {
566 if (in_array($stageAssignment->getUserGroupId(), $authorUserGroupIds)) {
567 $authorDashboard =
true;
573 if ($submission->getSubmissionProgress() > 0 &&
575 $user->hasRole(array(ROLE_ID_MANAGER), $submissionContext->getId()) ||
576 $user->hasRole(array(ROLE_ID_SITE_ADMIN), CONTEXT_SITE))) {
577 return $dispatcher->url(
580 $submissionContext->getPath(),
583 $submission->getSubmissionProgress(),
584 array(
'submissionId' => $submission->getId())
589 if ($authorDashboard) {
590 return $dispatcher->url(
593 $submissionContext->getPath(),
602 $reviewAssignment = $reviewAssignmentDao->getLastReviewRoundReviewAssignmentByReviewer($submission->getId(), $user->getId());
603 if ($reviewAssignment) {
604 return $dispatcher->url(
607 $submissionContext->getPath(),
616 return $dispatcher->url(
619 $submissionContext->getPath(),
634 if (!is_a($submission,
'Submission')) {
635 $submission = $this->
get((int) $submission);
642 $contextId = $submission->getContextId();
644 $currentUser = $request->getUser();
653 if ($currentUser->hasRole(array(ROLE_ID_MANAGER), $contextId) || $currentUser->hasRole(array(ROLE_ID_SITE_ADMIN), CONTEXT_SITE)) {
656 if ($submission->getSubmissionProgress() != 0 ) {
658 $assignments = $stageAssignmentDao->getBySubmissionAndRoleId($submission->getId(), ROLE_ID_AUTHOR, WORKFLOW_STAGE_ID_SUBMISSION, $currentUser->getId());
659 $assignment = $assignments->next();
677 return $reviewRoundDao->getBySubmissionId($submission->getId())->toIterator();
687 import(
'lib.pkp.classes.submission.reviewAssignment.ReviewAssignmentDAO');
689 return $reviewAssignmentDao->getBySubmissionId($submission->getId());
695 public function validate($action, $props, $allowedLocales, $primaryLocale) {
698 import(
'lib.pkp.classes.validation.ValidatorFactory');
718 $validator->after(
function($validator) use ($props) {
719 if (isset($props[
'contextid']) && !$validator->errors()->get(
'contextid')) {
720 $submissionContext = Services::get(
'context')->get($props[
'contextid']);
721 if (!$submissionContext) {
722 $validator->errors()->add(
'contextId', __(
'submission.submit.noContext'));
727 if ($validator->fails()) {
728 $errors = $schemaService->formatValidationErrors($validator->errors(), $schemaService->get(
SCHEMA_SUBMISSION), $allowedLocales);
731 \HookRegistry::call(
'Submission::validate', [&$errors, $action, $props, $allowedLocales, $primaryLocale]);
739 public function add($submission, $request) {
740 $submission->stampLastActivity();
741 $submission->stampModified();
742 if (!$submission->getData(
'dateSubmitted') && !$submission->getData(
'submissionProgress')) {
746 $submissionId = $submissionDao->insertObject($submission);
747 $submission = $this->
get($submissionId);
757 public function edit($submission, $params, $request) {
760 $newSubmission = $submissionDao->newDataObject();
761 $newSubmission->_data = array_merge($submission->_data, $params);
762 $submission->stampLastActivity();
763 $submission->stampModified();
767 $submissionDao->updateObject($newSubmission);
768 $newSubmission = $this->
get($newSubmission->getId());
770 return $newSubmission;
776 public function delete($submission) {
780 $submissionDao->deleteObject($submission);
794 $stageAssignments = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId($submissionId, $userId,
null);
796 while ($stageAssignment = $stageAssignments->next()) {
797 if ($stageAssignment->getCanChangeMetadata()) {
803 if ($stageAssignments->wasEmpty() && $this->_canUserAccessUnassignedSubmissions($context->getId(), $userId)){
816 private static function _canUserAccessUnassignedSubmissions($contextId, $userId) {
818 $roles = $roleDao->getByUserId($userId, $contextId);
820 $allowedRoles = $userGroupDao->getNotChangeMetadataEditPermissionRoles();
821 foreach ($roles as $role) {
822 if (in_array($role->getRoleId(), $allowedRoles))
838 $status = $newStatus = $submission->getData(
'status');
839 $currentPublicationId = $submission->getData(
'currentPublicationId');
840 $publications = $submission->getData(
'publications');
844 if (empty($publications)) {
845 throw new \Exception(
'Tried to update the status of submission ' . $submission->getId() .
' and no publications were found.');
850 $newCurrentPublicationId = array_reduce($publications,
function($a, $b) {
851 return $b->getData(
'status') === STATUS_PUBLISHED && $b->getId() > $a ? $b->getId() : $a;
853 if (!$newCurrentPublicationId) {
854 $newCurrentPublicationId = array_reduce($publications,
function($a, $b) {
855 return $a > $b->getId() ? $a : $b->getId();
861 if ($status !== STATUS_DECLINED) {
862 $newStatus = STATUS_QUEUED;
863 foreach ($publications as $publication) {
864 if ($publication->getData(
'status') === STATUS_PUBLISHED) {
865 $newStatus = STATUS_PUBLISHED;
868 if ($publication->getData(
'status') === STATUS_SCHEDULED) {
869 $newStatus = STATUS_SCHEDULED;
878 if ($status !== $newStatus) {
879 $updateParams[
'status'] = $newStatus;
881 if ($currentPublicationId !== $newCurrentPublicationId) {
882 $updateParams[
'currentPublicationId'] = $newCurrentPublicationId;
884 if (!empty($updateParams)) {
885 $submission = $this->edit($submission, $updateParams,
Application::get()->getRequest());