<?php
/**
* @file api/v1/_submissions/PKPBackendSubmissionsHandler.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPBackendSubmissionsHandler
*
* @ingroup api_v1_backend
*
* @brief Handle API requests for backend operations.
*
*/
namespace PKP\API\v1\_submissions;
use APP\core\Application;
use APP\facades\Repo;
use APP\submission\Collector;
use PKP\core\APIResponse;
use PKP\db\DAORegistry;
use PKP\handler\APIHandler;
use PKP\plugins\Hook;
use PKP\security\authorization\ContextAccessPolicy;
use PKP\security\authorization\SubmissionAccessPolicy;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\Role;
use Slim\Http\Request;
use Slim\Http\Response;
abstract class PKPBackendSubmissionsHandler extends APIHandler
{
/** @var int Max items that can be requested */
public const MAX_COUNT = 100;
/**
* Constructor
*/
public function __construct()
{
$rootPattern = '/{contextPath}/api/{version}/_submissions';
$this->_endpoints = array_merge_recursive($this->_endpoints, [
'GET' => [
[
'pattern' => "{$rootPattern}",
'handler' => [$this, 'getMany'],
'roles' => [
Role::ROLE_ID_SITE_ADMIN,
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_SUB_EDITOR,
Role::ROLE_ID_AUTHOR,
Role::ROLE_ID_REVIEWER,
Role::ROLE_ID_ASSISTANT,
],
],
],
'DELETE' => [
[
'pattern' => "{$rootPattern}/{submissionId:\d+}",
'handler' => [$this, 'delete'],
'roles' => [
Role::ROLE_ID_SITE_ADMIN,
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_AUTHOR,
],
],
],
]);
parent::__construct();
}
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new UserRolesRequiredPolicy($request), true);
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));
$routeName = $this->getSlimRequest()->getAttribute('route')->getName();
if (in_array($routeName, ['delete'])) {
$this->addPolicy(new SubmissionAccessPolicy($request, $args, $roleAssignments));
}
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Get a collection of submissions
*
* @param Request $slimRequest Slim request object
* @param APIResponse $response object
* @param array $args arguments
*
* @return Response
*/
public function getMany($slimRequest, $response, $args)
{
$request = Application::get()->getRequest();
$currentUser = $request->getUser();
$context = $request->getContext();
if (!$context) {
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
}
$collector = $this->getSubmissionCollector($slimRequest->getQueryParams());
// Anyone not a manager or site admin can only access their assigned
// submissions
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
$canAccessUnassignedSubmission = !empty(array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER], $userRoles));
Hook::call('API::submissions::params', [$collector, $slimRequest]);
if (!$canAccessUnassignedSubmission) {
if (!is_array($collector->assignedTo)) {
$collector->assignedTo([$currentUser->getId()]);
} elseif ($collector->assignedTo != [$currentUser->getId()]) {
return $response->withStatus(403)->withJsonError('api.submissions.403.requestedOthersUnpublishedSubmissions');
}
}
$submissions = $collector->getMany();
$userGroups = Repo::userGroup()->getCollector()
->filterByContextIds([$context->getId()])
->getMany();
/** @var \PKP\submission\GenreDAO $genreDao */
$genreDao = DAORegistry::getDAO('GenreDAO');
$genres = $genreDao->getByContextId($context->getId())->toArray();
return $response->withJson([
'itemsMax' => $collector->getCount(),
'items' => Repo::submission()->getSchemaMap()->mapManyToSubmissionsList($submissions, $userGroups, $genres)->values(),
], 200);
}
/**
* Configure a submission Collector based on the query params
*/
protected function getSubmissionCollector(array $queryParams): Collector
{
$request = Application::get()->getRequest();
$context = $request->getContext();
$collector = Repo::submission()->getCollector()
->filterByContextIds([$context->getId()])
->limit(30)
->offset(0);
foreach ($queryParams as $param => $val) {
switch ($param) {
case 'orderBy':
if (in_array($val, [
$collector::ORDERBY_DATE_PUBLISHED,
$collector::ORDERBY_DATE_SUBMITTED,
$collector::ORDERBY_LAST_ACTIVITY,
$collector::ORDERBY_LAST_MODIFIED,
$collector::ORDERBY_SEQUENCE,
$collector::ORDERBY_TITLE,
])) {
$direction = isset($queryParams['orderDirection']) && $queryParams['orderDirection'] === $collector::ORDER_DIR_ASC
? $collector::ORDER_DIR_ASC
: $collector::ORDER_DIR_DESC;
$collector->orderBy($val, $direction);
}
break;
case 'status':
$collector->filterByStatus(array_map('intval', $this->paramToArray($val)));
break;
case 'stageIds':
$collector->filterByStageIds(array_map('intval', $this->paramToArray($val)));
break;
case 'categoryIds':
$collector->filterByCategoryIds(array_map('intval', $this->paramToArray($val)));
break;
case 'assignedTo':
$val = array_map('intval', $this->paramToArray($val));
if ($val == [\PKP\submission\Collector::UNASSIGNED]) {
$val = array_shift($val);
}
$collector->assignedTo($val);
break;
case 'daysInactive':
$collector->filterByDaysInactive((int) $val);
break;
case 'offset':
$collector->offset((int) $val);
break;
case 'searchPhrase':
$collector->searchPhrase($val);
break;
case 'count':
$collector->limit(min(self::MAX_COUNT, (int) $val));
break;
case 'isIncomplete':
$collector->filterByIncomplete(true);
break;
case 'isOverdue':
$collector->filterByOverdue(true);
break;
}
}
return $collector;
}
/**
* Delete a submission
*
* @param Request $slimRequest Slim request object
* @param APIResponse $response object
* @param array $args arguments
*
* @return Response
*/
public function delete($slimRequest, $response, $args)
{
$request = $this->getRequest();
$context = $request->getContext();
$submissionId = (int) $args['submissionId'];
$submission = Repo::submission()->get($submissionId);
if (!$submission) {
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
}
if ($context->getId() != $submission->getData('contextId')) {
return $response->withStatus(403)->withJsonError('api.submissions.403.deleteSubmissionOutOfContext');
}
if (!Repo::submission()->canCurrentUserDelete($submission)) {
return $response->withStatus(403)->withJsonError('api.submissions.403.unauthorizedDeleteSubmission');
}
Repo::submission()->delete($submission);
return $response->withJson(true);
}
}
|