← Back
Editing: ProposalController.php
<?php declare(strict_types=1); /** * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Calendar\Controller; use OCA\Calendar\AppInfo\Application; use OCA\Calendar\Objects\Proposal\ProposalObject; use OCA\Calendar\Objects\Proposal\ProposalResponseObject; use OCA\Calendar\Service\Proposal\ProposalService; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\AnonRateLimit; use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\Attribute\UserRateLimit; use OCP\AppFramework\Http\JSONResponse; use OCP\IGroupManager; use OCP\IRequest; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; class ProposalController extends ApiController { public function __construct( IRequest $request, private IUserSession $userSession, private IUserManager $userManager, private IGroupManager $groupManager, private ProposalService $proposalService, ) { parent::__construct(Application::APP_ID, $request); } private function authorize(?string $userId = null): JSONResponse|IUser { // evaluate if user is logged in and has permissions if (!$this->userSession->isLoggedIn()) { return new JSONResponse([], Http::STATUS_UNAUTHORIZED); } if ($userId !== null) { if ($this->userSession->getUser()->getUID() !== $userId && $this->groupManager->isAdmin($this->userSession->getUser()->getUID()) === false) { return new JSONResponse([], Http::STATUS_UNAUTHORIZED); } if (!$this->userManager->userExists($userId)) { return new JSONResponse(['error' => 'user not found'], Http::STATUS_BAD_REQUEST); } $user = $this->userManager->get($userId); } else { $user = $this->userSession->getUser(); } if ($user === null) { return new JSONResponse(['error' => 'user not found'], Http::STATUS_BAD_REQUEST); } return $user; } /** * Retrieve list of available proposals */ #[ApiRoute(verb: 'POST', url: '/proposal/list', root: '/calendar')] #[NoAdminRequired] #[UserRateLimit(limit: 10, period: 60)] public function list(?string $user = null): JSONResponse { // authorize request $authorization = $this->authorize($user); if ($authorization instanceof JSONResponse) { return $authorization; } $userObject = $authorization; // retrieve proposals for the user $proposals = $this->proposalService->listProposals($userObject); return new JSONResponse($proposals->toJson('private'), Http::STATUS_OK); } /** * Fetch a proposal by its token */ #[ApiRoute(verb: 'POST', url: '/proposal/fetch', root: '/calendar')] #[PublicPage] #[NoCSRFRequired] #[NoAdminRequired] #[AnonRateLimit(limit: 10, period: 300)] #[UserRateLimit(limit: 10, period: 300)] public function fetchByToken(string $token): JSONResponse { $proposal = $this->proposalService->fetchByToken($token); if ($proposal === null) { return new JSONResponse(['error' => 'Proposal not found'], HTTP::STATUS_NOT_FOUND); } // enrich proposal with user information // as this is most likely a public request from the voting page $user = $this->userManager->get($proposal->getUid()); if ($user !== null) { $proposal->setUname($user->getDisplayName()); } return new JSONResponse($proposal->toJson('public'), Http::STATUS_OK); } /** * Create a new proposal */ #[ApiRoute(verb: 'POST', url: '/proposal/create', root: '/calendar')] #[NoAdminRequired] #[UserRateLimit(limit: 10, period: 60)] public function create(array $proposal, ?string $user = null): JSONResponse { // authorize request $authorization = $this->authorize($user); if ($authorization instanceof JSONResponse) { return $authorization; } $userObject = $authorization; // construct proposal object from the provided data $proposalObject = new ProposalObject(); try { $proposalObject->fromJson($proposal); } catch (\InvalidArgumentException $e) { return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST); } // handle the creation of the proposal $proposalObject = $this->proposalService->createProposal($userObject, $proposalObject); return new JSONResponse($proposalObject->toJson('private'), Http::STATUS_OK); } /** * Modify a proposal */ #[ApiRoute(verb: 'POST', url: '/proposal/modify', root: '/calendar')] #[NoAdminRequired] #[UserRateLimit(limit: 10, period: 60)] public function modify(array $proposal, ?string $user = null): JSONResponse { // authorize request $authorization = $this->authorize($user); if ($authorization instanceof JSONResponse) { return $authorization; } $userObject = $authorization; // construct proposal object from the provided data $proposalObject = new ProposalObject(); try { $proposalObject->fromJson($proposal); } catch (\InvalidArgumentException $e) { return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_BAD_REQUEST); } // handle the modification of the proposal try { $proposalObject = $this->proposalService->modifyProposal($userObject, $proposalObject); } catch (\InvalidArgumentException $e) { return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_NOT_FOUND); } return new JSONResponse($proposalObject->toJson('private'), Http::STATUS_OK); } /** * Destroy a proposal */ #[ApiRoute(verb: 'POST', url: '/proposal/destroy', root: '/calendar')] #[NoAdminRequired] #[UserRateLimit(limit: 10, period: 60)] public function destroy(int $id, ?string $user = null): JSONResponse { // authorize request $authorization = $this->authorize($user); if ($authorization instanceof JSONResponse) { return $authorization; } $userObject = $authorization; // handle the destruction of the proposal try { $this->proposalService->destroyProposal($userObject, $id); } catch (\InvalidArgumentException $e) { return new JSONResponse(['error' => 'Proposal not found'], Http::STATUS_NOT_FOUND); } return new JSONResponse([], Http::STATUS_OK); } /** * Convert a proposed date to a meeting */ #[ApiRoute(verb: 'POST', url: '/proposal/convert', root: '/calendar')] #[NoAdminRequired] #[UserRateLimit(limit: 10, period: 60)] public function convert(int $proposalId, int $dateId, array $options = [], ?string $user = null): JSONResponse { // authorize request $authorization = $this->authorize($user); if ($authorization instanceof JSONResponse) { return $authorization; } $userObject = $authorization; // handle the conversion try { $this->proposalService->convertProposal($userObject, $proposalId, $dateId, $options); } catch (\InvalidArgumentException $e) { return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_NOT_FOUND); } return new JSONResponse([], Http::STATUS_OK); } /** * Public view proposal response/vote */ #[ApiRoute(verb: 'POST', url: '/proposal/response', root: '/calendar')] #[PublicPage] #[NoCSRFRequired] #[NoAdminRequired] #[AnonRateLimit(limit: 10, period: 300)] #[UserRateLimit(limit: 10, period: 300)] public function response(array $response): JSONResponse { try { // construct proposal response object from the provided data $proposalResponse = new ProposalResponseObject(); $proposalResponse->fromJson($response); // handle the response $this->proposalService->storeResponse($proposalResponse); return new JSONResponse([], Http::STATUS_OK); } catch (\Exception $e) { return new JSONResponse(['error' => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR); } } }
Save File
Cancel