<?php namespace JsonApi\Routes\Courseware; use Courseware\StructuralElement; use JsonApi\Errors\AuthorizationFailedException; use JsonApi\Errors\RecordNotFoundException; use JsonApi\JsonApiController; use Neomerx\JsonApi\Contracts\Http\ResponsesInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; /** * Displays all descendants of a structural element. */ class DescendantsOfStructuralElementsIndex extends JsonApiController { protected $allowedPagingParameters = ['offset', 'limit']; protected $allowedIncludePaths = ['containers', 'course', 'editor', 'owner', 'parent']; /** * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __invoke(Request $request, Response $response, $args) { /** @var ?StructuralElement $resource */ $resource = StructuralElement::find($args['id']); if (!$resource) { throw new RecordNotFoundException(); } if (!Authority::canShowStructuralElement($user = $this->getUser($request), $resource)) { throw new AuthorizationFailedException(); } $descendants = $resource->findDescendants($user); list($offset, $limit) = $this->getOffsetAndLimit(); $page = array_slice($descendants, $offset, $limit); $total = count($descendants); // compute ETag, compare it and short-cut this route if they match $etag = $this->getETag($user, $resource, $descendants); if ($request->hasHeader('If-None-Match')) { $sentETag = $request->getHeaderLine('If-None-Match'); if ($etag === $sentETag) { return $response->withStatus(304)->withHeader('Cache-Control', 'private, must-revalidate'); } } return $this->getPaginatedContentResponse( $page, $total, ResponsesInterface::HTTP_OK, [], [], [ 'Cache-Control' => 'private, must-revalidate', 'ETag' => $etag, ] ); } private function getEtag(\User $user, StructuralElement $resource, array $elements): string { $ids = join(',', array_column($elements, 'id')); $maxChdate = count($elements) ? max(array_column($elements, 'chdate')) : ''; $payload = [$user->id, $ids, $maxChdate]; return 'W/"' . md5(join(',', $payload)) . '"'; } }