Completed
Push — master ( 6c5a34...58d4d9 )
by André
52:17 queued 34:30
created

TrashService::emptyTrash()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 0
dl 0
loc 19
rs 9.6333
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\API\Repository\Values\Content\Content;
14
use eZ\Publish\API\Repository\Exceptions\UnauthorizedException as APIUnauthorizedException;
15
use eZ\Publish\SPI\Persistence\Handler;
16
use eZ\Publish\API\Repository\Values\Content\Location;
17
use eZ\Publish\Core\Repository\Values\Content\TrashItem;
18
use eZ\Publish\API\Repository\Values\Content\TrashItem as APITrashItem;
19
use eZ\Publish\API\Repository\Values\Content\Query;
20
use eZ\Publish\SPI\Persistence\Content\Location\Trashed;
21
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
22
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
23
use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult;
24
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
25
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
26
use DateTime;
27
use Exception;
28
29
/**
30
 * Trash service, used for managing trashed content.
31
 */
32
class TrashService implements TrashServiceInterface
33
{
34
    /**
35
     * @var \eZ\Publish\Core\Repository\Repository
36
     */
37
    protected $repository;
38
39
    /**
40
     * @var \eZ\Publish\SPI\Persistence\Handler
41
     */
42
    protected $persistenceHandler;
43
44
    /**
45
     * @var array
46
     */
47
    protected $settings;
48
49
    /**
50
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
51
     */
52
    protected $nameSchemaService;
53
54
    /**
55
     * Setups service with reference to repository object that created it & corresponding handler.
56
     *
57
     * @param \eZ\Publish\API\Repository\Repository $repository
58
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
59
     * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService
60
     * @param array $settings
61
     */
62
    public function __construct(
63
        RepositoryInterface $repository,
64
        Handler $handler,
65
        Helper\NameSchemaService $nameSchemaService,
66
        array $settings = array()
67
    ) {
68
        $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...
69
        $this->persistenceHandler = $handler;
70
        $this->nameSchemaService = $nameSchemaService;
71
        // Union makes sure default settings are ignored if provided in argument
72
        $this->settings = $settings + array(
73
            //'defaultSetting' => array(),
74
        );
75
    }
76
77
    /**
78
     * Loads a trashed location object from its $id.
79
     *
80
     * Note that $id is identical to original location, which has been previously trashed
81
     *
82
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the trashed location
83
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the location with the given id does not exist
84
     *
85
     * @param mixed $trashItemId
86
     *
87
     * @return \eZ\Publish\API\Repository\Values\Content\TrashItem
88
     */
89
    public function loadTrashItem($trashItemId)
90
    {
91
        $spiTrashItem = $this->persistenceHandler->trashHandler()->loadTrashItem($trashItemId);
92
        $trash = $this->buildDomainTrashItemObject(
93
            $spiTrashItem,
94
            $this->repository->getContentService()->internalLoadContent($spiTrashItem->contentId)
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
95
        );
96
        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...
97
            throw new UnauthorizedException('content', 'read');
98
        }
99
100
        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...
101
            throw new UnauthorizedException('content', 'restore');
102
        }
103
104
        return $trash;
105
    }
106
107
    /**
108
     * Sends $location and all its children to trash and returns the corresponding trash item.
109
     *
110
     * The current user may not have access to the returned trash item, check before using it.
111
     * Content is left untouched.
112
     *
113
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to trash the given location
114
     *
115
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
116
     *
117
     * @return null|\eZ\Publish\API\Repository\Values\Content\TrashItem null if location was deleted, otherwise TrashItem
118
     */
119
    public function trash(Location $location)
120
    {
121
        if (!is_numeric($location->id)) {
122
            throw new InvalidArgumentValue('id', $location->id, 'Location');
123
        }
124
125
        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...
126
            throw new UnauthorizedException('content', 'remove');
127
        }
128
129
        $this->repository->beginTransaction();
130
        try {
131
            $spiTrashItem = $this->persistenceHandler->trashHandler()->trashSubtree($location->id);
132
            $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id);
133
            $this->repository->commit();
134
        } catch (Exception $e) {
135
            $this->repository->rollback();
136
            throw $e;
137
        }
138
139
        // Use internalLoadContent() as we want a trash item regardless of user access to the trash or not.
140
        try {
141
            return isset($spiTrashItem)
142
                ? $this->buildDomainTrashItemObject(
143
                    $spiTrashItem,
144
                    $this->repository->getContentService()->internalLoadContent($spiTrashItem->contentId)
0 ignored issues
show
Bug introduced by
The method internalLoadContent() does not exist on eZ\Publish\API\Repository\ContentService. Did you maybe mean loadContent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
145
                )
146
                : null;
147
        } catch (Exception $e) {
148
            return null;
149
        }
150
    }
151
152
    /**
153
     * Recovers the $trashedLocation at its original place if possible.
154
     *
155
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to recover the trash item at the parent location location
156
     *
157
     * If $newParentLocation is provided, $trashedLocation will be restored under it.
158
     *
159
     * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem
160
     * @param \eZ\Publish\API\Repository\Values\Content\Location $newParentLocation
161
     *
162
     * @return \eZ\Publish\API\Repository\Values\Content\Location the newly created or recovered location
163
     */
164
    public function recover(APITrashItem $trashItem, Location $newParentLocation = null)
165
    {
166
        if (!is_numeric($trashItem->id)) {
167
            throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem');
168
        }
169
170
        if ($newParentLocation === null && !is_numeric($trashItem->parentLocationId)) {
171
            throw new InvalidArgumentValue('parentLocationId', $trashItem->parentLocationId, 'TrashItem');
172
        }
173
174
        if ($newParentLocation !== null && !is_numeric($newParentLocation->id)) {
175
            throw new InvalidArgumentValue('parentLocationId', $newParentLocation->id, 'Location');
176
        }
177
178
        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...
179
            'content',
180
            'restore',
181
            $trashItem->getContentInfo(),
182
            [$newParentLocation ?: $trashItem]
183
        )) {
184
            throw new UnauthorizedException('content', 'restore');
185
        }
186
187
        $this->repository->beginTransaction();
188
        try {
189
            $newParentLocationId = $newParentLocation ? $newParentLocation->id : $trashItem->parentLocationId;
190
            $newLocationId = $this->persistenceHandler->trashHandler()->recover(
191
                $trashItem->id,
192
                $newParentLocationId
193
            );
194
195
            $content = $this->repository->getContentService()->loadContent($trashItem->contentId);
196
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
197
198
            // Publish URL aliases for recovered location
199
            foreach ($urlAliasNames as $languageCode => $name) {
200
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
201
                    $newLocationId,
202
                    $newParentLocationId,
203
                    $name,
204
                    $languageCode,
205
                    $content->contentInfo->alwaysAvailable
206
                );
207
            }
208
209
            $this->repository->commit();
210
        } catch (Exception $e) {
211
            $this->repository->rollback();
212
            throw $e;
213
        }
214
215
        return $this->repository->getLocationService()->loadLocation($newLocationId);
216
    }
217
218
    /**
219
     * Empties trash.
220
     *
221
     * All locations contained in the trash will be removed. Content objects will be removed
222
     * if all locations of the content are gone.
223
     *
224
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to empty the trash
225
     */
226
    public function emptyTrash()
227
    {
228
        // Will throw if you have Role assignment limitation where you have content/cleantrash permission.
229
        // This is by design and means you can only delete one and one trash item, or you'll need to change how
230
        // permissions is assigned to be on separate role with no Role assignment limitation.
231
        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...
232
            throw new UnauthorizedException('content', 'cleantrash');
233
        }
234
235
        $this->repository->beginTransaction();
236
        try {
237
            // Persistence layer takes care of deleting content objects
238
            $this->persistenceHandler->trashHandler()->emptyTrash();
239
            $this->repository->commit();
240
        } catch (Exception $e) {
241
            $this->repository->rollback();
242
            throw $e;
243
        }
244
    }
245
246
    /**
247
     * Deletes a trash item.
248
     *
249
     * The corresponding content object will be removed
250
     *
251
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete this trash item
252
     *
253
     * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem
254
     */
255
    public function deleteTrashItem(APITrashItem $trashItem)
256
    {
257
        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...
258
            throw new UnauthorizedException('content', 'cleantrash');
259
        }
260
261
        if (!is_numeric($trashItem->id)) {
262
            throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem');
263
        }
264
265
        $this->repository->beginTransaction();
266
        try {
267
            $this->persistenceHandler->trashHandler()->deleteTrashItem($trashItem->id);
268
            $this->repository->commit();
269
        } catch (Exception $e) {
270
            $this->repository->rollback();
271
            throw $e;
272
        }
273
    }
274
275
    /**
276
     * Returns a collection of Trashed locations contained in the trash, which are readable by the current user.
277
     *
278
     * $query allows to filter/sort the elements to be contained in the collection.
279
     *
280
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
281
     *
282
     * @return \eZ\Publish\API\Repository\Values\Content\Trash\SearchResult
283
     */
284
    public function findTrashItems(Query $query)
285
    {
286
        if ($query->filter !== null && !$query->filter instanceof Criterion) {
287
            throw new InvalidArgumentValue('query->filter', $query->filter, 'Query');
288
        }
289
290
        if ($query->sortClauses !== null) {
291
            if (!is_array($query->sortClauses)) {
292
                throw new InvalidArgumentValue('query->sortClauses', $query->sortClauses, 'Query');
293
            }
294
295
            foreach ($query->sortClauses as $sortClause) {
296
                if (!$sortClause instanceof SortClause) {
297
                    throw new InvalidArgumentValue('query->sortClauses', 'only instances of SortClause class are allowed');
298
                }
299
            }
300
        }
301
302 View Code Duplication
        if ($query->offset !== null && !is_numeric($query->offset)) {
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...
303
            throw new InvalidArgumentValue('query->offset', $query->offset, 'Query');
304
        }
305
306 View Code Duplication
        if ($query->limit !== null && !is_numeric($query->limit)) {
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...
307
            throw new InvalidArgumentValue('query->limit', $query->limit, 'Query');
308
        }
309
310
        $spiTrashItems = $this->persistenceHandler->trashHandler()->findTrashItems(
311
            $query->filter,
312
            $query->offset !== null && $query->offset > 0 ? (int)$query->offset : 0,
313
            $query->limit !== null && $query->limit >= 1 ? (int)$query->limit : null,
314
            $query->sortClauses
315
        );
316
317
        $trashItems = $this->buildDomainTrashItems($spiTrashItems);
318
        $searchResult = new SearchResult();
319
        $searchResult->totalCount = $searchResult->count = count($trashItems);
0 ignored issues
show
Deprecated Code introduced by
The property eZ\Publish\API\Repositor...sh\SearchResult::$count has been deprecated with message: Property is here purely for BC with 5.x/6.x.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
320
        $searchResult->items = $trashItems;
321
322
        return $searchResult;
323
    }
324
325
    protected function buildDomainTrashItems(array $spiTrashItems): array
326
    {
327
        $trashItems = array();
328
        // TODO: load content in bulk once API allows for it
329
        foreach ($spiTrashItems as $spiTrashItem) {
330
            try {
331
                $trashItems[] = $this->buildDomainTrashItemObject(
332
                    $spiTrashItem,
333
                    $this->repository->getContentService()->loadContent($spiTrashItem->contentId)
334
                );
335
            } catch (APIUnauthorizedException $e) {
336
                // Do nothing, thus exclude items the current user doesn't have read access to.
337
            }
338
        }
339
340
        return $trashItems;
341
    }
342
343 View Code Duplication
    protected function buildDomainTrashItemObject(Trashed $spiTrashItem, Content $content): APITrashItem
344
    {
345
        return new TrashItem(
346
            array(
347
                'content' => $content,
348
                'contentInfo' => $content->contentInfo,
349
                'id' => $spiTrashItem->id,
350
                'priority' => $spiTrashItem->priority,
351
                'hidden' => $spiTrashItem->hidden,
352
                'invisible' => $spiTrashItem->invisible,
353
                'remoteId' => $spiTrashItem->remoteId,
354
                'parentLocationId' => $spiTrashItem->parentId,
355
                'pathString' => $spiTrashItem->pathString,
356
                'depth' => $spiTrashItem->depth,
357
                'sortField' => $spiTrashItem->sortField,
358
                'sortOrder' => $spiTrashItem->sortOrder,
359
            )
360
        );
361
    }
362
363
    /**
364
     * @param int $timestamp
365
     *
366
     * @return \DateTime
367
     */
368
    protected function getDateTime($timestamp)
369
    {
370
        $dateTime = new DateTime();
371
        $dateTime->setTimestamp($timestamp);
372
373
        return $dateTime;
374
    }
375
}
376