Completed
Push — EZP-31287 ( fe8c5c...af9523 )
by
unknown
34:01
created

LocationService::loadAllLocations()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 2
dl 0
loc 52
rs 9.0472
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Repository;
8
9
use eZ\Publish\API\Repository\PermissionCriterionResolver;
10
use eZ\Publish\API\Repository\Values\Content\Language;
11
use eZ\Publish\API\Repository\Values\Content\Location;
12
use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct;
13
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
14
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
15
use eZ\Publish\API\Repository\Values\Content\Location as APILocation;
16
use eZ\Publish\API\Repository\Values\Content\LocationList;
17
use eZ\Publish\API\Repository\Values\Content\VersionInfo;
18
use eZ\Publish\SPI\Persistence\Content\Location as SPILocation;
19
use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct;
20
use eZ\Publish\API\Repository\LocationService as LocationServiceInterface;
21
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
22
use eZ\Publish\SPI\Persistence\Handler;
23
use eZ\Publish\API\Repository\Values\Content\Query;
24
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
25
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
26
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd;
27
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot as CriterionLogicalNot;
28
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree as CriterionSubtree;
29
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
30
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
31
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
32
use eZ\Publish\Core\Base\Exceptions\BadStateException;
33
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
34
use Exception;
35
use Psr\Log\LoggerInterface;
36
use Psr\Log\NullLogger;
37
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
38
39
/**
40
 * Location service, used for complex subtree operations.
41
 *
42
 * @example Examples/location.php
43
 */
44
class LocationService implements LocationServiceInterface
45
{
46
    /** @var \eZ\Publish\Core\Repository\Repository */
47
    protected $repository;
48
49
    /** @var \eZ\Publish\SPI\Persistence\Handler */
50
    protected $persistenceHandler;
51
52
    /** @var array */
53
    protected $settings;
54
55
    /** @var \eZ\Publish\Core\Repository\Helper\DomainMapper */
56
    protected $domainMapper;
57
58
    /** @var \eZ\Publish\Core\Repository\Helper\NameSchemaService */
59
    protected $nameSchemaService;
60
61
    /** @var \eZ\Publish\API\Repository\PermissionCriterionResolver */
62
    protected $permissionCriterionResolver;
63
64
    /** @var \Psr\Log\LoggerInterface */
65
    private $logger;
66
67
    /**
68
     * Setups service with reference to repository object that created it & corresponding handler.
69
     *
70
     * @param \eZ\Publish\API\Repository\Repository $repository
71
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
72
     * @param \eZ\Publish\Core\Repository\Helper\DomainMapper $domainMapper
73
     * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService
74
     * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver
75
     * @param array $settings
76
     * @param \Psr\Log\LoggerInterface|null $logger
77
     */
78
    public function __construct(
79
        RepositoryInterface $repository,
80
        Handler $handler,
81
        Helper\DomainMapper $domainMapper,
82
        Helper\NameSchemaService $nameSchemaService,
83
        PermissionCriterionResolver $permissionCriterionResolver,
84
        array $settings = [],
85
        LoggerInterface $logger = null
86
    ) {
87
        $this->repository = $repository;
0 ignored issues
show
Documentation Bug introduced by
$repository is of type object<eZ\Publish\API\Repository\Repository>, but the property $repository was declared to be of type object<eZ\Publish\Core\Repository\Repository>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
88
        $this->persistenceHandler = $handler;
89
        $this->domainMapper = $domainMapper;
90
        $this->nameSchemaService = $nameSchemaService;
91
        // Union makes sure default settings are ignored if provided in argument
92
        $this->settings = $settings + [
93
            //'defaultSetting' => array(),
94
        ];
95
        $this->permissionCriterionResolver = $permissionCriterionResolver;
96
        $this->logger = null !== $logger ? $logger : new NullLogger();
97
    }
98
99
    /**
100
     * Copies the subtree starting from $subtree as a new subtree of $targetLocation.
101
     *
102
     * Only the items on which the user has read access are copied.
103
     *
104
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed copy the subtree to the given parent location
105
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user does not have read access to the whole source subtree
106
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the target location is a sub location of the given location
107
     *
108
     * @param \eZ\Publish\API\Repository\Values\Content\Location $subtree - the subtree denoted by the location to copy
109
     * @param \eZ\Publish\API\Repository\Values\Content\Location $targetParentLocation - the target parent location for the copy operation
110
     *
111
     * @return \eZ\Publish\API\Repository\Values\Content\Location The newly created location of the copied subtree
112
     */
113
    public function copySubtree(APILocation $subtree, APILocation $targetParentLocation)
114
    {
115
        $loadedSubtree = $this->loadLocation($subtree->id);
116
        $loadedTargetLocation = $this->loadLocation($targetParentLocation->id);
117
118
        if (stripos($loadedTargetLocation->pathString, $loadedSubtree->pathString) !== false) {
119
            throw new InvalidArgumentException('targetParentLocation', 'target parent location is a sub location of the given subtree');
120
        }
121
122
        // check create permission on target
123
        if (!$this->repository->canUser('content', 'create', $loadedSubtree->getContentInfo(), $loadedTargetLocation)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
124
            throw new UnauthorizedException('content', 'create', ['locationId' => $loadedTargetLocation->id]);
125
        }
126
127
        // Check read access to whole source subtree
128
        $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'read');
129 View Code Duplication
        if ($contentReadCriterion === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130
            throw new UnauthorizedException('content', 'read');
131
        } elseif ($contentReadCriterion !== true) {
132
            // Query if there are any content in subtree current user don't have access to
133
            $query = new Query(
134
                [
135
                    'limit' => 0,
136
                    'filter' => new CriterionLogicalAnd(
137
                        [
138
                            new CriterionSubtree($loadedSubtree->pathString),
139
                            new CriterionLogicalNot($contentReadCriterion),
0 ignored issues
show
Bug introduced by
It seems like $contentReadCriterion defined by $this->permissionCriteri...rion('content', 'read') on line 128 can also be of type boolean; however, eZ\Publish\API\Repositor...gicalNot::__construct() does only seem to accept object<eZ\Publish\API\Re...ontent\Query\Criterion>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
140
                        ]
141
                    ),
142
                ]
143
            );
144
            $result = $this->repository->getSearchService()->findContent($query, [], false);
145
            if ($result->totalCount > 0) {
146
                throw new UnauthorizedException('content', 'read');
147
            }
148
        }
149
150
        $this->repository->beginTransaction();
151
        try {
152
            $newLocation = $this->persistenceHandler->locationHandler()->copySubtree(
153
                $loadedSubtree->id,
154
                $loadedTargetLocation->id,
155
                $this->repository->getPermissionResolver()->getCurrentUserReference()->getUserId()
156
            );
157
158
            $content = $this->repository->getContentService()->loadContent($newLocation->contentId);
159
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
160 View Code Duplication
            foreach ($urlAliasNames as $languageCode => $name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
161
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
162
                    $newLocation->id,
163
                    $loadedTargetLocation->id,
164
                    $name,
165
                    $languageCode,
166
                    $content->contentInfo->alwaysAvailable
167
                );
168
            }
169
170
            $this->persistenceHandler->urlAliasHandler()->locationCopied(
171
                $loadedSubtree->id,
172
                $newLocation->id,
173
                $loadedTargetLocation->id
174
            );
175
176
            $this->repository->commit();
177
        } catch (Exception $e) {
178
            $this->repository->rollback();
179
            throw $e;
180
        }
181
182
        return $this->domainMapper->buildLocationWithContent($newLocation, $content);
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function loadLocation($locationId, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null)
189
    {
190
        $spiLocation = $this->persistenceHandler->locationHandler()->load($locationId, $prioritizedLanguages, $useAlwaysAvailable ?? true);
0 ignored issues
show
Bug introduced by
It seems like $prioritizedLanguages defined by parameter $prioritizedLanguages on line 188 can also be of type array; however, eZ\Publish\SPI\Persisten...ocation\Handler::load() does only seem to accept null|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
191
        $location = $this->domainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true);
192
        if (!$this->repository->canUser('content', 'read', $location->getContentInfo(), $location)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
193
            throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]);
194
        }
195
196
        return $location;
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202
    public function loadLocationList(array $locationIds, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null): iterable
203
    {
204
        $spiLocations = $this->persistenceHandler->locationHandler()->loadList(
205
            $locationIds,
206
            $prioritizedLanguages,
0 ignored issues
show
Bug introduced by
It seems like $prioritizedLanguages defined by parameter $prioritizedLanguages on line 202 can also be of type array; however, eZ\Publish\SPI\Persisten...ion\Handler::loadList() does only seem to accept null|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
207
            $useAlwaysAvailable ?? true
208
        );
209
        if (empty($spiLocations)) {
210
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type declared by the interface eZ\Publish\API\Repositor...rvice::loadLocationList of type eZ\Publish\API\Repositor...API\Repository\iterable.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
211
        }
212
213
        // Get content id's
214
        $contentIds = [];
215
        foreach ($spiLocations as $spiLocation) {
216
            $contentIds[] = $spiLocation->contentId;
217
        }
218
219
        // Load content info and Get content proxy
220
        $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds);
221
        $contentProxyList = $this->domainMapper->buildContentProxyList(
222
            $spiContentInfoList,
223
            $prioritizedLanguages ?? [],
224
            $useAlwaysAvailable ?? true
225
        );
226
227
        // Build locations using the bulk retrieved content info and bulk lazy loaded content proxies.
228
        $locations = [];
229
        $permissionResolver = $this->repository->getPermissionResolver();
230
        foreach ($spiLocations as $spiLocation) {
231
            $location = $this->domainMapper->buildLocationWithContent(
232
                $spiLocation,
233
                $contentProxyList[$spiLocation->contentId] ?? null,
234
                $spiContentInfoList[$spiLocation->contentId] ?? null
235
            );
236
237
            if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) {
238
                $locations[$spiLocation->id] = $location;
239
            }
240
        }
241
242
        return $locations;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $locations; (array) is incompatible with the return type declared by the interface eZ\Publish\API\Repositor...rvice::loadLocationList of type eZ\Publish\API\Repositor...API\Repository\iterable.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
243
    }
244
245
    /**
246
     * {@inheritdoc}
247
     */
248
    public function loadLocationByRemoteId($remoteId, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null)
249
    {
250
        if (!is_string($remoteId)) {
251
            throw new InvalidArgumentValue('remoteId', $remoteId);
252
        }
253
254
        $spiLocation = $this->persistenceHandler->locationHandler()->loadByRemoteId($remoteId, $prioritizedLanguages, $useAlwaysAvailable ?? true);
0 ignored issues
show
Bug introduced by
It seems like $prioritizedLanguages defined by parameter $prioritizedLanguages on line 248 can also be of type array; however, eZ\Publish\SPI\Persisten...ndler::loadByRemoteId() does only seem to accept null|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
255
        $location = $this->domainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true);
256
        if (!$this->repository->canUser('content', 'read', $location->getContentInfo(), $location)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
257
            throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]);
258
        }
259
260
        return $location;
261
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266
    public function loadLocations(ContentInfo $contentInfo, APILocation $rootLocation = null, array $prioritizedLanguages = null)
267
    {
268
        if (!$contentInfo->published) {
269
            throw new BadStateException('$contentInfo', 'ContentInfo has no published versions');
270
        }
271
272
        $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent(
273
            $contentInfo->id,
274
            $rootLocation !== null ? $rootLocation->id : null
275
        );
276
277
        $locations = [];
278
        $spiInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($contentInfo->id);
279
        $content = $this->domainMapper->buildContentProxy($spiInfo, $prioritizedLanguages ?: []);
280
        foreach ($spiLocations as $spiLocation) {
281
            $location = $this->domainMapper->buildLocationWithContent($spiLocation, $content, $spiInfo);
282
            if ($this->repository->canUser('content', 'read', $location->getContentInfo(), $location)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
283
                $locations[] = $location;
284
            }
285
        }
286
287
        return $locations;
288
    }
289
290
    /**
291
     * {@inheritdoc}
292
     */
293
    public function loadLocationChildren(APILocation $location, $offset = 0, $limit = 25, array $prioritizedLanguages = null)
294
    {
295
        if (!$this->domainMapper->isValidLocationSortField($location->sortField)) {
296
            throw new InvalidArgumentValue('sortField', $location->sortField, 'Location');
297
        }
298
299
        if (!$this->domainMapper->isValidLocationSortOrder($location->sortOrder)) {
300
            throw new InvalidArgumentValue('sortOrder', $location->sortOrder, 'Location');
301
        }
302
303
        if (!is_int($offset)) {
304
            throw new InvalidArgumentValue('offset', $offset);
305
        }
306
307
        if (!is_int($limit)) {
308
            throw new InvalidArgumentValue('limit', $limit);
309
        }
310
311
        $childLocations = [];
312
        $searchResult = $this->searchChildrenLocations($location, $offset, $limit, $prioritizedLanguages ?: []);
313
        foreach ($searchResult->searchHits as $searchHit) {
314
            $childLocations[] = $searchHit->valueObject;
315
        }
316
317
        return new LocationList(
318
            [
319
                'locations' => $childLocations,
320
                'totalCount' => $searchResult->totalCount,
321
            ]
322
        );
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328
    public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, array $prioritizedLanguages = null)
329
    {
330
        if (!$versionInfo->isDraft()) {
331
            throw new BadStateException(
332
                '$contentInfo',
333
                sprintf(
334
                    'Content [%d] %s has been already published. Use LocationService::loadLocations instead.',
335
                    $versionInfo->contentInfo->id,
336
                    $versionInfo->contentInfo->name
337
                )
338
            );
339
        }
340
341
        $spiLocations = $this->persistenceHandler
342
            ->locationHandler()
343
            ->loadParentLocationsForDraftContent($versionInfo->contentInfo->id);
344
345
        $contentIds = [];
346
        foreach ($spiLocations as $spiLocation) {
347
            $contentIds[] = $spiLocation->contentId;
348
        }
349
350
        $locations = [];
351
        $permissionResolver = $this->repository->getPermissionResolver();
352
        $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds);
353
        $contentList = $this->domainMapper->buildContentProxyList($spiContentInfoList, $prioritizedLanguages ?: []);
354
        foreach ($spiLocations as $spiLocation) {
355
            $location = $this->domainMapper->buildLocationWithContent(
356
                $spiLocation,
357
                $contentList[$spiLocation->contentId],
358
                $spiContentInfoList[$spiLocation->contentId]
359
            );
360
361
            if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) {
362
                $locations[] = $location;
363
            }
364
        }
365
366
        return $locations;
367
    }
368
369
    /**
370
     * Returns the number of children which are readable by the current user of a location object.
371
     *
372
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
373
     *
374
     * @return int
375
     */
376
    public function getLocationChildCount(APILocation $location)
377
    {
378
        $searchResult = $this->searchChildrenLocations($location, 0, 0);
379
380
        return $searchResult->totalCount;
381
    }
382
383
    /**
384
     * Searches children locations of the provided parent location id.
385
     *
386
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
387
     * @param int $offset
388
     * @param int $limit
389
     *
390
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
391
     */
392
    protected function searchChildrenLocations(APILocation $location, $offset = 0, $limit = -1, array $prioritizedLanguages = null)
393
    {
394
        $query = new LocationQuery([
395
            'filter' => new Criterion\ParentLocationId($location->id),
396
            'offset' => $offset >= 0 ? (int)$offset : 0,
397
            'limit' => $limit >= 0 ? (int)$limit : null,
398
            'sortClauses' => $location->getSortClauses(),
399
        ]);
400
401
        return $this->repository->getSearchService()->findLocations($query, ['languages' => $prioritizedLanguages]);
402
    }
403
404
    /**
405
     * Creates the new $location in the content repository for the given content.
406
     *
407
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create this location
408
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the content is already below the specified parent
409
     *                                        or the parent is a sub location of the location of the content
410
     *                                        or if set the remoteId exists already
411
     *
412
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
413
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $locationCreateStruct
414
     *
415
     * @return \eZ\Publish\API\Repository\Values\Content\Location the newly created Location
416
     */
417
    public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct)
418
    {
419
        $content = $this->domainMapper->buildContentDomainObjectFromPersistence(
420
            $this->persistenceHandler->contentHandler()->load($contentInfo->id),
421
            $this->persistenceHandler->contentTypeHandler()->load($contentInfo->contentTypeId)
422
        );
423
424
        $parentLocation = $this->domainMapper->buildLocation(
425
            $this->persistenceHandler->locationHandler()->load($locationCreateStruct->parentLocationId)
426
        );
427
428
        $contentType = $content->getContentType();
429
430
        $locationCreateStruct->sortField = $locationCreateStruct->sortField
431
            ?? ($contentType->defaultSortField ?? Location::SORT_FIELD_NAME);
432
        $locationCreateStruct->sortOrder = $locationCreateStruct->sortOrder
433
            ?? ($contentType->defaultSortOrder ?? Location::SORT_ORDER_ASC);
434
435
        $contentInfo = $content->contentInfo;
436
437
        if (!$this->repository->canUser('content', 'manage_locations', $contentInfo, $parentLocation)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
438
            throw new UnauthorizedException('content', 'manage_locations', ['contentId' => $contentInfo->id]);
439
        }
440
441
        if (!$this->repository->canUser('content', 'create', $contentInfo, $parentLocation)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
442
            throw new UnauthorizedException('content', 'create', ['locationId' => $parentLocation->id]);
443
        }
444
445
        // Check if the parent is a sub location of one of the existing content locations (this also solves the
446
        // situation where parent location actually one of the content locations),
447
        // or if the content already has location below given location create struct parent
448
        $existingContentLocations = $this->loadLocations($contentInfo);
449
        if (!empty($existingContentLocations)) {
450
            foreach ($existingContentLocations as $existingContentLocation) {
451
                if (stripos($parentLocation->pathString, $existingContentLocation->pathString) !== false) {
452
                    throw new InvalidArgumentException(
453
                        '$locationCreateStruct',
454
                        'Specified parent is a sub location of one of the existing content locations.'
455
                    );
456
                }
457
                if ($parentLocation->id == $existingContentLocation->parentLocationId) {
458
                    throw new InvalidArgumentException(
459
                        '$locationCreateStruct',
460
                        'Content is already below the specified parent.'
461
                    );
462
                }
463
            }
464
        }
465
466
        $spiLocationCreateStruct = $this->domainMapper->buildSPILocationCreateStruct(
467
            $locationCreateStruct,
468
            $parentLocation,
469
            $contentInfo->mainLocationId ?? true,
470
            $contentInfo->id,
471
            $contentInfo->currentVersionNo
472
        );
473
474
        $this->repository->beginTransaction();
475
        try {
476
            $newLocation = $this->persistenceHandler->locationHandler()->create($spiLocationCreateStruct);
477
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
478
            foreach ($urlAliasNames as $languageCode => $name) {
479
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
480
                    $newLocation->id,
481
                    $newLocation->parentId,
482
                    $name,
483
                    $languageCode,
484
                    $contentInfo->alwaysAvailable,
485
                    // @todo: this is legacy storage specific for updating ezcontentobject_tree.path_identification_string, to be removed
486
                    $languageCode === $contentInfo->mainLanguageCode
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $languageCode (integer) and $contentInfo->mainLanguageCode (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
487
                );
488
            }
489
490
            $this->repository->commit();
491
        } catch (Exception $e) {
492
            $this->repository->rollback();
493
            throw $e;
494
        }
495
496
        return $this->domainMapper->buildLocationWithContent($newLocation, $content);
497
    }
498
499
    /**
500
     * Updates $location in the content repository.
501
     *
502
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to update this location
503
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException   if if set the remoteId exists already
504
     *
505
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
506
     * @param \eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct $locationUpdateStruct
507
     *
508
     * @return \eZ\Publish\API\Repository\Values\Content\Location the updated Location
509
     */
510
    public function updateLocation(APILocation $location, LocationUpdateStruct $locationUpdateStruct)
511
    {
512
        if (!$this->domainMapper->isValidLocationPriority($locationUpdateStruct->priority)) {
513
            throw new InvalidArgumentValue('priority', $locationUpdateStruct->priority, 'LocationUpdateStruct');
514
        }
515
516
        if ($locationUpdateStruct->remoteId !== null && (!is_string($locationUpdateStruct->remoteId) || empty($locationUpdateStruct->remoteId))) {
517
            throw new InvalidArgumentValue('remoteId', $locationUpdateStruct->remoteId, 'LocationUpdateStruct');
518
        }
519
520
        if ($locationUpdateStruct->sortField !== null && !$this->domainMapper->isValidLocationSortField($locationUpdateStruct->sortField)) {
521
            throw new InvalidArgumentValue('sortField', $locationUpdateStruct->sortField, 'LocationUpdateStruct');
522
        }
523
524
        if ($locationUpdateStruct->sortOrder !== null && !$this->domainMapper->isValidLocationSortOrder($locationUpdateStruct->sortOrder)) {
525
            throw new InvalidArgumentValue('sortOrder', $locationUpdateStruct->sortOrder, 'LocationUpdateStruct');
526
        }
527
528
        $loadedLocation = $this->loadLocation($location->id);
529
530
        if ($locationUpdateStruct->remoteId !== null) {
531
            try {
532
                $existingLocation = $this->loadLocationByRemoteId($locationUpdateStruct->remoteId);
533
                if ($existingLocation !== null && $existingLocation->id !== $loadedLocation->id) {
534
                    throw new InvalidArgumentException('locationUpdateStruct', 'location with provided remote ID already exists');
535
                }
536
            } catch (APINotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
537
            }
538
        }
539
540
        if (!$this->repository->canUser('content', 'edit', $loadedLocation->getContentInfo(), $loadedLocation)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
541
            throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation->id]);
542
        }
543
544
        $updateStruct = new UpdateStruct();
545
        $updateStruct->priority = $locationUpdateStruct->priority !== null ? $locationUpdateStruct->priority : $loadedLocation->priority;
546
        $updateStruct->remoteId = $locationUpdateStruct->remoteId !== null ? trim($locationUpdateStruct->remoteId) : $loadedLocation->remoteId;
547
        $updateStruct->sortField = $locationUpdateStruct->sortField !== null ? $locationUpdateStruct->sortField : $loadedLocation->sortField;
548
        $updateStruct->sortOrder = $locationUpdateStruct->sortOrder !== null ? $locationUpdateStruct->sortOrder : $loadedLocation->sortOrder;
549
550
        $this->repository->beginTransaction();
551
        try {
552
            $this->persistenceHandler->locationHandler()->update($updateStruct, $loadedLocation->id);
553
            $this->repository->commit();
554
        } catch (Exception $e) {
555
            $this->repository->rollback();
556
            throw $e;
557
        }
558
559
        return $this->loadLocation($loadedLocation->id);
560
    }
561
562
    /**
563
     * Swaps the contents held by $location1 and $location2.
564
     *
565
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to swap content
566
     *
567
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location1
568
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location2
569
     */
570
    public function swapLocation(APILocation $location1, APILocation $location2)
571
    {
572
        $loadedLocation1 = $this->loadLocation($location1->id);
573
        $loadedLocation2 = $this->loadLocation($location2->id);
574
575
        if (!$this->repository->canUser('content', 'edit', $loadedLocation1->getContentInfo(), $loadedLocation1)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
576
            throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation1->id]);
577
        }
578
        if (!$this->repository->canUser('content', 'edit', $loadedLocation2->getContentInfo(), $loadedLocation2)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
579
            throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation2->id]);
580
        }
581
582
        $this->repository->beginTransaction();
583
        try {
584
            $this->persistenceHandler->locationHandler()->swap($loadedLocation1->id, $loadedLocation2->id);
585
            $this->persistenceHandler->urlAliasHandler()->locationSwapped(
586
                $location1->id,
587
                $location1->parentLocationId,
588
                $location2->id,
589
                $location2->parentLocationId
590
            );
591
            $this->persistenceHandler->bookmarkHandler()->locationSwapped($loadedLocation1->id, $loadedLocation2->id);
592
            $this->repository->commit();
593
        } catch (Exception $e) {
594
            $this->repository->rollback();
595
            throw $e;
596
        }
597
    }
598
599
    /**
600
     * Hides the $location and marks invisible all descendants of $location.
601
     *
602
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to hide this location
603
     *
604
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
605
     *
606
     * @return \eZ\Publish\API\Repository\Values\Content\Location $location, with updated hidden value
607
     */
608 View Code Duplication
    public function hideLocation(APILocation $location)
609
    {
610
        if (!$this->repository->canUser('content', 'hide', $location->getContentInfo(), $location)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
611
            throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]);
612
        }
613
614
        $this->repository->beginTransaction();
615
        try {
616
            $this->persistenceHandler->locationHandler()->hide($location->id);
617
            $this->repository->commit();
618
        } catch (Exception $e) {
619
            $this->repository->rollback();
620
            throw $e;
621
        }
622
623
        return $this->loadLocation($location->id);
624
    }
625
626
    /**
627
     * Unhides the $location.
628
     *
629
     * This method and marks visible all descendants of $locations
630
     * until a hidden location is found.
631
     *
632
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to unhide this location
633
     *
634
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
635
     *
636
     * @return \eZ\Publish\API\Repository\Values\Content\Location $location, with updated hidden value
637
     */
638 View Code Duplication
    public function unhideLocation(APILocation $location)
639
    {
640
        if (!$this->repository->canUser('content', 'hide', $location->getContentInfo(), $location)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
641
            throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]);
642
        }
643
644
        $this->repository->beginTransaction();
645
        try {
646
            $this->persistenceHandler->locationHandler()->unHide($location->id);
647
            $this->repository->commit();
648
        } catch (Exception $e) {
649
            $this->repository->rollback();
650
            throw $e;
651
        }
652
653
        return $this->loadLocation($location->id);
654
    }
655
656
    /**
657
     * {@inheritdoc}
658
     */
659
    public function moveSubtree(APILocation $location, APILocation $newParentLocation)
660
    {
661
        $location = $this->loadLocation($location->id);
662
        $newParentLocation = $this->loadLocation($newParentLocation->id);
663
664
        if ($newParentLocation->id === $location->parentLocationId) {
665
            throw new InvalidArgumentException(
666
                '$newParentLocation', 'new parent Location is the same as the current one'
667
            );
668
        }
669
        if (strpos($newParentLocation->pathString, $location->pathString) === 0) {
670
            throw new InvalidArgumentException(
671
                '$newParentLocation',
672
                'new parent Location is in a subtree of the given $location'
673
            );
674
        }
675
        if (!$newParentLocation->getContent()->getContentType()->isContainer) {
676
            throw new InvalidArgumentException(
677
                '$newParentLocation',
678
                'Cannot move Location to a parent that is not a container'
679
            );
680
        }
681
682
        // check create permission on target location
683
        if (!$this->repository->canUser('content', 'create', $location->getContentInfo(), $newParentLocation)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
684
            throw new UnauthorizedException('content', 'create', ['locationId' => $newParentLocation->id]);
685
        }
686
687
        /** Check read access to whole source subtree.
688
         * @var bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion
689
         */
690
        $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'read');
691 View Code Duplication
        if ($contentReadCriterion === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
692
            throw new UnauthorizedException('content', 'read');
693
        } elseif ($contentReadCriterion !== true) {
694
            // Query if there are any content in subtree current user don't have access to
695
            $query = new Query(
696
                [
697
                    'limit' => 0,
698
                    'filter' => new CriterionLogicalAnd(
699
                        [
700
                            new CriterionSubtree($location->pathString),
701
                            new CriterionLogicalNot($contentReadCriterion),
0 ignored issues
show
Bug introduced by
It seems like $contentReadCriterion defined by $this->permissionCriteri...rion('content', 'read') on line 690 can also be of type boolean; however, eZ\Publish\API\Repositor...gicalNot::__construct() does only seem to accept object<eZ\Publish\API\Re...ontent\Query\Criterion>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
702
                        ]
703
                    ),
704
                ]
705
            );
706
            $result = $this->repository->getSearchService()->findContent($query, [], false);
707
            if ($result->totalCount > 0) {
708
                throw new UnauthorizedException('content', 'read');
709
            }
710
        }
711
712
        $this->repository->beginTransaction();
713
        try {
714
            $this->persistenceHandler->locationHandler()->move($location->id, $newParentLocation->id);
715
716
            $content = $this->repository->getContentService()->loadContent($location->contentId);
717
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
718 View Code Duplication
            foreach ($urlAliasNames as $languageCode => $name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
719
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
720
                    $location->id,
721
                    $newParentLocation->id,
722
                    $name,
723
                    $languageCode,
724
                    $content->contentInfo->alwaysAvailable
725
                );
726
            }
727
728
            $this->persistenceHandler->urlAliasHandler()->locationMoved(
729
                $location->id,
730
                $location->parentLocationId,
731
                $newParentLocation->id
732
            );
733
734
            $this->repository->commit();
735
        } catch (Exception $e) {
736
            $this->repository->rollback();
737
            throw $e;
738
        }
739
    }
740
741
    /**
742
     * Deletes $location and all its descendants.
743
     *
744
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user is not allowed to delete this location or a descendant
745
     *
746
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
747
     */
748
    public function deleteLocation(APILocation $location)
749
    {
750
        $location = $this->loadLocation($location->id);
751
752
        if (!$this->repository->canUser('content', 'manage_locations', $location->getContentInfo())) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
753
            throw new UnauthorizedException('content', 'manage_locations', ['locationId' => $location->id]);
754
        }
755
        if (!$this->repository->canUser('content', 'remove', $location->getContentInfo(), $location)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
756
            throw new UnauthorizedException('content', 'remove', ['locationId' => $location->id]);
757
        }
758
759
        // Check remove access to descendants
760
        $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove');
761 View Code Duplication
        if ($contentReadCriterion === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
762
            throw new UnauthorizedException('content', 'remove');
763
        } elseif ($contentReadCriterion !== true) {
764
            // Query if there are any content in subtree current user don't have access to
765
            $query = new Query(
766
                [
767
                    'limit' => 0,
768
                    'filter' => new CriterionLogicalAnd(
769
                        [
770
                            new CriterionSubtree($location->pathString),
771
                            new CriterionLogicalNot($contentReadCriterion),
0 ignored issues
show
Bug introduced by
It seems like $contentReadCriterion defined by $this->permissionCriteri...on('content', 'remove') on line 760 can also be of type boolean; however, eZ\Publish\API\Repositor...gicalNot::__construct() does only seem to accept object<eZ\Publish\API\Re...ontent\Query\Criterion>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
772
                        ]
773
                    ),
774
                ]
775
            );
776
            $result = $this->repository->getSearchService()->findContent($query, [], false);
777
            if ($result->totalCount > 0) {
778
                throw new UnauthorizedException('content', 'remove');
779
            }
780
        }
781
782
        $this->repository->beginTransaction();
783
        try {
784
            $this->persistenceHandler->locationHandler()->removeSubtree($location->id);
785
            $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id);
786
            $this->repository->commit();
787
        } catch (Exception $e) {
788
            $this->repository->rollback();
789
            throw $e;
790
        }
791
    }
792
793
    /**
794
     * Instantiates a new location create class.
795
     *
796
     * @param mixed $parentLocationId the parent under which the new location should be created
797
     * @param eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType
798
     *
799
     * @return \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct
800
     */
801 View Code Duplication
    public function newLocationCreateStruct($parentLocationId, ContentType $contentType = null)
802
    {
803
        $properties = [
804
            'parentLocationId' => $parentLocationId,
805
        ];
806
        if ($contentType) {
807
            $properties['sortField'] = $contentType->defaultSortField;
808
            $properties['sortOrder'] = $contentType->defaultSortOrder;
809
        }
810
811
        return new LocationCreateStruct($properties);
812
    }
813
814
    /**
815
     * Instantiates a new location update class.
816
     *
817
     * @return \eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct
818
     */
819
    public function newLocationUpdateStruct()
820
    {
821
        return new LocationUpdateStruct();
822
    }
823
824
    /**
825
     * Get the total number of all existing Locations. Can be combined with loadAllLocations.
826
     *
827
     * @see loadAllLocations
828
     *
829
     * @return int Total number of Locations
830
     */
831
    public function getAllLocationsCount(): int
832
    {
833
        return $this->persistenceHandler->locationHandler()->countAllLocations();
834
    }
835
836
    /**
837
     * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results.
838
     *
839
     * @param int $offset
840
     * @param int $limit
841
     *
842
     * @return \eZ\Publish\API\Repository\Values\Content\Location[]
843
     *
844
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
845
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
846
     */
847
    public function loadAllLocations(int $offset = 0, int $limit = 25): array
848
    {
849
        $spiLocations = $this->persistenceHandler->locationHandler()->loadAllLocations(
850
            $offset,
851
            $limit
852
        );
853
        $contentIds = array_unique(
854
            array_map(
855
                function (SPILocation $spiLocation) {
856
                    return $spiLocation->contentId;
857
                },
858
                $spiLocations
859
            )
860
        );
861
862
        $permissionResolver = $this->repository->getPermissionResolver();
863
        $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList(
864
            $contentIds
865
        );
866
        $contentList = $this->domainMapper->buildContentProxyList(
867
            $spiContentInfoList,
868
            Language::ALL,
869
            false
870
        );
871
        $locations = [];
872
        foreach ($spiLocations as $spiLocation) {
873
            if (!isset($spiContentInfoList[$spiLocation->contentId], $contentList[$spiLocation->contentId])) {
874
                $this->logger->warning(
875
                    sprintf(
876
                        'Location %d has missing Content %d',
877
                        $spiLocation->id,
878
                        $spiLocation->contentId
879
                    )
880
                );
881
                continue;
882
            }
883
884
            $location = $this->domainMapper->buildLocationWithContent(
885
                $spiLocation,
886
                $contentList[$spiLocation->contentId],
887
                $spiContentInfoList[$spiLocation->contentId]
888
            );
889
890
            $contentInfo = $location->getContentInfo();
891
            if (!$permissionResolver->canUser('content', 'read', $contentInfo, [$location])) {
892
                continue;
893
            }
894
            $locations[] = $location;
895
        }
896
897
        return $locations;
898
    }
899
}
900