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()) {
319 $currentUser = $request->getUser();
320 $context = $request->getContext();
321 $dateFormatShort = $context->getLocalizedDateFormatShort();
322 $due = is_null($reviewAssignment->getDateDue()) ? null : strftime($dateFormatShort, strtotime($reviewAssignment->getDateDue()));
323 $responseDue = is_null($reviewAssignment->getDateResponseDue()) ? null : strftime($dateFormatShort, strtotime($reviewAssignment->getDateResponseDue()));
326 'id' => (
int) $reviewAssignment->getId(),
327 'isCurrentUserAssigned' => $currentUser->getId() == (
int) $reviewAssignment->getReviewerId(),
328 'statusId' => (
int) $reviewAssignment->getStatus(),
329 'status' => __($reviewAssignment->getStatusKey()),
331 'responseDue' => $responseDue,
332 'round' => (
int) $reviewAssignment->getRound(),
333 'roundId' => (
int) $reviewAssignment->getReviewRoundId(),
352 foreach ($reviewRounds as $reviewRound) {
354 'id' => $reviewRound->getId(),
355 'round' => $reviewRound->getRound(),
356 'stageId' => $reviewRound->getStageId(),
357 'statusId' => $reviewRound->determineStatus(),
358 'status' => __($reviewRound->getStatusKey()),
392 if (is_null($stageIds)) {
394 } elseif (is_int($stageIds)) {
395 $stageIds = array($stageIds);
400 $contextId = $context ? $context->getId() : CONTEXT_ID_NONE;
403 foreach ($stageIds as $stageId) {
405 import(
'lib.pkp.classes.workflow.WorkflowStageDAO');
408 'id' => (
int) $stageId,
409 'label' => __($workflowStageDao->getTranslationKeyFromId($stageId)),
410 'isActiveStage' => $submission->getStageId() == $stageId,
414 $stage[
'queries'] = array();
416 import(
'lib.pkp.classes.query.QueryDAO');
418 $queries = $queryDao->getByAssoc(
419 ASSOC_TYPE_SUBMISSION,
420 $submission->getId(),
422 $request->getUser()->getId()
425 while ($query = $queries->next()) {
426 $stage[
'queries'][] = array(
427 'id' => (
int) $query->getId(),
428 'assocType' => (
int) $query->getAssocType(),
429 'assocId' => (
int) $query->getAssocId(),
430 'stageId' => $stageId,
431 'seq' => (
int) $query->getSequence(),
432 'closed' => (
bool) $query->getIsClosed(),
436 $currentUserAssignedRoles = array();
439 $stageAssignmentsResult = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId($submission->getId(), $currentUser->getId(), $stageId);
441 while ($stageAssignment = $stageAssignmentsResult->next()) {
442 $userGroup = $userGroupDao->getById($stageAssignment->getUserGroupId(), $contextId);
443 $currentUserAssignedRoles[] = (int) $userGroup->getRoleId();
446 $stage[
'currentUserAssignedRoles'] = array_values(array_unique($currentUserAssignedRoles));
451 case WORKFLOW_STAGE_ID_SUBMISSION:
452 import(
'lib.pkp.classes.stageAssignment.StageAssignmentDAO');
454 $assignedEditors = $stageAssignmentDao->editorAssignedToStage($submission->getId(), $stageId);
455 if (!$assignedEditors) {
457 $stage[
'status'] = __(
'submissions.queuedUnassigned');
461 $stage[
'files'] = array(
466 case WORKFLOW_STAGE_ID_INTERNAL_REVIEW:
467 case WORKFLOW_STAGE_ID_EXTERNAL_REVIEW:
468 import(
'lib.pkp.classes.submission.reviewRound.ReviewRoundDAO');
470 $reviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $stageId);
472 $stage[
'statusId'] = $reviewRound->determineStatus();
473 $stage[
'status'] = __($reviewRound->getStatusKey());
476 import(
'lib.pkp.classes.submission.SubmissionFile');
478 $submissionFiles = $submissionFileDao->getRevisionsByReviewRound($reviewRound, SUBMISSION_FILE_REVIEW_REVISION);
479 $stage[
'files'] = array(
480 'count' => count($submissionFiles),
485 $user = $request->getUser();
486 $editorsStageAssignments = $stageAssignmentDao->getEditorsAssignedToStage($submission->getId(), $stageId);
489 $stage[
'currentUserCanRecommendOnly'] =
false;
490 foreach ($editorsStageAssignments as $editorsStageAssignment) {
491 if ($editorsStageAssignment->getUserId() == $user->getId() && $editorsStageAssignment->getRecommendOnly()) {
492 $stage[
'currentUserCanRecommendOnly'] =
true;
498 $stage[
'files'] = array(
506 case WORKFLOW_STAGE_ID_EDITING:
507 case WORKFLOW_STAGE_ID_PRODUCTION:
508 import(
'lib.pkp.classes.submission.SubmissionFile');
510 $fileStageIId = $stageId === WORKFLOW_STAGE_ID_EDITING ? SUBMISSION_FILE_COPYEDIT : SUBMISSION_FILE_PROOF;
511 $submissionFiles = $submissionFileDao->getLatestRevisions($submission->getId(), $fileStageIId);
512 $stage[
'files'] = array(
513 'count' => count($submissionFiles),
541 if (is_null($userId)) {
542 $user = $request->getUser();
545 $user = $userDao->getById($userId);
548 if (is_null($user)) {
552 $submissionContext = $request->getContext();
554 if (!$submissionContext || $submissionContext->getId() != $submission->getData(
'contextId')) {
555 $submissionContext =
Services::get(
'context')->get($submission->getData(
'contextId'));
558 $dispatcher = $request->getDispatcher();
563 $authorUserGroupIds = $userGroupDao->getUserGroupIdsByRoleId(ROLE_ID_AUTHOR);
564 $stageAssignmentsFactory = $stageAssignmentDao->getBySubmissionAndStageId($submission->getId(),
null,
null, $user->getId());
566 $authorDashboard =
false;
567 while ($stageAssignment = $stageAssignmentsFactory->next()) {
568 if (in_array($stageAssignment->getUserGroupId(), $authorUserGroupIds)) {
569 $authorDashboard =
true;
575 if ($submission->getSubmissionProgress() > 0 &&
577 $user->hasRole(array(ROLE_ID_MANAGER), $submissionContext->getId()) ||
578 $user->hasRole(array(ROLE_ID_SITE_ADMIN), CONTEXT_SITE))) {
579 return $dispatcher->url(
582 $submissionContext->getPath(),
585 $submission->getSubmissionProgress(),
586 array(
'submissionId' => $submission->getId())
591 if ($authorDashboard) {
592 return $dispatcher->url(
595 $submissionContext->getPath(),
604 $reviewAssignment = $reviewAssignmentDao->getLastReviewRoundReviewAssignmentByReviewer($submission->getId(), $user->getId());
605 if ($reviewAssignment) {
606 return $dispatcher->url(
609 $submissionContext->getPath(),
618 return $dispatcher->url(
621 $submissionContext->getPath(),
636 if (!is_a($submission,
'Submission')) {
637 $submission = $this->
get((int) $submission);
644 $contextId = $submission->getContextId();
646 $currentUser = $request->getUser();
655 if ($currentUser->hasRole(array(ROLE_ID_MANAGER), $contextId) || $currentUser->hasRole(array(ROLE_ID_SITE_ADMIN), CONTEXT_SITE)) {
658 if ($submission->getSubmissionProgress() != 0 ) {
660 $assignments = $stageAssignmentDao->getBySubmissionAndRoleId($submission->getId(), ROLE_ID_AUTHOR, WORKFLOW_STAGE_ID_SUBMISSION, $currentUser->getId());
661 $assignment = $assignments->next();
679 return $reviewRoundDao->getBySubmissionId($submission->getId())->toIterator();
689 import(
'lib.pkp.classes.submission.reviewAssignment.ReviewAssignmentDAO');
691 return $reviewAssignmentDao->getBySubmissionId($submission->getId());
697 public function validate($action, $props, $allowedLocales, $primaryLocale) {
700 import(
'lib.pkp.classes.validation.ValidatorFactory');
720 $validator->after(
function($validator) use ($props) {
721 if (isset($props[
'contextid']) && !$validator->errors()->get(
'contextid')) {
722 $submissionContext = Services::get(
'context')->get($props[
'contextid']);
723 if (!$submissionContext) {
724 $validator->errors()->add(
'contextId', __(
'submission.submit.noContext'));
729 if ($validator->fails()) {
730 $errors = $schemaService->formatValidationErrors($validator->errors(), $schemaService->get(
SCHEMA_SUBMISSION), $allowedLocales);
733 \HookRegistry::call(
'Submission::validate', [&$errors, $action, $props, $allowedLocales, $primaryLocale]);
741 public function add($submission, $request) {
742 $submission->stampLastActivity();
743 $submission->stampModified();
744 if (!$submission->getData(
'dateSubmitted') && !$submission->getData(
'submissionProgress')) {
748 $submissionId = $submissionDao->insertObject($submission);
749 $submission = $this->
get($submissionId);
759 public function edit($submission, $params, $request) {
762 $newSubmission = $submissionDao->newDataObject();
763 $newSubmission->_data = array_merge($submission->_data, $params);
764 $submission->stampLastActivity();
765 $submission->stampModified();
769 $submissionDao->updateObject($newSubmission);
770 $newSubmission = $this->
get($newSubmission->getId());
772 return $newSubmission;
778 public function delete($submission) {
782 $submissionDao->deleteObject($submission);
796 $stageAssignments = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId($submissionId, $userId,
null);
798 while ($stageAssignment = $stageAssignments->next()) {
799 if ($stageAssignment->getCanChangeMetadata()) {
805 if ($stageAssignments->wasEmpty() && $this->_canUserAccessUnassignedSubmissions($context->getId(), $userId)){
818 private static function _canUserAccessUnassignedSubmissions($contextId, $userId) {
820 $roles = $roleDao->getByUserId($userId, $contextId);
822 $allowedRoles = $userGroupDao->getNotChangeMetadataEditPermissionRoles();
823 foreach ($roles as $role) {
824 if (in_array($role->getRoleId(), $allowedRoles))
840 $status = $newStatus = $submission->getData(
'status');
841 $currentPublicationId = $submission->getData(
'currentPublicationId');
842 $publications = $submission->getData(
'publications');
846 if (empty($publications)) {
847 throw new \Exception(
'Tried to update the status of submission ' . $submission->getId() .
' and no publications were found.');
852 $newCurrentPublicationId = array_reduce($publications,
function($a, $b) {
853 return $b->getData(
'status') === STATUS_PUBLISHED && $b->getId() > $a ? $b->getId() : $a;
855 if (!$newCurrentPublicationId) {
856 $newCurrentPublicationId = array_reduce($publications,
function($a, $b) {
857 return $a > $b->getId() ? $a : $b->getId();
863 if ($status !== STATUS_DECLINED) {
864 $newStatus = STATUS_QUEUED;
865 foreach ($publications as $publication) {
866 if ($publication->getData(
'status') === STATUS_PUBLISHED) {
867 $newStatus = STATUS_PUBLISHED;
870 if ($publication->getData(
'status') === STATUS_SCHEDULED) {
871 $newStatus = STATUS_SCHEDULED;
880 if ($status !== $newStatus) {
881 $updateParams[
'status'] = $newStatus;
883 if ($currentPublicationId !== $newCurrentPublicationId) {
884 $updateParams[
'currentPublicationId'] = $newCurrentPublicationId;
886 if (!empty($updateParams)) {
887 $submission = $this->edit($submission, $updateParams,
Application::get()->getRequest());