-
Notifications
You must be signed in to change notification settings - Fork 19
IBX-6773: Fixed loading Bookmarks for non-accessible content items #476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 4.6
Are you sure you want to change the base?
Changes from all commits
e5245db
f015051
0bf5649
117d9e2
3837c20
7dfe142
648ac3e
ba26848
5fd0eb2
993e850
e436267
d9b18bc
5343730
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,8 @@ public function loadUserIdsByLocation(Location $location): array; | |
| /** | ||
| * Loads bookmarks owned by user. | ||
| * | ||
| * @deprecated The "Handler::loadUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::find()" and "Criterion\IsBookmarked" instead. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deprecating it in 6.0 is fine for now, but the annotation format is not correct and most likely it's gonna affect or even crash phpDocumentor ;-) |
||
| * | ||
| * @param int $userId | ||
| * @param int $offset the start offset for paging | ||
| * @param int $limit the number of bookmarked locations returned | ||
|
|
@@ -61,6 +63,8 @@ public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1) | |
| /** | ||
| * Count bookmarks owned by user. | ||
| * | ||
| * @deprecated The "Handler::countUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::count()" and "Criterion\IsBookmarked" instead. | ||
| * | ||
| * @param int $userId | ||
| * | ||
| * @return int | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
| * @license For full copyright and license information view LICENSE file distributed with this source code. | ||
| */ | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; | ||
|
|
||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator\Specifications; | ||
| use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion; | ||
|
|
||
| /** | ||
| * A criterion that matches locations of bookmarks for a given user id. | ||
| * | ||
| * Supported operators: | ||
| * - EQ: matches against a unique user id | ||
| */ | ||
| final class IsBookmarked extends Criterion implements FilteringCriterion | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extending criterion means that this would be available not just for filtering but for search too. Hence it would require implementing handlers for all supported search engines. Filtering was supposed initially to mirror search service, so there are no examples of Filtering Criteria that are not Search Criteria at the same time, but the intention of Filtering was that it can exist as a separate concepts for data which are not supposed to be a part of Search Index but a database. ATM it's possible to use it with search and get the following fatal error at runtime: If it doesn't rely on Criterion, the error should appear on at least static analysis level, which provides better DX. |
||
| { | ||
| public function __construct(int $value) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Important API design remark Typically you'd want here User ID value should be part of a handler (query builder), not a criterion. That being said, I see you provided a case for IsBookmark of user X or user Z. It's a bit out of scope of the usage of this criterion, but if you indeed want such flexibility, user ID should be an optional 2nd argument (by default null, taken from current user in query builder). |
||
| { | ||
| parent::__construct(null, null, $value); | ||
| } | ||
|
|
||
| public function getSpecifications(): array | ||
| { | ||
| return [ | ||
| new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), | ||
| ]; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
| * @license For full copyright and license information view LICENSE file distributed with this source code. | ||
| */ | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; | ||
|
|
||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; | ||
| use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause; | ||
|
|
||
| /** | ||
| * Sets sort direction on the bookmark id for a location query containing IsBookmarked criterion. | ||
| */ | ||
| final class BookmarkId extends SortClause implements FilteringSortClause | ||
| { | ||
| public function __construct(string $sortDirection = Query::SORT_ASC) | ||
| { | ||
| parent::__construct('id', $sortDirection); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,8 @@ function (Bookmark $bookmark) { | |
| } | ||
|
|
||
| /** | ||
| * @deprecated The "BookmarkHandler::loadUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::find()" and "Criterion\IsBookmarked" instead. | ||
| * | ||
| * {@inheritdoc} | ||
|
Comment on lines
+86
to
88
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't repeat the same message here. It should be present on the interface only. |
||
| */ | ||
| public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array | ||
|
|
@@ -97,6 +99,8 @@ public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1) | |
| } | ||
|
|
||
| /** | ||
| * @deprecated The "BookmarkHandler::countUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::count()" and "Criterion\IsBookmarked" instead. | ||
| * | ||
| * {@inheritdoc} | ||
| */ | ||
| public function countUserBookmarks(int $userId): int | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -124,6 +124,8 @@ public function loadUserIdsByLocation(Location $location): array | |
| } | ||
|
|
||
| /** | ||
| * @deprecated The "DoctrineDatabase::loadUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::find()" and "Criterion\IsBookmarked" instead. | ||
| * | ||
| * {@inheritdoc} | ||
| */ | ||
| public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array | ||
|
|
@@ -146,6 +148,8 @@ public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1) | |
| } | ||
|
|
||
| /** | ||
| * @deprecated The "DoctrineDatabase::countUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::count()" and "Criterion\IsBookmarked" instead. | ||
| * | ||
|
Comment on lines
127
to
+152
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same remark here |
||
| * {@inheritdoc} | ||
| */ | ||
| public function countUserBookmarks(int $userId): int | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,6 +57,11 @@ public function loadBookmarkDataByUserIdAndLocationId(int $userId, array $locati | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * @deprecated The "ExceptionConversion::loadUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::find()" and "Criterion\IsBookmarked" instead. | ||
| * | ||
| * @return array | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This PHPDoc return doesn't provide any extra value. It should be documented as at least |
||
| */ | ||
| public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array | ||
| { | ||
| try { | ||
|
|
@@ -66,6 +71,9 @@ public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1) | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * @deprecated The "ExceptionConversion::countUserBookmarks()" method is deprecated, will be removed in 6.0.0. Use "LocationService::count()" and "Criterion\IsBookmarked" instead. | ||
|
Comment on lines
61
to
+75
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here too |
||
| */ | ||
| public function countUserBookmarks(int $userId): int | ||
| { | ||
| try { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
| * @license For full copyright and license information view LICENSE file distributed with this source code. | ||
| */ | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; | ||
|
|
||
| use Doctrine\DBAL\ParameterType; | ||
| use Ibexa\Contracts\Core\Persistence\Filter\Doctrine\FilteringQueryBuilder; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\IsBookmarked; | ||
| use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion; | ||
| use Ibexa\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase; | ||
|
|
||
| /** | ||
| * @internal for internal use by Repository Filtering | ||
| */ | ||
| final class BookmarkQueryBuilder extends BaseLocationCriterionQueryBuilder | ||
| { | ||
| public function accepts(FilteringCriterion $criterion): bool | ||
| { | ||
| return $criterion instanceof IsBookmarked; | ||
| } | ||
|
|
||
| public function buildQueryConstraint( | ||
| FilteringQueryBuilder $queryBuilder, | ||
| FilteringCriterion $criterion | ||
| ): string { | ||
| $queryBuilder | ||
| ->joinOnce( | ||
| 'location', | ||
| DoctrineDatabase::TABLE_BOOKMARKS, | ||
| 'bookmark', | ||
| 'location.node_id = bookmark.node_id' | ||
| ); | ||
|
|
||
| /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\IsBookmarked $criterion */ | ||
| $value = $criterion->value; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As described earlier, this should be part of the logic here, not user input by default. Inject PermissionResolver and get current user ID |
||
|
|
||
| if (\is_array($value)) { | ||
| if (!isset($value[0])) { | ||
| throw new \InvalidArgumentException('IsBookmarked criterion value must contain userId at index 0.'); | ||
| } | ||
| $value = $value[0]; | ||
| } | ||
|
|
||
| return $queryBuilder->expr()->eq( | ||
| 'bookmark.user_id', | ||
| $queryBuilder->createNamedParameter( | ||
| (int)$value, | ||
| ParameterType::INTEGER | ||
| ) | ||
| ); | ||
|
Comment on lines
+39
to
+55
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Following the API design remark, This could be either: $userId = $criterion->getUserId() ?? $this->permissionResolver->getCurrentUserReference()->getUserId();or simply $userId = $this->permissionResolver->getCurrentUserReference()->getUserId();PermissionResolver should be injected via constructor. As for boolean value of is or is not bookmarked, I'd probably use ternary expression based on |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @copyright Copyright (C) Ibexa AS. All rights reserved. | ||
| * @license For full copyright and license information view LICENSE file distributed with this source code. | ||
| */ | ||
| declare(strict_types=1); | ||
|
|
||
| namespace Ibexa\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Bookmark; | ||
|
|
||
| use Ibexa\Contracts\Core\Persistence\Filter\Doctrine\FilteringQueryBuilder; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\BookmarkId; | ||
| use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause; | ||
| use Ibexa\Contracts\Core\Repository\Values\Filter\SortClauseQueryBuilder; | ||
|
|
||
| final class IdSortClauseQueryBuilder implements SortClauseQueryBuilder | ||
| { | ||
| public function accepts(FilteringSortClause $sortClause): bool | ||
| { | ||
| return $sortClause instanceof BookmarkId; | ||
| } | ||
|
|
||
| public function buildQuery( | ||
| FilteringQueryBuilder $queryBuilder, | ||
| FilteringSortClause $sortClause | ||
| ): void { | ||
| if (!$sortClause instanceof BookmarkId) { | ||
| throw new \InvalidArgumentException(sprintf( | ||
| 'Expected %s, got %s', | ||
| BookmarkId::class, | ||
| get_class($sortClause), | ||
| )); | ||
| } | ||
| /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\BookmarkId $sortClause */ | ||
| $queryBuilder->addSelect('bookmark.id'); | ||
| $queryBuilder->addOrderBy('bookmark.id', $sortClause->direction); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,33 +9,40 @@ | |
| namespace Ibexa\Core\Repository; | ||
|
|
||
| use Exception; | ||
| use Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark; | ||
| use Ibexa\Contracts\Core\Persistence\Bookmark\CreateStruct; | ||
| use Ibexa\Contracts\Core\Persistence\Bookmark\Handler as BookmarkHandler; | ||
| use Ibexa\Contracts\Core\Repository\BookmarkService as BookmarkServiceInterface; | ||
| use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; | ||
| use Ibexa\Contracts\Core\Repository\Repository as RepositoryInterface; | ||
| use Ibexa\Contracts\Core\Repository\Values\Bookmark\BookmarkList; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Location; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; | ||
| use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; | ||
| use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; | ||
| use Ibexa\Core\Base\Exceptions\InvalidArgumentException; | ||
| use Psr\Log\LoggerInterface; | ||
| use Psr\Log\NullLogger; | ||
|
|
||
| class BookmarkService implements BookmarkServiceInterface | ||
| { | ||
| /** @var \Ibexa\Contracts\Core\Repository\Repository */ | ||
| protected $repository; | ||
| protected RepositoryInterface $repository; | ||
|
|
||
| /** @var \Ibexa\Contracts\Core\Persistence\Bookmark\Handler */ | ||
| protected $bookmarkHandler; | ||
| protected BookmarkHandler $bookmarkHandler; | ||
|
|
||
| private LoggerInterface $logger; | ||
|
|
||
| /** | ||
| * BookmarkService constructor. | ||
| * | ||
| * @param \Ibexa\Contracts\Core\Repository\Repository $repository | ||
| * @param \Ibexa\Contracts\Core\Persistence\Bookmark\Handler $bookmarkHandler | ||
| */ | ||
| public function __construct(RepositoryInterface $repository, BookmarkHandler $bookmarkHandler) | ||
| public function __construct(RepositoryInterface $repository, BookmarkHandler $bookmarkHandler, LoggerInterface $logger = null) | ||
| { | ||
| $this->repository = $repository; | ||
| $this->bookmarkHandler = $bookmarkHandler; | ||
| $this->logger = $logger ?? new NullLogger(); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -97,16 +104,26 @@ public function loadBookmarks(int $offset = 0, int $limit = 25): BookmarkList | |
| { | ||
| $currentUserId = $this->getCurrentUserId(); | ||
|
|
||
| $list = new BookmarkList(); | ||
| $list->totalCount = $this->bookmarkHandler->countUserBookmarks($currentUserId); | ||
| if ($list->totalCount > 0) { | ||
| $bookmarks = $this->bookmarkHandler->loadUserBookmarks($currentUserId, $offset, $limit); | ||
|
|
||
| $list->items = array_map(function (Bookmark $bookmark) { | ||
| return $this->repository->getLocationService()->loadLocation($bookmark->locationId); | ||
| }, $bookmarks); | ||
| $filter = new Filter(); | ||
| try { | ||
| $filter | ||
| ->withCriterion(new Criterion\IsBookmarked($currentUserId)) | ||
| ->withSortClause(new SortClause\BookmarkId(Query::SORT_DESC)) | ||
| ->sliceBy($limit, $offset); | ||
|
|
||
| $result = $this->repository->getLocationService()->find($filter, []); | ||
| } catch (BadStateException $e) { | ||
| $this->logger->debug($e->getMessage(), [ | ||
| 'exception' => $e, | ||
| ]); | ||
|
|
||
| return new BookmarkList(); | ||
| } | ||
|
|
||
| $list = new BookmarkList(); | ||
| $list->totalCount = $result->totalCount; | ||
| $list->items = $result->locations; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| return $list; | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.