<?php
/**
* @file classes/submission/Repository.php
*
* Copyright (c) 2014-2020 Simon Fraser University
* Copyright (c) 2000-2020 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class Repository
*
* @brief A repository to find and manage submissions.
*/
namespace PKP\submission;
use APP\author\Author;
use APP\core\Application;
use APP\core\Request;
use APP\core\Services;
use APP\facades\Repo;
use APP\publication\Publication;
use APP\section\Section;
use APP\submission\Collector;
use APP\submission\DAO;
use APP\submission\Submission;
use Illuminate\Support\Enumerable;
use Illuminate\Support\LazyCollection;
use PKP\context\Context;
use PKP\core\Core;
use PKP\db\DAORegistry;
use PKP\doi\exceptions\DoiException;
use PKP\facades\Locale;
use PKP\observers\events\SubmissionSubmitted;
use PKP\plugins\Hook;
use PKP\query\QueryDAO;
use PKP\security\Role;
use PKP\security\RoleDAO;
use PKP\services\PKPSchemaService;
use PKP\stageAssignment\StageAssignmentDAO;
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
use PKP\submissionFile\SubmissionFile;
use PKP\user\User;
use PKP\validation\ValidatorFactory;
abstract class Repository
{
public const STAGE_STATUS_SUBMISSION_UNASSIGNED = 1;
/** @var DAO $dao */
public $dao;
/** @var string $schemaMap The name of the class to map this entity to its schema */
public $schemaMap = maps\Schema::class;
/** @var Request $request */
protected $request;
/** @var PKPSchemaService<Submission> $schemaService */
protected $schemaService;
public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
{
$this->dao = $dao;
$this->request = $request;
$this->schemaService = $schemaService;
}
/** @copydoc DAO::newDataObject() */
public function newDataObject(array $params = []): Submission
{
$object = $this->dao->newDataObject();
if (!empty($params)) {
$object->setAllData($params);
}
return $object;
}
/** @copydoc DAO::exists() */
public function exists(int $id, int $contextId = null): bool
{
return $this->dao->exists($id, $contextId);
}
/** @copydoc DAO::get() */
public function get(int $id, int $contextId = null): ?Submission
{
return $this->dao->get($id, $contextId);
}
/** @copydoc DAO::getCollector() */
public function getCollector(): Collector
{
return app(Collector::class);
}
/**
* Get an instance of the map class for mapping
* submissions to their schema
*/
public function getSchemaMap(): maps\Schema
{
return app('maps')->withExtensions($this->schemaMap);
}
/**
* Get a submission by "best" submission id -- url path if it exists,
* falling back on the internal submission ID otherwise.
*/
public function getByBestId(string $idOrUrlPath, int $contextId = null): ?Submission
{
return ctype_digit((string) $idOrUrlPath)
? $this->get((int) $idOrUrlPath, $contextId)
: $this->getByUrlPath($idOrUrlPath, $contextId);
}
/**
* Get a submission by its urlPath
*
* This returns a submission if any of its publications have a
* matching urlPath.
*/
public function getByUrlPath(string $urlPath, int $contextId): ?Submission
{
$submissionId = $this->dao->getIdByUrlPath($urlPath, $contextId);
return $submissionId
? $this->get($submissionId)
: null;
}
/**
* Gets a submission by its current publication's DOI
*
*
*/
public function getByDoi(string $doi, int $contextId): ?Submission
{
return $this->dao->getByDoi($doi, $contextId);
}
/** @copydoc DAO::getIdsBySetting() */
public function getIdsBySetting(string $settingName, $settingValue, int $contextId): Enumerable
{
return $this->dao->getIdsBySetting($settingName, $settingValue, $contextId);
}
/**
* Get the correct access URL for a submission's workflow based on a user's
* role.
*
* The returned URL will point to the correct workflow page based on whether
* the user should be treated as an author, reviewer or editor/assistant for
* this submission.
*/
public function getWorkflowUrlByUserRoles(Submission $submission, ?int $userId = null): string
{
$request = Application::get()->getRequest();
if (is_null($userId)) {
$user = $request->getUser();
} else {
$user = Repo::user()->get($userId);
}
if (is_null($user)) {
return '';
}
$submissionContext = $request->getContext();
if (!$submissionContext || $submissionContext->getId() != $submission->getData('contextId')) {
$submissionContext = Services::get('context')->get($submission->getData('contextId'));
}
$dispatcher = $request->getDispatcher();
// Check if the user is an author of this submission
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
$authorUserGroupIds = Repo::userGroup()->getArrayIdByRoleId(Role::ROLE_ID_AUTHOR);
$stageAssignmentsFactory = $stageAssignmentDao->getBySubmissionAndStageId($submission->getId(), null, null, $user->getId());
$authorDashboard = false;
while ($stageAssignment = $stageAssignmentsFactory->next()) {
if (in_array($stageAssignment->getUserGroupId(), $authorUserGroupIds)) {
$authorDashboard = true;
}
}
// Send authors, journal managers and site admins to the submission
// wizard for incomplete submissions
if ($submission->getSubmissionProgress() &&
($authorDashboard ||
$user->hasRole([Role::ROLE_ID_MANAGER], $submissionContext->getId()) ||
$user->hasRole([Role::ROLE_ID_SITE_ADMIN], Application::CONTEXT_SITE))) {
return $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$submissionContext->getPath(),
'submission',
null,
null,
['id' => $submission->getId()]
);
}
// Send authors to author dashboard
if ($authorDashboard) {
return $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$submissionContext->getPath(),
'authorDashboard',
'submission',
$submission->getId()
);
}
// Send reviewers to review wizard
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
$reviewAssignment = $reviewAssignmentDao->getLastReviewRoundReviewAssignmentByReviewer($submission->getId(), $user->getId());
if ($reviewAssignment && !$reviewAssignment->getCancelled() && !$reviewAssignment->getDeclined()) {
return $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$submissionContext->getPath(),
'reviewer',
'submission',
$submission->getId()
);
}
// Give any other users the editorial workflow URL. If they can't access
// it, they'll be blocked there.
return $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$submissionContext->getPath(),
'workflow',
'access',
$submission->getId()
);
}
/**
* Validate properties for a submission
*
* Perform validation checks on data used to add or edit a submission.
*
* @param Submission|null $submission The submission being edited. Pass `null` if creating a new submission
* @param array $props A key/value array with the new data to validate
*
* @return array A key/value array with validation errors. Empty if no errors
*/
public function validate(?Submission $submission, array $props, Context $context): array
{
$primaryLocale = $props['locale'] ?? $submission?->getLocale() ?? $context->getPrimaryLocale();
$allowedLocales = $context->getSupportedSubmissionLocales();
$errors = [];
$validator = ValidatorFactory::make(
$props,
$this->schemaService->getValidationRules(PKPSchemaService::SCHEMA_SUBMISSION, $allowedLocales)
);
// Check required fields
ValidatorFactory::required(
$validator,
$submission,
$this->schemaService->getRequiredProps(PKPSchemaService::SCHEMA_SUBMISSION),
$this->schemaService->getMultilingualProps(PKPSchemaService::SCHEMA_SUBMISSION),
$primaryLocale,
$allowedLocales
);
// Check for input from disallowed locales
ValidatorFactory::allowedLocales($validator, $this->schemaService->getMultilingualProps(PKPSchemaService::SCHEMA_SUBMISSION), $allowedLocales);
// The submission's locale must be one of the context's supported submission locales
$validator->after(function ($validator) use ($props, $allowedLocales) {
if (isset($props['locale']) && !$validator->errors()->get('locale')) {
if (!in_array($props['locale'], $allowedLocales)) {
$validator->errors()->add('locale', __('validator.locale'));
}
}
});
// The contextId must match an existing context
$validator->after(function ($validator) use ($props) {
if (isset($props['contextId']) && !$validator->errors()->get('contextId')) {
$submissionContext = Services::get('context')->exists($props['contextId']);
if (!$submissionContext) {
$validator->errors()->add('contextId', __('submission.submit.noContext'));
}
}
});
// The sectionId must match an existing section in this context
$validator->after(function ($validator) use ($props, $submission) {
$propName = Application::getSectionIdPropName();
if ($validator->errors()->get($propName)) {
return;
}
$sectionId = $props[$propName] ?? ($submission ? $submission->getCurrentPublication()->getData($propName) : null);
if (!$sectionId) {
return;
}
$contextId = $props['contextId'] ?? ($submission ? $submission->getData('contextId') : null);
if (!Repo::section()->exists($sectionId, $contextId)) {
$validator->errors()->add($propName, __('submission.sectionNotFound'));
return;
}
});
// Comments for the editors are invalid after a submission has been submitted
if ($submission && !$submission->getData('submissionProgress')) {
$validator->after(function ($validator) use ($props, $submission) {
if (isset($props['commentsForTheEditors']) && !$validator->errors()->get('commentsForTheEditors')) {
$validator->errors()->add('commentsForTheEditors', __('form.disallowedProp'));
}
});
}
if ($validator->fails()) {
$errors = $this->schemaService->formatValidationErrors($validator->errors());
}
Hook::call('Submission::validate', [&$errors, $submission, $props, $allowedLocales, $primaryLocale]);
return $errors;
}
/**
* Check if a submission meets all requirements to be submitted
*
* @return array A key/value array with validation errors. Empty if no errors
*/
public function validateSubmit(Submission $submission, Context $context): array
{
$locale = $submission->getData('locale');
$publication = $submission->getCurrentPublication();
$errors = [];
// Can't submit a submission twice or a submission with the wrong status
if (!$submission->getData('submissionProgress') || ($submission->getData('status') !== Submission::STATUS_QUEUED)) {
$errors['submissionProgress'] = __(
'submission.wizard.alreadySubmitted',
[
'url' => Application::get()
->getDispatcher()
->url(
Application::get()->getRequest(),
Application::ROUTE_PAGE,
$context->getData('path'),
'submissions'
)
]
);
}
// Title required in submission locale
if (!$publication->getData('title', $locale)) {
$errors['title'] = [$locale => [__('validator.required')]];
}
// Author names required in submission locale
foreach ($publication->getData('authors') as $author) {
/** @var Author $author */
if (!$author->getGivenName($submission->getLocale())) {
if (!isset($errors['contributors'])) {
$errors['contributors'] = [];
}
$errors['contributors'][] = __('submission.wizard.missingContributorLanguage', ['language' => Locale::getMetadata($locale)->getDisplayName()]);
break;
}
}
// Required metadata
$publicationSchema = $this->schemaService->get(PKPSchemaService::SCHEMA_PUBLICATION);
foreach ($context->getRequiredMetadata() as $metadata) {
// The `citations` metadata is received and validated at `citationsRaw`
if ($metadata === 'citations') {
$metadata = 'citationsRaw';
}
// The `supportingAgencies` metadata is called `agencies` on the context
if ($metadata === 'agencies') {
$metadata = 'supportingAgencies';
}
$schema = $publicationSchema->properties?->{$metadata};
if (!$schema) {
continue;
}
if (empty($schema->multilingual) && empty($publication->getData($metadata))) {
$errors[$metadata] = [__('validator.required')];
} elseif (!empty($schema->multilingual) && empty($publication->getData($metadata, $locale))) {
$errors[$metadata] = [$locale => [__('validator.required')]];
}
}
// Required submission files
$genreDao = DAORegistry::getDAO('GenreDAO'); /** @var GenreDAO $genreDao */
$requiredGenres = $genreDao->getRequiredToSubmit($context->getId());
if (!$requiredGenres->isEmpty()) {
$submissionFiles = Repo::submissionFile()
->getCollector()
->filterBySubmissionIds([$submission->getId()])
->filterByGenreIds(
$requiredGenres->map(
function (Genre $genre) {
return $genre->getId();
}
)->toArray()
)
->getMany();
$missingGenres = $submissionFiles->isEmpty()
? clone $requiredGenres
: $requiredGenres->filter(
function (Genre $genre) use ($submissionFiles) {
$exists = $submissionFiles->first(
function (SubmissionFile $submissionFile) use ($genre) {
return $submissionFile->getData('genreId') === $genre->getId();
}
);
return !$exists;
}
);
if ($missingGenres->count()) {
$missingGenreNames = $missingGenres->map(
function (Genre $genre) {
return $genre->getLocalizedName();
}
);
$errors['files'] = [
$missingGenres->count() > 1
? __('submission.files.required.genres', [
'genres' => $missingGenreNames->join(__('common.commaListSeparator'))
])
: __('submission.files.required.genre', ['genre' => $missingGenreNames->first()])
];
}
}
Hook::call('Submission::validateSubmit', [&$errors, $submission, $context]);
return $errors;
}
/**
* Check if a user can delete a submission
*/
public function canCurrentUserDelete(Submission $submission): bool
{
$this->request = Application::get()->getRequest();
$contextId = $submission->getData('contextId');
$currentUser = $this->request->getUser();
if (!$currentUser) {
return false;
}
$canDelete = false;
// Only allow admins and journal managers to delete submissions, except
// for authors who can delete their own incomplete submissions
if ($currentUser->hasRole([Role::ROLE_ID_MANAGER], $contextId) || $currentUser->hasRole([Role::ROLE_ID_SITE_ADMIN], Application::CONTEXT_SITE)) {
$canDelete = true;
} else {
if ($submission->getData('submissionProgress')) {
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
$assignments = $stageAssignmentDao->getBySubmissionAndRoleIds($submission->getId(), [Role::ROLE_ID_AUTHOR], WORKFLOW_STAGE_ID_SUBMISSION, $currentUser->getId());
$assignment = $assignments->next();
if ($assignment) {
$canDelete = true;
}
}
}
return $canDelete;
}
/**
* Check if a user can edit the publication metadata of a submission
*/
public function canEditPublication(int $submissionId, int $userId): bool
{
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
$stageAssignments = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId($submissionId, $userId, null)->toArray();
// Check for permission from stage assignments
foreach ($stageAssignments as $stageAssignment) {
if ($stageAssignment->getCanChangeMetadata()) {
return true;
}
}
// If user has no stage assigments, check if user can edit anyway ie. is manager
$context = Application::get()->getRequest()->getContext();
if (count($stageAssignments) == 0 && $this->_canUserAccessUnassignedSubmissions($context->getId(), $userId)) {
return true;
}
// Else deny access
return false;
}
/**
* Checks if this user is granted reader access to pre-publication submissions
* based on their roles in the context (i.e. Manager, Editor, etc).
*/
public function canPreview(?User $user, Submission $submission): bool
{
// Only grant access when in copyediting or production stage
if (!in_array($submission->getData('stageId'), [WORKFLOW_STAGE_ID_EDITING, WORKFLOW_STAGE_ID_PRODUCTION])) {
return false;
}
if ($this->_roleCanPreview($user, $submission)) {
return true;
}
if ($user) {
/** @var StageAssignmentDAO */
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO');
$stageAssignments = $stageAssignmentDao->getBySubmissionAndRoleId($submission->getId(), Role::ROLE_ID_AUTHOR, null, $user->getId());
$stageAssignment = $stageAssignments->next();
if ($stageAssignment) {
return true;
}
}
return false;
}
/**
* Add a new submission
*/
public function add(Submission $submission, Publication $publication, Context $context): int
{
$submission->stampLastActivity();
$submission->stampModified();
if (!$submission->getData('dateSubmitted') && !$submission->getData('submissionProgress')) {
$submission->setData('dateSubmitted', Core::getCurrentDate());
}
if (!$submission->getData('status')) {
$submission->setData('status', Submission::STATUS_QUEUED);
}
if (!$submission->getData('locale')) {
$submission->setData('locale', $context->getPrimaryLocale());
}
$submissionId = $this->dao->insert($submission);
$submission = Repo::submission()->get($submissionId);
$publication->setData('submissionId', $submission->getId());
$publication->setData('version', 1);
if (!$publication->getData('status')) {
$publication->setData('status', $submission->getData('status'));
}
$publicationId = Repo::publication()->add($publication);
$this->edit($submission, ['currentPublicationId' => $publicationId]);
Hook::call('Submission::add', [$submission]);
return $submission->getId();
}
/** @copydoc DAO::update */
public function edit(Submission $submission, array $params)
{
$newSubmission = Repo::submission()->newDataObject(array_merge($submission->_data, $params));
$newSubmission->stampLastActivity();
$newSubmission->stampModified();
Hook::call('Submission::edit', [$newSubmission, $submission, $params]);
$this->dao->update($newSubmission);
}
/**
* Submit a submission
*
* Changes the submissionProgress property, creates the comments
* for the editors discussion, and fires the SubmissionSubmitted
* event.
*/
public function submit(Submission $submission, Context $context): void
{
$this->edit($submission, [
'submissionProgress' => '',
'dateSubmitted' => Core::getCurrentDate(),
]);
$submission = $this->get($submission->getId());
event(
new SubmissionSubmitted(
$submission,
$context
)
);
if ($submission->getData('commentsForTheEditors')) {
/** @var QueryDAO $queryDao */
$queryDao = DAORegistry::getDAO('QueryDAO');
$queryDao->addCommentsForEditorsQuery($submission);
}
}
/** @copydoc DAO::delete */
public function delete(Submission $submission)
{
Hook::call('Submission::delete::before', [&$submission]);
$this->dao->delete($submission);
Hook::call('Submission::delete', [$submission]);
}
/**
* Delete all submissions in a context
*/
public function deleteByContextId(int $contextId)
{
$submissionIds = Repo::submission()->getCollector()->filterByContextIds([$contextId])->getIds();
foreach ($submissionIds as $submissionId) {
$this->dao->deleteById($submissionId);
}
}
/**
* Update a submission's status
*
* Changes a submission's status. Or, if no new status is provided,
* sets the appropriate status based on all of the submission's
* publications.
*
* This method performs any actions necessary when a submission's
* status changes, such as changing the current publication ID
* and creating or deleting tombstones.
*
* @param ?Section $section If this submission is being deleted, its previous section ID should be specified
* in order to ensure a correctly created tombstone.
*/
public function updateStatus(Submission $submission, ?int $newStatus = null, ?Section $section = null)
{
$status = $submission->getData('status');
if ($newStatus === null) {
$newStatus = $this->getStatusByPublications($submission);
}
Hook::call('Submission::updateStatus', [&$newStatus, $status, $submission]);
if ($status !== $newStatus) {
$submission->setData('status', $newStatus);
}
$currentPublicationId = $newCurrentPublicationId = $submission->getData('currentPublicationId');
$newCurrentPublicationId = $this->getCurrentPublicationIdByPublications($submission);
if ($currentPublicationId !== $newCurrentPublicationId) {
$submission->setData('currentPublicationId', $newCurrentPublicationId);
}
// Use the DAO instead of the Repository to prevent
// calling this method over and over again.
$this->dao->update($submission);
}
/**
* Set license information for all submissions in a context
* to the context's default license.
*/
public function resetPermissions(int $contextId)
{
$submissions = Repo::submission()->getCollector()->filterByContextIds([$contextId])->getMany();
foreach ($submissions as $submission) {
$publications = $submission->getData('publications');
if (empty($publications)) {
continue;
}
$params = [
'copyrightYear' => $submission->_getContextLicenseFieldValue(null, Submission::PERMISSIONS_FIELD_COPYRIGHT_YEAR),
'copyrightHolder' => $submission->_getContextLicenseFieldValue(null, Submission::PERMISSIONS_FIELD_COPYRIGHT_HOLDER),
'licenseUrl' => $submission->_getContextLicenseFieldValue(null, Submission::PERMISSIONS_FIELD_LICENSE_URL),
];
foreach ($publications as $publication) {
Repo::publication()->edit($publication, $params);
}
}
}
/**
* Get an array of sort options used in forms when configuring
* how published submissions are displayed
*/
public function getSortSelectOptions(): array
{
return [
$this->getSortOption(Collector::ORDERBY_TITLE, Collector::ORDER_DIR_ASC) => __('catalog.sortBy.titleAsc'),
$this->getSortOption(Collector::ORDERBY_TITLE, Collector::ORDER_DIR_DESC) => __('catalog.sortBy.titleDesc'),
$this->getSortOption(Collector::ORDERBY_DATE_PUBLISHED, Collector::ORDER_DIR_ASC) => __('catalog.sortBy.datePublishedAsc'),
$this->getSortOption(Collector::ORDERBY_DATE_PUBLISHED, Collector::ORDER_DIR_DESC) => __('catalog.sortBy.datePublishedDesc'),
];
}
/**
* Get the default sort option used in forms when configuring
* how published submissions are displayed
*
* @see self::getSortSelectOptions()
*/
public function getDefaultSortOption(): string
{
return $this->getSortOption(Collector::ORDERBY_DATE_PUBLISHED, Collector::ORDER_DIR_DESC);
}
/**
* Get the URL to the API endpoint for a submission
*/
public function getUrlApi(Context $context, ?int $submissionId = null): string
{
return Application::get()->getDispatcher()->url(
Application::get()->getRequest(),
Application::ROUTE_API,
$context->getData('urlPath'),
'submissions' . ($submissionId ? '/' . $submissionId : ''),
);
}
/**
* Get the URL to the author workflow for a submission
*/
public function getUrlAuthorWorkflow(Context $context, int $submissionId): string
{
return Application::get()->getDispatcher()->url(
Application::get()->getRequest(),
Application::ROUTE_PAGE,
$context->getData('urlPath'),
'authorDashboard',
'submission',
$submissionId
);
}
/**
* Get the URL to the editorial workflow for a submission
*/
public function getUrlEditorialWorkflow(Context $context, int $submissionId): string
{
return Application::get()->getDispatcher()->url(
Application::get()->getRequest(),
Application::ROUTE_PAGE,
$context->getData('urlPath'),
'workflow',
'access',
$submissionId
);
}
/**
* Get the URL to the submission wizard for a submission
*/
public function getUrlSubmissionWizard(Context $context, ?int $submissionId = null): string
{
return Application::get()->getDispatcher()->url(
Application::get()->getRequest(),
Application::ROUTE_PAGE,
$context->getData('urlPath'),
'submission',
null,
null,
$submissionId
? ['id' => $submissionId]
: null
);
}
/**
* Creates and assigns DOIs to all sub-objects if:
* 1) the suffix pattern can currently be created, and
* 2) it does not already exist.
*
* @return DoiException[]
*/
abstract public function createDois(Submission $submission): array;
/**
* Compile the sort orderBy and orderDirection into an option
* used in forms
*/
protected function getSortOption(string $sortBy, string $sortDir): string
{
return $sortBy . '-' . $sortDir;
}
/**
* Check if a user is allowed to edit publication metadata for submissions
* they are not assigned to
*/
protected function _canUserAccessUnassignedSubmissions(int $contextId, int $userId): bool
{
$roleDao = DAORegistry::getDAO('RoleDAO'); /** @var RoleDAO $roleDao */
$roles = $roleDao->getByUserId($userId, $contextId);
$allowedRoles = Repo::userGroup()::NOT_CHANGE_METADATA_EDIT_PERMISSION_ROLES;
foreach ($roles as $role) {
if (in_array($role->getRoleId(), $allowedRoles)) {
return true;
}
}
return false;
}
/**
* Get the appropriate status of a submission based on the
* statuses of its publications
*/
protected function getStatusByPublications(Submission $submission): int
{
$publications = $submission->getData('publications'); /** @var LazyCollection $publications */
// Declined submissions should remain declined regardless of their publications' statuses
if ($submission->getData('status') === Submission::STATUS_DECLINED) {
return Submission::STATUS_DECLINED;
}
// If there are no publications, we are probably in the process of deleting a submission.
// To be safe, reset the status anyway.
if (!$publications->count()) {
return Submission::STATUS_DECLINED
? Submission::STATUS_DECLINED
: Submission::STATUS_QUEUED;
}
$newStatus = Submission::STATUS_QUEUED;
foreach ($publications as $publication) {
if ($publication->getData('status') === Submission::STATUS_PUBLISHED) {
$newStatus = Submission::STATUS_PUBLISHED;
break;
}
if ($publication->getData('status') === Submission::STATUS_SCHEDULED) {
$newStatus = Submission::STATUS_SCHEDULED;
continue;
}
}
return $newStatus;
}
/**
* Get the appropriate currentPublicationId for a submission based on the
* statues of its publications
*/
protected function getCurrentPublicationIdByPublications(Submission $submission): ?int
{
$publications = $submission->getData('publications'); /** @var LazyCollection $publications */
if (!$publications->count()) {
return null;
}
// Use the latest published publication
$newCurrentPublicationId = $publications->reduce(function ($a, $b) {
return $b->getData('status') === Submission::STATUS_PUBLISHED && $b->getId() > $a ? $b->getId() : $a;
}, 0);
// If there is no published publication, use the latest publication
if (!$newCurrentPublicationId) {
$newCurrentPublicationId = $publications->reduce(function ($a, $b) {
return $a > $b->getId() ? $a : $b->getId();
}, 0);
}
return $newCurrentPublicationId ?? $submission->getData('currentPublicationId');
}
/**
* Checks if this user is granted access to preview
* based on their roles in the context (i.e. Manager, Editor, etc).
*
* @param User $user
*
*/
protected function _roleCanPreview(?User $user, Submission $submission): bool
{
if (!$user) {
return false;
}
$subscriptionAssumedRoles = [
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_SUB_EDITOR,
Role::ROLE_ID_ASSISTANT,
Role::ROLE_ID_SUBSCRIPTION_MANAGER
];
/** @var RoleDAO */
$roleDao = DAORegistry::getDAO('RoleDAO');
$roles = $roleDao->getByUserId($user->getId(), $submission->getData('contextId'));
foreach ($roles as $role) {
if (in_array($role->getRoleId(), $subscriptionAssumedRoles)) {
return true;
}
}
return false;
}
}
|