Completed
Push — repo_use_canUser ( af0135 )
by André
20:12
created

TrashService::trash()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 9
nop 1
dl 0
loc 33
rs 8.7697
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the eZ\Publish\Core\Repository\TrashService 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\TrashService as TrashServiceInterface;
12
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
13
use eZ\Publish\SPI\Persistence\Handler;
14
use eZ\Publish\API\Repository\Values\Content\Location;
15
use eZ\Publish\Core\Repository\Values\Content\TrashItem;
16
use eZ\Publish\API\Repository\Values\Content\TrashItem as APITrashItem;
17
use eZ\Publish\API\Repository\Values\Content\Query;
18
use eZ\Publish\SPI\Persistence\Content\Location\Trashed;
19
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
20
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
21
use eZ\Publish\API\Repository\Values\Content\SearchResult;
22
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
23
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
24
use DateTime;
25
use Exception;
26
27
/**
28
 * Trash service, used for managing trashed content.
29
 */
30
class TrashService implements TrashServiceInterface
31
{
32
    /**
33
     * @var \eZ\Publish\Core\Repository\Repository
34
     */
35
    protected $repository;
36
37
    /**
38
     * @var \eZ\Publish\SPI\Persistence\Handler
39
     */
40
    protected $persistenceHandler;
41
42
    /**
43
     * @var array
44
     */
45
    protected $settings;
46
47
    /**
48
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
49
     */
50
    protected $nameSchemaService;
51
52
    /**
53
     * Setups service with reference to repository object that created it & corresponding handler.
54
     *
55
     * @param \eZ\Publish\API\Repository\Repository $repository
56
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
57
     * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService
58
     * @param array $settings
59
     */
60
    public function __construct(
61
        RepositoryInterface $repository,
62
        Handler $handler,
63
        Helper\NameSchemaService $nameSchemaService,
64
        array $settings = array()
65
    ) {
66
        $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...
67
        $this->persistenceHandler = $handler;
68
        $this->nameSchemaService = $nameSchemaService;
69
        // Union makes sure default settings are ignored if provided in argument
70
        $this->settings = $settings + array(
71
            //'defaultSetting' => array(),
72
        );
73
    }
74
75
    /**
76
     * Loads a trashed location object from its $id.
77
     *
78
     * Note that $id is identical to original location, which has been previously trashed
79
     *
80
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the trashed location
81
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the location with the given id does not exist
82
     *
83
     * @param mixed $trashItemId
84
     *
85
     * @return \eZ\Publish\API\Repository\Values\Content\TrashItem
86
     */
87
    public function loadTrashItem($trashItemId)
88
    {
89
        $spiTrashItem = $this->persistenceHandler->trashHandler()->loadTrashItem($trashItemId);
90
        $trash = $this->buildDomainTrashItemObject($spiTrashItem);
91
        if (!$this->repository->canUser('content', 'read', $trash->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...
92
            throw new UnauthorizedException('content', 'read');
93
        }
94
95
        // TODO: Need to check (integration tests + self + QA) how Role limitation will behave with this (as there is no location)
96
        // we could pass trash as target (same goes for content/read above), but again would need to check how it will behave.
97
        if (!$this->repository->canUser('content', 'restore', $trash->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...
98
            throw new UnauthorizedException('content', 'restore');
99
        }
100
101
        return $trash;
102
    }
103
104
    /**
105
     * Sends $location and all its children to trash and returns the corresponding trash item.
106
     *
107
     * The current user may not have access to the returned trash item, check before using it.
108
     * Content is left untouched.
109
     *
110
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to trash the given location
111
     *
112
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
113
     *
114
     * @return null|\eZ\Publish\API\Repository\Values\Content\TrashItem null if location was deleted, otherwise TrashItem
115
     */
116
    public function trash(Location $location)
117
    {
118
        if (!is_numeric($location->id)) {
119
            throw new InvalidArgumentValue('id', $location->id, 'Location');
120
        }
121
122
        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...
123
            throw new UnauthorizedException('content', 'remove');
124
        }
125
126
        $this->repository->beginTransaction();
127
        try {
128
            $spiTrashItem = $this->persistenceHandler->trashHandler()->trashSubtree($location->id);
129
            $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id);
130
            $this->repository->commit();
131
        } catch (Exception $e) {
132
            $this->repository->rollback();
133
            throw $e;
134
        }
135
136
        // Use sudo as we want a trash item regardless of user access to the trash.
137
        try {
138
            return isset($spiTrashItem)
139
                ? $this->repository->sudo(
140
                    function () use ($spiTrashItem) {
141
                        return $this->buildDomainTrashItemObject($spiTrashItem);
142
                    }
143
                )
144
                : null;
145
        } catch (Exception $e) {
146
            return null;
147
        }
148
    }
149
150
    /**
151
     * Recovers the $trashedLocation at its original place if possible.
152
     *
153
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to recover the trash item at the parent location location
154
     *
155
     * If $newParentLocation is provided, $trashedLocation will be restored under it.
156
     *
157
     * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem
158
     * @param \eZ\Publish\API\Repository\Values\Content\Location $newParentLocation
159
     *
160
     * @return \eZ\Publish\API\Repository\Values\Content\Location the newly created or recovered location
161
     */
162
    public function recover(APITrashItem $trashItem, Location $newParentLocation = null)
163
    {
164
        if (!is_numeric($trashItem->id)) {
165
            throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem');
166
        }
167
168
        if ($newParentLocation === null && !is_numeric($trashItem->parentLocationId)) {
169
            throw new InvalidArgumentValue('parentLocationId', $trashItem->parentLocationId, 'TrashItem');
170
        }
171
172
        if ($newParentLocation !== null && !is_numeric($newParentLocation->id)) {
173
            throw new InvalidArgumentValue('parentLocationId', $newParentLocation->id, 'Location');
174
        }
175
176
        if (!$this->repository->canUser(
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...
177
            'content',
178
            'restore',
179
            $trashItem->getContentInfo(),
180
            $newParentLocation ? [$newParentLocation] : null
181
        )) {
182
            throw new UnauthorizedException('content', 'restore');
183
        }
184
185
        $this->repository->beginTransaction();
186
        try {
187
            $newParentLocationId = $newParentLocation ? $newParentLocation->id : $trashItem->parentLocationId;
188
            $newLocationId = $this->persistenceHandler->trashHandler()->recover(
189
                $trashItem->id,
190
                $newParentLocationId
191
            );
192
193
            $content = $this->repository->getContentService()->loadContent($trashItem->contentId);
194
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
195
196
            // Publish URL aliases for recovered location
197
            foreach ($urlAliasNames as $languageCode => $name) {
198
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
199
                    $newLocationId,
200
                    $newParentLocationId,
201
                    $name,
202
                    $languageCode,
203
                    $content->contentInfo->alwaysAvailable
204
                );
205
            }
206
207
            $this->repository->commit();
208
        } catch (Exception $e) {
209
            $this->repository->rollback();
210
            throw $e;
211
        }
212
213
        return $this->repository->getLocationService()->loadLocation($newLocationId);
214
    }
215
216
    /**
217
     * Empties trash.
218
     *
219
     * All locations contained in the trash will be removed. Content objects will be removed
220
     * if all locations of the content are gone.
221
     *
222
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to empty the trash
223
     */
224
    public function emptyTrash()
225
    {
226
        // Will throw if you have Role assignment limitation where you have content/cleantrash permission.
227
        // This is by design and means you can only delete one and one trash item, or you'll need to change how
228
        // permissions is assigned to be on separate role with no Role assignment limitation.
229
        if ($this->repository->hasAccess('content', 'cleantrash') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead. Check if user has access to a given module / function. Low level function, use canUser instead if you have objects to check against.

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...
230
            throw new UnauthorizedException('content', 'cleantrash');
231
        }
232
233
        $this->repository->beginTransaction();
234
        try {
235
            // Persistence layer takes care of deleting content objects
236
            $this->persistenceHandler->trashHandler()->emptyTrash();
237
            $this->repository->commit();
238
        } catch (Exception $e) {
239
            $this->repository->rollback();
240
            throw $e;
241
        }
242
    }
243
244
    /**
245
     * Deletes a trash item.
246
     *
247
     * The corresponding content object will be removed
248
     *
249
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete this trash item
250
     *
251
     * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem
252
     */
253
    public function deleteTrashItem(APITrashItem $trashItem)
254
    {
255
        if (!$this->repository->canUser('content', 'cleantrash', $trashItem->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...
256
            throw new UnauthorizedException('content', 'cleantrash');
257
        }
258
259
        if (!is_numeric($trashItem->id)) {
260
            throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem');
261
        }
262
263
        $this->repository->beginTransaction();
264
        try {
265
            $this->persistenceHandler->trashHandler()->deleteTrashItem($trashItem->id);
266
            $this->repository->commit();
267
        } catch (Exception $e) {
268
            $this->repository->rollback();
269
            throw $e;
270
        }
271
    }
272
273
    /**
274
     * Returns a collection of Trashed locations contained in the trash, which are readable by the current user.
275
     *
276
     * $query allows to filter/sort the elements to be contained in the collection.
277
     *
278
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
279
     *
280
     * @return \eZ\Publish\API\Repository\Values\Content\SearchResult
281
     */
282
    public function findTrashItems(Query $query)
283
    {
284
        if ($query->filter !== null && !$query->filter instanceof Criterion) {
285
            throw new InvalidArgumentValue('query->filter', $query->filter, 'Query');
286
        }
287
288
        if ($query->sortClauses !== null) {
289
            if (!is_array($query->sortClauses)) {
290
                throw new InvalidArgumentValue('query->sortClauses', $query->sortClauses, 'Query');
291
            }
292
293
            foreach ($query->sortClauses as $sortClause) {
294
                if (!$sortClause instanceof SortClause) {
295
                    throw new InvalidArgumentValue('query->sortClauses', 'only instances of SortClause class are allowed');
296
                }
297
            }
298
        }
299
300
        if ($query->offset !== null && !is_numeric($query->offset)) {
301
            throw new InvalidArgumentValue('query->offset', $query->offset, 'Query');
302
        }
303
304
        if ($query->limit !== null && !is_numeric($query->limit)) {
305
            throw new InvalidArgumentValue('query->limit', $query->limit, 'Query');
306
        }
307
308
        $spiTrashItems = $this->persistenceHandler->trashHandler()->findTrashItems(
309
            $query->filter !== null ? $query->filter : null,
310
            $query->offset !== null && $query->offset > 0 ? (int)$query->offset : 0,
311
            $query->limit !== null && $query->limit >= 1 ? (int)$query->limit : null,
312
            $query->sortClauses !== null ? $query->sortClauses : null
0 ignored issues
show
Bug introduced by
It seems like $query->sortClauses !== ...ery->sortClauses : null can also be of type array; however, eZ\Publish\SPI\Persisten...ndler::findTrashItems() does only seem to accept null|array<integer,objec...tent\Query\SortClause>>, 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...
313
        );
314
315
        $trashItems = array();
316
        foreach ($spiTrashItems as $spiTrashItem) {
317
            try {
318
                $trashItems[] = $this->buildDomainTrashItemObject($spiTrashItem);
319
            } catch (UnauthorizedException $e) {
320
                // Do nothing, thus exclude items the current user doesn't have read access to.
321
            }
322
        }
323
324
        $searchResult = new SearchResult();
0 ignored issues
show
Deprecated Code introduced by
The class eZ\Publish\API\Repositor...es\Content\SearchResult has been deprecated with message: This class is returned by find methods providing a result of a search.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

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

Loading history...
325
        $searchResult->count = count($trashItems);
326
        $searchResult->items = $trashItems;
327
        $searchResult->query = $query;
328
329
        return $searchResult;
330
    }
331
332
    /**
333
     * Builds the domain TrashItem object from provided persistence trash item.
334
     *
335
     * @param \eZ\Publish\SPI\Persistence\Content\Location\Trashed $spiTrashItem
336
     *
337
     * @return \eZ\Publish\API\Repository\Values\Content\TrashItem
338
     */
339 View Code Duplication
    protected function buildDomainTrashItemObject(Trashed $spiTrashItem)
340
    {
341
        return new TrashItem(
342
            array(
343
                'contentInfo' => $this->repository->getContentService()->loadContentInfo($spiTrashItem->contentId),
344
                'id' => $spiTrashItem->id,
345
                'priority' => $spiTrashItem->priority,
346
                'hidden' => $spiTrashItem->hidden,
347
                'invisible' => $spiTrashItem->invisible,
348
                'remoteId' => $spiTrashItem->remoteId,
349
                'parentLocationId' => $spiTrashItem->parentId,
350
                'pathString' => $spiTrashItem->pathString,
351
                'depth' => $spiTrashItem->depth,
352
                'sortField' => $spiTrashItem->sortField,
353
                'sortOrder' => $spiTrashItem->sortOrder,
354
            )
355
        );
356
    }
357
358
    /**
359
     * @param int $timestamp
360
     *
361
     * @return \DateTime
362
     */
363
    protected function getDateTime($timestamp)
364
    {
365
        $dateTime = new DateTime();
366
        $dateTime->setTimestamp($timestamp);
367
368
        return $dateTime;
369
    }
370
}
371