Completed
Push — location_content_property ( b180d5 )
by André
16:28
created

LocationService::copySubtree()   C

Complexity

Conditions 8
Paths 26

Size

Total Lines 72
Code Lines 44

Duplication

Lines 29
Ratio 40.28 %

Importance

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