Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkp/pkp-lib#1660 Customizable Reviewer Recommendations #10583

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
219 changes: 219 additions & 0 deletions api/v1/reviewers/recommendations/ReviewerRecommendationController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
<?php

/**
* @file api/v1/reviewers/recommendations/ReviewerRecommendationController.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ReviewerRecommendationController
*
* @brief API controller class to handle actions on reviewer recommendations
*
*/

namespace PKP\API\v1\reviewers\recommendations;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Route;
use PKP\API\v1\reviewers\recommendations\formRequests\UpdateStatusReviewerRecommendation;
use PKP\API\v1\reviewers\recommendations\resources\ReviewerRecommendationResource;
use PKP\submission\reviewer\recommendation\ReviewerRecommendation;
use PKP\API\v1\reviewers\recommendations\formRequests\AddReviewerRecommendation;
use PKP\API\v1\reviewers\recommendations\formRequests\EditReviewerRecommendation;
use PKP\core\PKPBaseController;
use PKP\core\PKPRequest;
use PKP\security\authorization\ContextAccessPolicy;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\Role;

class ReviewerRecommendationController extends PKPBaseController
{
/**
* @copydoc \PKP\core\PKPBaseController::getHandlerPath()
*/
public function getHandlerPath(): string
{
return 'contexts/{contextId}/reviewers/recommendations';
}

/**
* @copydoc \PKP\core\PKPBaseController::getRouteGroupMiddleware()
*/
public function getRouteGroupMiddleware(): array
{
return [
'has.user',
'has.context',
self::roleAuthorizer([
Role::ROLE_ID_SITE_ADMIN,
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_SUB_EDITOR,
]),
];
}

/**
* @copydoc \PKP\core\PKPBaseController::authorize()
*/
public function authorize(PKPRequest $request, array &$args, array $roleAssignments): bool
{
$this->addPolicy(new UserRolesRequiredPolicy($request), true);
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));

return parent::authorize($request, $args, $roleAssignments);
}

/**
* @copydoc \PKP\core\PKPBaseController::getGroupRoutes()
*/
public function getGroupRoutes(): void
{
Route::get('{recommendationId}', $this->get(...))
->name('reviewer.recommendations.get')
->whereNumber(['contextId', 'recommendationId']);

Route::get('', $this->getMany(...))
->name('reviewer.recommendations.getMany')
->whereNumber(['contextId']);

Route::post('', $this->add(...))
->name('reviewer.recommendations.add')
->whereNumber(['contextId']);

Route::put('{recommendationId}', $this->edit(...))
->name('reviewer.recommendations.edit')
->whereNumber(['contextId', 'recommendationId']);

Route::put('{recommendationId}/status', $this->updateStatus(...))
->name('reviewer.recommendations.edit.status')
->whereNumber(['contextId', 'recommendationId']);

Route::delete('{recommendationId}', $this->delete(...))
->name('reviewer.recommendations.delete')
->whereNumber(['contextId', 'recommendationId']);
}

/**
* Get specific recommendation response
*/
public function get(Request $illuminateRequest): JsonResponse
{
$recommendation = ReviewerRecommendation::find($illuminateRequest->route('recommendationId'));

if (!$recommendation) {
return response()->json([
'error' => __('api.404.resourceNotFound'),
], Response::HTTP_NOT_FOUND);
}

return response()->json(
(new ReviewerRecommendationResource($recommendation))->toArray($illuminateRequest),
Response::HTTP_OK
);
}

/**
* Get all recommendations response
*/
public function getMany(Request $illuminateRequest): JsonResponse
{
$recommendations = ReviewerRecommendation::query()
->withContextId($illuminateRequest->route('contextId'))
->get();

return response()->json([
'items' => ReviewerRecommendationResource::collection($recommendations),
'itemMax' => $recommendations->count(),
], Response::HTTP_OK);
}

/**
* Add new recommendation
*/
public function add(AddReviewerRecommendation $illuminateRequest): JsonResponse
{
$validateds = $illuminateRequest->validated();

$recommendation = ReviewerRecommendation::create($validateds);

return response()->json(
(new ReviewerRecommendationResource($recommendation->refresh()))
->toArray($illuminateRequest),
Response::HTTP_OK
);
}

/**
* Update existing recommendation
*/
public function edit(EditReviewerRecommendation $illuminateRequest): JsonResponse
{
$validated = $illuminateRequest->validated();

$recommendation = ReviewerRecommendation::find($illuminateRequest->route('recommendationId'));

if (!$recommendation->removable) {
return response()->json([
'error' => __('api.406.notAcceptable'),
], Response::HTTP_NOT_ACCEPTABLE);
}

if (!$recommendation->update($validated)) {
return response()->json([
'error' => __('api.409.resourceActionConflict'),
], Response::HTTP_CONFLICT);
}

return response()->json(
(new ReviewerRecommendationResource($recommendation->refresh()))
->toArray($illuminateRequest),
Response::HTTP_OK
);
}

/**
* Update the status of existing recommendation
*/
public function updateStatus(UpdateStatusReviewerRecommendation $illuminateRequest): JsonResponse
{
$validated = $illuminateRequest->validated();

$recommendation = ReviewerRecommendation::find($illuminateRequest->route('recommendationId'));

$recommendation->update($validated);

return response()->json(
(new ReviewerRecommendationResource($recommendation->refresh()))
->toArray($illuminateRequest),
Response::HTTP_OK
);
}

/**
* Delete existing recommendation
*/
public function delete(Request $illuminateRequest): JsonResponse
{
$recommendation = ReviewerRecommendation::find($illuminateRequest->route('recommendationId'));

if (!$recommendation) {
return response()->json([
'error' => __('api.404.resourceNotFound'),
], Response::HTTP_NOT_FOUND);
}

if (!$recommendation->removable) {
return response()->json([
'error' => __('api.406.notAcceptable'),
], Response::HTTP_NOT_ACCEPTABLE);
}

$recommendation->delete();

return response()->json([], Response::HTTP_OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

// TODO : update once pkp/pkp-lib#4787 merged
/**
* @file api/v1/reviewers/recommendations/formRequests/AddReviewerRecommendation.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class AddReviewerRecommendation
*
* @brief Form request class to validation storing of resource
*
*/

namespace PKP\API\v1\reviewers\recommendations\formRequests;

use APP\core\Application;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;

class AddReviewerRecommendation extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
$contextDao = Application::getContextDAO();

return [
'contextId' => [
'required',
'integer',
Rule::exists($contextDao->tableName, $contextDao->primaryKeyColumn),
],
'title' => [
'required',
// need to add multilingual validation once #4787 merged
],
'status' => [
'required',
'boolean'
],
];
}

/**
* Prepare the data for validation.
*/
protected function prepareForValidation(): void
{
$this->merge([
'contextId' => $this->route('contextId'),
'removable' => 1,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

// TODO : update once pkp/pkp-lib#4787 merged
/**
* @file api/v1/reviewers/recommendations/formRequests/EditReviewerRecommendation.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class EditReviewerRecommendation
*
* @brief Form request class to validation updating of resource
*
*/

namespace PKP\API\v1\reviewers\recommendations\formRequests;

use Illuminate\Foundation\Http\FormRequest;

class EditReviewerRecommendation extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'title' => [
'required',
// need to add multilingual validation once #4787 merged
],
'status' => [
'required',
'boolean'
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* @file api/v1/reviewers/recommendations/formRequests/UpdateStatusReviewerRecommendation.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class UpdateStatusReviewerRecommendation
*
* @brief Form request class to validation updating of resource status
*
*/

namespace PKP\API\v1\reviewers\recommendations\formRequests;

use PKP\API\v1\reviewers\recommendations\formRequests\EditReviewerRecommendation;

class UpdateStatusReviewerRecommendation extends EditReviewerRecommendation
{
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'status' => [
'required',
'boolean'
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/**
* @file api/v1/reviewers/recommendations/resources/ReviewerRecommendationResource.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ReviewerRecommendationResource
*
* @brief API resource class
*
*/

namespace PKP\API\v1\reviewers\recommendations\resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ReviewerRecommendationResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request)
{
return [
'id' => $this->id,
'contextId' => $this->contextId,
'value' => $this->value,
'status' => $this->status,
'removable' => $this->removable,
'title' => $this->title,
];
}
}
Loading