Completed
Push — master ( 65ba07...f0ec2b )
by André
104:16 queued 82:47
created

ContentService::removeTranslation()   B

Complexity

Conditions 8
Paths 15

Size

Total Lines 59
Code Lines 33

Duplication

Lines 10
Ratio 16.95 %

Importance

Changes 0
Metric Value
cc 8
eloc 33
nc 15
nop 2
dl 10
loc 59
rs 7.132
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\ContentService 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\ContentService as ContentServiceInterface;
12
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
13
use eZ\Publish\SPI\Persistence\Handler;
14
use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct as APIContentUpdateStruct;
15
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
16
use eZ\Publish\API\Repository\Values\Content\TranslationInfo;
17
use eZ\Publish\API\Repository\Values\Content\TranslationValues as APITranslationValues;
18
use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct;
19
use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct;
20
use eZ\Publish\API\Repository\Values\Content\Content as APIContent;
21
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
22
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
23
use eZ\Publish\API\Repository\Values\User\User;
24
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
25
use eZ\Publish\API\Repository\Values\Content\Field;
26
use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation;
27
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
28
use eZ\Publish\Core\Base\Exceptions\BadStateException;
29
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
30
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
31
use eZ\Publish\Core\Base\Exceptions\ContentValidationException;
32
use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException;
33
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
34
use eZ\Publish\Core\FieldType\ValidationError;
35
use eZ\Publish\Core\Repository\Values\Content\VersionInfo;
36
use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct;
37
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
38
use eZ\Publish\Core\Repository\Values\Content\TranslationValues;
39
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct;
40
use eZ\Publish\SPI\Persistence\Content\CreateStruct as SPIContentCreateStruct;
41
use eZ\Publish\SPI\Persistence\Content\UpdateStruct as SPIContentUpdateStruct;
42
use eZ\Publish\SPI\Persistence\Content\Field as SPIField;
43
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as SPIRelationCreateStruct;
44
use Exception;
45
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
46
47
/**
48
 * This class provides service methods for managing content.
49
 *
50
 * @example Examples/content.php
51
 */
52
class ContentService implements ContentServiceInterface
53
{
54
    /**
55
     * @var \eZ\Publish\Core\Repository\Repository
56
     */
57
    protected $repository;
58
59
    /**
60
     * @var \eZ\Publish\SPI\Persistence\Handler
61
     */
62
    protected $persistenceHandler;
63
64
    /**
65
     * @var array
66
     */
67
    protected $settings;
68
69
    /**
70
     * @var \eZ\Publish\Core\Repository\Helper\DomainMapper
71
     */
72
    protected $domainMapper;
73
74
    /**
75
     * @var \eZ\Publish\Core\Repository\Helper\RelationProcessor
76
     */
77
    protected $relationProcessor;
78
79
    /**
80
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
81
     */
82
    protected $nameSchemaService;
83
84
    /**
85
     * @var \eZ\Publish\Core\Repository\Helper\FieldTypeRegistry
86
     */
87
    protected $fieldTypeRegistry;
88
89
    /**
90
     * Setups service with reference to repository object that created it & corresponding handler.
91
     *
92
     * @param \eZ\Publish\API\Repository\Repository $repository
93
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
94
     * @param \eZ\Publish\Core\Repository\Helper\DomainMapper $domainMapper
95
     * @param \eZ\Publish\Core\Repository\Helper\RelationProcessor $relationProcessor
96
     * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService
97
     * @param \eZ\Publish\Core\Repository\Helper\FieldTypeRegistry $fieldTypeRegistry,
0 ignored issues
show
Documentation introduced by
There is no parameter named $fieldTypeRegistry,. Did you maybe mean $fieldTypeRegistry?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
98
     * @param array $settings
99
     */
100
    public function __construct(
101
        RepositoryInterface $repository,
102
        Handler $handler,
103
        Helper\DomainMapper $domainMapper,
104
        Helper\RelationProcessor $relationProcessor,
105
        Helper\NameSchemaService $nameSchemaService,
106
        Helper\FieldTypeRegistry $fieldTypeRegistry,
107
        array $settings = array()
108
    ) {
109
        $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...
110
        $this->persistenceHandler = $handler;
111
        $this->domainMapper = $domainMapper;
112
        $this->relationProcessor = $relationProcessor;
113
        $this->nameSchemaService = $nameSchemaService;
114
        $this->fieldTypeRegistry = $fieldTypeRegistry;
115
        // Union makes sure default settings are ignored if provided in argument
116
        $this->settings = $settings + array(
117
            // Version archive limit (0-50), only enforced on publish, not on un-publish.
118
            'default_version_archive_limit' => 5,
119
        );
120
    }
121
122
    /**
123
     * Loads a content info object.
124
     *
125
     * To load fields use loadContent
126
     *
127
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content
128
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist
129
     *
130
     * @param int $contentId
131
     *
132
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
133
     */
134 View Code Duplication
    public function loadContentInfo($contentId)
135
    {
136
        $contentInfo = $this->internalLoadContentInfo($contentId);
137
        if (!$this->repository->canUser('content', 'read', $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...
138
            throw new UnauthorizedException('content', 'read', array('contentId' => $contentId));
139
        }
140
141
        return $contentInfo;
142
    }
143
144
    /**
145
     * Loads a content info object.
146
     *
147
     * To load fields use loadContent
148
     *
149
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist
150
     *
151
     * @param mixed $id
152
     * @param bool $isRemoteId
153
     *
154
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
155
     */
156
    public function internalLoadContentInfo($id, $isRemoteId = false)
157
    {
158
        try {
159
            $method = $isRemoteId ? 'loadContentInfoByRemoteId' : 'loadContentInfo';
160
161
            return $this->domainMapper->buildContentInfoDomainObject(
162
                $this->persistenceHandler->contentHandler()->$method($id)
163
            );
164
        } catch (APINotFoundException $e) {
165
            throw new NotFoundException(
166
                'Content',
167
                $id,
168
                $e
169
            );
170
        }
171
    }
172
173
    /**
174
     * Loads a content info object for the given remoteId.
175
     *
176
     * To load fields use loadContent
177
     *
178
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content
179
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given remote id does not exist
180
     *
181
     * @param string $remoteId
182
     *
183
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
184
     */
185 View Code Duplication
    public function loadContentInfoByRemoteId($remoteId)
186
    {
187
        $contentInfo = $this->internalLoadContentInfo($remoteId, true);
188
189
        if (!$this->repository->canUser('content', 'read', $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...
190
            throw new UnauthorizedException('content', 'read', array('remoteId' => $remoteId));
191
        }
192
193
        return $contentInfo;
194
    }
195
196
    /**
197
     * Loads a version info of the given content object.
198
     *
199
     * If no version number is given, the method returns the current version
200
     *
201
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist
202
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
203
     *
204
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
205
     * @param int $versionNo the version number. If not given the current version is returned.
206
     *
207
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo
208
     */
209
    public function loadVersionInfo(ContentInfo $contentInfo, $versionNo = null)
210
    {
211
        return $this->loadVersionInfoById($contentInfo->id, $versionNo);
212
    }
213
214
    /**
215
     * Loads a version info of the given content object id.
216
     *
217
     * If no version number is given, the method returns the current version
218
     *
219
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist
220
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
221
     *
222
     * @param mixed $contentId
223
     * @param int $versionNo the version number. If not given the current version is returned.
224
     *
225
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo
226
     */
227
    public function loadVersionInfoById($contentId, $versionNo = null)
228
    {
229
        if ($versionNo === null) {
230
            $versionNo = $this->loadContentInfo($contentId)->currentVersionNo;
231
        }
232
233
        try {
234
            $spiVersionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo(
235
                $contentId,
236
                $versionNo
237
            );
238
        } catch (APINotFoundException $e) {
239
            throw new NotFoundException(
240
                'VersionInfo',
241
                array(
242
                    'contentId' => $contentId,
243
                    'versionNo' => $versionNo,
244
                ),
245
                $e
246
            );
247
        }
248
249
        $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
250
251
        if ($versionInfo->isPublished()) {
252
            $function = 'read';
253
        } else {
254
            $function = 'versionread';
255
        }
256
257
        if (!$this->repository->canUser('content', $function, $versionInfo)) {
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', $function, array('contentId' => $contentId));
259
        }
260
261
        return $versionInfo;
262
    }
263
264
    /**
265
     * {@inheritdoc}
266
     */
267
    public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
268
    {
269
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
270
        if ($useAlwaysAvailable && !$contentInfo->alwaysAvailable) {
271
            $useAlwaysAvailable = false;
272
        }
273
274
        // As we have content info we can avoid that current version is looked up using spi in loadContent() if not set
275
        if ($versionNo === null) {
276
            $versionNo = $contentInfo->currentVersionNo;
277
        }
278
279
        return $this->loadContent(
280
            $contentInfo->id,
281
            $languages,
282
            $versionNo,
283
            $useAlwaysAvailable
284
        );
285
    }
286
287
    /**
288
     * {@inheritdoc}
289
     */
290
    public function loadContentByVersionInfo(APIVersionInfo $versionInfo, array $languages = null, $useAlwaysAvailable = true)
291
    {
292
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
293
        if ($useAlwaysAvailable && !$versionInfo->getContentInfo()->alwaysAvailable) {
294
            $useAlwaysAvailable = false;
295
        }
296
297
        return $this->loadContent(
298
            $versionInfo->getContentInfo()->id,
299
            $languages,
300
            $versionInfo->versionNo,
301
            $useAlwaysAvailable
302
        );
303
    }
304
305
    /**
306
     * {@inheritdoc}
307
     */
308 View Code Duplication
    public function loadContent($contentId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
309
    {
310
        $content = $this->internalLoadContent($contentId, $languages, $versionNo, false, $useAlwaysAvailable);
311
312
        if (!$this->repository->canUser('content', 'read', $content)) {
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...
313
            throw new UnauthorizedException('content', 'read', array('contentId' => $contentId));
314
        }
315
        if (
316
            !$content->getVersionInfo()->isPublished()
317
            && !$this->repository->canUser('content', 'versionread', $content)
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...
318
        ) {
319
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentId, 'versionNo' => $versionNo));
320
        }
321
322
        return $content;
323
    }
324
325
    /**
326
     * Loads content in a version of the given content object.
327
     *
328
     * If no version number is given, the method returns the current version
329
     *
330
     * @internal
331
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist
332
     *
333
     * @param mixed $id
334
     * @param array|null $languages A language priority, filters returned fields and is used as prioritized language code on
335
     *                         returned value object. If not given all languages are returned.
336
     * @param int|null $versionNo the version number. If not given the current version is returned
337
     * @param bool $isRemoteId
338
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
339
     *
340
     * @return \eZ\Publish\API\Repository\Values\Content\Content
341
     */
342
    public function internalLoadContent($id, array $languages = null, $versionNo = null, $isRemoteId = false, $useAlwaysAvailable = true)
343
    {
344
        try {
345
            // Get Content ID if lookup by remote ID
346
            if ($isRemoteId) {
347
                $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($id);
348
                $id = $spiContentInfo->id;
349
            }
350
351
            // Get current version if $versionNo is not defined
352
            if ($versionNo === null) {
353
                if (!isset($spiContentInfo)) {
354
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
355
                }
356
357
                $versionNo = $spiContentInfo->currentVersionNo;
358
            }
359
360
            $loadLanguages = $languages;
361
            $alwaysAvailableLanguageCode = null;
362
            // Set main language on $languages filter if not empty (all) and $useAlwaysAvailable being true
363
            if (!empty($loadLanguages) && $useAlwaysAvailable) {
364
                if (!isset($spiContentInfo)) {
365
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
366
                }
367
368
                if ($spiContentInfo->alwaysAvailable) {
369
                    $loadLanguages[] = $alwaysAvailableLanguageCode = $spiContentInfo->mainLanguageCode;
370
                    $loadLanguages = array_unique($loadLanguages);
371
                }
372
            }
373
374
            $spiContent = $this->persistenceHandler->contentHandler()->load(
375
                $id,
376
                $versionNo,
377
                $loadLanguages
0 ignored issues
show
Bug introduced by
It seems like $loadLanguages defined by $languages on line 360 can also be of type array; however, eZ\Publish\SPI\Persistence\Content\Handler::load() does only seem to accept null|array<integer,string>, 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...
378
            );
379
        } catch (APINotFoundException $e) {
380
            throw new NotFoundException(
381
                'Content',
382
                array(
383
                    $isRemoteId ? 'remoteId' : 'id' => $id,
384
                    'languages' => $languages,
385
                    'versionNo' => $versionNo,
386
                ),
387
                $e
388
            );
389
        }
390
391
        return $this->domainMapper->buildContentDomainObject(
392
            $spiContent,
393
            null,
394
            empty($languages) ? null : $languages,
395
            $alwaysAvailableLanguageCode
396
        );
397
    }
398
399
    /**
400
     * Loads content in a version for the content object reference by the given remote id.
401
     *
402
     * If no version is given, the method returns the current version
403
     *
404
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist
405
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions
406
     *
407
     * @param string $remoteId
408
     * @param array $languages A language filter for fields. If not given all languages are returned
409
     * @param int $versionNo the version number. If not given the current version is returned
410
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
411
     *
412
     * @return \eZ\Publish\API\Repository\Values\Content\Content
413
     */
414 View Code Duplication
    public function loadContentByRemoteId($remoteId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
415
    {
416
        $content = $this->internalLoadContent($remoteId, $languages, $versionNo, true, $useAlwaysAvailable);
417
418
        if (!$this->repository->canUser('content', 'read', $content)) {
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...
419
            throw new UnauthorizedException('content', 'read', array('remoteId' => $remoteId));
420
        }
421
422
        if (
423
            !$content->getVersionInfo()->isPublished()
424
            && !$this->repository->canUser('content', 'versionread', $content)
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...
425
        ) {
426
            throw new UnauthorizedException('content', 'versionread', array('remoteId' => $remoteId, 'versionNo' => $versionNo));
427
        }
428
429
        return $content;
430
    }
431
432
    /**
433
     * Creates a new content draft assigned to the authenticated user.
434
     *
435
     * If a different userId is given in $contentCreateStruct it is assigned to the given user
436
     * but this required special rights for the authenticated user
437
     * (this is useful for content staging where the transfer process does not
438
     * have to authenticate with the user which created the content object in the source server).
439
     * The user has to publish the draft if it should be visible.
440
     * In 4.x at least one location has to be provided in the location creation array.
441
     *
442
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location
443
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the provided remoteId exists in the system, required properties on
444
     *                                                                        struct are missing or invalid, or if multiple locations are under the
445
     *                                                                        same parent.
446
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
447
     *                                                                               or if a required field is missing / set to an empty value.
448
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
449
     *                                                                          or value is set for non-translatable field in language
450
     *                                                                          other than main.
451
     *
452
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
453
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs For each location parent under which a location should be created for the content
454
     *
455
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
456
     */
457
    public function createContent(APIContentCreateStruct $contentCreateStruct, array $locationCreateStructs = array())
458
    {
459
        if ($contentCreateStruct->mainLanguageCode === null) {
460
            throw new InvalidArgumentException('$contentCreateStruct', "'mainLanguageCode' property must be set");
461
        }
462
463
        if ($contentCreateStruct->contentType === null) {
464
            throw new InvalidArgumentException('$contentCreateStruct', "'contentType' property must be set");
465
        }
466
467
        $contentCreateStruct = clone $contentCreateStruct;
468
469
        if ($contentCreateStruct->ownerId === null) {
470
            $contentCreateStruct->ownerId = $this->repository->getCurrentUserReference()->getUserId();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Reposito...tCurrentUserReference() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user reference.

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...
471
        }
472
473
        if ($contentCreateStruct->alwaysAvailable === null) {
474
            $contentCreateStruct->alwaysAvailable = false;
475
        }
476
477
        $contentCreateStruct->contentType = $this->repository->getContentTypeService()->loadContentType(
478
            $contentCreateStruct->contentType->id
479
        );
480
481
        if (empty($contentCreateStruct->sectionId)) {
482
            if (isset($locationCreateStructs[0])) {
483
                $location = $this->repository->getLocationService()->loadLocation(
484
                    $locationCreateStructs[0]->parentLocationId
485
                );
486
                $contentCreateStruct->sectionId = $location->contentInfo->sectionId;
487
            } else {
488
                $contentCreateStruct->sectionId = 1;
489
            }
490
        }
491
492
        if (!$this->repository->canUser('content', 'create', $contentCreateStruct, $locationCreateStructs)) {
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...
493
            throw new UnauthorizedException(
494
                'content',
495
                'create',
496
                array(
497
                    'parentLocationId' => isset($locationCreateStructs[0]) ?
498
                            $locationCreateStructs[0]->parentLocationId :
499
                            null,
500
                    'sectionId' => $contentCreateStruct->sectionId,
501
                )
502
            );
503
        }
504
505
        if (!empty($contentCreateStruct->remoteId)) {
506
            try {
507
                $this->loadContentByRemoteId($contentCreateStruct->remoteId);
508
509
                throw new InvalidArgumentException(
510
                    '$contentCreateStruct',
511
                    "Another content with remoteId '{$contentCreateStruct->remoteId}' exists"
512
                );
513
            } catch (APINotFoundException $e) {
514
                // Do nothing
515
            }
516
        } else {
517
            $contentCreateStruct->remoteId = $this->domainMapper->getUniqueHash($contentCreateStruct);
518
        }
519
520
        $spiLocationCreateStructs = $this->buildSPILocationCreateStructs($locationCreateStructs);
0 ignored issues
show
Documentation introduced by
$locationCreateStructs is of type array<integer,object<eZ\...tionCreateStruct>|null>, but the function expects a array<integer,object<eZ\...\LocationCreateStruct>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
521
522
        $languageCodes = $this->getLanguageCodesForCreate($contentCreateStruct);
523
        $fields = $this->mapFieldsForCreate($contentCreateStruct);
524
525
        $fieldValues = array();
526
        $spiFields = array();
527
        $allFieldErrors = array();
528
        $inputRelations = array();
529
        $locationIdToContentIdMapping = array();
530
531
        foreach ($contentCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) {
532
            /** @var $fieldType \eZ\Publish\Core\FieldType\FieldType */
533
            $fieldType = $this->fieldTypeRegistry->getFieldType(
534
                $fieldDefinition->fieldTypeIdentifier
535
            );
536
537
            foreach ($languageCodes as $languageCode) {
538
                $isEmptyValue = false;
539
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $contentCreateStruct->mainLanguageCode;
540
                $isLanguageMain = $languageCode === $contentCreateStruct->mainLanguageCode;
541
                if (isset($fields[$fieldDefinition->identifier][$valueLanguageCode])) {
542
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
543
                } else {
544
                    $fieldValue = $fieldDefinition->defaultValue;
0 ignored issues
show
Documentation introduced by
The property $defaultValue is declared protected in eZ\Publish\API\Repositor...entType\FieldDefinition. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
545
                }
546
547
                $fieldValue = $fieldType->acceptValue($fieldValue);
548
549 View Code Duplication
                if ($fieldType->isEmptyValue($fieldValue)) {
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...
550
                    $isEmptyValue = true;
551
                    if ($fieldDefinition->isRequired) {
552
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
553
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
554
                            null,
555
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
556
                            'empty'
557
                        );
558
                    }
559
                } else {
560
                    $fieldErrors = $fieldType->validate(
561
                        $fieldDefinition,
562
                        $fieldValue
563
                    );
564
                    if (!empty($fieldErrors)) {
565
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
566
                    }
567
                }
568
569
                if (!empty($allFieldErrors)) {
570
                    continue;
571
                }
572
573
                $this->relationProcessor->appendFieldRelations(
574
                    $inputRelations,
575
                    $locationIdToContentIdMapping,
576
                    $fieldType,
577
                    $fieldValue,
578
                    $fieldDefinition->id
579
                );
580
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
581
582
                // Only non-empty value for: translatable field or in main language
583
                if (
584
                    (!$isEmptyValue && $fieldDefinition->isTranslatable) ||
585
                    (!$isEmptyValue && $isLanguageMain)
586
                ) {
587
                    $spiFields[] = new SPIField(
588
                        array(
589
                            'id' => null,
590
                            'fieldDefinitionId' => $fieldDefinition->id,
591
                            'type' => $fieldDefinition->fieldTypeIdentifier,
592
                            'value' => $fieldType->toPersistenceValue($fieldValue),
593
                            'languageCode' => $languageCode,
594
                            'versionNo' => null,
595
                        )
596
                    );
597
                }
598
            }
599
        }
600
601
        if (!empty($allFieldErrors)) {
602
            throw new ContentFieldValidationException($allFieldErrors);
603
        }
604
605
        $spiContentCreateStruct = new SPIContentCreateStruct(
606
            array(
607
                'name' => $this->nameSchemaService->resolve(
608
                    $contentCreateStruct->contentType->nameSchema,
609
                    $contentCreateStruct->contentType,
610
                    $fieldValues,
611
                    $languageCodes
612
                ),
613
                'typeId' => $contentCreateStruct->contentType->id,
614
                'sectionId' => $contentCreateStruct->sectionId,
615
                'ownerId' => $contentCreateStruct->ownerId,
616
                'locations' => $spiLocationCreateStructs,
617
                'fields' => $spiFields,
618
                'alwaysAvailable' => $contentCreateStruct->alwaysAvailable,
619
                'remoteId' => $contentCreateStruct->remoteId,
620
                'modified' => isset($contentCreateStruct->modificationDate) ? $contentCreateStruct->modificationDate->getTimestamp() : time(),
621
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
622
                    $contentCreateStruct->mainLanguageCode
623
                )->id,
624
            )
625
        );
626
627
        $defaultObjectStates = $this->getDefaultObjectStates();
628
629
        $this->repository->beginTransaction();
630
        try {
631
            $spiContent = $this->persistenceHandler->contentHandler()->create($spiContentCreateStruct);
632
            $this->relationProcessor->processFieldRelations(
633
                $inputRelations,
634
                $spiContent->versionInfo->contentInfo->id,
635
                $spiContent->versionInfo->versionNo,
636
                $contentCreateStruct->contentType
637
            );
638
639
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
640
                $this->persistenceHandler->objectStateHandler()->setContentState(
641
                    $spiContent->versionInfo->contentInfo->id,
642
                    $objectStateGroupId,
643
                    $objectState->id
644
                );
645
            }
646
647
            $this->repository->commit();
648
        } catch (Exception $e) {
649
            $this->repository->rollback();
650
            throw $e;
651
        }
652
653
        return $this->domainMapper->buildContentDomainObject($spiContent);
654
    }
655
656
    /**
657
     * Returns an array of default content states with content state group id as key.
658
     *
659
     * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[]
660
     */
661
    protected function getDefaultObjectStates()
662
    {
663
        $defaultObjectStatesMap = array();
664
        $objectStateHandler = $this->persistenceHandler->objectStateHandler();
665
666
        foreach ($objectStateHandler->loadAllGroups() as $objectStateGroup) {
667
            foreach ($objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) {
668
                // Only register the first object state which is the default one.
669
                $defaultObjectStatesMap[$objectStateGroup->id] = $objectState;
670
                break;
671
            }
672
        }
673
674
        return $defaultObjectStatesMap;
675
    }
676
677
    /**
678
     * Returns all language codes used in given $fields.
679
     *
680
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value is set in main language
681
     *
682
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
683
     *
684
     * @return string[]
685
     */
686
    protected function getLanguageCodesForCreate(APIContentCreateStruct $contentCreateStruct)
687
    {
688
        $languageCodes = array();
689
690 View Code Duplication
        foreach ($contentCreateStruct->fields as $field) {
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...
691
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
692
                continue;
693
            }
694
695
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
696
                $field->languageCode
697
            );
698
            $languageCodes[$field->languageCode] = true;
699
        }
700
701
        if (!isset($languageCodes[$contentCreateStruct->mainLanguageCode])) {
702
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
703
                $contentCreateStruct->mainLanguageCode
704
            );
705
            $languageCodes[$contentCreateStruct->mainLanguageCode] = true;
706
        }
707
708
        return array_keys($languageCodes);
709
    }
710
711
    /**
712
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
713
     *
714
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
715
     *                                                                          or value is set for non-translatable field in language
716
     *                                                                          other than main
717
     *
718
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
719
     *
720
     * @return array
721
     */
722
    protected function mapFieldsForCreate(APIContentCreateStruct $contentCreateStruct)
723
    {
724
        $fields = array();
725
726
        foreach ($contentCreateStruct->fields as $field) {
727
            $fieldDefinition = $contentCreateStruct->contentType->getFieldDefinition($field->fieldDefIdentifier);
728
729
            if ($fieldDefinition === null) {
730
                throw new ContentValidationException(
731
                    "Field definition '%identifier%' does not exist in given ContentType",
732
                    ['%identifier%' => $field->fieldDefIdentifier]
733
                );
734
            }
735
736
            if ($field->languageCode === null) {
737
                $field = $this->cloneField(
738
                    $field,
739
                    array('languageCode' => $contentCreateStruct->mainLanguageCode)
740
                );
741
            }
742
743 View Code Duplication
            if (!$fieldDefinition->isTranslatable && ($field->languageCode != $contentCreateStruct->mainLanguageCode)) {
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...
744
                throw new ContentValidationException(
745
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
746
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
747
                );
748
            }
749
750
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
751
        }
752
753
        return $fields;
754
    }
755
756
    /**
757
     * Clones $field with overriding specific properties from given $overrides array.
758
     *
759
     * @param Field $field
760
     * @param array $overrides
761
     *
762
     * @return Field
763
     */
764
    private function cloneField(Field $field, array $overrides = array())
765
    {
766
        $fieldData = array_merge(
767
            array(
768
                'id' => $field->id,
769
                'value' => $field->value,
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
770
                'languageCode' => $field->languageCode,
771
                'fieldDefIdentifier' => $field->fieldDefIdentifier,
772
            ),
773
            $overrides
774
        );
775
776
        return new Field($fieldData);
777
    }
778
779
    /**
780
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
781
     *
782
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
783
     *
784
     * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct[]
785
     */
786
    protected function buildSPILocationCreateStructs(array $locationCreateStructs)
787
    {
788
        $spiLocationCreateStructs = array();
789
        $parentLocationIdSet = array();
790
        $mainLocation = true;
791
792
        foreach ($locationCreateStructs as $locationCreateStruct) {
793
            if (isset($parentLocationIdSet[$locationCreateStruct->parentLocationId])) {
794
                throw new InvalidArgumentException(
795
                    '$locationCreateStructs',
796
                    "Multiple LocationCreateStructs with the same parent Location '{$locationCreateStruct->parentLocationId}' are given"
797
                );
798
            }
799
800
            $parentLocationIdSet[$locationCreateStruct->parentLocationId] = true;
801
            $parentLocation = $this->repository->getLocationService()->loadLocation(
802
                $locationCreateStruct->parentLocationId
803
            );
804
805
            $spiLocationCreateStructs[] = $this->domainMapper->buildSPILocationCreateStruct(
806
                $locationCreateStruct,
807
                $parentLocation,
808
                $mainLocation,
809
                // For Content draft contentId and contentVersionNo are set in ContentHandler upon draft creation
810
                null,
811
                null
812
            );
813
814
            // First Location in the list will be created as main Location
815
            $mainLocation = false;
816
        }
817
818
        return $spiLocationCreateStructs;
819
    }
820
821
    /**
822
     * Updates the metadata.
823
     *
824
     * (see {@link ContentMetadataUpdateStruct}) of a content object - to update fields use updateContent
825
     *
826
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data
827
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists
828
     *
829
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
830
     * @param \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct
831
     *
832
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content with the updated attributes
833
     */
834
    public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct)
835
    {
836
        $propertyCount = 0;
837
        foreach ($contentMetadataUpdateStruct as $propertyName => $propertyValue) {
0 ignored issues
show
Bug introduced by
The expression $contentMetadataUpdateStruct of type object<eZ\Publish\API\Re...ntMetadataUpdateStruct> is not traversable.
Loading history...
838
            if (isset($contentMetadataUpdateStruct->$propertyName)) {
839
                $propertyCount += 1;
840
            }
841
        }
842
        if ($propertyCount === 0) {
843
            throw new InvalidArgumentException(
844
                '$contentMetadataUpdateStruct',
845
                'At least one property must be set'
846
            );
847
        }
848
849
        $loadedContentInfo = $this->loadContentInfo($contentInfo->id);
850
851
        if (!$this->repository->canUser('content', 'edit', $loadedContentInfo)) {
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...
852
            throw new UnauthorizedException('content', 'edit', array('contentId' => $loadedContentInfo->id));
853
        }
854
855
        if (isset($contentMetadataUpdateStruct->remoteId)) {
856
            try {
857
                $existingContentInfo = $this->loadContentInfoByRemoteId($contentMetadataUpdateStruct->remoteId);
858
859
                if ($existingContentInfo->id !== $loadedContentInfo->id) {
860
                    throw new InvalidArgumentException(
861
                        '$contentMetadataUpdateStruct',
862
                        "Another content with remoteId '{$contentMetadataUpdateStruct->remoteId}' exists"
863
                    );
864
                }
865
            } catch (APINotFoundException $e) {
866
                // Do nothing
867
            }
868
        }
869
870
        $this->repository->beginTransaction();
871
        try {
872
            if ($propertyCount > 1 || !isset($contentMetadataUpdateStruct->mainLocationId)) {
873
                $this->persistenceHandler->contentHandler()->updateMetadata(
874
                    $loadedContentInfo->id,
875
                    new SPIMetadataUpdateStruct(
876
                        array(
877
                            'ownerId' => $contentMetadataUpdateStruct->ownerId,
878
                            'publicationDate' => isset($contentMetadataUpdateStruct->publishedDate) ?
879
                                $contentMetadataUpdateStruct->publishedDate->getTimestamp() :
880
                                null,
881
                            'modificationDate' => isset($contentMetadataUpdateStruct->modificationDate) ?
882
                                $contentMetadataUpdateStruct->modificationDate->getTimestamp() :
883
                                null,
884
                            'mainLanguageId' => isset($contentMetadataUpdateStruct->mainLanguageCode) ?
885
                                $this->repository->getContentLanguageService()->loadLanguage(
886
                                    $contentMetadataUpdateStruct->mainLanguageCode
887
                                )->id :
888
                                null,
889
                            'alwaysAvailable' => $contentMetadataUpdateStruct->alwaysAvailable,
890
                            'remoteId' => $contentMetadataUpdateStruct->remoteId,
891
                        )
892
                    )
893
                );
894
            }
895
896
            // Change main location
897
            if (isset($contentMetadataUpdateStruct->mainLocationId)
898
                && $loadedContentInfo->mainLocationId !== $contentMetadataUpdateStruct->mainLocationId) {
899
                $this->persistenceHandler->locationHandler()->changeMainLocation(
900
                    $loadedContentInfo->id,
901
                    $contentMetadataUpdateStruct->mainLocationId
902
                );
903
            }
904
905
            // Republish URL aliases to update always-available flag
906
            if (isset($contentMetadataUpdateStruct->alwaysAvailable)
907
                && $loadedContentInfo->alwaysAvailable !== $contentMetadataUpdateStruct->alwaysAvailable) {
908
                $content = $this->loadContent($loadedContentInfo->id);
909
                $this->publishUrlAliasesForContent($content, false);
910
            }
911
912
            $this->repository->commit();
913
        } catch (Exception $e) {
914
            $this->repository->rollback();
915
            throw $e;
916
        }
917
918
        return isset($content) ? $content : $this->loadContent($loadedContentInfo->id);
919
    }
920
921
    /**
922
     * Publishes URL aliases for all locations of a given content.
923
     *
924
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
925
     * @param bool $updatePathIdentificationString this parameter is legacy storage specific for updating
926
     *                      ezcontentobject_tree.path_identification_string, it is ignored by other storage engines
927
     */
928
    protected function publishUrlAliasesForContent(APIContent $content, $updatePathIdentificationString = true)
929
    {
930
        $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
931
        $locations = $this->repository->getLocationService()->loadLocations(
932
            $content->getVersionInfo()->getContentInfo()
933
        );
934
        foreach ($locations as $location) {
935
            foreach ($urlAliasNames as $languageCode => $name) {
936
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
937
                    $location->id,
938
                    $location->parentLocationId,
939
                    $name,
940
                    $languageCode,
941
                    $content->contentInfo->alwaysAvailable,
942
                    $updatePathIdentificationString ? $languageCode === $content->contentInfo->mainLanguageCode : false
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...
943
                );
944
            }
945
        }
946
    }
947
948
    /**
949
     * Deletes a content object including all its versions and locations including their subtrees.
950
     *
951
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete the content (in one of the locations of the given content object)
952
     *
953
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
954
     *
955
     * @return mixed[] Affected Location Id's
956
     */
957
    public function deleteContent(ContentInfo $contentInfo)
958
    {
959
        $contentInfo = $this->internalLoadContentInfo($contentInfo->id);
960
961
        if (!$this->repository->canUser('content', 'remove', $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...
962
            throw new UnauthorizedException('content', 'remove', array('contentId' => $contentInfo->id));
963
        }
964
965
        $affectedLocations = [];
966
        $this->repository->beginTransaction();
967
        try {
968
            // Load Locations first as deleting Content also deletes belonging Locations
969
            $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($contentInfo->id);
970
            $this->persistenceHandler->contentHandler()->deleteContent($contentInfo->id);
971
            foreach ($spiLocations as $spiLocation) {
972
                $this->persistenceHandler->urlAliasHandler()->locationDeleted($spiLocation->id);
973
                $affectedLocations[] = $spiLocation->id;
974
            }
975
            $this->repository->commit();
976
        } catch (Exception $e) {
977
            $this->repository->rollback();
978
            throw $e;
979
        }
980
981
        return $affectedLocations;
982
    }
983
984
    /**
985
     * Creates a draft from a published or archived version.
986
     *
987
     * If no version is given, the current published version is used.
988
     * 4.x: The draft is created with the initialLanguage code of the source version or if not present with the main language.
989
     * It can be changed on updating the version.
990
     *
991
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft
992
     *
993
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
994
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
995
     * @param \eZ\Publish\API\Repository\Values\User\User $creator if set given user is used to create the draft - otherwise the current-user is used
996
     *
997
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
998
     */
999
    public function createContentDraft(ContentInfo $contentInfo, APIVersionInfo $versionInfo = null, User $creator = null)
1000
    {
1001
        $contentInfo = $this->loadContentInfo($contentInfo->id);
1002
1003
        if ($versionInfo !== null) {
1004
            // Check that given $contentInfo and $versionInfo belong to the same content
1005
            if ($versionInfo->getContentInfo()->id != $contentInfo->id) {
1006
                throw new InvalidArgumentException(
1007
                    '$versionInfo',
1008
                    'VersionInfo does not belong to the same content as given ContentInfo'
1009
                );
1010
            }
1011
1012
            $versionInfo = $this->loadVersionInfoById($contentInfo->id, $versionInfo->versionNo);
1013
1014
            switch ($versionInfo->status) {
1015
                case VersionInfo::STATUS_PUBLISHED:
1016
                case VersionInfo::STATUS_ARCHIVED:
1017
                    break;
1018
1019
                default:
1020
                    // @todo: throw an exception here, to be defined
1021
                    throw new BadStateException(
1022
                        '$versionInfo',
1023
                        'Draft can not be created from a draft version'
1024
                    );
1025
            }
1026
1027
            $versionNo = $versionInfo->versionNo;
1028
        } elseif ($contentInfo->published) {
1029
            $versionNo = $contentInfo->currentVersionNo;
1030
        } else {
1031
            // @todo: throw an exception here, to be defined
1032
            throw new BadStateException(
1033
                '$contentInfo',
1034
                'Content is not published, draft can be created only from published or archived version'
1035
            );
1036
        }
1037
1038
        if ($creator === null) {
1039
            $creator = $this->repository->getCurrentUserReference();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Reposito...tCurrentUserReference() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user reference.

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...
1040
        }
1041
1042
        if (!$this->repository->canUser('content', 'edit', $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...
1043
            throw new UnauthorizedException('content', 'edit', array('contentId' => $contentInfo->id));
1044
        }
1045
1046
        $this->repository->beginTransaction();
1047
        try {
1048
            $spiContent = $this->persistenceHandler->contentHandler()->createDraftFromVersion(
1049
                $contentInfo->id,
1050
                $versionNo,
1051
                $creator->getUserId()
1052
            );
1053
            $this->repository->commit();
1054
        } catch (Exception $e) {
1055
            $this->repository->rollback();
1056
            throw $e;
1057
        }
1058
1059
        return $this->domainMapper->buildContentDomainObject($spiContent);
1060
    }
1061
1062
    /**
1063
     * Loads drafts for a user.
1064
     *
1065
     * If no user is given the drafts for the authenticated user a returned
1066
     *
1067
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to load the draft list
1068
     *
1069
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $user
1070
     *
1071
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo the drafts ({@link VersionInfo}) owned by the given user
1072
     */
1073
    public function loadContentDrafts(User $user = null)
1074
    {
1075
        if ($user === null) {
1076
            $user = $this->repository->getCurrentUserReference();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Reposito...tCurrentUserReference() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user reference.

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...
1077
        }
1078
1079
        // throw early if user has absolutely no access to versionread
1080
        if ($this->repository->hasAccess('content', 'versionread') === false) {
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...
1081
            throw new UnauthorizedException('content', 'versionread');
1082
        }
1083
1084
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftsForUser($user->getUserId());
1085
        $versionInfoList = array();
1086 View Code Duplication
        foreach ($spiVersionInfoList as $spiVersionInfo) {
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...
1087
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1088
            if (!$this->repository->canUser('content', 'versionread', $versionInfo)) {
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...
1089
                throw new UnauthorizedException('content', 'versionread', array('contentId' => $versionInfo->contentInfo->id));
1090
            }
1091
1092
            $versionInfoList[] = $versionInfo;
1093
        }
1094
1095
        return $versionInfoList;
1096
    }
1097
1098
    /**
1099
     * Translate a version.
1100
     *
1101
     * updates the destination version given in $translationInfo with the provided translated fields in $translationValues
1102
     *
1103
     * @example Examples/translation_5x.php
1104
     *
1105
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to update this version
1106
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the given destination version is not a draft
1107
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $translationValues is not valid, or if a required field is missing or is set to an empty value.
1108
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1109
     *                                                                          or value is set for non-translatable field in language
1110
     *                                                                          other than main.
1111
     *
1112
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1113
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationValues $translationValues
1114
     * @param \eZ\Publish\API\Repository\Values\User\User $modifier If set, this user is taken as modifier of the version
1115
     *
1116
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the translated fields
1117
     *
1118
     * @since 5.0
1119
     */
1120
    public function translateVersion(TranslationInfo $translationInfo, APITranslationValues $translationValues, User $modifier = null)
1121
    {
1122
        throw new NotImplementedException(__METHOD__);
1123
    }
1124
1125
    /**
1126
     * Updates the fields of a draft.
1127
     *
1128
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version
1129
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1130
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid.
1131
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
1132
     *                                                                               or if a required field is missing / set to an empty value.
1133
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
1134
     *                                                                          or value is set for non-translatable field in language
1135
     *                                                                          other than main.
1136
     *
1137
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1138
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1139
     *
1140
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the updated fields
1141
     */
1142
    public function updateContent(APIVersionInfo $versionInfo, APIContentUpdateStruct $contentUpdateStruct)
1143
    {
1144
        $contentUpdateStruct = clone $contentUpdateStruct;
1145
1146
        /** @var $content \eZ\Publish\Core\Repository\Values\Content\Content */
1147
        $content = $this->loadContent(
1148
            $versionInfo->getContentInfo()->id,
1149
            null,
1150
            $versionInfo->versionNo
1151
        );
1152
        if (!$content->versionInfo->isDraft()) {
1153
            throw new BadStateException(
1154
                '$versionInfo',
1155
                'Version is not a draft and can not be updated'
1156
            );
1157
        }
1158
1159
        if (!$this->repository->canUser('content', 'edit', $content)) {
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...
1160
            throw new UnauthorizedException('content', 'edit', array('contentId' => $content->id));
1161
        }
1162
1163
        $mainLanguageCode = $content->contentInfo->mainLanguageCode;
1164
        $languageCodes = $this->getLanguageCodesForUpdate($contentUpdateStruct, $content);
1165
        $contentType = $this->repository->getContentTypeService()->loadContentType(
1166
            $content->contentInfo->contentTypeId
1167
        );
1168
        $fields = $this->mapFieldsForUpdate(
1169
            $contentUpdateStruct,
1170
            $contentType,
1171
            $mainLanguageCode
1172
        );
1173
1174
        $fieldValues = array();
1175
        $spiFields = array();
1176
        $allFieldErrors = array();
1177
        $inputRelations = array();
1178
        $locationIdToContentIdMapping = array();
1179
1180
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
1181
            /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */
1182
            $fieldType = $this->fieldTypeRegistry->getFieldType(
1183
                $fieldDefinition->fieldTypeIdentifier
1184
            );
1185
1186
            foreach ($languageCodes as $languageCode) {
1187
                $isCopied = $isEmpty = $isRetained = false;
1188
                $isLanguageNew = !in_array($languageCode, $content->versionInfo->languageCodes);
1189
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $mainLanguageCode;
1190
                $isFieldUpdated = isset($fields[$fieldDefinition->identifier][$valueLanguageCode]);
1191
                $isProcessed = isset($fieldValues[$fieldDefinition->identifier][$valueLanguageCode]);
1192
1193
                if (!$isFieldUpdated && !$isLanguageNew) {
1194
                    $isRetained = true;
1195
                    $fieldValue = $content->getField($fieldDefinition->identifier, $valueLanguageCode)->value;
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1196
                } elseif (!$isFieldUpdated && $isLanguageNew && !$fieldDefinition->isTranslatable) {
1197
                    $isCopied = true;
1198
                    $fieldValue = $content->getField($fieldDefinition->identifier, $valueLanguageCode)->value;
0 ignored issues
show
Documentation introduced by
The property $value is declared protected in eZ\Publish\API\Repository\Values\Content\Field. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1199
                } elseif ($isFieldUpdated) {
1200
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
1201
                } else {
1202
                    $fieldValue = $fieldDefinition->defaultValue;
0 ignored issues
show
Documentation introduced by
The property $defaultValue is declared protected in eZ\Publish\API\Repositor...entType\FieldDefinition. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1203
                }
1204
1205
                $fieldValue = $fieldType->acceptValue($fieldValue);
1206
1207 View Code Duplication
                if ($fieldType->isEmptyValue($fieldValue)) {
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...
1208
                    $isEmpty = true;
1209
                    if ($fieldDefinition->isRequired) {
1210
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
1211
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
1212
                            null,
1213
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
1214
                            'empty'
1215
                        );
1216
                    }
1217
                } else {
1218
                    $fieldErrors = $fieldType->validate(
1219
                        $fieldDefinition,
1220
                        $fieldValue
1221
                    );
1222
                    if (!empty($fieldErrors)) {
1223
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
1224
                    }
1225
                }
1226
1227
                if (!empty($allFieldErrors)) {
1228
                    continue;
1229
                }
1230
1231
                $this->relationProcessor->appendFieldRelations(
1232
                    $inputRelations,
1233
                    $locationIdToContentIdMapping,
1234
                    $fieldType,
1235
                    $fieldValue,
0 ignored issues
show
Compatibility introduced by
$fieldValue of type object<eZ\Publish\SPI\FieldType\Value> is not a sub-type of object<eZ\Publish\Core\FieldType\Value>. It seems like you assume a concrete implementation of the interface eZ\Publish\SPI\FieldType\Value to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1236
                    $fieldDefinition->id
1237
                );
1238
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
1239
1240
                if ($isRetained || $isCopied || ($isLanguageNew && $isEmpty) || $isProcessed) {
1241
                    continue;
1242
                }
1243
1244
                $spiFields[] = new SPIField(
1245
                    array(
1246
                        'id' => $isLanguageNew ?
1247
                            null :
1248
                            $content->getField($fieldDefinition->identifier, $languageCode)->id,
1249
                        'fieldDefinitionId' => $fieldDefinition->id,
1250
                        'type' => $fieldDefinition->fieldTypeIdentifier,
1251
                        'value' => $fieldType->toPersistenceValue($fieldValue),
1252
                        'languageCode' => $languageCode,
1253
                        'versionNo' => $versionInfo->versionNo,
1254
                    )
1255
                );
1256
            }
1257
        }
1258
1259
        if (!empty($allFieldErrors)) {
1260
            throw new ContentFieldValidationException($allFieldErrors);
1261
        }
1262
1263
        $spiContentUpdateStruct = new SPIContentUpdateStruct(
1264
            array(
1265
                'name' => $this->nameSchemaService->resolveNameSchema(
1266
                    $content,
1267
                    $fieldValues,
1268
                    $languageCodes,
1269
                    $contentType
1270
                ),
1271
                'creatorId' => $contentUpdateStruct->creatorId ?: $this->repository->getCurrentUserReference()->getUserId(),
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Reposito...tCurrentUserReference() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user reference.

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...
1272
                'fields' => $spiFields,
1273
                'modificationDate' => time(),
1274
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1275
                    $contentUpdateStruct->initialLanguageCode
1276
                )->id,
1277
            )
1278
        );
1279
        $existingRelations = $this->loadRelations($versionInfo);
1280
1281
        $this->repository->beginTransaction();
1282
        try {
1283
            $spiContent = $this->persistenceHandler->contentHandler()->updateContent(
1284
                $versionInfo->getContentInfo()->id,
1285
                $versionInfo->versionNo,
1286
                $spiContentUpdateStruct
1287
            );
1288
            $this->relationProcessor->processFieldRelations(
1289
                $inputRelations,
1290
                $spiContent->versionInfo->contentInfo->id,
1291
                $spiContent->versionInfo->versionNo,
1292
                $contentType,
1293
                $existingRelations
1294
            );
1295
            $this->repository->commit();
1296
        } catch (Exception $e) {
1297
            $this->repository->rollback();
1298
            throw $e;
1299
        }
1300
1301
        return $this->domainMapper->buildContentDomainObject(
1302
            $spiContent,
1303
            $contentType
1304
        );
1305
    }
1306
1307
    /**
1308
     * Returns all language codes used in given $fields.
1309
     *
1310
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value exists in initial language
1311
     *
1312
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1313
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1314
     *
1315
     * @return array
1316
     */
1317
    protected function getLanguageCodesForUpdate(APIContentUpdateStruct $contentUpdateStruct, APIContent $content)
1318
    {
1319
        if ($contentUpdateStruct->initialLanguageCode !== null) {
1320
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1321
                $contentUpdateStruct->initialLanguageCode
1322
            );
1323
        } else {
1324
            $contentUpdateStruct->initialLanguageCode = $content->contentInfo->mainLanguageCode;
1325
        }
1326
1327
        $languageCodes = array_fill_keys($content->versionInfo->languageCodes, true);
1328
        $languageCodes[$contentUpdateStruct->initialLanguageCode] = true;
1329
1330 View Code Duplication
        foreach ($contentUpdateStruct->fields as $field) {
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...
1331
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1332
                continue;
1333
            }
1334
1335
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1336
                $field->languageCode
1337
            );
1338
            $languageCodes[$field->languageCode] = true;
1339
        }
1340
1341
        return array_keys($languageCodes);
1342
    }
1343
1344
    /**
1345
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
1346
     *
1347
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1348
     *                                                                          or value is set for non-translatable field in language
1349
     *                                                                          other than main
1350
     *
1351
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1352
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1353
     * @param string $mainLanguageCode
1354
     *
1355
     * @return array
1356
     */
1357
    protected function mapFieldsForUpdate(
1358
        APIContentUpdateStruct $contentUpdateStruct,
1359
        ContentType $contentType,
1360
        $mainLanguageCode
1361
    ) {
1362
        $fields = array();
1363
1364
        foreach ($contentUpdateStruct->fields as $field) {
1365
            $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier);
1366
1367
            if ($fieldDefinition === null) {
1368
                throw new ContentValidationException(
1369
                    "Field definition '%identifier%' does not exist in given ContentType",
1370
                    ['%identifier%' => $field->fieldDefIdentifier]
1371
                );
1372
            }
1373
1374
            if ($field->languageCode === null) {
1375
                if ($fieldDefinition->isTranslatable) {
1376
                    $languageCode = $contentUpdateStruct->initialLanguageCode;
1377
                } else {
1378
                    $languageCode = $mainLanguageCode;
1379
                }
1380
                $field = $this->cloneField($field, array('languageCode' => $languageCode));
1381
            }
1382
1383 View Code Duplication
            if (!$fieldDefinition->isTranslatable && ($field->languageCode != $mainLanguageCode)) {
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...
1384
                throw new ContentValidationException(
1385
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
1386
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
1387
                );
1388
            }
1389
1390
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
1391
        }
1392
1393
        return $fields;
1394
    }
1395
1396
    /**
1397
     * Publishes a content version.
1398
     *
1399
     * Publishes a content version and deletes archive versions if they overflow max archive versions.
1400
     * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future.
1401
     *
1402
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version
1403
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1404
     *
1405
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1406
     *
1407
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1408
     */
1409
    public function publishVersion(APIVersionInfo $versionInfo)
1410
    {
1411
        $content = $this->internalLoadContent(
1412
            $versionInfo->contentInfo->id,
1413
            null,
1414
            $versionInfo->versionNo
1415
        );
1416
1417
        if (!$this->repository->canUser('content', 'publish', $content)) {
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...
1418
            throw new UnauthorizedException('content', 'publish', array('contentId' => $content->id));
1419
        }
1420
1421
        $this->repository->beginTransaction();
1422
        try {
1423
            $content = $this->internalPublishVersion($content->getVersionInfo());
1424
            $this->repository->commit();
1425
        } catch (Exception $e) {
1426
            $this->repository->rollback();
1427
            throw $e;
1428
        }
1429
1430
        return $content;
1431
    }
1432
1433
    /**
1434
     * Publishes a content version.
1435
     *
1436
     * Publishes a content version and deletes archive versions if they overflow max archive versions.
1437
     * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future.
1438
     *
1439
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1440
     *
1441
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1442
     * @param int|null $publicationDate If null existing date is kept if there is one, otherwise current time is used.
1443
     *
1444
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1445
     */
1446
    protected function internalPublishVersion(APIVersionInfo $versionInfo, $publicationDate = null)
1447
    {
1448
        if (!$versionInfo->isDraft()) {
1449
            throw new BadStateException('$versionInfo', 'Only versions in draft status can be published.');
1450
        }
1451
1452
        $currentTime = time();
1453
        if ($publicationDate === null && $versionInfo->versionNo === 1) {
1454
            $publicationDate = $currentTime;
1455
        }
1456
1457
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
1458
        $metadataUpdateStruct->publicationDate = $publicationDate;
1459
        $metadataUpdateStruct->modificationDate = $currentTime;
1460
1461
        $contentId = $versionInfo->getContentInfo()->id;
1462
        $spiContent = $this->persistenceHandler->contentHandler()->publish(
1463
            $contentId,
1464
            $versionInfo->versionNo,
1465
            $metadataUpdateStruct
1466
        );
1467
1468
        $content = $this->domainMapper->buildContentDomainObject($spiContent);
1469
1470
        $this->publishUrlAliasesForContent($content);
1471
1472
        // Delete version archive overflow if any, limit is 0-50 (however 0 will mean 1 if content is unpublished)
1473
        $archiveList = $this->persistenceHandler->contentHandler()->listVersions(
1474
            $contentId,
1475
            APIVersionInfo::STATUS_ARCHIVED,
1476
            100 // Limited to avoid publishing taking to long, besides SE limitations this is why limit is max 50
1477
        );
1478
1479
        $maxVersionArchiveCount = max(0, min(50, $this->settings['default_version_archive_limit']));
1480
        while (!empty($archiveList) && count($archiveList) > $maxVersionArchiveCount) {
1481
            /** @var \eZ\Publish\SPI\Persistence\Content\VersionInfo $archiveVersion */
1482
            $archiveVersion = array_shift($archiveList);
1483
            $this->persistenceHandler->contentHandler()->deleteVersion(
1484
                $contentId,
1485
                $archiveVersion->versionNo
1486
            );
1487
        }
1488
1489
        return $content;
1490
    }
1491
1492
    /**
1493
     * Removes the given version.
1494
     *
1495
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is in
1496
     *         published state or is the last version of the Content
1497
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version
1498
     *
1499
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1500
     */
1501
    public function deleteVersion(APIVersionInfo $versionInfo)
1502
    {
1503
        if ($versionInfo->isPublished()) {
1504
            throw new BadStateException(
1505
                '$versionInfo',
1506
                'Version is published and can not be removed'
1507
            );
1508
        }
1509
1510 View Code Duplication
        if (!$this->repository->canUser('content', 'versionremove', $versionInfo)) {
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...
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...
1511
            throw new UnauthorizedException(
1512
                'content',
1513
                'versionremove',
1514
                array('contentId' => $versionInfo->contentInfo->id, 'versionNo' => $versionInfo->versionNo)
1515
            );
1516
        }
1517
1518
        $versionList = $this->persistenceHandler->contentHandler()->listVersions(
1519
            $versionInfo->contentInfo->id,
1520
            null,
1521
            2
1522
        );
1523
1524
        if (count($versionList) === 1) {
1525
            throw new BadStateException(
1526
                '$versionInfo',
1527
                'Version is the last version of the Content and can not be removed'
1528
            );
1529
        }
1530
1531
        $this->repository->beginTransaction();
1532
        try {
1533
            $this->persistenceHandler->contentHandler()->deleteVersion(
1534
                $versionInfo->getContentInfo()->id,
1535
                $versionInfo->versionNo
1536
            );
1537
            $this->repository->commit();
1538
        } catch (Exception $e) {
1539
            $this->repository->rollback();
1540
            throw $e;
1541
        }
1542
    }
1543
1544
    /**
1545
     * Loads all versions for the given content.
1546
     *
1547
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions
1548
     *
1549
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1550
     *
1551
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] Sorted by creation date
1552
     */
1553
    public function loadVersions(ContentInfo $contentInfo)
1554
    {
1555
        if (!$this->repository->canUser('content', 'versionread', $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...
1556
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentInfo->id));
1557
        }
1558
1559
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->listVersions($contentInfo->id);
1560
1561
        $versions = array();
1562 View Code Duplication
        foreach ($spiVersionInfoList as $spiVersionInfo) {
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...
1563
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1564
            if (!$this->repository->canUser('content', 'versionread', $versionInfo)) {
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...
1565
                throw new UnauthorizedException('content', 'versionread', array('versionId' => $versionInfo->id));
1566
            }
1567
1568
            $versions[] = $versionInfo;
1569
        }
1570
1571
        return $versions;
1572
    }
1573
1574
    /**
1575
     * Copies the content to a new location. If no version is given,
1576
     * all versions are copied, otherwise only the given version.
1577
     *
1578
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location
1579
     *
1580
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1581
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to
1582
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1583
     *
1584
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1585
     */
1586
    public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, APIVersionInfo $versionInfo = null)
1587
    {
1588 View Code Duplication
        if (!$this->repository->canUser('content', 'create', $contentInfo, $destinationLocationCreateStruct)) {
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...
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...
1589
            throw new UnauthorizedException(
1590
                'content',
1591
                'create',
1592
                array(
1593
                    'parentLocationId' => $destinationLocationCreateStruct->parentLocationId,
1594
                    'sectionId' => $contentInfo->sectionId,
1595
                )
1596
            );
1597
        }
1598
1599
        $defaultObjectStates = $this->getDefaultObjectStates();
1600
1601
        $this->repository->beginTransaction();
1602
        try {
1603
            $spiContent = $this->persistenceHandler->contentHandler()->copy(
1604
                $contentInfo->id,
1605
                $versionInfo ? $versionInfo->versionNo : null
1606
            );
1607
1608
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
1609
                $this->persistenceHandler->objectStateHandler()->setContentState(
1610
                    $spiContent->versionInfo->contentInfo->id,
1611
                    $objectStateGroupId,
1612
                    $objectState->id
1613
                );
1614
            }
1615
1616
            $content = $this->internalPublishVersion(
1617
                $this->domainMapper->buildVersionInfoDomainObject($spiContent->versionInfo),
1618
                $spiContent->versionInfo->creationDate
1619
            );
1620
1621
            $this->repository->getLocationService()->createLocation(
1622
                $content->getVersionInfo()->getContentInfo(),
1623
                $destinationLocationCreateStruct
1624
            );
1625
            $this->repository->commit();
1626
        } catch (Exception $e) {
1627
            $this->repository->rollback();
1628
            throw $e;
1629
        }
1630
1631
        return $this->internalLoadContent($content->id);
1632
    }
1633
1634
    /**
1635
     * Loads all outgoing relations for the given version.
1636
     *
1637
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1638
     *
1639
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1640
     *
1641
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1642
     */
1643
    public function loadRelations(APIVersionInfo $versionInfo)
1644
    {
1645
        if ($versionInfo->isPublished()) {
1646
            $function = 'read';
1647
        } else {
1648
            $function = 'versionread';
1649
        }
1650
1651
        if (!$this->repository->canUser('content', $function, $versionInfo)) {
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...
1652
            throw new UnauthorizedException('content', $function);
1653
        }
1654
1655
        $contentInfo = $versionInfo->getContentInfo();
1656
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1657
            $contentInfo->id,
1658
            $versionInfo->versionNo
1659
        );
1660
1661
        /** @var $relations \eZ\Publish\API\Repository\Values\Content\Relation[] */
1662
        $relations = array();
1663 View Code Duplication
        foreach ($spiRelations as $spiRelation) {
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...
1664
            $destinationContentInfo = $this->internalLoadContentInfo($spiRelation->destinationContentId);
1665
            if (!$this->repository->canUser('content', 'read', $destinationContentInfo)) {
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...
1666
                continue;
1667
            }
1668
1669
            $relations[] = $this->domainMapper->buildRelationDomainObject(
1670
                $spiRelation,
1671
                $contentInfo,
1672
                $destinationContentInfo
1673
            );
1674
        }
1675
1676
        return $relations;
1677
    }
1678
1679
    /**
1680
     * Loads all incoming relations for a content object.
1681
     *
1682
     * The relations come only from published versions of the source content objects
1683
     *
1684
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1685
     *
1686
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1687
     *
1688
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1689
     */
1690
    public function loadReverseRelations(ContentInfo $contentInfo)
1691
    {
1692
        if ($this->repository->hasAccess('content', 'reverserelatedlist') !== 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...
1693
            throw new UnauthorizedException('content', 'reverserelatedlist', array('contentId' => $contentInfo->id));
1694
        }
1695
1696
        $spiRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations(
1697
            $contentInfo->id
1698
        );
1699
1700
        $returnArray = array();
1701 View Code Duplication
        foreach ($spiRelations as $spiRelation) {
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...
1702
            $sourceContentInfo = $this->internalLoadContentInfo($spiRelation->sourceContentId);
1703
            if (!$this->repository->canUser('content', 'read', $sourceContentInfo)) {
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...
1704
                continue;
1705
            }
1706
1707
            $returnArray[] = $this->domainMapper->buildRelationDomainObject(
1708
                $spiRelation,
1709
                $sourceContentInfo,
1710
                $contentInfo
1711
            );
1712
        }
1713
1714
        return $returnArray;
1715
    }
1716
1717
    /**
1718
     * Adds a relation of type common.
1719
     *
1720
     * The source of the relation is the content and version
1721
     * referenced by $versionInfo.
1722
     *
1723
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version
1724
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1725
     *
1726
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1727
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation
1728
     *
1729
     * @return \eZ\Publish\API\Repository\Values\Content\Relation the newly created relation
1730
     */
1731
    public function addRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1732
    {
1733
        $sourceVersion = $this->loadVersionInfoById(
1734
            $sourceVersion->contentInfo->id,
1735
            $sourceVersion->versionNo
1736
        );
1737
1738
        if (!$sourceVersion->isDraft()) {
1739
            throw new BadStateException(
1740
                '$sourceVersion',
1741
                'Relations of type common can only be added to versions of status draft'
1742
            );
1743
        }
1744
1745
        if (!$this->repository->canUser('content', 'edit', $sourceVersion)) {
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...
1746
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1747
        }
1748
1749
        $sourceContentInfo = $sourceVersion->getContentInfo();
1750
1751
        $this->repository->beginTransaction();
1752
        try {
1753
            $spiRelation = $this->persistenceHandler->contentHandler()->addRelation(
1754
                new SPIRelationCreateStruct(
1755
                    array(
1756
                        'sourceContentId' => $sourceContentInfo->id,
1757
                        'sourceContentVersionNo' => $sourceVersion->versionNo,
1758
                        'sourceFieldDefinitionId' => null,
1759
                        'destinationContentId' => $destinationContent->id,
1760
                        'type' => APIRelation::COMMON,
1761
                    )
1762
                )
1763
            );
1764
            $this->repository->commit();
1765
        } catch (Exception $e) {
1766
            $this->repository->rollback();
1767
            throw $e;
1768
        }
1769
1770
        return $this->domainMapper->buildRelationDomainObject($spiRelation, $sourceContentInfo, $destinationContent);
1771
    }
1772
1773
    /**
1774
     * Removes a relation of type COMMON from a draft.
1775
     *
1776
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version
1777
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1778
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination
1779
     *
1780
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1781
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent
1782
     */
1783
    public function deleteRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1784
    {
1785
        $sourceVersion = $this->loadVersionInfoById(
1786
            $sourceVersion->contentInfo->id,
1787
            $sourceVersion->versionNo
1788
        );
1789
1790
        if (!$sourceVersion->isDraft()) {
1791
            throw new BadStateException(
1792
                '$sourceVersion',
1793
                'Relations of type common can only be removed from versions of status draft'
1794
            );
1795
        }
1796
1797
        if (!$this->repository->canUser('content', 'edit', $sourceVersion)) {
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...
1798
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1799
        }
1800
1801
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1802
            $sourceVersion->getContentInfo()->id,
1803
            $sourceVersion->versionNo,
1804
            APIRelation::COMMON
1805
        );
1806
1807
        if (empty($spiRelations)) {
1808
            throw new InvalidArgumentException(
1809
                '$sourceVersion',
1810
                'There are no relations of type COMMON for the given destination'
1811
            );
1812
        }
1813
1814
        // there should be only one relation of type COMMON for each destination,
1815
        // but in case there were ever more then one, we will remove them all
1816
        // @todo: alternatively, throw BadStateException?
1817
        $this->repository->beginTransaction();
1818
        try {
1819
            foreach ($spiRelations as $spiRelation) {
1820
                if ($spiRelation->destinationContentId == $destinationContent->id) {
1821
                    $this->persistenceHandler->contentHandler()->removeRelation(
1822
                        $spiRelation->id,
1823
                        APIRelation::COMMON
1824
                    );
1825
                }
1826
            }
1827
            $this->repository->commit();
1828
        } catch (Exception $e) {
1829
            $this->repository->rollback();
1830
            throw $e;
1831
        }
1832
    }
1833
1834
    /**
1835
     * Adds translation information to the content object.
1836
     *
1837
     * @example Examples/translation_5x.php
1838
     *
1839
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed add a translation info
1840
     *
1841
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1842
     *
1843
     * @since 5.0
1844
     */
1845
    public function addTranslationInfo(TranslationInfo $translationInfo)
1846
    {
1847
        throw new NotImplementedException(__METHOD__);
1848
    }
1849
1850
    /**
1851
     * lists the translations done on this content object.
1852
     *
1853
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed read translation infos
1854
     *
1855
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1856
     * @param array $filter
1857
     *
1858
     * @todo TBD - filter by source version destination version and languages
1859
     *
1860
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo[]
1861
     *
1862
     * @since 5.0
1863
     */
1864
    public function loadTranslationInfos(ContentInfo $contentInfo, array $filter = array())
1865
    {
1866
        throw new NotImplementedException(__METHOD__);
1867
    }
1868
1869
    /**
1870
     * Remove Content Object translation from all Versions (including archived ones) of a Content Object.
1871
     *
1872
     * NOTE: this operation is risky and permanent, so user interface (ideally CLI) should provide
1873
     *       a warning before performing it.
1874
     *
1875
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the specified translation
1876
     *         is the only one a Version has or it is the main translation of a Content Object.
1877
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed
1878
     *         to delete the content (in one of the locations of the given Content Object).
1879
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument
1880
     *         is invalid for the given content.
1881
     *
1882
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1883
     * @param string $languageCode
1884
     *
1885
     * @since 6.11
1886
     */
1887
    public function removeTranslation(ContentInfo $contentInfo, $languageCode)
1888
    {
1889
        if ($contentInfo->mainLanguageCode === $languageCode) {
1890
            throw new BadStateException(
1891
                '$languageCode',
1892
                'Specified translation is the main translation of the Content Object'
1893
            );
1894
        }
1895
        // before actual removal, collect information on Versions
1896
        $versions = [];
1897
        foreach ($this->loadVersions($contentInfo) as $versionInfo) {
1898
            // check if user is authorized to delete Version
1899 View Code Duplication
            if (!$this->repository->canUser('content', 'delete', $versionInfo)) {
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...
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...
1900
                throw new UnauthorizedException(
1901
                    'content',
1902
                    'delete',
1903
                    [
1904
                        'contentId' => $contentInfo->id,
1905
                        'versionNo' => $versionInfo->versionNo,
1906
                    ]
1907
                );
1908
            }
1909
            // check if the specified translation exists for the Version
1910
            if (!in_array($languageCode, $versionInfo->languageCodes)) {
1911
                // if translation does not exist, simply ignore Version (see InvalidArgumentException later on)
1912
                continue;
1913
            }
1914
            // check if the specified translation is not the only one
1915
            if (count($versionInfo->languageCodes) < 2) {
1916
                throw new BadStateException(
1917
                    '$languageCode',
1918
                    'Specified translation is the only one Content Object Version has'
1919
                );
1920
            }
1921
            // add version to the list
1922
            $versions[] = $versionInfo->versionNo;
1923
        }
1924
1925
        // if there are no Versions with the given translation, $languageCode arg is invalid
1926
        if (empty($versions)) {
1927
            throw new InvalidArgumentException(
1928
                '$languageCode',
1929
                sprintf(
1930
                    'Specified translation does not exist in the Content Object(id=%d)',
1931
                     $contentInfo->id
1932
                )
1933
            );
1934
        }
1935
1936
        $this->repository->beginTransaction();
1937
        try {
1938
            $this->persistenceHandler->contentHandler()->removeTranslationFromContent($contentInfo->id, $languageCode);
1939
            $this->repository->commit();
1940
        } catch (Exception $e) {
1941
            $this->repository->rollback();
1942
            // cover generic unexpected exception to fulfill API promise on @throws
1943
            throw new BadStateException('$contentInfo', 'Translation removal failed', $e);
1944
        }
1945
    }
1946
1947
    /**
1948
     * Instantiates a new content create struct object.
1949
     *
1950
     * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable
1951
     *
1952
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1953
     * @param string $mainLanguageCode
1954
     *
1955
     * @return \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct
1956
     */
1957
    public function newContentCreateStruct(ContentType $contentType, $mainLanguageCode)
1958
    {
1959
        return new ContentCreateStruct(
1960
            array(
1961
                'contentType' => $contentType,
1962
                'mainLanguageCode' => $mainLanguageCode,
1963
                'alwaysAvailable' => $contentType->defaultAlwaysAvailable,
1964
            )
1965
        );
1966
    }
1967
1968
    /**
1969
     * Instantiates a new content meta data update struct.
1970
     *
1971
     * @return \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct
1972
     */
1973
    public function newContentMetadataUpdateStruct()
1974
    {
1975
        return new ContentMetadataUpdateStruct();
1976
    }
1977
1978
    /**
1979
     * Instantiates a new content update struct.
1980
     *
1981
     * @return \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct
1982
     */
1983
    public function newContentUpdateStruct()
1984
    {
1985
        return new ContentUpdateStruct();
1986
    }
1987
1988
    /**
1989
     * Instantiates a new TranslationInfo object.
1990
     *
1991
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo
1992
     */
1993
    public function newTranslationInfo()
1994
    {
1995
        return new TranslationInfo();
1996
    }
1997
1998
    /**
1999
     * Instantiates a Translation object.
2000
     *
2001
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationValues
2002
     */
2003
    public function newTranslationValues()
2004
    {
2005
        return new TranslationValues();
2006
    }
2007
}
2008