Skip to content
Snippets Groups Projects
Commit 6d5d90e2 authored by Marcus Eibrink-Lunzenauer's avatar Marcus Eibrink-Lunzenauer
Browse files

Add more bookmark routes to users.

parent a0470bbb
No related branches found
No related tags found
No related merge requests found
......@@ -139,7 +139,7 @@ class Contents_CoursewareController extends AuthenticatedController
{
Navigation::activateItem('/contents/courseware/bookmarks');
$this->bookmarks = array();
$cw_bookmarks = Courseware\Bookmark::findUsersBookmarks($this->user->id);
$cw_bookmarks = Courseware\Bookmark::findUsersBookmarks($this->user);
foreach($cw_bookmarks as $bookmark) {
$bm = array();
$bm['bookmark'] = $bookmark;
......
......@@ -307,6 +307,14 @@ class RouteMap
);
$group->get('/courseware-instances/{id}/bookmarks', Routes\Courseware\BookmarkedStructuralElementsIndex::class);
$group->get('/users/{id}/courseware-bookmarks', Routes\Courseware\UsersBookmarkedStructuralElementsIndex::class);
$this->addRelationship(
$group,
'/users/{id}/relationships/courseware-bookmarks',
Routes\Courseware\Rel\UsersBookmarkedStructuralElements::class
);
$group->get('/courseware-blocks/{id}', Routes\Courseware\BlocksShow::class);
$group->post('/courseware-blocks', Routes\Courseware\BlocksCreate::class);
$group->patch('/courseware-blocks/{id}', Routes\Courseware\BlocksUpdate::class);
......
......@@ -189,6 +189,21 @@ class Authority
return self::canShowCoursewareInstance($user, $resource);
}
public static function canAddBookmarkToAUser(User $actor, User $user)
{
return $actor->id === $user->id;
}
public static function canModifyBookmarksOfAUser(User $actor, User $user)
{
return $actor->id === $user->id;
}
public static function canIndexBookmarksOfAUser(User $actor, User $user)
{
return $actor->id === $user->id;
}
/**
* @SuppressWarnings(PHPMD.Superglobals)
*/
......
......@@ -48,6 +48,6 @@ class BookmarkedStructuralElementsIndex extends JsonApiController
$total = count($resources);
list($offset, $limit) = $this->getOffsetAndLimit();
return $this->getPaginatedResponse(array_slice($resources, $offset, $limit), $total);
return $this->getPaginatedContentResponse(array_slice($resources, $offset, $limit), $total);
}
}
......@@ -11,7 +11,10 @@ trait CoursewareInstancesHelper
{
private function findInstance(string $instanceId): Instance
{
list($rangeType, $rangeId) = explode('_', $instanceId);
[$rangeType, $rangeId] = explode('_', $instanceId);
if (!is_string($rangeType) || !is_string($rangeId)) {
throw new BadRequestException('Invalid instance id: "' . $instanceId . '".');
}
return $this->findInstanceWithRange($rangeType, $rangeId);
}
......
......@@ -169,6 +169,9 @@ class BookmarkedStructuralElements extends RelationshipsController
private function addBookmarks(\User $user, array $newIds): void
{
foreach ($newIds as $structuralElementId) {
if (Bookmark::countBySQL('user_id = ? AND element_id = ?', [$user->id, $structuralElementId])) {
continue;
}
Bookmark::create(['user_id' => $user->id, 'element_id' => $structuralElementId]);
}
}
......
<?php
namespace JsonApi\Routes\Courseware\Rel;
use Courseware\Bookmark;
use Courseware\Instance;
use Courseware\StructuralElement;
use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\BadRequestException;
use JsonApi\Errors\ConflictException;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\Routes\Courseware\Authority;
use JsonApi\Routes\Courseware\CoursewareInstancesHelper;
use JsonApi\Routes\RelationshipsController;
use Psr\Http\Message\ServerRequestInterface as Request;
class UsersBookmarkedStructuralElements extends RelationshipsController
{
use CoursewareInstancesHelper;
protected $allowedPagingParameters = ['offset', 'limit'];
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function fetchRelationship(Request $request, $related)
{
$bookmarks = array_column(Bookmark::findUsersBookmarks($related), 'element');
$total = count($bookmarks);
list($offset, $limit) = $this->getOffsetAndLimit();
$page = array_slice($bookmarks, $offset, $limit);
return $this->getPaginatedIdentifiersResponse($page, $total);
}
protected function replaceRelationship(Request $request, $related)
{
$json = $this->validate($request);
$structuralElements = $this->validateStructuralElements($user = $this->getUser($request), $json, $related);
$this->replaceBookmarks($related, $structuralElements);
return $this->getCodeResponse(204);
}
protected function addToRelationship(Request $request, $related)
{
$json = $this->validate($request);
$structuralElements = $this->validateStructuralElements($user = $this->getUser($request), $json, $related);
$this->addBookmarks($related, $structuralElements);
return $this->getCodeResponse(204);
}
protected function removeFromRelationship(Request $request, $related)
{
$json = $this->validate($request);
$structuralElements = $this->validateStructuralElements($user = $this->getUser($request), $json, $related);
$this->removeBookmarks($user, $structuralElements);
return $this->getCodeResponse(204);
}
protected function findRelated(array $args)
{
if (!($related = \User::find($args['id']))) {
throw new RecordNotFoundException();
}
return $related;
}
protected function authorize(Request $request, $resource)
{
$observer = $this->getUser($request);
$observed = $resource;
switch ($request->getMethod()) {
case 'GET':
return Authority::canIndexBookmarksOfAUser($observer, $observed);
case 'DELETE':
case 'PATCH':
case 'POST':
return Authority::canModifyBookmarksOfAUser($observer, $observed);
default:
return false;
}
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function getRelationshipSelfLink($resource, $schema, $userData)
{
return $schema->getRelationshipSelfLink($resource, \JsonApi\Schemas\User::REL_COURSEWARE_BOOKMARKS);
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function getRelationshipRelatedLink($resource, $schema, $userData)
{
return $schema->getRelationshipRelatedLink($resource, \JsonApi\Schemas\User::REL_COURSEWARE_BOOKMARKS);
}
protected function validateResourceDocument($json, $data)
{
if (!self::arrayHas($json, 'data')) {
return 'Missing `data` member at document´s top level.';
}
$data = self::arrayGet($json, 'data');
if (!is_array($data)) {
return 'Document´s ´data´ must be an array.';
}
foreach ($data as $item) {
if (\JsonApi\Schemas\Courseware\StructuralElement::TYPE !== self::arrayGet($item, 'type')) {
return 'Wrong `type` in document´s `data`.';
}
if (!self::arrayGet($item, 'id')) {
return 'Missing `id` of document´s `data`.';
}
}
if (self::arrayHas($json, 'data.attributes')) {
return 'Document must not have `attributes`.';
}
}
private function validateStructuralElements(\User $actor, $json, \User $user)
{
$structuralElements = [];
foreach (self::arrayGet($json, 'data') as $structuralElementResource) {
if (!($structuralElement = StructuralElement::find($structuralElementResource['id']))) {
throw new RecordNotFoundException();
}
if (!Authority::canModifyBookmarksOfAUser($actor, $user)) {
throw new AuthorizationFailedException();
}
if (!Authority::canShowStructuralElement($user, $structuralElement)) {
throw new RecordNotFoundException();
}
$structuralElements[] = $structuralElement->id;
}
return $structuralElements;
}
private function replaceBookmarks(\User $user, array $newIds)
{
$oldIds = array_column(Bookmark::findUsersBookmarks($user), 'element_id');
$onlyInOld = array_diff($oldIds, $newIds);
$onlyInNew = array_diff($newIds, $oldIds);
$this->removeBookmarks($user, $onlyInOld);
$this->addBookmarks($user, $onlyInNew);
}
private function addBookmarks(\User $user, array $newIds): void
{
foreach ($newIds as $structuralElementId) {
if (Bookmark::countBySQL('user_id = ? AND element_id = ?', [$user->id, $structuralElementId])) {
continue;
}
Bookmark::create(['user_id' => $user->id, 'element_id' => $structuralElementId]);
}
}
private function removeBookmarks(\User $user, array $oldIds): void
{
Bookmark::deleteBySQL('user_id = ? AND element_id IN (?)', [$user->id, $oldIds]);
}
}
<?php
namespace JsonApi\Routes\Courseware;
use Courseware\Bookmark;
use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\JsonApiController;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Displays the user's bookmarked structural elements.
*/
class UsersBookmarkedStructuralElementsIndex extends JsonApiController
{
use CoursewareInstancesHelper;
protected $allowedIncludePaths = [
'ancestors',
'containers',
'containers.blocks',
'containers.blocks.edit-blocker',
'containers.blocks.editor',
'containers.blocks.owner',
'containers.blocks.user-data-field',
'containers.blocks.user-progress',
'course',
'editor',
'owner',
'parent',
];
protected $allowedPagingParameters = ['offset', 'limit'];
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __invoke(Request $request, Response $response, $args)
{
if (!($user = \User::find($args['id']))) {
throw new RecordNotFoundException();
}
$actor = $this->getUser($request);
if (!Authority::canIndexBookmarksOfAUser($actor, $user)) {
throw new AuthorizationFailedException();
}
$resources = array_column(Bookmark::findUsersBookmarks($user), 'element');
$total = count($resources);
[$offset, $limit] = $this->getOffsetAndLimit();
return $this->getPaginatedContentResponse(array_slice($resources, $offset, $limit), $total);
}
}
......@@ -16,6 +16,7 @@ class User extends SchemaProvider
const REL_CONTACTS = 'contacts';
const REL_COURSES = 'courses';
const REL_COURSE_MEMBERSHIPS = 'course-memberships';
const REL_COURSEWARE_BOOKMARKS = 'courseware-bookmarks';
const REL_EVENTS = 'events';
const REL_FILES = 'file-refs';
const REL_FOLDERS = 'folders';
......@@ -165,6 +166,8 @@ class User extends SchemaProvider
$relationships = $this->getNewsRelationship($relationships, $user, $this->shouldInclude($context, self::REL_NEWS));
$relationships = $this->getOutboxRelationship($relationships, $user, $this->shouldInclude($context, self::REL_OUTBOX));
$relationships = $this->getScheduleRelationship($relationships, $user, $this->shouldInclude($context, self::REL_SCHEDULE));
$relationships = $this->getCoursewareBookmarksRelationship($relationships, $user, $this->shouldInclude($context, self::REL_COURSEWARE_BOOKMARKS));
}
return $relationships;
......@@ -256,6 +259,21 @@ class User extends SchemaProvider
return $relationships;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
private function getCoursewareBookmarksRelationship(array $relationships, \User $user, $includeData)
{
$relationships[self::REL_COURSEWARE_BOOKMARKS] = [
self::RELATIONSHIP_LINKS_SELF => true,
self::RELATIONSHIP_LINKS => [
Link::RELATED => $this->getRelationshipRelatedLink($user, self::REL_COURSEWARE_BOOKMARKS),
],
];
return $relationships;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
......
......@@ -54,12 +54,12 @@ class Bookmark extends \SimpleORMap
/**
* Returns all bookmarks of a user.
*
* @param string $userId the user's ID for whom to search for bookmarks
* @param \User $user the user for whom to search for bookmarks
*
* @return Bookmark[] the list of bookmarks
*/
public function findUsersBookmarks(string $userId): array
public static function findUsersBookmarks($user): array
{
return self::findBySQL('user_id = ?', [$userId]);
return self::findBySQL('user_id = ? ORDER BY chdate', [$user->id]);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment