diff --git a/api/v1/submissions/PKPSubmissionController.php b/api/v1/submissions/PKPSubmissionController.php index 9b8bb930dd8..97f1ef1001f 100644 --- a/api/v1/submissions/PKPSubmissionController.php +++ b/api/v1/submissions/PKPSubmissionController.php @@ -902,7 +902,7 @@ public function getParticipants(Request $illuminateRequest): JsonResponse $context = $request->getContext(); $submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION); $args = $illuminateRequest->input(); - $stageId = $args['stageId'] ?? null; + $stageId = $args['stageId'] ?? $illuminateRequest->route('stageId') !== null ? (int) $illuminateRequest->route('stageId') : null; if (!$submission || $submission->getData('contextId') !== $context->getId()) { return response()->json([ @@ -919,7 +919,7 @@ public function getParticipants(Request $illuminateRequest): JsonResponse $map = Repo::user()->getSchemaMap(); foreach ($usersIterator as $user) { - $data[] = $map->summarizeReviewer($user); + $data[] = $map->summarizeReviewer($user, ['submission' => $submission, 'stageId' => $stageId]); } return response()->json($data, Response::HTTP_OK); diff --git a/classes/stageAssignment/StageAssignment.php b/classes/stageAssignment/StageAssignment.php index 7103657b4a1..04da0d97002 100644 --- a/classes/stageAssignment/StageAssignment.php +++ b/classes/stageAssignment/StageAssignment.php @@ -90,7 +90,7 @@ public function scopeWithStageIds(Builder $query, ?array $stageIds): Builder public function scopeWithSubmissionIds(Builder $query, ?array $submissionIds): Builder { return $query->when($submissionIds !== null, function ($query) use ($submissionIds) { - return $query->whereIn('submission_id', $submissionIds); + return $query->whereIn('stage_assignments.submission_id', $submissionIds); }); } @@ -142,7 +142,7 @@ public function scopeWithContextId(Builder $query, ?int $contextId): Builder { return $query->when($contextId !== null, function ($query) use ($contextId) { return $query->join('submissions', 'stage_assignments.submission_id', '=', 'submissions.submission_id') - ->where('submissions.context_id', $contextId); + ->where('submissions.context_id', $contextId); }); } } diff --git a/classes/user/maps/Schema.php b/classes/user/maps/Schema.php index 182e1accde8..a153f4cacf1 100644 --- a/classes/user/maps/Schema.php +++ b/classes/user/maps/Schema.php @@ -17,8 +17,14 @@ use Illuminate\Support\Enumerable; use PKP\db\DAORegistry; use PKP\plugins\Hook; +use PKP\security\Role; use PKP\services\PKPSchemaService; +use PKP\stageAssignment\StageAssignment; +use PKP\user\InterestDAO; +use PKP\user\InterestEntryDAO; use PKP\user\User; +use PKP\workflow\WorkflowStageDAO; +use Submission; class Schema extends \PKP\core\maps\Schema { @@ -26,36 +32,6 @@ class Schema extends \PKP\core\maps\Schema public string $schema = PKPSchemaService::SCHEMA_USER; - /** - * Map a publication - * - * Includes all properties in the user schema. - */ - public function map(User $item): array - { - return $this->mapByProperties($this->getProps(), $item); - } - - /** - * Summarize a user - * - * Includes properties with the apiSummary flag in the user schema. - */ - public function summarize(User $item): array - { - return $this->mapByProperties($this->getSummaryProps(), $item); - } - - /** - * Summarize a user with reviewer data - * - * Includes properties with the apiSummary flag in the user schema. - */ - public function summarizeReviewer(User $item): array - { - return $this->mapByProperties(array_merge($this->getSummaryProps(), ['reviewsActive', 'reviewsCompleted', 'reviewsDeclined', 'reviewsCancelled', 'averageReviewCompletionDays', 'dateLastReviewAssignment', 'reviewerRating']), $item); - } - /** * Map a collection of Users * @@ -70,44 +46,30 @@ public function mapMany(Enumerable $collection): Enumerable } /** - * Summarize a collection of users - * - * @see self::summarize - */ - public function summarizeMany(Enumerable $collection): Enumerable - { - $this->collection = $collection; - return $collection->map(function ($item) { - return $this->summarize($item); - }); - } - - /** - * Summarize a collection of reviewers + * Map a publication * - * @see self::summarizeReviewer + * Includes all properties in the user schema. */ - public function summarizeManyReviewers(Enumerable $collection): Enumerable + public function map(User $item): array { - $this->collection = $collection; - return $collection->map(function ($item) { - return $this->summarizeReviewer($item); - }); + return $this->mapByProperties($this->getProps(), $item); } /** * Map schema properties of a user to an assoc array * + * @param array $auxiliaryData - Associative array used to supplementary data needed to populate properties on the response. + * * @hook UserSchema::getProperties::values [[$this, &$output, $user, $props]] */ - protected function mapByProperties(array $props, User $user): array + protected function mapByProperties(array $props, User $user, array $auxiliaryData): array { $output = []; foreach ($props as $prop) { switch ($prop) { case 'id': - $output[$prop] = (int) $user->getId(); + $output[$prop] = (int)$user->getId(); break; case 'fullName': $output[$prop] = $user->getFullName(); @@ -151,14 +113,14 @@ protected function mapByProperties(array $props, User $user): array $output[$prop] = []; foreach ($userGroups as $userGroup) { $output[$prop][] = [ - 'id' => (int) $userGroup->getId(), + 'id' => (int)$userGroup->getId(), 'name' => $userGroup->getName(null), 'abbrev' => $userGroup->getAbbrev(null), - 'roleId' => (int) $userGroup->getRoleId(), - 'showTitle' => (bool) $userGroup->getShowTitle(), - 'permitSelfRegistration' => (bool) $userGroup->getPermitSelfRegistration(), - 'permitMetadataEdit' => (bool) $userGroup->getPermitMetadataEdit(), - 'recommendOnly' => (bool) $userGroup->getRecommendOnly(), + 'roleId' => (int)$userGroup->getRoleId(), + 'showTitle' => (bool)$userGroup->getShowTitle(), + 'permitSelfRegistration' => (bool)$userGroup->getPermitSelfRegistration(), + 'permitMetadataEdit' => (bool)$userGroup->getPermitMetadataEdit(), + 'recommendOnly' => (bool)$userGroup->getRecommendOnly(), ]; } } @@ -166,20 +128,70 @@ protected function mapByProperties(array $props, User $user): array case 'interests': $output[$prop] = []; if ($this->context) { - $interestDao = DAORegistry::getDAO('InterestDAO'); /** @var \PKP\user\InterestDAO $interestDao */ + $interestDao = DAORegistry::getDAO('InterestDAO'); + /** @var InterestDAO $interestDao */ $interestEntryIds = $interestDao->getUserInterestIds($user->getId()); if (!empty($interestEntryIds)) { - $interestEntryDao = DAORegistry::getDAO('InterestEntryDAO'); /** @var \PKP\user\InterestEntryDAO $interestEntryDao */ + $interestEntryDao = DAORegistry::getDAO('InterestEntryDAO'); + /** @var InterestEntryDAO $interestEntryDao */ $results = $interestEntryDao->getByIds($interestEntryIds); $output[$prop] = []; while ($interest = $results->next()) { $output[$prop][] = [ - 'id' => (int) $interest->getId(), + 'id' => (int)$interest->getId(), 'interest' => $interest->getInterest(), ]; } } } + break; + case 'stageAssignments': + $submission = $auxiliaryData['submission']; + $stageId = $auxiliaryData['stageId']; + + if((!isset($submission) || !isset($stageId)) || (!($submission instanceof Submission) || !is_numeric($auxiliaryData['stageId']))) { + $output['stageAssignments'] = []; + break; + } + + // Get User's stage assignments for submission. + // Note: + // - A User can potentially have multiple assignments for a submission. + // - A User can potentially have multiple assignments for a stage of a submission. + $stageAssignments = StageAssignment::withSubmissionIds([$submission->getId()]) + ->withStageIds($stageId ? [$stageId] : []) + ->withUserId($user->getId()) + ->withContextId($this->context->getId()) + ->get(); + + $results = []; + + foreach ($stageAssignments as $stageAssignment /**@var StageAssignment $stageAssignment*/) { + // Get related user group info for stage assignment + $userGroup = Repo::userGroup()->get($stageAssignment->userGroupId); + + // Only prepare data for non-reviewer participants + if ($userGroup->getRoleId() !== Role::ROLE_ID_REVIEWER) { + $entry = [ + 'stageAssignmentId' => $stageAssignment->id, + 'stageAssignmentUserGroup' => $userGroup->getAllData(), + 'stageAssignmentStageId' => $stageId, + ]; + + + $workflowStageDao = DAORegistry::getDAO('WorkflowStageDAO'); /** @var WorkflowStageDAO $workflowStageDao */ + $entry['stageAssignmentStage'] = [ + 'id' => $stageId, + 'label' => __($workflowStageDao->getTranslationKeyFromId($stageId)), + ]; + + + $results[] = $entry; + } + + $output['stageAssignments'] = $results; + } + break; default: $output[$prop] = $user->getData($prop); @@ -195,4 +207,50 @@ protected function mapByProperties(array $props, User $user): array return $output; } + + /** + * Summarize a collection of users + * + * @see self::summarize + */ + public function summarizeMany(Enumerable $collection): Enumerable + { + $this->collection = $collection; + return $collection->map(function ($item) { + return $this->summarize($item); + }); + } + + /** + * Summarize a user + * + * Includes properties with the apiSummary flag in the user schema. + */ + public function summarize(User $item): array + { + return $this->mapByProperties($this->getSummaryProps(), $item); + } + + /** + * Summarize a collection of reviewers + * + * @see self::summarizeReviewer + */ + public function summarizeManyReviewers(Enumerable $collection): Enumerable + { + $this->collection = $collection; + return $collection->map(function ($item) { + return $this->summarizeReviewer($item); + }); + } + + /** + * Summarize a user with reviewer data + * + * Includes properties with the apiSummary flag in the user schema. + */ + public function summarizeReviewer(User $item, array $auxiliaryData): array + { + return $this->mapByProperties(array_merge($this->getSummaryProps(), ['reviewsActive', 'reviewsCompleted', 'reviewsDeclined', 'reviewsCancelled', 'averageReviewCompletionDays', 'dateLastReviewAssignment', 'reviewerRating']), $item, $auxiliaryData); + } } diff --git a/schemas/user.json b/schemas/user.json index ee96b45233e..7a3e8f718e0 100644 --- a/schemas/user.json +++ b/schemas/user.json @@ -290,6 +290,37 @@ "validation": [ "nullable" ] + }, + "stageAssignments": { + "apiSummary": true, + "readOnly": true, + "type": "array", + "items": { + "type": "object", + "properties": { + "stageAssignmentId": { + "type": "integer" + }, + "stageAssignmentUserGroup": { + "type": "object", + "$ref": "#/definitions/UserGroup" + }, + "stageAssignmentStageId": { + "type": "integer" + }, + "stageAssignmentStage": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "label": { + "type": "string" + } + } + } + } + } } } }