Completed
Push — ezp-28439 ( b6528d )
by
unknown
19:13
created

TrashService   C

Complexity

Total Complexity 47

Size/Duplication

Total Lines 349
Duplicated Lines 16.91 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
dl 59
loc 349
c 0
b 0
f 0
rs 6.0316
wmc 47
lcom 1
cbo 17

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 1
A loadTrashItem() 0 14 3
B trash() 0 33 6
C recover() 0 48 10
A emptyTrash() 16 16 3
A deleteTrashItem() 19 19 4
D findTrashItems() 6 48 17
A buildDomainTrashItemObject() 18 18 1
A getDateTime() 0 7 1
A isContentInTrash() 0 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TrashService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TrashService, and based on these observations, apply Extract Interface, too.

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\Exceptions\NotFoundException;
12
use eZ\Publish\API\Repository\TrashService as TrashServiceInterface;
13
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
14
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
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
        if ($this->repository->hasAccess('content', 'restore') !== 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...
92
            throw new UnauthorizedException('content', 'restore');
93
        }
94
95
        $spiTrashItem = $this->persistenceHandler->trashHandler()->loadTrashItem($trashItemId);
96
        $trash = $this->buildDomainTrashItemObject($spiTrashItem);
97
        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...
98
            throw new UnauthorizedException('content', 'read');
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', 'manage_locations', $location->getContentInfo(), $location) !== true) {
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', 'manage_locations');
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->hasAccess('content', 'restore') !== 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...
177
            throw new UnauthorizedException('content', 'restore');
178
        }
179
180
        $this->repository->beginTransaction();
181
        try {
182
            $newParentLocationId = $newParentLocation ? $newParentLocation->id : $trashItem->parentLocationId;
183
            $newLocationId = $this->persistenceHandler->trashHandler()->recover(
184
                $trashItem->id,
185
                $newParentLocationId
186
            );
187
188
            $content = $this->repository->getContentService()->loadContent($trashItem->contentId);
189
            $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
190
191
            // Publish URL aliases for recovered location
192
            foreach ($urlAliasNames as $languageCode => $name) {
193
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
194
                    $newLocationId,
195
                    $newParentLocationId,
196
                    $name,
197
                    $languageCode,
198
                    $content->contentInfo->alwaysAvailable
199
                );
200
            }
201
202
            $this->repository->commit();
203
        } catch (Exception $e) {
204
            $this->repository->rollback();
205
            throw $e;
206
        }
207
208
        return $this->repository->getLocationService()->loadLocation($newLocationId);
209
    }
210
211
    /**
212
     * Empties trash.
213
     *
214
     * All locations contained in the trash will be removed. Content objects will be removed
215
     * if all locations of the content are gone.
216
     *
217
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to empty the trash
218
     */
219 View Code Duplication
    public function emptyTrash()
220
    {
221
        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...
222
            throw new UnauthorizedException('content', 'cleantrash');
223
        }
224
225
        $this->repository->beginTransaction();
226
        try {
227
            // Persistence layer takes care of deleting content objects
228
            $this->persistenceHandler->trashHandler()->emptyTrash();
229
            $this->repository->commit();
230
        } catch (Exception $e) {
231
            $this->repository->rollback();
232
            throw $e;
233
        }
234
    }
235
236
    /**
237
     * Deletes a trash item.
238
     *
239
     * The corresponding content object will be removed
240
     *
241
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete this trash item
242
     *
243
     * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem
244
     */
245 View Code Duplication
    public function deleteTrashItem(APITrashItem $trashItem)
246
    {
247
        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...
248
            throw new UnauthorizedException('content', 'cleantrash');
249
        }
250
251
        if (!is_numeric($trashItem->id)) {
252
            throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem');
253
        }
254
255
        $this->repository->beginTransaction();
256
        try {
257
            $this->persistenceHandler->trashHandler()->deleteTrashItem($trashItem->id);
258
            $this->repository->commit();
259
        } catch (Exception $e) {
260
            $this->repository->rollback();
261
            throw $e;
262
        }
263
    }
264
265
    /**
266
     * Returns a collection of Trashed locations contained in the trash, which are readable by the current user.
267
     *
268
     * $query allows to filter/sort the elements to be contained in the collection.
269
     *
270
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
271
     *
272
     * @return \eZ\Publish\API\Repository\Values\Content\Trash\SearchResult
273
     */
274
    public function findTrashItems(Query $query)
275
    {
276
        if ($query->filter !== null && !$query->filter instanceof Criterion) {
277
            throw new InvalidArgumentValue('query->filter', $query->filter, 'Query');
278
        }
279
280
        if ($query->sortClauses !== null) {
281
            if (!is_array($query->sortClauses)) {
282
                throw new InvalidArgumentValue('query->sortClauses', $query->sortClauses, 'Query');
283
            }
284
285
            foreach ($query->sortClauses as $sortClause) {
286
                if (!$sortClause instanceof SortClause) {
287
                    throw new InvalidArgumentValue('query->sortClauses', 'only instances of SortClause class are allowed');
288
                }
289
            }
290
        }
291
292 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...
293
            throw new InvalidArgumentValue('query->offset', $query->offset, 'Query');
294
        }
295
296 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...
297
            throw new InvalidArgumentValue('query->limit', $query->limit, 'Query');
298
        }
299
300
        $spiTrashItems = $this->persistenceHandler->trashHandler()->findTrashItems(
301
            $query->filter,
302
            $query->offset !== null && $query->offset > 0 ? (int)$query->offset : 0,
303
            $query->limit !== null && $query->limit >= 1 ? (int)$query->limit : null,
304
            $query->sortClauses
305
        );
306
307
        $trashItems = array();
308
        foreach ($spiTrashItems as $spiTrashItem) {
309
            try {
310
                $trashItems[] = $this->buildDomainTrashItemObject($spiTrashItem);
311
            } catch (UnauthorizedException $e) {
312
                // Do nothing, thus exclude items the current user doesn't have read access to.
313
            }
314
        }
315
316
        $searchResult = new SearchResult();
317
        $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...
318
        $searchResult->items = $trashItems;
319
320
        return $searchResult;
321
    }
322
323
    /**
324
     * Builds the domain TrashItem object from provided persistence trash item.
325
     *
326
     * @param \eZ\Publish\SPI\Persistence\Content\Location\Trashed $spiTrashItem
327
     *
328
     * @return \eZ\Publish\API\Repository\Values\Content\TrashItem
329
     */
330 View Code Duplication
    protected function buildDomainTrashItemObject(Trashed $spiTrashItem)
331
    {
332
        return new TrashItem(
333
            array(
334
                'contentInfo' => $this->repository->getContentService()->loadContentInfo($spiTrashItem->contentId),
335
                'id' => $spiTrashItem->id,
336
                'priority' => $spiTrashItem->priority,
337
                'hidden' => $spiTrashItem->hidden,
338
                'invisible' => $spiTrashItem->invisible,
339
                'remoteId' => $spiTrashItem->remoteId,
340
                'parentLocationId' => $spiTrashItem->parentId,
341
                'pathString' => $spiTrashItem->pathString,
342
                'depth' => $spiTrashItem->depth,
343
                'sortField' => $spiTrashItem->sortField,
344
                'sortOrder' => $spiTrashItem->sortOrder,
345
            )
346
        );
347
    }
348
349
    /**
350
     * @param int $timestamp
351
     *
352
     * @return \DateTime
353
     */
354
    protected function getDateTime($timestamp)
355
    {
356
        $dateTime = new DateTime();
357
        $dateTime->setTimestamp($timestamp);
358
359
        return $dateTime;
360
    }
361
362
    /**
363
     * Checks if Content is in trash when we can't rely only on locationId (ie. because ContentId is all we have,
364
     * and Content in trash is not part of a tree anymore, so does not have mainLocationId.
365
     *
366
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
0 ignored issues
show
Bug introduced by
There is no parameter named $contentInfo. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
367
     *
368
     * @return bool
369
     */
370
    public function isContentInTrash($contentId)
371
    {
372
        $result = $this->repository->sudo(
373
            function () use ($contentId) {
374
                return $this->persistenceHandler->trashHandler()->loadTrashItemsByContentId($contentId);
375
            }
376
        );
377
378
        return !empty($result);
379
    }
380
}
381