Completed
Push — location_multi_load ( e5e305 )
by André
32:49 queued 12:49
created

LocationService::loadLocationList()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 42

Duplication

Lines 11
Ratio 26.19 %

Importance

Changes 0
Metric Value
dl 11
loc 42
c 0
b 0
f 0
cc 5
nc 7
nop 3
rs 8.9368
1
<?php
2
3
/**
4
 * File containing the eZ\Publish\Core\Repository\LocationService class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Repository;
10
11
use eZ\Publish\API\Repository\PermissionCriterionResolver;
12
use eZ\Publish\API\Repository\Values\Content\Language;
13
use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct;
14
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
15
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
16
use eZ\Publish\API\Repository\Values\Content\Location as APILocation;
17
use eZ\Publish\API\Repository\Values\Content\LocationList;
18
use eZ\Publish\API\Repository\Values\Content\VersionInfo;
19
use eZ\Publish\SPI\Persistence\Content\Location as SPILocation;
20
use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct;
21
use eZ\Publish\API\Repository\LocationService as LocationServiceInterface;
22
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
23
use eZ\Publish\SPI\Persistence\Handler;
24
use eZ\Publish\API\Repository\Values\Content\Query;
25
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
26
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
27
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd;
28
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot as CriterionLogicalNot;
29
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree as CriterionSubtree;
30
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
31
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
32
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
33
use eZ\Publish\Core\Base\Exceptions\BadStateException;
34
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
35
use Exception;
36
use Psr\Log\LoggerInterface;
37
use Psr\Log\NullLogger;
38
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
39
40
/**
41
 * Location service, used for complex subtree operations.
42
 *
43
 * @example Examples/location.php
44
 */
45
class LocationService implements LocationServiceInterface
46
{
47
    /**
48
     * @var \eZ\Publish\Core\Repository\Repository
49
     */
50
    protected $repository;
51
52
    /**
53
     * @var \eZ\Publish\SPI\Persistence\Handler
54
     */
55
    protected $persistenceHandler;
56
57
    /**
58
     * @var array
59
     */
60
    protected $settings;
61
62
    /**
63
     * @var \eZ\Publish\Core\Repository\Helper\DomainMapper
64
     */
65
    protected $domainMapper;
66
67
    /**
68
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
69
     */
70
    protected $nameSchemaService;
71
72
    /**
73
     * @var \eZ\Publish\API\Repository\PermissionCriterionResolver
74
     */
75
    protected $permissionCriterionResolver;
76
77
    /**
78
     * @var \Psr\Log\LoggerInterface
79
     */
80
    private $logger;
81
82
    /**
83
     * Setups service with reference to repository object that created it & corresponding handler.
84
     *
85
     * @param \eZ\Publish\API\Repository\Repository $repository
86
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
87
     * @param \eZ\Publish\Core\Repository\Helper\DomainMapper $domainMapper
88
     * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService
89
     * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver
90
     * @param array $settings
91
     * @param \Psr\Log\LoggerInterface|null $logger
92
     */
93
    public function __construct(
94
        RepositoryInterface $repository,
95
        Handler $handler,
96
        Helper\DomainMapper $domainMapper,
97
        Helper\NameSchemaService $nameSchemaService,
98
        PermissionCriterionResolver $permissionCriterionResolver,
99
        array $settings = array(),
100
        LoggerInterface $logger = null
101
    ) {
102
        $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...
103
        $this->persistenceHandler = $handler;
104
        $this->domainMapper = $domainMapper;
105
        $this->nameSchemaService = $nameSchemaService;
106
        // Union makes sure default settings are ignored if provided in argument
107
        $this->settings = $settings + array(
108
            //'defaultSetting' => array(),
109
        );
110
        $this->permissionCriterionResolver = $permissionCriterionResolver;
111
        $this->logger = null !== $logger ? $logger : new NullLogger();
112
    }
113
114
    /**
115
     * Copies the subtree starting from $subtree as a new subtree of $targetLocation.
116
     *
117
     * Only the items on which the user has read access are copied.
118
     *
119
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed copy the subtree to the given parent location
120
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user does not have read access to the whole source subtree
121
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the target location is a sub location of the given location
122
     *
123
     * @param \eZ\Publish\API\Repository\Values\Content\Location $subtree - the subtree denoted by the location to copy
124
     * @param \eZ\Publish\API\Repository\Values\Content\Location $targetParentLocation - the target parent location for the copy operation
125
     *
126
     * @return \eZ\Publish\API\Repository\Values\Content\Location The newly created location of the copied subtree
127
     */
128
    public function copySubtree(APILocation $subtree, APILocation $targetParentLocation)
129
    {
130
        $loadedSubtree = $this->loadLocation($subtree->id);
131
        $loadedTargetLocation = $this->loadLocation($targetParentLocation->id);
132
133
        if (stripos($loadedTargetLocation->pathString, $loadedSubtree->pathString) !== false) {
134
            throw new InvalidArgumentException('targetParentLocation', 'target parent location is a sub location of the given subtree');
135
        }
136
137
        // check create permission on target
138
        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...
139
            throw new UnauthorizedException('content', 'create');
140
        }
141
142
        /** Check read access to whole source subtree
143
         * @var bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion
144
         */
145
        $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion();
0 ignored issues
show
Bug introduced by
The call to getPermissionsCriterion() misses some required arguments starting with $module.
Loading history...
146 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...
147
            throw new UnauthorizedException('content', 'read');
148
        } elseif ($contentReadCriterion !== true) {
149
            // Query if there are any content in subtree current user don't have access to
150
            $query = new Query(
151
                array(
152
                    'limit' => 0,
153
                    'filter' => new CriterionLogicalAnd(
154
                        array(
155
                            new CriterionSubtree($loadedSubtree->pathString),
156
                            new CriterionLogicalNot($contentReadCriterion),
0 ignored issues
show
Bug introduced by
It seems like $contentReadCriterion defined by $this->permissionCriteri...tPermissionsCriterion() on line 145 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...
157
                        )
158
                    ),
159
                )
160
            );
161
            $result = $this->repository->getSearchService()->findContent($query, array(), false);
162
            if ($result->totalCount > 0) {
163
                throw new UnauthorizedException('content', 'read');
164
            }
165
        }
166
167
        $this->repository->beginTransaction();
168
        try {
169
            $newLocation = $this->persistenceHandler->locationHandler()->copySubtree(
170
                $loadedSubtree->id,
171
                $loadedTargetLocation->id,
172
                $this->repository->getPermissionResolver()->getCurrentUserReference()->getUserId()
173
            );
174
175
            $content = $this->repository->getContentService()->loadContent($newLocation->contentId);
176
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
177 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...
178
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
179
                    $newLocation->id,
180
                    $loadedTargetLocation->id,
181
                    $name,
182
                    $languageCode,
183
                    $content->contentInfo->alwaysAvailable
184
                );
185
            }
186
187
            $this->persistenceHandler->urlAliasHandler()->locationCopied(
188
                $loadedSubtree->id,
189
                $newLocation->id,
190
                $loadedTargetLocation->id
191
            );
192
193
            $this->repository->commit();
194
        } catch (Exception $e) {
195
            $this->repository->rollback();
196
            throw $e;
197
        }
198
199
        return $this->domainMapper->buildLocationWithContent($newLocation, $content);
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function loadLocation($locationId, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null)
206
    {
207
        $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 205 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...
208
        $location = $this->domainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true);
209
        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...
210
            throw new UnauthorizedException('content', 'read');
211
        }
212
213
        return $location;
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    public function loadLocationList(array $locationIds, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null): iterable
220
    {
221
        $spiLocations = $this->persistenceHandler->locationHandler()->loadList(
222
            $locationIds,
223
            $prioritizedLanguages,
0 ignored issues
show
Bug introduced by
It seems like $prioritizedLanguages defined by parameter $prioritizedLanguages on line 219 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...
224
            $useAlwaysAvailable ?? true
225
        );
226
        if (empty($spiLocations)) {
227
            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...
228
        }
229
230
        // Get content id's
231
        $contentIds = [];
232
        foreach ($spiLocations as $spiLocation) {
233
            $contentIds[] = $spiLocation->contentId;
234
        }
235
236
        // Load content info and Get content proxy
237
        $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds);
238
        $contentProxyList = $this->domainMapper->buildContentProxyList(
239
            $spiContentInfoList,
240
            $prioritizedLanguages ?? [],
241
            $useAlwaysAvailable ?? true
242
        );
243
244
        // Build locations using the bulk retrieved content info and bulk lazy loaded content proxies.
245
        $locations = [];
246
        $permissionResolver = $this->repository->getPermissionResolver();
247 View Code Duplication
        foreach ($spiLocations as $spiLocation) {
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...
248
            $location = $this->domainMapper->buildLocationWithContent(
249
                $spiLocation,
250
                $contentProxyList[$spiLocation->contentId],
251
                $spiContentInfoList[$spiLocation->contentId]
252
            );
253
254
            if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) {
255
                $locations[$spiLocation->id] = $location;
256
            }
257
        }
258
259
        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...
260
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265
    public function loadLocationByRemoteId($remoteId, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null)
266
    {
267
        if (!is_string($remoteId)) {
268
            throw new InvalidArgumentValue('remoteId', $remoteId);
269
        }
270
271
        $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 265 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...
272
        $location = $this->domainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true);
273
        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...
274
            throw new UnauthorizedException('content', 'read');
275
        }
276
277
        return $location;
278
    }
279
280
    /**
281
     * {@inheritdoc}
282
     */
283
    public function loadLocations(ContentInfo $contentInfo, APILocation $rootLocation = null, array $prioritizedLanguages = null)
284
    {
285
        if (!$contentInfo->published) {
286
            throw new BadStateException('$contentInfo', 'ContentInfo has no published versions');
287
        }
288
289
        $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent(
290
            $contentInfo->id,
291
            $rootLocation !== null ? $rootLocation->id : null
292
        );
293
294
        $locations = [];
295
        $spiInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($contentInfo->id);
296
        $content = $this->domainMapper->buildContentProxy($spiInfo, $prioritizedLanguages ?: []);
297
        foreach ($spiLocations as $spiLocation) {
298
            $location = $this->domainMapper->buildLocationWithContent($spiLocation, $content, $spiInfo);
299
            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...
300
                $locations[] = $location;
301
            }
302
        }
303
304
        return $locations;
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    public function loadLocationChildren(APILocation $location, $offset = 0, $limit = 25, array $prioritizedLanguages = null)
311
    {
312
        if (!$this->domainMapper->isValidLocationSortField($location->sortField)) {
313
            throw new InvalidArgumentValue('sortField', $location->sortField, 'Location');
314
        }
315
316
        if (!$this->domainMapper->isValidLocationSortOrder($location->sortOrder)) {
317
            throw new InvalidArgumentValue('sortOrder', $location->sortOrder, 'Location');
318
        }
319
320
        if (!is_int($offset)) {
321
            throw new InvalidArgumentValue('offset', $offset);
322
        }
323
324
        if (!is_int($limit)) {
325
            throw new InvalidArgumentValue('limit', $limit);
326
        }
327
328
        $childLocations = array();
329
        $searchResult = $this->searchChildrenLocations($location, $offset, $limit, $prioritizedLanguages ?: []);
330
        foreach ($searchResult->searchHits as $searchHit) {
331
            $childLocations[] = $searchHit->valueObject;
332
        }
333
334
        return new LocationList(
335
            array(
336
                'locations' => $childLocations,
337
                'totalCount' => $searchResult->totalCount,
338
            )
339
        );
340
    }
341
342
    /**
343
     * {@inheritdoc}
344
     */
345
    public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, array $prioritizedLanguages = null)
346
    {
347
        if (!$versionInfo->isDraft()) {
348
            throw new BadStateException(
349
                '$contentInfo',
350
                sprintf(
351
                    'Content [%d] %s has been already published. Use LocationService::loadLocations instead.',
352
                    $versionInfo->contentInfo->id,
353
                    $versionInfo->contentInfo->name
354
                )
355
            );
356
        }
357
358
        $spiLocations = $this->persistenceHandler
359
            ->locationHandler()
360
            ->loadParentLocationsForDraftContent($versionInfo->contentInfo->id);
361
362
        $contentIds = [];
363
        foreach ($spiLocations as $spiLocation) {
364
            $contentIds[] = $spiLocation->contentId;
365
        }
366
367
        $locations = [];
368
        $permissionResolver = $this->repository->getPermissionResolver();
369
        $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds);
370
        $contentList = $this->domainMapper->buildContentProxyList($spiContentInfoList, $prioritizedLanguages ?: []);
371 View Code Duplication
        foreach ($spiLocations as $spiLocation) {
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...
372
            $location = $this->domainMapper->buildLocationWithContent(
373
                $spiLocation,
374
                $contentList[$spiLocation->contentId],
375
                $spiContentInfoList[$spiLocation->contentId]
376
            );
377
378
            if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) {
379
                $locations[] = $location;
380
            }
381
        }
382
383
        return $locations;
384
    }
385
386
    /**
387
     * Returns the number of children which are readable by the current user of a location object.
388
     *
389
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
390
     *
391
     * @return int
392
     */
393
    public function getLocationChildCount(APILocation $location)
394
    {
395
        $searchResult = $this->searchChildrenLocations($location, 0, 0);
396
397
        return $searchResult->totalCount;
398
    }
399
400
    /**
401
     * Searches children locations of the provided parent location id.
402
     *
403
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
404
     * @param int $offset
405
     * @param int $limit
406
     *
407
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
408
     */
409
    protected function searchChildrenLocations(APILocation $location, $offset = 0, $limit = -1, array $prioritizedLanguages = null)
410
    {
411
        $query = new LocationQuery([
412
            'filter' => new Criterion\ParentLocationId($location->id),
413
            'offset' => $offset >= 0 ? (int)$offset : 0,
414
            'limit' => $limit >= 0 ? (int)$limit : null,
415
            'sortClauses' => $location->getSortClauses(),
416
        ]);
417
418
        return $this->repository->getSearchService()->findLocations($query, ['languages' => $prioritizedLanguages]);
419
    }
420
421
    /**
422
     * Creates the new $location in the content repository for the given content.
423
     *
424
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create this location
425
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the content is already below the specified parent
426
     *                                        or the parent is a sub location of the location of the content
427
     *                                        or if set the remoteId exists already
428
     *
429
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
430
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $locationCreateStruct
431
     *
432
     * @return \eZ\Publish\API\Repository\Values\Content\Location the newly created Location
433
     */
434
    public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct)
435
    {
436
        $content = $this->repository->getContentService()->loadContent($contentInfo->id);
437
        $parentLocation = $this->loadLocation($locationCreateStruct->parentLocationId);
438
439
        if (!$this->repository->canUser('content', 'manage_locations', $content->contentInfo)) {
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...
440
            throw new UnauthorizedException('content', 'manage_locations');
441
        }
442
443
        if (!$this->repository->canUser('content', 'create', $content->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...
444
            throw new UnauthorizedException('content', 'create');
445
        }
446
447
        // Check if the parent is a sub location of one of the existing content locations (this also solves the
448
        // situation where parent location actually one of the content locations),
449
        // or if the content already has location below given location create struct parent
450
        $existingContentLocations = $this->loadLocations($content->contentInfo);
451
        if (!empty($existingContentLocations)) {
452
            foreach ($existingContentLocations as $existingContentLocation) {
453
                if (stripos($parentLocation->pathString, $existingContentLocation->pathString) !== false) {
454
                    throw new InvalidArgumentException(
455
                        '$locationCreateStruct',
456
                        'Specified parent is a sub location of one of the existing content locations.'
457
                    );
458
                }
459
                if ($parentLocation->id == $existingContentLocation->parentLocationId) {
460
                    throw new InvalidArgumentException(
461
                        '$locationCreateStruct',
462
                        'Content is already below the specified parent.'
463
                    );
464
                }
465
            }
466
        }
467
468
        $spiLocationCreateStruct = $this->domainMapper->buildSPILocationCreateStruct(
469
            $locationCreateStruct,
470
            $parentLocation,
471
            $content->contentInfo->mainLocationId !== null ? $content->contentInfo->mainLocationId : true,
472
            $content->contentInfo->id,
473
            $content->contentInfo->currentVersionNo
474
        );
475
476
        $this->repository->beginTransaction();
477
        try {
478
            $newLocation = $this->persistenceHandler->locationHandler()->create($spiLocationCreateStruct);
479
480
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
481 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...
482
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
483
                    $newLocation->id,
484
                    $newLocation->parentId,
485
                    $name,
486
                    $languageCode,
487
                    $content->contentInfo->alwaysAvailable,
488
                    // @todo: this is legacy storage specific for updating ezcontentobject_tree.path_identification_string, to be removed
489
                    $languageCode === $content->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 $content->contentInfo->mainLanguageCode (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
490
                );
491
            }
492
493
            $this->repository->commit();
494
        } catch (Exception $e) {
495
            $this->repository->rollback();
496
            throw $e;
497
        }
498
499
        return $this->domainMapper->buildLocationWithContent($newLocation, $content);
500
    }
501
502
    /**
503
     * Updates $location in the content repository.
504
     *
505
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to update this location
506
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException   if if set the remoteId exists already
507
     *
508
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
509
     * @param \eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct $locationUpdateStruct
510
     *
511
     * @return \eZ\Publish\API\Repository\Values\Content\Location the updated Location
512
     */
513
    public function updateLocation(APILocation $location, LocationUpdateStruct $locationUpdateStruct)
514
    {
515
        if (!$this->domainMapper->isValidLocationPriority($locationUpdateStruct->priority)) {
516
            throw new InvalidArgumentValue('priority', $locationUpdateStruct->priority, 'LocationUpdateStruct');
517
        }
518
519
        if ($locationUpdateStruct->remoteId !== null && (!is_string($locationUpdateStruct->remoteId) || empty($locationUpdateStruct->remoteId))) {
520
            throw new InvalidArgumentValue('remoteId', $locationUpdateStruct->remoteId, 'LocationUpdateStruct');
521
        }
522
523
        if ($locationUpdateStruct->sortField !== null && !$this->domainMapper->isValidLocationSortField($locationUpdateStruct->sortField)) {
524
            throw new InvalidArgumentValue('sortField', $locationUpdateStruct->sortField, 'LocationUpdateStruct');
525
        }
526
527
        if ($locationUpdateStruct->sortOrder !== null && !$this->domainMapper->isValidLocationSortOrder($locationUpdateStruct->sortOrder)) {
528
            throw new InvalidArgumentValue('sortOrder', $locationUpdateStruct->sortOrder, 'LocationUpdateStruct');
529
        }
530
531
        $loadedLocation = $this->loadLocation($location->id);
532
533
        if ($locationUpdateStruct->remoteId !== null) {
534
            try {
535
                $existingLocation = $this->loadLocationByRemoteId($locationUpdateStruct->remoteId);
536
                if ($existingLocation !== null && $existingLocation->id !== $loadedLocation->id) {
537
                    throw new InvalidArgumentException('locationUpdateStruct', 'location with provided remote ID already exists');
538
                }
539
            } catch (APINotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
540
            }
541
        }
542
543
        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...
544
            throw new UnauthorizedException('content', 'edit');
545
        }
546
547
        $updateStruct = new UpdateStruct();
548
        $updateStruct->priority = $locationUpdateStruct->priority !== null ? $locationUpdateStruct->priority : $loadedLocation->priority;
549
        $updateStruct->remoteId = $locationUpdateStruct->remoteId !== null ? trim($locationUpdateStruct->remoteId) : $loadedLocation->remoteId;
550
        $updateStruct->sortField = $locationUpdateStruct->sortField !== null ? $locationUpdateStruct->sortField : $loadedLocation->sortField;
551
        $updateStruct->sortOrder = $locationUpdateStruct->sortOrder !== null ? $locationUpdateStruct->sortOrder : $loadedLocation->sortOrder;
552
553
        $this->repository->beginTransaction();
554
        try {
555
            $this->persistenceHandler->locationHandler()->update($updateStruct, $loadedLocation->id);
556
            $this->repository->commit();
557
        } catch (Exception $e) {
558
            $this->repository->rollback();
559
            throw $e;
560
        }
561
562
        return $this->loadLocation($loadedLocation->id);
563
    }
564
565
    /**
566
     * Swaps the contents held by $location1 and $location2.
567
     *
568
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to swap content
569
     *
570
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location1
571
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location2
572
     */
573
    public function swapLocation(APILocation $location1, APILocation $location2)
574
    {
575
        $loadedLocation1 = $this->loadLocation($location1->id);
576
        $loadedLocation2 = $this->loadLocation($location2->id);
577
578
        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...
579
            throw new UnauthorizedException('content', 'edit');
580
        }
581
        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...
582
            throw new UnauthorizedException('content', 'edit');
583
        }
584
585
        $this->repository->beginTransaction();
586
        try {
587
            $this->persistenceHandler->locationHandler()->swap($loadedLocation1->id, $loadedLocation2->id);
588
            $this->persistenceHandler->urlAliasHandler()->locationSwapped(
589
                $location1->id,
590
                $location1->parentLocationId,
591
                $location2->id,
592
                $location2->parentLocationId
593
            );
594
            $this->persistenceHandler->bookmarkHandler()->locationSwapped($loadedLocation1->id, $loadedLocation2->id);
595
            $this->repository->commit();
596
        } catch (Exception $e) {
597
            $this->repository->rollback();
598
            throw $e;
599
        }
600
    }
601
602
    /**
603
     * Hides the $location and marks invisible all descendants of $location.
604
     *
605
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to hide this location
606
     *
607
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
608
     *
609
     * @return \eZ\Publish\API\Repository\Values\Content\Location $location, with updated hidden value
610
     */
611 View Code Duplication
    public function hideLocation(APILocation $location)
612
    {
613
        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...
614
            throw new UnauthorizedException('content', 'hide');
615
        }
616
617
        $this->repository->beginTransaction();
618
        try {
619
            $this->persistenceHandler->locationHandler()->hide($location->id);
620
            $this->repository->commit();
621
        } catch (Exception $e) {
622
            $this->repository->rollback();
623
            throw $e;
624
        }
625
626
        return $this->loadLocation($location->id);
627
    }
628
629
    /**
630
     * Unhides the $location.
631
     *
632
     * This method and marks visible all descendants of $locations
633
     * until a hidden location is found.
634
     *
635
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to unhide this location
636
     *
637
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
638
     *
639
     * @return \eZ\Publish\API\Repository\Values\Content\Location $location, with updated hidden value
640
     */
641 View Code Duplication
    public function unhideLocation(APILocation $location)
642
    {
643
        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...
644
            throw new UnauthorizedException('content', 'hide');
645
        }
646
647
        $this->repository->beginTransaction();
648
        try {
649
            $this->persistenceHandler->locationHandler()->unHide($location->id);
650
            $this->repository->commit();
651
        } catch (Exception $e) {
652
            $this->repository->rollback();
653
            throw $e;
654
        }
655
656
        return $this->loadLocation($location->id);
657
    }
658
659
    /**
660
     * Moves the subtree to $newParentLocation.
661
     *
662
     * If a user has the permission to move the location to a target location
663
     * he can do it regardless of an existing descendant on which the user has no permission.
664
     *
665
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to move this location to the target
666
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user does not have read access to the whole source subtree
667
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the new parent is in a subtree of the location
668
     *
669
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
670
     * @param \eZ\Publish\API\Repository\Values\Content\Location $newParentLocation
671
     */
672
    public function moveSubtree(APILocation $location, APILocation $newParentLocation)
673
    {
674
        $location = $this->loadLocation($location->id);
675
        $newParentLocation = $this->loadLocation($newParentLocation->id);
676
677
        // check create permission on target location
678
        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...
679
            throw new UnauthorizedException('content', 'create');
680
        }
681
682
        /** Check read access to whole source subtree
683
         * @var bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion
684
         */
685
        $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion();
0 ignored issues
show
Bug introduced by
The call to getPermissionsCriterion() misses some required arguments starting with $module.
Loading history...
686 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...
687
            throw new UnauthorizedException('content', 'read');
688
        } elseif ($contentReadCriterion !== true) {
689
            // Query if there are any content in subtree current user don't have access to
690
            $query = new Query(
691
                array(
692
                    'limit' => 0,
693
                    'filter' => new CriterionLogicalAnd(
694
                        array(
695
                            new CriterionSubtree($location->pathString),
696
                            new CriterionLogicalNot($contentReadCriterion),
0 ignored issues
show
Bug introduced by
It seems like $contentReadCriterion defined by $this->permissionCriteri...tPermissionsCriterion() on line 685 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...
697
                        )
698
                    ),
699
                )
700
            );
701
            $result = $this->repository->getSearchService()->findContent($query, array(), false);
702
            if ($result->totalCount > 0) {
703
                throw new UnauthorizedException('content', 'read');
704
            }
705
        }
706
707
        if (strpos($newParentLocation->pathString, $location->pathString) === 0) {
708
            throw new InvalidArgumentException(
709
                '$newParentLocation',
710
                'new parent location is in a subtree of the given $location'
711
            );
712
        }
713
714
        $this->repository->beginTransaction();
715
        try {
716
            $this->persistenceHandler->locationHandler()->move($location->id, $newParentLocation->id);
717
718
            $content = $this->repository->getContentService()->loadContent($location->contentId);
719
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
720 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...
721
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
722
                    $location->id,
723
                    $newParentLocation->id,
724
                    $name,
725
                    $languageCode,
726
                    $content->contentInfo->alwaysAvailable
727
                );
728
            }
729
730
            $this->persistenceHandler->urlAliasHandler()->locationMoved(
731
                $location->id,
732
                $location->parentLocationId,
733
                $newParentLocation->id
734
            );
735
736
            $this->repository->commit();
737
        } catch (Exception $e) {
738
            $this->repository->rollback();
739
            throw $e;
740
        }
741
    }
742
743
    /**
744
     * Deletes $location and all its descendants.
745
     *
746
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user is not allowed to delete this location or a descendant
747
     *
748
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
749
     */
750
    public function deleteLocation(APILocation $location)
751
    {
752
        $location = $this->loadLocation($location->id);
753
754
        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...
755
            throw new UnauthorizedException('content', 'manage_locations');
756
        }
757
        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...
758
            throw new UnauthorizedException('content', 'remove');
759
        }
760
761
        /** Check remove access to descendants
762
         * @var bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion
763
         */
764
        $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove');
765 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...
766
            throw new UnauthorizedException('content', 'remove');
767
        } elseif ($contentReadCriterion !== true) {
768
            // Query if there are any content in subtree current user don't have access to
769
            $query = new Query(
770
                array(
771
                    'limit' => 0,
772
                    'filter' => new CriterionLogicalAnd(
773
                        array(
774
                            new CriterionSubtree($location->pathString),
775
                            new CriterionLogicalNot($contentReadCriterion),
0 ignored issues
show
Bug introduced by
It seems like $contentReadCriterion defined by $this->permissionCriteri...on('content', 'remove') on line 764 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...
776
                        )
777
                    ),
778
                )
779
            );
780
            $result = $this->repository->getSearchService()->findContent($query, array(), false);
781
            if ($result->totalCount > 0) {
782
                throw new UnauthorizedException('content', 'remove');
783
            }
784
        }
785
786
        $this->repository->beginTransaction();
787
        try {
788
            $this->persistenceHandler->locationHandler()->removeSubtree($location->id);
789
            $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id);
790
            $this->repository->commit();
791
        } catch (Exception $e) {
792
            $this->repository->rollback();
793
            throw $e;
794
        }
795
    }
796
797
    /**
798
     * Instantiates a new location create class.
799
     *
800
     * @param mixed $parentLocationId the parent under which the new location should be created
801
     * @param eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType
802
     *
803
     * @return \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct
804
     */
805 View Code Duplication
    public function newLocationCreateStruct($parentLocationId, ContentType $contentType = null)
806
    {
807
        $properties = [
808
            'parentLocationId' => $parentLocationId,
809
        ];
810
        if ($contentType) {
811
            $properties['sortField'] = $contentType->defaultSortField;
812
            $properties['sortOrder'] = $contentType->defaultSortOrder;
813
        }
814
815
        return new LocationCreateStruct($properties);
816
    }
817
818
    /**
819
     * Instantiates a new location update class.
820
     *
821
     * @return \eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct
822
     */
823
    public function newLocationUpdateStruct()
824
    {
825
        return new LocationUpdateStruct();
826
    }
827
828
    /**
829
     * Get the total number of all existing Locations. Can be combined with loadAllLocations.
830
     *
831
     * @see loadAllLocations
832
     *
833
     * @return int Total number of Locations
834
     */
835
    public function getAllLocationsCount(): int
836
    {
837
        return $this->persistenceHandler->locationHandler()->countAllLocations();
838
    }
839
840
    /**
841
     * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results.
842
     *
843
     * @param int $offset
844
     * @param int $limit
845
     *
846
     * @return \eZ\Publish\API\Repository\Values\Content\Location[]
847
     *
848
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
849
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
850
     */
851
    public function loadAllLocations(int $offset = 0, int $limit = 25): array
852
    {
853
        $spiLocations = $this->persistenceHandler->locationHandler()->loadAllLocations(
854
            $offset,
855
            $limit
856
        );
857
        $contentIds = array_unique(
858
            array_map(
859
                function (SPILocation $spiLocation) {
860
                    return $spiLocation->contentId;
861
                },
862
                $spiLocations
863
            )
864
        );
865
866
        $permissionResolver = $this->repository->getPermissionResolver();
867
        $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList(
868
            $contentIds
869
        );
870
        $contentList = $this->domainMapper->buildContentProxyList(
871
            $spiContentInfoList,
872
            Language::ALL,
873
            false
874
        );
875
        $locations = [];
876
        foreach ($spiLocations as $spiLocation) {
877
            if (!isset($spiContentInfoList[$spiLocation->contentId], $contentList[$spiLocation->contentId])) {
878
                $this->logger->warning(
879
                    sprintf(
880
                        'Location %d has missing Content %d',
881
                        $spiLocation->id,
882
                        $spiLocation->contentId
883
                    )
884
                );
885
                continue;
886
            }
887
888
            $location = $this->domainMapper->buildLocationWithContent(
889
                $spiLocation,
890
                $contentList[$spiLocation->contentId],
891
                $spiContentInfoList[$spiLocation->contentId]
892
            );
893
894
            $contentInfo = $location->getContentInfo();
895
            if (!$permissionResolver->canUser('content', 'read', $contentInfo, [$location])) {
896
                continue;
897
            }
898
            $locations[] = $location;
899
        }
900
901
        return $locations;
902
    }
903
}
904