diff options
Diffstat (limited to 'lib/classes/JsonApi/Routes')
12 files changed, 489 insertions, 9 deletions
diff --git a/lib/classes/JsonApi/Routes/Clipboards/Authority.php b/lib/classes/JsonApi/Routes/Clipboards/Authority.php new file mode 100644 index 0000000..5cc053a --- /dev/null +++ b/lib/classes/JsonApi/Routes/Clipboards/Authority.php @@ -0,0 +1,28 @@ +<?php +namespace JsonApi\Routes\Clipboards; + +use User; + +final class Authority +{ + public static function canCreateClipboard(User $user): bool + { + return true; + } + + public static function canAccessClipboard(User $user, \Clipboard $clipboard): bool + { + return $user->id === $clipboard->user_id + || $user->perms === 'root'; + } + + public static function canUpdateClipboard(User $user, \Clipboard $clipboard): bool + { + return self::canAccessClipboard($user, $clipboard); + } + + public static function canDeleteClipboard(User $user, \Clipboard $clipboard): bool + { + return self::canUpdateClipboard($user, $clipboard); + } +} diff --git a/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsCreate.php b/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsCreate.php new file mode 100644 index 0000000..d57d0c5 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsCreate.php @@ -0,0 +1,106 @@ +<?php +namespace JsonApi\Routes\Clipboards; + +use JsonApi\Errors\AuthorizationFailedException; +use JsonApi\JsonApiController; +use JsonApi\Routes\ValidationTrait; +use JsonApi\Schemas\Clipboard; +use Psr\Http\Message\{ + ResponseInterface as Response, + ServerRequestInterface as Request +}; + +final class ClipboardItemsCreate extends JsonApiController +{ + use ValidationTrait; + + public function __invoke(Request $request, Response $response, $args): Response + { + $json = $this->validate($request, $args); + + $clipboard_id = $args['id'] ?? $json['data']['relationships']['clipboard']['data']['id']; + $clipboard = \Clipboard::find($clipboard_id); + + $user = $this->getUser($request); + if (!Authority::canUpdateClipboard($user, $clipboard)) { + throw new AuthorizationFailedException(); + } + + $range_id = $json['data']['attributes']['range_id']; + $range_type = $json['data']['attributes']['range_type']; + + $item = \ClipboardItem::findOneBySql( + 'clipboard_id = ? AND range_id = ? AND range_type = ?', + [$clipboard_id, $range_id, $range_type] + ); + + if ($item) { + return $this->getCodeResponse(302, [ + 'Location' => $this->getLinkToItem($item), + ]); + } + + $item = \ClipboardItem::create([ + 'clipboard_id' => $clipboard_id, + 'range_id' => $range_id, + 'range_type' => $range_type, + ]); + + return $this->getContentResponse($item); + } + + protected function validateResourceDocument($json, $data) + { + $clipboardValidationError = $this->validateRequestContainsValidClipboard($json, $data); + if ($clipboardValidationError !== null) { + return $clipboardValidationError; + } + + if (!self::arrayHas($json, 'data.attributes.range_id')) { + return 'No range_id defined'; + } + + if (!self::arrayHas($json, 'data.attributes.range_type')) { + return 'No range_type defined'; + } + + $range_type = self::arrayGet($json, 'data.attributes.range_type'); + if (!is_a($range_type, \StudipItem::class, true)) { + return 'Range type must implement interface StudipItem'; + } + + return null; + } + + private function validateRequestContainsValidClipboard($json, $data): ?string + { + if (isset($data['id'])) { + if (!\Clipboard::exists($data['id'])) { + return 'Provided clipboard id is invalid'; + } + } else { + if (!self::arrayHas($json, 'data.relationships.clipboard')) { + return 'No clipboard relationship defined'; + } + + $clipboard = self::arrayGet($json, 'data.relationships.clipboard'); + if ( + !isset($clipboard['data']['type'], $clipboard['data']['id']) + || $clipboard['data']['type'] !== Clipboard::TYPE + ) { + return 'Defined clipboard relationship has invalid format.'; + } + if (!\Clipboard::exists($clipboard['data']['id'])) { + return 'Related clipboard does not exist.'; + } + } + + return null; + } + + private function getLinkToItem(\ClipboardItem $item): string + { + $json = $this->encoder->encodeData($item); + return json_decode($json, true)['data']['links']['self']; + } +} diff --git a/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsDelete.php b/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsDelete.php new file mode 100644 index 0000000..a9c7cd4 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsDelete.php @@ -0,0 +1,54 @@ +<?php +namespace JsonApi\Routes\Clipboards; + +use JsonApi\Errors\BadRequestException; +use JsonApi\Errors\RecordNotFoundException; +use JsonApi\JsonApiController; +use Psr\Http\Message\{ + ResponseInterface as Response, + ServerRequestInterface as Request +}; + +final class ClipboardItemsDelete extends JsonApiController +{ + protected $allowedFilteringParameters = ['range_id']; + + public function __invoke(Request $request, Response $response, $args): Response + { + $clipboard = \Clipboard::find($args['id']); + if (!$clipboard) { + throw new RecordNotFoundException('Clipboard not found'); + } + + $user = $this->getUser($request); + if (!Authority::canUpdateClipboard($user, $clipboard)) { + throw new \AccessDeniedException(); + } + + $item = null; + if (isset($args['itemId'])) { + $item = \ClipboardItem::find($args['itemId']); + } else { + $filtering = iterator_to_array($this->getQueryParameters()->getFilters()); + if (!isset($filtering['range_id'])) { + throw new BadRequestException('No range_id filter given'); + } + $item = \ClipboardItem::findOneBySQL( + 'clipboard_id = ? AND range_id = ?', + [$clipboard->id, $filtering['range_id']] + ); + } + + if (!$item) { + throw new RecordNotFoundException('Item not found'); + } + + if ($item->clipboard_id !== $clipboard->id) { + throw new BadRequestException('Item does not belong to clipboard'); + } + + $item->delete(); + + return $this->getCodeResponse(204); + } +} diff --git a/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsShow.php b/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsShow.php new file mode 100644 index 0000000..3c91708 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Clipboards/ClipboardItemsShow.php @@ -0,0 +1,28 @@ +<?php +namespace JsonApi\Routes\Clipboards; + +use JsonApi\Errors\AuthorizationFailedException; +use JsonApi\Errors\RecordNotFoundException; +use JsonApi\JsonApiController; +use Psr\Http\Message\{ + ResponseInterface as Response, + ServerRequestInterface as Request +}; + +final class ClipboardItemsShow extends JsonApiController +{ + public function __invoke(Request $request, Response $response, $args): Response + { + $item = \ClipboardItem::find($args['id']); + if (!$item) { + throw new RecordNotFoundException(); + } + + $user = $this->getUser($request); + if (!Authority::canAccessClipboard($user, $item->clipboard)) { + throw new AuthorizationFailedException(); + } + + return $this->getContentResponse($item); + } +} diff --git a/lib/classes/JsonApi/Routes/Clipboards/ClipboardsCreate.php b/lib/classes/JsonApi/Routes/Clipboards/ClipboardsCreate.php new file mode 100644 index 0000000..57fd9b9 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Clipboards/ClipboardsCreate.php @@ -0,0 +1,46 @@ +<?php +namespace JsonApi\Routes\Clipboards; + +use JsonApi\Errors\AuthorizationFailedException; +use JsonApi\JsonApiController; +use JsonApi\Routes\ValidationTrait; +use Psr\Http\Message\{ + ResponseInterface as Response, + ServerRequestInterface as Request +}; + +final class ClipboardsCreate extends JsonApiController +{ + use ValidationTrait; + + public function __invoke(Request $request, Response $response, $args): Response + { + $user = $this->getUser($request); + + if (!Authority::canCreateClipboard($user)) { + throw new AuthorizationFailedException(); + } + + $json = $this->validate($request, $args); + + $clipboard = \Clipboard::create([ + 'name' => $json['data']['attributes']['name'], + 'user_id' => $user->id, + ]); + + return $this->getContentResponse($clipboard); + } + + protected function validateResourceDocument($json, $data) + { + if (!self::arrayHas($json, 'data.attributes.name')) { + return 'No name for the clipboard defined'; + } + + if (!trim(self::arrayGet($json, 'data.attributes.name'))) { + return 'Name of the clipboard may not be empty'; + } + + return null; + } +} diff --git a/lib/classes/JsonApi/Routes/Clipboards/ClipboardsDelete.php b/lib/classes/JsonApi/Routes/Clipboards/ClipboardsDelete.php new file mode 100644 index 0000000..0897843 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Clipboards/ClipboardsDelete.php @@ -0,0 +1,31 @@ +<?php +namespace JsonApi\Routes\Clipboards; + +use JsonApi\Errors\AuthorizationFailedException; +use JsonApi\Errors\RecordNotFoundException; +use JsonApi\JsonApiController; +use Psr\Http\Message\{ + ResponseInterface as Response, + ServerRequestInterface as Request +}; + +final class ClipboardsDelete extends JsonApiController +{ + public function __invoke(Request $request, Response $response, $args): Response + { + $clipboard = \Clipboard::find($args['id']); + if (!$clipboard) { + throw new RecordNotFoundException(); + } + + $user = $this->getUser($request); + + if (!Authority::canDeleteClipboard($user, $clipboard)) { + throw new AuthorizationFailedException(); + } + + $clipboard->delete(); + + return $this->getCodeResponse(204); + } +} diff --git a/lib/classes/JsonApi/Routes/Clipboards/ClipboardsUpdate.php b/lib/classes/JsonApi/Routes/Clipboards/ClipboardsUpdate.php new file mode 100644 index 0000000..83d9539 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Clipboards/ClipboardsUpdate.php @@ -0,0 +1,50 @@ +<?php +namespace JsonApi\Routes\Clipboards; + +use JsonApi\Errors\AuthorizationFailedException; +use JsonApi\Errors\RecordNotFoundException; +use JsonApi\JsonApiController; +use JsonApi\Routes\ValidationTrait; +use Psr\Http\Message\{ + ResponseInterface as Response, + ServerRequestInterface as Request +}; + +final class ClipboardsUpdate extends JsonApiController +{ + use ValidationTrait; + + public function __invoke(Request $request, Response $response, $args): Response + { + $clipboard = \Clipboard::find($args['id']); + if (!$clipboard) { + throw new RecordNotFoundException(); + } + + $user = $this->getUser($request); + + if (!Authority::canUpdateClipboard($user, $clipboard)) { + throw new AuthorizationFailedException(); + } + + $json = $this->validate($request, $args); + + $clipboard->name = $json['data']['attributes']['name']; + $clipboard->store(); + + return $this->getContentResponse($clipboard); + } + + protected function validateResourceDocument($json, $data) + { + if (!self::arrayHas($json, 'data.attributes.name')) { + return 'No name for the clipboard defined'; + } + + if (!trim(self::arrayGet($json, 'data.attributes.name'))) { + return 'Name of the clipboard may not be empty'; + } + + return null; + } +} diff --git a/lib/classes/JsonApi/Routes/Consultations/SlotCreationCount.php b/lib/classes/JsonApi/Routes/Consultations/SlotCreationCount.php new file mode 100644 index 0000000..c378771 --- /dev/null +++ b/lib/classes/JsonApi/Routes/Consultations/SlotCreationCount.php @@ -0,0 +1,105 @@ +<?php +namespace JsonApi\Routes\Consultations; + +use ConsultationBlock; +use JsonApi\Errors\BadRequestException; +use JsonApi\NonJsonApiController; +use Neomerx\JsonApi\Exceptions\JsonApiException; +use Neomerx\JsonApi\Schema\ErrorCollection; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; + +final class SlotCreationCount extends NonJsonApiController +{ + public function __invoke(Request $request, Response $response, array $args) + { + $parameters = $request->getQueryParams(); + + $this->validateParameters($parameters); + + // Determine duration of a slot and pause times + $slot_count = ConsultationBlock::countSlots( + strtotime($parameters['start']), + strtotime($parameters['end']), + $parameters['dow'], + $parameters['interval'], + $parameters['duration'], + $parameters['pause_time'] ?? null, + $parameters['pause_duration'] ?? null + ); + + $response->getBody()->write((string) $slot_count); + return $response->withAddedHeader('Content-Type', 'application/json'); + } + + private function validateParameters(array $parameters): void + { + $collection = new ErrorCollection(); + + foreach (['start', 'end', 'dow', 'interval', 'duration'] as $key) { + if (!isset($parameters[$key])) { + $collection->addQueryParameterError($key, 'Parameter is missing'); + } + } + + if (isset($parameters['start'], $parameters['end'])) { + $start = strtotime($parameters['start']); + $end = strtotime($parameters['end']); + + if (!$start) { + $collection->addQueryParameterError('start', 'Parameter has invalid datetime format'); + } + + if (!$end) { + $collection->addQueryParameterError('end', 'Parameter has invalid datetime format'); + } + + if ($start && $end && $start > $end) { + $collection->addQueryParameterError('start', 'Datetime value of start must be before end'); + } + } + + if ( + isset($parameters['dow']) + && ( + !ctype_digit($parameters['dow']) + || $parameters['dow'] < 0 + || $parameters['dow'] > 6 + ) + ) { + $collection->addQueryParameterError('dow', 'Parameter must be a number between 0 and 6'); + } + + if ( + isset($parameters['interval']) + && ( + !ctype_digit($parameters['interval']) + || $parameters['interval'] < 0 + || $parameters['interval'] > 4 + ) + ) { + $collection->addQueryParameterError('interval', 'Parameter must be a number between 0 and 4'); + } + + if ( + isset($parameters['duration']) + && ( + !ctype_digit($parameters['duration']) + || $parameters['duration'] <= 0 + ) + ) { + $collection->addQueryParameterError('duration', 'Parameter must be a positive number'); + } + + if ( + isset($parameters['pause_time'], $parameters['duration']) + && $parameters['pause_time'] < $parameters['duration'] + ) { + $collection->addQueryParameterError('pause_time', 'The defined time to a pause is shorter than the duration of a slot.'); + } + + if (count($collection) > 0) { + throw new JsonApiException($collection); + } + } +} diff --git a/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php b/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php index e676507..d913966 100644 --- a/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php +++ b/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php @@ -100,6 +100,20 @@ class UnitsCreate extends JsonApiController 'commentable' => 0 ]); + \Courseware\Container::create([ + 'structural_element_id' => $struct->id, + 'owner_id' => $user->id, + 'editor_id' => $user->id, + 'edit_blocker_id' => '', + 'position' => 0, + 'container_type' => 'list', + + 'payload' => json_encode([ + 'colspan' => 'full', + 'sections' => [['name' => _('erstes Element'), 'icon' => '','blocks' => []]] + ]), + ]); + $unit = \Courseware\Unit::create([ 'range_id' => $range->getRangeId(), 'range_type' => $range->getRangeType(), diff --git a/lib/classes/JsonApi/Routes/Files/RangeFileRefsIndex.php b/lib/classes/JsonApi/Routes/Files/RangeFileRefsIndex.php index 8f69d6a..773071e 100644 --- a/lib/classes/JsonApi/Routes/Files/RangeFileRefsIndex.php +++ b/lib/classes/JsonApi/Routes/Files/RangeFileRefsIndex.php @@ -13,7 +13,7 @@ class RangeFileRefsIndex extends AbstractRangeIndex $filerefs = []; foreach ($filesAndFolders['files'] as $file_object) { - if (method_exists($file_object, "getFileRef")) { + if (method_exists($file_object, 'getFileRef')) { $filerefs[] = $file_object->getFileRef(); } } diff --git a/lib/classes/JsonApi/Routes/Files/SubfilerefsIndex.php b/lib/classes/JsonApi/Routes/Files/SubfilerefsIndex.php index 0ff0603..2ed1a23 100644 --- a/lib/classes/JsonApi/Routes/Files/SubfilerefsIndex.php +++ b/lib/classes/JsonApi/Routes/Files/SubfilerefsIndex.php @@ -2,6 +2,7 @@ namespace JsonApi\Routes\Files; +use FileRef; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; use JsonApi\Errors\AuthorizationFailedException; @@ -28,8 +29,14 @@ class SubfilerefsIndex extends JsonApiController throw new AuthorizationFailedException(); } - $fileRefs = $folder->file_refs->getArrayCopy(); - list($offset, $limit) = $this->getOffsetAndLimit(); + $fileRefs = array_map( + function (\FileType $file): FileRef { + return $file->getFileRef(); + }, + $folder->getFiles() + ); + + [$offset, $limit] = $this->getOffsetAndLimit(); return $this->getPaginatedContentResponse( array_slice($fileRefs, $offset, $limit), diff --git a/lib/classes/JsonApi/Routes/Files/SubfoldersIndex.php b/lib/classes/JsonApi/Routes/Files/SubfoldersIndex.php index e8f4d13..f0ad18c 100644 --- a/lib/classes/JsonApi/Routes/Files/SubfoldersIndex.php +++ b/lib/classes/JsonApi/Routes/Files/SubfoldersIndex.php @@ -19,20 +19,31 @@ class SubfoldersIndex extends JsonApiController */ public function __invoke(Request $request, Response $response, $args) { - if (!$folder = \FileManager::getTypedFolder($args['id'])) { + $folder = \FileManager::getTypedFolder($args['id']); + if (!$folder) { throw new RecordNotFoundException(); } - if (!Authority::canShowFolder($this->getUser($request), $folder)) { + $user = $this->getUser($request); + + if (!Authority::canShowFolder($user, $folder)) { throw new AuthorizationFailedException(); } - $subfolders = array_map( - function ($subfolder) { - return $subfolder->getTypedFolder(); + $subfolders = array_reduce( + $folder->subfolders->getArrayCopy(), + function ($result, $subfolder) use ($user) { + $folder = $subfolder->getTypedFolder(); + + if (Authority::canShowFolder($user, $folder)) { + $result[] = $folder; + } + + return $result; }, - $folder->subfolders->getArrayCopy() + [] ); + list($offset, $limit) = $this->getOffsetAndLimit(); return $this->getPaginatedContentResponse( |
