diff --git a/api/v1/_submissions/PKPBackendSubmissionsController.php b/api/v1/_submissions/PKPBackendSubmissionsController.php index 8ffd6053a7e..d6af90f196f 100644 --- a/api/v1/_submissions/PKPBackendSubmissionsController.php +++ b/api/v1/_submissions/PKPBackendSubmissionsController.php @@ -97,14 +97,6 @@ public function getGroupRoutes(): void if (Config::getVar('features', 'enable_new_submission_listing')) { - Route::get('needsEditor', $this->needsEditor(...)) - ->name('_submission.needsEditor') - ->middleware([ - self::roleAuthorizer([ - Role::ROLE_ID_MANAGER, - ]), - ]); - Route::get('assigned', $this->assigned(...)) ->name('_submission.assigned') ->middleware([ @@ -116,7 +108,7 @@ public function getGroupRoutes(): void ]), ]); - Route::get('reviews', $this->assigned(...)) + Route::get('reviews', $this->reviews(...)) ->name('_submission.reviews') ->middleware([ self::roleAuthorizer([ @@ -127,7 +119,7 @@ public function getGroupRoutes(): void ]) ]); - Route::get('viewsCount', $this->assigned(...)) + Route::get('viewsCount', $this->getViewsCount(...)) ->name('_submission.getViewsCount') ->middleware([ self::roleAuthorizer([ @@ -137,7 +129,7 @@ public function getGroupRoutes(): void ]) ]); - Route::get('reviewAssignments', $this->assigned(...)) + Route::get('reviewerAssignments', $this->getReviewAssignments(...)) ->name('_submission.getReviewAssignments') ->middleware([ Role::ROLE_ID_REVIEWER, @@ -271,16 +263,19 @@ public function assigned(Request $illuminateRequest): JsonResponse /** * Get submission undergoing the review */ - public function reviews(SlimRequest $slimRequest, APIResponse $response, array $args) + public function reviews(Request $illuminateRequest): JsonResponse { $request = Application::get()->getRequest(); $context = $request->getContext(); if (!$context) { - return $response->withStatus(404)->withJsonError('api.404.resourceNotFound'); + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); } $currentUser = $request->getUser(); + $queryParams = $illuminateRequest->query(); - $collector = $this->getSubmissionCollector($slimRequest->getQueryParams()); + $collector = $this->getSubmissionCollector($queryParams); $collector ->filterByContextIds([$context->getId()]) ->filterByStatus([PKPSubmission::STATUS_QUEUED]) @@ -292,7 +287,6 @@ public function reviews(SlimRequest $slimRequest, APIResponse $response, array $ $collector->assignedTo([$currentUser->getId()]); } - $queryParams = $slimRequest->getQueryParams(); foreach ($queryParams as $param => $val) { switch ($param) { case 'needsReviewers': @@ -327,40 +321,68 @@ public function reviews(SlimRequest $slimRequest, APIResponse $response, array $ $genreDao = DAORegistry::getDAO('GenreDAO'); $genres = $genreDao->getByContextId($context->getId())->toArray(); - return $response->withJson([ + return response()->json([ 'itemsMax' => $collector->limit(null)->offset(null)->getCount(), 'items' => Repo::submission()->getSchemaMap()->mapManyToSubmissionsList($submissions, $userGroups, $genres)->values(), - ], 200); + ], Response::HTTP_OK); } /** * Get a number of the submissions for each view */ - public function getViewsCount(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse + public function getViewsCount(Request $illuminateRequest): JsonResponse { $request = Application::get()->getRequest(); $context = $request->getContext(); if (!$context) { - return $response->withStatus(404)->withJsonError('api.404.resourceNotFound'); + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); } $currentUser = $request->getUser(); $dashboardViews = Repo::submission()->getDashboardViews($context, $currentUser); - return $response->withJson($dashboardViews->map(fn(DashboardView $view) => $view->getCount()), 200); + return response()->json( + $dashboardViews->map(fn(DashboardView $view) => $view->getCount()), + Response::HTTP_OK + ); } /** * Get all reviewer's assignments */ - public function getReviewAssignments(SlimRequest $slimRequest, APIResponse $response, array $args) + public function getReviewAssignments(Request $illuminateRequest): JsonResponse { $request = Application::get()->getRequest(); $context = $request->getContext(); if (!$context) { - return $response->withStatus(404)->withJsonError('api.404.resourceNotFound'); + return response()->json([ + 'error' => __('api.404.resourceNotFound'), + ], Response::HTTP_NOT_FOUND); } $currentUser = $request->getUser(); + $collector = Repo::reviewAssignment()->getCollector() + ->filterByReviewRoundIds([$currentUser->getId()]) + ->filterByContextIds([$context->getId()]); + + foreach ($illuminateRequest->query() as $param => $val) { + switch ($param) { + case 'pending': + $collector->filterByIsIncomplete(true); + break; + case 'archived': + $collector->filterByIsArchived(true); + break; + } + } + + $reviewAssignments = $collector->getMany(); + + return response()->json([ + 'itemsMax' => $collector->limit(null)->offset(null)->getCount(), + 'items' => Repo::reviewAssignment()->getSchemaMap()->mapMany($reviewAssignments)->values(), + ], Response::HTTP_OK); } /** diff --git a/classes/submission/Collector.php b/classes/submission/Collector.php index f9075098d5a..85c1b596e96 100644 --- a/classes/submission/Collector.php +++ b/classes/submission/Collector.php @@ -37,6 +37,8 @@ */ abstract class Collector implements CollectorInterface { + use ViewsCount; + public const ORDERBY_DATE_PUBLISHED = 'datePublished'; public const ORDERBY_DATE_SUBMITTED = 'dateSubmitted'; public const ORDERBY_ID = 'id'; @@ -640,23 +642,6 @@ public function getQueryBuilder(): Builder return $q; } - /** - * Builds a single query to retrieve submissions count for all dashboard views - * @param Collection [ - * Dashboard view unique ID => Submission Collector with filters applied - * ] - */ - public static function getViewsCountBuilder(Collection $keyCollectorPair): Builder - { - $q = DB::query(); - $keyCollectorPair->each(function(AppCollector $collector, string $key) use ($q) { - // Get query builder from a collector instance, override a select statement to retrieve submissions count instead of submissions data - $subQuery = $collector->getQueryBuilder()->select([])->selectRaw('COUNT(s.submission_id)'); - $q->selectSub($subQuery, $key); - }); - return $q; - } - /** * Build queries to retrieve review stage related */ diff --git a/classes/submission/DashboardView.php b/classes/submission/DashboardView.php index 6e85aa5088d..85062606e2b 100644 --- a/classes/submission/DashboardView.php +++ b/classes/submission/DashboardView.php @@ -19,6 +19,8 @@ use Illuminate\Support\Collection; use Illuminate\Support\Str; use PKP\security\Role; +use PKP\submission\Collector as SubmissionCollector; +use PKP\submission\reviewAssignment\Collector as ReviewAssignmentCollector; class DashboardView { @@ -40,7 +42,7 @@ class DashboardView const TYPE_INCOMPLETE_SUBMISSIONS = 'incomplete-submissions'; const TYPE_REVIEWER_ASSIGNMENTS_ALL = 'reviewer-assignments-all'; const TYPE_REVIEWER_ASSIGNMENTS_PENDING = 'reviewer-assignments-pending'; - const TYPE_REVIEWER_ASSIGNMENTS_FINISHED = 'reviewer-assignments-finished'; + const TYPE_REVIEWER_ASSIGNMENTS_ARCHIVED = 'reviewer-assignments-archived'; // The number of submissions in the view protected int $count; @@ -49,7 +51,7 @@ public function __construct( protected string $type, // View type, used also as the unique ID of the view's front-end part protected string $name, // View name as a localized string protected array $roles, // User roles having access to the view - protected Collector $submissionCollector, // Collector with correspondent filters applied + protected SubmissionCollector|ReviewAssignmentCollector $submissionCollector, // Collector with correspondent filters applied protected ?string $op = null, // Dashboard handler operation to retrieve filtered submissions protected ?array $queryParams = null // Optional query parameters ) diff --git a/classes/submission/Repository.php b/classes/submission/Repository.php index 47d6db62d5a..8e10db0a52b 100644 --- a/classes/submission/Repository.php +++ b/classes/submission/Repository.php @@ -1005,6 +1005,40 @@ public function getDashboardViews(Context $context, User $user): Collection 'assigned', ['isIncomplete' => true] ); + case DashboardView::TYPE_REVIEWER_ASSIGNMENTS_ALL: + return new DashboardView( + $key, + __('submission.dashboard.view.reviewAssignments.all'), + [Role::ROLE_ID_REVIEWER], + Repo::reviewAssignment()->getCollector() + ->filterByReviewerIds([$user->getId()]) + ->filterByContextIds([$context->getId()]), + 'reviewerAssignments' + ); + case DashboardView::TYPE_REVIEWER_ASSIGNMENTS_PENDING: + return new DashboardView( + $key, + __('submission.dashboard.view.reviewAssignments.pending'), + [Role::ROLE_ID_REVIEWER], + Repo::reviewAssignment()->getCollector() + ->filterByReviewerIds([$user->getId()]) + ->filterByContextIds([$context->getId()]) + ->filterByIsIncomplete(true), + 'reviewerAssignments', + ['pending' => true] + ); + case DashboardView::TYPE_REVIEWER_ASSIGNMENTS_ARCHIVED: + return new DashboardView( + $key, + __('submission.dashboard.view.reviewAssignments.archived'), + [Role::ROLE_ID_REVIEWER], + Repo::reviewAssignment()->getCollector() + ->filterByReviewerIds([$user->getId()]) + ->filterByContextIds([$context->getId()]) + ->filterByIsArchived(true), + 'reviewerAssignments', + ['archived' => true] + ); } }); diff --git a/classes/submission/ViewsCount.php b/classes/submission/ViewsCount.php new file mode 100644 index 00000000000..06bc7a60511 --- /dev/null +++ b/classes/submission/ViewsCount.php @@ -0,0 +1,41 @@ + Submission Collector with filters applied + * ] + */ + public static function getViewsCountBuilder(Collection $keyCollectorPair): Builder + { + $q = DB::query(); + $keyCollectorPair->each(function(AppCollector $collector, string $key) use ($q) { + // Get query builder from a collector instance, override a select statement to retrieve submissions count instead of submissions data + $subQuery = $collector->getQueryBuilder()->select([])->selectRaw( + 'COUNT('. $this->dao->table . '.' . $this->dao->primaryKeyColumn . ')' + ); + $q->selectSub($subQuery, $key); + }); + return $q; + } +} diff --git a/classes/submission/reviewAssignment/Collector.php b/classes/submission/reviewAssignment/Collector.php index 1213148b716..e31aee13c8d 100644 --- a/classes/submission/reviewAssignment/Collector.php +++ b/classes/submission/reviewAssignment/Collector.php @@ -20,16 +20,20 @@ use Illuminate\Support\LazyCollection; use PKP\core\Core; use PKP\core\interfaces\CollectorInterface; +use PKP\submission\ViewsCount; /** * @template T of ReviewAssignment */ class Collector implements CollectorInterface { + use ViewsCount; + public DAO $dao; public ?array $contextIds = null; public ?array $submissionIds = null; public bool $isIncomplete = false; + public bool $isArchived = false; public bool $isOverdue = false; public ?array $reviewRoundIds = null; public ?array $reviewerIds = null; @@ -98,6 +102,15 @@ public function filterByIsIncomplete(?bool $isIncomplete): self return $this; } + /** + * Filter by completed or declined assignments + */ + public function filterByIsArchived(?bool $isArchived): self + { + $this->isArchived = $isArchived; + return $this; + } + /** * Filter by overdue assignments */ @@ -218,6 +231,13 @@ public function getQueryBuilder(): Builder ); }); + $q->when($this->isArchived, fn(Builder $q) => + $q->where(fn(Builder $q) => $q + ->whereNotNull('ra.date_completed') + ->orWhere('declined', 1) + ) + ); + $q->when($this->isOverdue, fn(Builder $q) => $q ->where(fn(Builder $q) => $q ->whereNull('ra.date_completed')