Skip to content

Commit

Permalink
pkp#10459 Support for role assignment invitation
Browse files Browse the repository at this point in the history
  • Loading branch information
defstat committed Sep 26, 2024
1 parent 9b0f74a commit 0f866e8
Show file tree
Hide file tree
Showing 58 changed files with 2,979 additions and 459 deletions.
236 changes: 191 additions & 45 deletions api/v1/invitations/InvitationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,42 @@
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Route;
use Illuminate\Validation\Rule;
use PKP\core\PKPBaseController;
use PKP\core\PKPRequest;
use PKP\invitation\core\contracts\IApiHandleable;
use PKP\invitation\core\CreateInvitationController;
use PKP\invitation\core\enums\InvitationStatus;
use PKP\invitation\core\Invitation;
use PKP\invitation\core\ReceiveInvitationController;
use PKP\invitation\core\traits\HasMailable;
use PKP\invitation\invitations\userRoleAssignment\resources\UserRoleAssignmentInviteResource;
use PKP\invitation\invitations\userRoleAssignment\rules\EmailMustNotExistRule;
use PKP\invitation\invitations\userRoleAssignment\rules\UserMustExistRule;
use PKP\invitation\models\InvitationModel;
use PKP\security\authorization\UserRolesRequiredPolicy;
use PKP\security\Role;
use PKP\validation\ValidatorFactory;
use Validator;

class InvitationController extends PKPBaseController
{
public const PARAM_TYPE = 'type';
public const PARAM_ID = 'invitationId';
public const PARAM_KEY = 'key';

public $notNeedAPIHandler = [
'getMany',
'getMailable',
'cancel',
];

public $noParamRequired = [
'getMany',
];

public $requiresType = [
'add',
];
Expand All @@ -43,11 +63,13 @@ class InvitationController extends PKPBaseController
'get',
'populate',
'invite',
'getMailable',
'cancel'
];

public $requiresIdAndKey = [
'receive',
'finalise',
'finalize',
'refine',
'decline',
];
Expand Down Expand Up @@ -91,33 +113,42 @@ public function getRouteGroupMiddleware(): array
*/
public function getGroupRoutes(): void
{
// Get By Id Methods
Route::get('{invitationId}', $this->get(...))
->name('invitation.get')
->whereNumber('invitationId')
->middleware([
self::roleAuthorizer(Role::getAllRoles()),
]);

Route::post('add/{type}', $this->add(...))
->name('invitation.add')
->middleware([
self::roleAuthorizer(Role::getAllRoles()),
]);

Route::put('{invitationId}/populate', $this->populate(...))
->name('invitation.populate')
->whereNumber('invitationId')
->middleware([
self::roleAuthorizer(Role::getAllRoles()),
]);

Route::put('{invitationId}/invite', $this->invite(...))
->name('invitation.invite')
->whereNumber('invitationId')
->middleware([
self::roleAuthorizer(Role::getAllRoles()),
]);
Route::middleware([
'has.user',
'has.context',
self::roleAuthorizer([
Role::ROLE_ID_SITE_ADMIN,
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_SUB_EDITOR,
ROLE::ROLE_ID_ASSISTANT,
]),
])->group(function () {

Route::get('', $this->getMany(...))
->name('invitation.getMany');

// Get By Id Methods
Route::get('{invitationId}', $this->get(...))
->name('invitation.get')
->whereNumber('invitationId');

Route::post('add/{type}', $this->add(...))
->name('invitation.add');

Route::put('{invitationId}/populate', $this->populate(...))
->name('invitation.populate')
->whereNumber('invitationId');

Route::put('{invitationId}/invite', $this->invite(...))
->name('invitation.invite')
->whereNumber('invitationId');

Route::get('{invitationId}/getMailable', $this->getMailable(...))
->name('invitation.getMailable');

Route::put('{invitationId}/cancel', $this->cancel(...))
->name('invitation.cancel');
});

// Get By Key methods.
Route::get('{invitationId}/key/{key}', $this->receive(...))
Expand Down Expand Up @@ -176,36 +207,86 @@ public function authorize(PKPRequest $request, array &$args, array $roleAssignme
$invitation = Repo::invitation()->getByIdAndKey($invitationId, $invitationKey);
}

if (!isset($invitation)) {
throw new Exception('Invitation could not be created');
}
if ($actionName == 'getMany') {
$this->addPolicy(new UserRolesRequiredPolicy($request), true);
} else {
if (!isset($invitation)) {
throw new Exception('Invitation could not be created');
}

$this->invitation = $invitation;
$this->invitation = $invitation;

if (!$this->invitation instanceof IApiHandleable) {
throw new Exception('This invitation does not support API handling');
}
if (!in_array($actionName, $this->notNeedAPIHandler)) {
if (!$this->invitation instanceof IApiHandleable) {
throw new Exception('This invitation does not support API handling');
}

$this->createInvitationHandler = $invitation->getCreateInvitationController();
$this->receiveInvitationHandler = $invitation->getReceiveInvitationController();
$this->createInvitationHandler = $invitation->getCreateInvitationController($this->invitation);
$this->receiveInvitationHandler = $invitation->getReceiveInvitationController($this->invitation);

if (!isset($this->createInvitationHandler) || !isset($this->receiveInvitationHandler)) {
throw new Exception('This invitation should have defined its API handling code');
}
if (!isset($this->createInvitationHandler) || !isset($this->receiveInvitationHandler)) {
throw new Exception('This invitation should have defined its API handling code');
}

$this->selectedHandler = $this->getHandlerForAction($actionName);
$this->selectedHandler = $this->getHandlerForAction($actionName);

if (!method_exists($this->selectedHandler, $actionName)) {
throw new Exception("The handler does not support the method: {$actionName}");
}
if (!method_exists($this->selectedHandler, $actionName)) {
throw new Exception("The handler does not support the method: {$actionName}");
}

$this->selectedHandler->authorize($this, $request, $args, $roleAssignments);
$this->selectedHandler->authorize($this, $request, $args, $roleAssignments);
}
}

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

public function add(Request $illuminateRequest): JsonResponse
{
$reqInput = $illuminateRequest->all();
$payload = $reqInput['invitationData'];

$rules = [
'userId' => [
Rule::prohibitedIf(isset($payload['inviteeEmail'])),
'bail',
'nullable',
'required_without:inviteeEmail',
'integer',
new UserMustExistRule($payload['userId']),
],
'inviteeEmail' => [
Rule::prohibitedIf(isset($payload['userId'])),
'bail',
'nullable',
'required_without:userId',
'email',
new EmailMustNotExistRule($payload['inviteeEmail']),
]
];

$messages = [
'inviteeEmail.prohibited' => __('invitation.api.error.initialization.noUserIdAndEmailTogether'),
'userId.prohibited' => __('invitation.api.error.initialization.noUserIdAndEmailTogether')
];

$validator = ValidatorFactory::make(
$payload,
$rules,
$messages
);

if ($validator->fails()) {
return response()->json([
'errors' => $validator->errors()
], Response::HTTP_UNPROCESSABLE_ENTITY);
}

$context = $this->getRequest()->getContext();
$inviter = $this->getRequest()->getUser();

$this->invitation->initialize($payload['userId'], $context->getId(), $payload['inviteeEmail'], $inviter->getId());

return $this->selectedHandler->add($illuminateRequest);
}

Expand Down Expand Up @@ -243,4 +324,69 @@ public function decline(Request $illuminateRequest): JsonResponse
{
return $this->selectedHandler->decline($illuminateRequest);
}

public function getMany(Request $illuminateRequest): JsonResponse
{
$context = $illuminateRequest->attributes->get('context'); /** @var \PKP\context\Context $context */
$invitationType = $this->getParameter(self::PARAM_TYPE);

$count = $illuminateRequest->query('count', 10); // default count to 10 if not provided
$offset = $illuminateRequest->query('offset', 0); // default offset to 0 if not provided

$query = InvitationModel::query()
->when($invitationType, function ($query, $invitationType) {
return $query->byType($invitationType);
})
->when($context, function ($query, $context) {
return $query->byContextId($context->getId());
})
->stillActive();

$maxCount = $query->count();

$invitations = $query->skip($offset)
->take($count)
->get();

$finalCollection = $invitations->map(function ($invitation) {
$specificInvitation = Repo::invitation()->getById($invitation->id);
return $specificInvitation;
});

return response()->json([
'itemsMax' => $maxCount,
'items' => (UserRoleAssignmentInviteResource::collection($finalCollection)),
], Response::HTTP_OK);
}

public function getMailable(Request $illuminateRequest): JsonResponse
{
if (in_array(HasMailable::class, class_uses($this->invitation))) {
$mailable = $this->invitation->getMailable();

return response()->json([
'mailable' => $mailable,
], Response::HTTP_OK);
}

return response()->json([
'error' => __('invitation.api.error.invitationTypeNotHasMailable'),
], Response::HTTP_BAD_REQUEST);
}

public function cancel(Request $illuminateRequest): JsonResponse
{
if (!$this->invitation->isPending()) {
return response()->json([
'error' => __('invitation.api.error.invitationCantBeCanceled'),
], Response::HTTP_BAD_REQUEST);
}

$this->invitation->updateStatus(InvitationStatus::CANCELLED);

return response()->json(
(new UserRoleAssignmentInviteResource($this->invitation))->toArray($illuminateRequest),
Response::HTTP_OK
);
}
}
5 changes: 4 additions & 1 deletion classes/invitation/core/CreateInvitationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*
* @class CreateInvitationController
*
* @brief Interface for all Invitation API Handlers
* @brief Defines the API actions of the "Create" phase of the invitation
*/

namespace PKP\invitation\core;
Expand All @@ -19,9 +19,12 @@
use Illuminate\Routing\Controller;
use PKP\core\PKPBaseController;
use PKP\core\PKPRequest;
use PKP\invitation\core\Invitation;

abstract class CreateInvitationController extends Controller
{
public ?PKPRequest $request = null;

abstract public function authorize(PKPBaseController $controller, PKPRequest $request, array &$args, array $roleAssignments): bool;
abstract public function add(Request $illuminateRequest): JsonResponse;
abstract public function populate(Request $illuminateRequest): JsonResponse;
Expand Down
20 changes: 20 additions & 0 deletions classes/invitation/core/EmptyInvitePayload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* @file classes/invitation/core/EmptyInvitePayload.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 EmptyInvitePayload
*
* @brief Empty Payload
*/

namespace PKP\invitation\core;

class EmptyInvitePayload extends InvitePayload
{
// No properties or methods
}
Loading

0 comments on commit 0f866e8

Please sign in to comment.