Completed
Push — 6.12 ( 1cff2f )
by Łukasz
64:51
created

ContentService::publishUrlAliasesForContent()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 3
nop 2
dl 0
loc 25
rs 8.5806
c 0
b 0
f 0
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\Core\Repository\Values\Content\Location;
14
use eZ\Publish\SPI\Persistence\Handler;
15
use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct as APIContentUpdateStruct;
16
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
17
use eZ\Publish\API\Repository\Values\Content\TranslationInfo;
18
use eZ\Publish\API\Repository\Values\Content\TranslationValues as APITranslationValues;
19
use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct;
20
use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct;
21
use eZ\Publish\API\Repository\Values\Content\Content as APIContent;
22
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
23
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
24
use eZ\Publish\API\Repository\Values\User\User;
25
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
26
use eZ\Publish\API\Repository\Values\Content\Field;
27
use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation;
28
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
29
use eZ\Publish\Core\Base\Exceptions\BadStateException;
30
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
31
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
32
use eZ\Publish\Core\Base\Exceptions\ContentValidationException;
33
use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException;
34
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
35
use eZ\Publish\Core\FieldType\ValidationError;
36
use eZ\Publish\Core\Repository\Values\Content\VersionInfo;
37
use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct;
38
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
39
use eZ\Publish\Core\Repository\Values\Content\TranslationValues;
40
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct;
41
use eZ\Publish\SPI\Persistence\Content\CreateStruct as SPIContentCreateStruct;
42
use eZ\Publish\SPI\Persistence\Content\UpdateStruct as SPIContentUpdateStruct;
43
use eZ\Publish\SPI\Persistence\Content\Field as SPIField;
44
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as SPIRelationCreateStruct;
45
use Exception;
46
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
47
48
/**
49
 * This class provides service methods for managing content.
50
 *
51
 * @example Examples/content.php
52
 */
53
class ContentService implements ContentServiceInterface
54
{
55
    /**
56
     * @var \eZ\Publish\Core\Repository\Repository
57
     */
58
    protected $repository;
59
60
    /**
61
     * @var \eZ\Publish\SPI\Persistence\Handler
62
     */
63
    protected $persistenceHandler;
64
65
    /**
66
     * @var array
67
     */
68
    protected $settings;
69
70
    /**
71
     * @var \eZ\Publish\Core\Repository\Helper\DomainMapper
72
     */
73
    protected $domainMapper;
74
75
    /**
76
     * @var \eZ\Publish\Core\Repository\Helper\RelationProcessor
77
     */
78
    protected $relationProcessor;
79
80
    /**
81
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
82
     */
83
    protected $nameSchemaService;
84
85
    /**
86
     * @var \eZ\Publish\Core\Repository\Helper\FieldTypeRegistry
87
     */
88
    protected $fieldTypeRegistry;
89
90
    /**
91
     * Setups service with reference to repository object that created it & corresponding handler.
92
     *
93
     * @param \eZ\Publish\API\Repository\Repository $repository
94
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
95
     * @param \eZ\Publish\Core\Repository\Helper\DomainMapper $domainMapper
96
     * @param \eZ\Publish\Core\Repository\Helper\RelationProcessor $relationProcessor
97
     * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService
98
     * @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...
99
     * @param array $settings
100
     */
101
    public function __construct(
102
        RepositoryInterface $repository,
103
        Handler $handler,
104
        Helper\DomainMapper $domainMapper,
105
        Helper\RelationProcessor $relationProcessor,
106
        Helper\NameSchemaService $nameSchemaService,
107
        Helper\FieldTypeRegistry $fieldTypeRegistry,
108
        array $settings = array()
109
    ) {
110
        $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...
111
        $this->persistenceHandler = $handler;
112
        $this->domainMapper = $domainMapper;
113
        $this->relationProcessor = $relationProcessor;
114
        $this->nameSchemaService = $nameSchemaService;
115
        $this->fieldTypeRegistry = $fieldTypeRegistry;
116
        // Union makes sure default settings are ignored if provided in argument
117
        $this->settings = $settings + array(
118
            // Version archive limit (0-50), only enforced on publish, not on un-publish.
119
            'default_version_archive_limit' => 5,
120
        );
121
    }
122
123
    /**
124
     * Loads a content info object.
125
     *
126
     * To load fields use loadContent
127
     *
128
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content
129
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist
130
     *
131
     * @param int $contentId
132
     *
133
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
134
     */
135 View Code Duplication
    public function loadContentInfo($contentId)
136
    {
137
        $contentInfo = $this->internalLoadContentInfo($contentId);
138
        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...
139
            throw new UnauthorizedException('content', 'read', array('contentId' => $contentId));
140
        }
141
142
        return $contentInfo;
143
    }
144
145
    /**
146
     * Loads a content info object.
147
     *
148
     * To load fields use loadContent
149
     *
150
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist
151
     *
152
     * @param mixed $id
153
     * @param bool $isRemoteId
154
     *
155
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
156
     */
157
    public function internalLoadContentInfo($id, $isRemoteId = false)
158
    {
159
        try {
160
            $method = $isRemoteId ? 'loadContentInfoByRemoteId' : 'loadContentInfo';
161
162
            return $this->domainMapper->buildContentInfoDomainObject(
163
                $this->persistenceHandler->contentHandler()->$method($id)
164
            );
165
        } catch (APINotFoundException $e) {
166
            throw new NotFoundException(
167
                'Content',
168
                $id,
169
                $e
170
            );
171
        }
172
    }
173
174
    /**
175
     * Loads a content info object for the given remoteId.
176
     *
177
     * To load fields use loadContent
178
     *
179
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content
180
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given remote id does not exist
181
     *
182
     * @param string $remoteId
183
     *
184
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
185
     */
186 View Code Duplication
    public function loadContentInfoByRemoteId($remoteId)
187
    {
188
        $contentInfo = $this->internalLoadContentInfo($remoteId, true);
189
190
        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...
191
            throw new UnauthorizedException('content', 'read', array('remoteId' => $remoteId));
192
        }
193
194
        return $contentInfo;
195
    }
196
197
    /**
198
     * Loads a version info of the given content object.
199
     *
200
     * If no version number is given, the method returns the current version
201
     *
202
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist
203
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
204
     *
205
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
206
     * @param int $versionNo the version number. If not given the current version is returned.
207
     *
208
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo
209
     */
210
    public function loadVersionInfo(ContentInfo $contentInfo, $versionNo = null)
211
    {
212
        return $this->loadVersionInfoById($contentInfo->id, $versionNo);
213
    }
214
215
    /**
216
     * Loads a version info of the given content object id.
217
     *
218
     * If no version number is given, the method returns the current version
219
     *
220
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist
221
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
222
     *
223
     * @param mixed $contentId
224
     * @param int $versionNo the version number. If not given the current version is returned.
225
     *
226
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo
227
     */
228
    public function loadVersionInfoById($contentId, $versionNo = null)
229
    {
230
        if ($versionNo === null) {
231
            $versionNo = $this->loadContentInfo($contentId)->currentVersionNo;
232
        }
233
234
        try {
235
            $spiVersionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo(
236
                $contentId,
237
                $versionNo
238
            );
239
        } catch (APINotFoundException $e) {
240
            throw new NotFoundException(
241
                'VersionInfo',
242
                array(
243
                    'contentId' => $contentId,
244
                    'versionNo' => $versionNo,
245
                ),
246
                $e
247
            );
248
        }
249
250
        $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
251
252
        if ($versionInfo->isPublished()) {
253
            $function = 'read';
254
        } else {
255
            $function = 'versionread';
256
        }
257
258
        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...
259
            throw new UnauthorizedException('content', $function, array('contentId' => $contentId));
260
        }
261
262
        return $versionInfo;
263
    }
264
265
    /**
266
     * {@inheritdoc}
267
     */
268
    public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
269
    {
270
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
271
        if ($useAlwaysAvailable && !$contentInfo->alwaysAvailable) {
272
            $useAlwaysAvailable = false;
273
        }
274
275
        // As we have content info we can avoid that current version is looked up using spi in loadContent() if not set
276
        if ($versionNo === null) {
277
            $versionNo = $contentInfo->currentVersionNo;
278
        }
279
280
        return $this->loadContent(
281
            $contentInfo->id,
282
            $languages,
283
            $versionNo,
284
            $useAlwaysAvailable
285
        );
286
    }
287
288
    /**
289
     * {@inheritdoc}
290
     */
291
    public function loadContentByVersionInfo(APIVersionInfo $versionInfo, array $languages = null, $useAlwaysAvailable = true)
292
    {
293
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
294
        if ($useAlwaysAvailable && !$versionInfo->getContentInfo()->alwaysAvailable) {
295
            $useAlwaysAvailable = false;
296
        }
297
298
        return $this->loadContent(
299
            $versionInfo->getContentInfo()->id,
300
            $languages,
301
            $versionInfo->versionNo,
302
            $useAlwaysAvailable
303
        );
304
    }
305
306
    /**
307
     * {@inheritdoc}
308
     */
309 View Code Duplication
    public function loadContent($contentId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
310
    {
311
        $content = $this->internalLoadContent($contentId, $languages, $versionNo, false, $useAlwaysAvailable);
312
313
        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...
314
            throw new UnauthorizedException('content', 'read', array('contentId' => $contentId));
315
        }
316
        if (
317
            !$content->getVersionInfo()->isPublished()
318
            && !$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...
319
        ) {
320
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentId, 'versionNo' => $versionNo));
321
        }
322
323
        return $content;
324
    }
325
326
    /**
327
     * Loads content in a version of the given content object.
328
     *
329
     * If no version number is given, the method returns the current version
330
     *
331
     * @internal
332
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist
333
     *
334
     * @param mixed $id
335
     * @param array|null $languages A language priority, filters returned fields and is used as prioritized language code on
336
     *                         returned value object. If not given all languages are returned.
337
     * @param int|null $versionNo the version number. If not given the current version is returned
338
     * @param bool $isRemoteId
339
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
340
     *
341
     * @return \eZ\Publish\API\Repository\Values\Content\Content
342
     */
343
    public function internalLoadContent($id, array $languages = null, $versionNo = null, $isRemoteId = false, $useAlwaysAvailable = true)
344
    {
345
        try {
346
            // Get Content ID if lookup by remote ID
347
            if ($isRemoteId) {
348
                $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($id);
349
                $id = $spiContentInfo->id;
350
            }
351
352
            // Get current version if $versionNo is not defined
353
            if ($versionNo === null) {
354
                if (!isset($spiContentInfo)) {
355
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
356
                }
357
358
                $versionNo = $spiContentInfo->currentVersionNo;
359
            }
360
361
            $loadLanguages = $languages;
362
            $alwaysAvailableLanguageCode = null;
363
            // Set main language on $languages filter if not empty (all) and $useAlwaysAvailable being true
364
            if (!empty($loadLanguages) && $useAlwaysAvailable) {
365
                if (!isset($spiContentInfo)) {
366
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
367
                }
368
369
                if ($spiContentInfo->alwaysAvailable) {
370
                    $loadLanguages[] = $alwaysAvailableLanguageCode = $spiContentInfo->mainLanguageCode;
371
                    $loadLanguages = array_unique($loadLanguages);
372
                }
373
            }
374
375
            $spiContent = $this->persistenceHandler->contentHandler()->load(
376
                $id,
377
                $versionNo,
378
                $loadLanguages
0 ignored issues
show
Bug introduced by
It seems like $loadLanguages defined by $languages on line 361 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...
379
            );
380
        } catch (APINotFoundException $e) {
381
            throw new NotFoundException(
382
                'Content',
383
                array(
384
                    $isRemoteId ? 'remoteId' : 'id' => $id,
385
                    'languages' => $languages,
386
                    'versionNo' => $versionNo,
387
                ),
388
                $e
389
            );
390
        }
391
392
        return $this->domainMapper->buildContentDomainObject(
393
            $spiContent,
394
            null,
395
            empty($languages) ? null : $languages,
396
            $alwaysAvailableLanguageCode
397
        );
398
    }
399
400
    /**
401
     * Loads content in a version for the content object reference by the given remote id.
402
     *
403
     * If no version is given, the method returns the current version
404
     *
405
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist
406
     * @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
407
     *
408
     * @param string $remoteId
409
     * @param array $languages A language filter for fields. If not given all languages are returned
410
     * @param int $versionNo the version number. If not given the current version is returned
411
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
412
     *
413
     * @return \eZ\Publish\API\Repository\Values\Content\Content
414
     */
415 View Code Duplication
    public function loadContentByRemoteId($remoteId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
416
    {
417
        $content = $this->internalLoadContent($remoteId, $languages, $versionNo, true, $useAlwaysAvailable);
418
419
        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...
420
            throw new UnauthorizedException('content', 'read', array('remoteId' => $remoteId));
421
        }
422
423
        if (
424
            !$content->getVersionInfo()->isPublished()
425
            && !$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...
426
        ) {
427
            throw new UnauthorizedException('content', 'versionread', array('remoteId' => $remoteId, 'versionNo' => $versionNo));
428
        }
429
430
        return $content;
431
    }
432
433
    /**
434
     * Creates a new content draft assigned to the authenticated user.
435
     *
436
     * If a different userId is given in $contentCreateStruct it is assigned to the given user
437
     * but this required special rights for the authenticated user
438
     * (this is useful for content staging where the transfer process does not
439
     * have to authenticate with the user which created the content object in the source server).
440
     * The user has to publish the draft if it should be visible.
441
     * In 4.x at least one location has to be provided in the location creation array.
442
     *
443
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location
444
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the provided remoteId exists in the system, required properties on
445
     *                                                                        struct are missing or invalid, or if multiple locations are under the
446
     *                                                                        same parent.
447
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
448
     *                                                                               or if a required field is missing / set to an empty value.
449
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
450
     *                                                                          or value is set for non-translatable field in language
451
     *                                                                          other than main.
452
     *
453
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
454
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs For each location parent under which a location should be created for the content
455
     *
456
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
457
     */
458
    public function createContent(APIContentCreateStruct $contentCreateStruct, array $locationCreateStructs = array())
459
    {
460
        if ($contentCreateStruct->mainLanguageCode === null) {
461
            throw new InvalidArgumentException('$contentCreateStruct', "'mainLanguageCode' property must be set");
462
        }
463
464
        if ($contentCreateStruct->contentType === null) {
465
            throw new InvalidArgumentException('$contentCreateStruct', "'contentType' property must be set");
466
        }
467
468
        $contentCreateStruct = clone $contentCreateStruct;
469
470
        if ($contentCreateStruct->ownerId === null) {
471
            $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...
472
        }
473
474
        if ($contentCreateStruct->alwaysAvailable === null) {
475
            $contentCreateStruct->alwaysAvailable = false;
476
        }
477
478
        $contentCreateStruct->contentType = $this->repository->getContentTypeService()->loadContentType(
479
            $contentCreateStruct->contentType->id
480
        );
481
482
        if (empty($contentCreateStruct->sectionId)) {
483
            if (isset($locationCreateStructs[0])) {
484
                $location = $this->repository->getLocationService()->loadLocation(
485
                    $locationCreateStructs[0]->parentLocationId
486
                );
487
                $contentCreateStruct->sectionId = $location->contentInfo->sectionId;
488
            } else {
489
                $contentCreateStruct->sectionId = 1;
490
            }
491
        }
492
493
        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...
494
            throw new UnauthorizedException(
495
                'content',
496
                'create',
497
                array(
498
                    'parentLocationId' => isset($locationCreateStructs[0]) ?
499
                            $locationCreateStructs[0]->parentLocationId :
500
                            null,
501
                    'sectionId' => $contentCreateStruct->sectionId,
502
                )
503
            );
504
        }
505
506
        if (!empty($contentCreateStruct->remoteId)) {
507
            try {
508
                $this->loadContentByRemoteId($contentCreateStruct->remoteId);
509
510
                throw new InvalidArgumentException(
511
                    '$contentCreateStruct',
512
                    "Another content with remoteId '{$contentCreateStruct->remoteId}' exists"
513
                );
514
            } catch (APINotFoundException $e) {
515
                // Do nothing
516
            }
517
        } else {
518
            $contentCreateStruct->remoteId = $this->domainMapper->getUniqueHash($contentCreateStruct);
519
        }
520
521
        $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...
522
523
        $languageCodes = $this->getLanguageCodesForCreate($contentCreateStruct);
524
        $fields = $this->mapFieldsForCreate($contentCreateStruct);
525
526
        $fieldValues = array();
527
        $spiFields = array();
528
        $allFieldErrors = array();
529
        $inputRelations = array();
530
        $locationIdToContentIdMapping = array();
531
532
        foreach ($contentCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) {
533
            /** @var $fieldType \eZ\Publish\Core\FieldType\FieldType */
534
            $fieldType = $this->fieldTypeRegistry->getFieldType(
535
                $fieldDefinition->fieldTypeIdentifier
536
            );
537
538
            foreach ($languageCodes as $languageCode) {
539
                $isEmptyValue = false;
540
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $contentCreateStruct->mainLanguageCode;
541
                $isLanguageMain = $languageCode === $contentCreateStruct->mainLanguageCode;
542
                if (isset($fields[$fieldDefinition->identifier][$valueLanguageCode])) {
543
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
544
                } else {
545
                    $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...
546
                }
547
548
                $fieldValue = $fieldType->acceptValue($fieldValue);
549
550 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...
551
                    $isEmptyValue = true;
552
                    if ($fieldDefinition->isRequired) {
553
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
554
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
555
                            null,
556
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
557
                            'empty'
558
                        );
559
                    }
560
                } else {
561
                    $fieldErrors = $fieldType->validate(
562
                        $fieldDefinition,
563
                        $fieldValue
564
                    );
565
                    if (!empty($fieldErrors)) {
566
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
567
                    }
568
                }
569
570
                if (!empty($allFieldErrors)) {
571
                    continue;
572
                }
573
574
                $this->relationProcessor->appendFieldRelations(
575
                    $inputRelations,
576
                    $locationIdToContentIdMapping,
577
                    $fieldType,
578
                    $fieldValue,
579
                    $fieldDefinition->id
580
                );
581
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
582
583
                // Only non-empty value for: translatable field or in main language
584
                if (
585
                    (!$isEmptyValue && $fieldDefinition->isTranslatable) ||
586
                    (!$isEmptyValue && $isLanguageMain)
587
                ) {
588
                    $spiFields[] = new SPIField(
589
                        array(
590
                            'id' => null,
591
                            'fieldDefinitionId' => $fieldDefinition->id,
592
                            'type' => $fieldDefinition->fieldTypeIdentifier,
593
                            'value' => $fieldType->toPersistenceValue($fieldValue),
594
                            'languageCode' => $languageCode,
595
                            'versionNo' => null,
596
                        )
597
                    );
598
                }
599
            }
600
        }
601
602
        if (!empty($allFieldErrors)) {
603
            throw new ContentFieldValidationException($allFieldErrors);
604
        }
605
606
        $spiContentCreateStruct = new SPIContentCreateStruct(
607
            array(
608
                'name' => $this->nameSchemaService->resolve(
609
                    $contentCreateStruct->contentType->nameSchema,
610
                    $contentCreateStruct->contentType,
611
                    $fieldValues,
612
                    $languageCodes
613
                ),
614
                'typeId' => $contentCreateStruct->contentType->id,
615
                'sectionId' => $contentCreateStruct->sectionId,
616
                'ownerId' => $contentCreateStruct->ownerId,
617
                'locations' => $spiLocationCreateStructs,
618
                'fields' => $spiFields,
619
                'alwaysAvailable' => $contentCreateStruct->alwaysAvailable,
620
                'remoteId' => $contentCreateStruct->remoteId,
621
                'modified' => isset($contentCreateStruct->modificationDate) ? $contentCreateStruct->modificationDate->getTimestamp() : time(),
622
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
623
                    $contentCreateStruct->mainLanguageCode
624
                )->id,
625
            )
626
        );
627
628
        $defaultObjectStates = $this->getDefaultObjectStates();
629
630
        $this->repository->beginTransaction();
631
        try {
632
            $spiContent = $this->persistenceHandler->contentHandler()->create($spiContentCreateStruct);
633
            $this->relationProcessor->processFieldRelations(
634
                $inputRelations,
635
                $spiContent->versionInfo->contentInfo->id,
636
                $spiContent->versionInfo->versionNo,
637
                $contentCreateStruct->contentType
638
            );
639
640
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
641
                $this->persistenceHandler->objectStateHandler()->setContentState(
642
                    $spiContent->versionInfo->contentInfo->id,
643
                    $objectStateGroupId,
644
                    $objectState->id
645
                );
646
            }
647
648
            $this->repository->commit();
649
        } catch (Exception $e) {
650
            $this->repository->rollback();
651
            throw $e;
652
        }
653
654
        return $this->domainMapper->buildContentDomainObject($spiContent);
655
    }
656
657
    /**
658
     * Returns an array of default content states with content state group id as key.
659
     *
660
     * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[]
661
     */
662
    protected function getDefaultObjectStates()
663
    {
664
        $defaultObjectStatesMap = array();
665
        $objectStateHandler = $this->persistenceHandler->objectStateHandler();
666
667
        foreach ($objectStateHandler->loadAllGroups() as $objectStateGroup) {
668
            foreach ($objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) {
669
                // Only register the first object state which is the default one.
670
                $defaultObjectStatesMap[$objectStateGroup->id] = $objectState;
671
                break;
672
            }
673
        }
674
675
        return $defaultObjectStatesMap;
676
    }
677
678
    /**
679
     * Returns all language codes used in given $fields.
680
     *
681
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value is set in main language
682
     *
683
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
684
     *
685
     * @return string[]
686
     */
687
    protected function getLanguageCodesForCreate(APIContentCreateStruct $contentCreateStruct)
688
    {
689
        $languageCodes = array();
690
691 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...
692
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
693
                continue;
694
            }
695
696
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
697
                $field->languageCode
698
            );
699
            $languageCodes[$field->languageCode] = true;
700
        }
701
702
        if (!isset($languageCodes[$contentCreateStruct->mainLanguageCode])) {
703
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
704
                $contentCreateStruct->mainLanguageCode
705
            );
706
            $languageCodes[$contentCreateStruct->mainLanguageCode] = true;
707
        }
708
709
        return array_keys($languageCodes);
710
    }
711
712
    /**
713
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
714
     *
715
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
716
     *                                                                          or value is set for non-translatable field in language
717
     *                                                                          other than main
718
     *
719
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
720
     *
721
     * @return array
722
     */
723
    protected function mapFieldsForCreate(APIContentCreateStruct $contentCreateStruct)
724
    {
725
        $fields = array();
726
727
        foreach ($contentCreateStruct->fields as $field) {
728
            $fieldDefinition = $contentCreateStruct->contentType->getFieldDefinition($field->fieldDefIdentifier);
729
730
            if ($fieldDefinition === null) {
731
                throw new ContentValidationException(
732
                    "Field definition '%identifier%' does not exist in given ContentType",
733
                    ['%identifier%' => $field->fieldDefIdentifier]
734
                );
735
            }
736
737
            if ($field->languageCode === null) {
738
                $field = $this->cloneField(
739
                    $field,
740
                    array('languageCode' => $contentCreateStruct->mainLanguageCode)
741
                );
742
            }
743
744 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...
745
                throw new ContentValidationException(
746
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
747
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
748
                );
749
            }
750
751
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
752
        }
753
754
        return $fields;
755
    }
756
757
    /**
758
     * Clones $field with overriding specific properties from given $overrides array.
759
     *
760
     * @param Field $field
761
     * @param array $overrides
762
     *
763
     * @return Field
764
     */
765
    private function cloneField(Field $field, array $overrides = array())
766
    {
767
        $fieldData = array_merge(
768
            array(
769
                'id' => $field->id,
770
                '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...
771
                'languageCode' => $field->languageCode,
772
                'fieldDefIdentifier' => $field->fieldDefIdentifier,
773
            ),
774
            $overrides
775
        );
776
777
        return new Field($fieldData);
778
    }
779
780
    /**
781
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
782
     *
783
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
784
     *
785
     * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct[]
786
     */
787
    protected function buildSPILocationCreateStructs(array $locationCreateStructs)
788
    {
789
        $spiLocationCreateStructs = array();
790
        $parentLocationIdSet = array();
791
        $mainLocation = true;
792
793
        foreach ($locationCreateStructs as $locationCreateStruct) {
794
            if (isset($parentLocationIdSet[$locationCreateStruct->parentLocationId])) {
795
                throw new InvalidArgumentException(
796
                    '$locationCreateStructs',
797
                    "Multiple LocationCreateStructs with the same parent Location '{$locationCreateStruct->parentLocationId}' are given"
798
                );
799
            }
800
801
            $parentLocationIdSet[$locationCreateStruct->parentLocationId] = true;
802
            $parentLocation = $this->repository->getLocationService()->loadLocation(
803
                $locationCreateStruct->parentLocationId
804
            );
805
806
            $spiLocationCreateStructs[] = $this->domainMapper->buildSPILocationCreateStruct(
807
                $locationCreateStruct,
808
                $parentLocation,
809
                $mainLocation,
810
                // For Content draft contentId and contentVersionNo are set in ContentHandler upon draft creation
811
                null,
812
                null
813
            );
814
815
            // First Location in the list will be created as main Location
816
            $mainLocation = false;
817
        }
818
819
        return $spiLocationCreateStructs;
820
    }
821
822
    /**
823
     * Updates the metadata.
824
     *
825
     * (see {@link ContentMetadataUpdateStruct}) of a content object - to update fields use updateContent
826
     *
827
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data
828
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists
829
     *
830
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
831
     * @param \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct
832
     *
833
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content with the updated attributes
834
     */
835
    public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct)
836
    {
837
        $propertyCount = 0;
838
        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...
839
            if (isset($contentMetadataUpdateStruct->$propertyName)) {
840
                $propertyCount += 1;
841
            }
842
        }
843
        if ($propertyCount === 0) {
844
            throw new InvalidArgumentException(
845
                '$contentMetadataUpdateStruct',
846
                'At least one property must be set'
847
            );
848
        }
849
850
        $loadedContentInfo = $this->loadContentInfo($contentInfo->id);
851
852
        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...
853
            throw new UnauthorizedException('content', 'edit', array('contentId' => $loadedContentInfo->id));
854
        }
855
856
        if (isset($contentMetadataUpdateStruct->remoteId)) {
857
            try {
858
                $existingContentInfo = $this->loadContentInfoByRemoteId($contentMetadataUpdateStruct->remoteId);
859
860
                if ($existingContentInfo->id !== $loadedContentInfo->id) {
861
                    throw new InvalidArgumentException(
862
                        '$contentMetadataUpdateStruct',
863
                        "Another content with remoteId '{$contentMetadataUpdateStruct->remoteId}' exists"
864
                    );
865
                }
866
            } catch (APINotFoundException $e) {
867
                // Do nothing
868
            }
869
        }
870
871
        $this->repository->beginTransaction();
872
        try {
873
            if ($propertyCount > 1 || !isset($contentMetadataUpdateStruct->mainLocationId)) {
874
                $this->persistenceHandler->contentHandler()->updateMetadata(
875
                    $loadedContentInfo->id,
876
                    new SPIMetadataUpdateStruct(
877
                        array(
878
                            'ownerId' => $contentMetadataUpdateStruct->ownerId,
879
                            'publicationDate' => isset($contentMetadataUpdateStruct->publishedDate) ?
880
                                $contentMetadataUpdateStruct->publishedDate->getTimestamp() :
881
                                null,
882
                            'modificationDate' => isset($contentMetadataUpdateStruct->modificationDate) ?
883
                                $contentMetadataUpdateStruct->modificationDate->getTimestamp() :
884
                                null,
885
                            'mainLanguageId' => isset($contentMetadataUpdateStruct->mainLanguageCode) ?
886
                                $this->repository->getContentLanguageService()->loadLanguage(
887
                                    $contentMetadataUpdateStruct->mainLanguageCode
888
                                )->id :
889
                                null,
890
                            'alwaysAvailable' => $contentMetadataUpdateStruct->alwaysAvailable,
891
                            'remoteId' => $contentMetadataUpdateStruct->remoteId,
892
                        )
893
                    )
894
                );
895
            }
896
897
            // Change main location
898
            if (isset($contentMetadataUpdateStruct->mainLocationId)
899
                && $loadedContentInfo->mainLocationId !== $contentMetadataUpdateStruct->mainLocationId) {
900
                $this->persistenceHandler->locationHandler()->changeMainLocation(
901
                    $loadedContentInfo->id,
902
                    $contentMetadataUpdateStruct->mainLocationId
903
                );
904
            }
905
906
            // Republish URL aliases to update always-available flag
907
            if (isset($contentMetadataUpdateStruct->alwaysAvailable)
908
                && $loadedContentInfo->alwaysAvailable !== $contentMetadataUpdateStruct->alwaysAvailable) {
909
                $content = $this->loadContent($loadedContentInfo->id);
910
                $this->publishUrlAliasesForContent($content, false);
911
            }
912
913
            $this->repository->commit();
914
        } catch (Exception $e) {
915
            $this->repository->rollback();
916
            throw $e;
917
        }
918
919
        return isset($content) ? $content : $this->loadContent($loadedContentInfo->id);
920
    }
921
922
    /**
923
     * Publishes URL aliases for all locations of a given content.
924
     *
925
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
926
     * @param bool $updatePathIdentificationString this parameter is legacy storage specific for updating
927
     *                      ezcontentobject_tree.path_identification_string, it is ignored by other storage engines
928
     */
929
    protected function publishUrlAliasesForContent(APIContent $content, $updatePathIdentificationString = true)
930
    {
931
        $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
932
        $locations = $this->repository->getLocationService()->loadLocations(
933
            $content->getVersionInfo()->getContentInfo()
934
        );
935
        foreach ($locations as $location) {
936
            foreach ($urlAliasNames as $languageCode => $name) {
937
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
938
                    $location->id,
939
                    $location->parentLocationId,
940
                    $name,
941
                    $languageCode,
942
                    $content->contentInfo->alwaysAvailable,
943
                    $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...
944
                );
945
            }
946
            // archive URL aliases of Translations that got deleted
947
            $this->persistenceHandler->urlAliasHandler()->archiveUrlAliasesForDeletedTranslations(
948
                $location->id,
949
                $location->parentLocationId,
950
                $content->versionInfo->languageCodes
951
            );
952
        }
953
    }
954
955
    /**
956
     * Deletes a content object including all its versions and locations including their subtrees.
957
     *
958
     * @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)
959
     *
960
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
961
     *
962
     * @return mixed[] Affected Location Id's
963
     */
964
    public function deleteContent(ContentInfo $contentInfo)
965
    {
966
        $contentInfo = $this->internalLoadContentInfo($contentInfo->id);
967
968
        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...
969
            throw new UnauthorizedException('content', 'remove', array('contentId' => $contentInfo->id));
970
        }
971
972
        $affectedLocations = [];
973
        $this->repository->beginTransaction();
974
        try {
975
            // Load Locations first as deleting Content also deletes belonging Locations
976
            $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($contentInfo->id);
977
            $this->persistenceHandler->contentHandler()->deleteContent($contentInfo->id);
978
            foreach ($spiLocations as $spiLocation) {
979
                $this->persistenceHandler->urlAliasHandler()->locationDeleted($spiLocation->id);
980
                $affectedLocations[] = $spiLocation->id;
981
            }
982
            $this->repository->commit();
983
        } catch (Exception $e) {
984
            $this->repository->rollback();
985
            throw $e;
986
        }
987
988
        return $affectedLocations;
989
    }
990
991
    /**
992
     * Creates a draft from a published or archived version.
993
     *
994
     * If no version is given, the current published version is used.
995
     * 4.x: The draft is created with the initialLanguage code of the source version or if not present with the main language.
996
     * It can be changed on updating the version.
997
     *
998
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft
999
     *
1000
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1001
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1002
     * @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
1003
     *
1004
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
1005
     */
1006
    public function createContentDraft(ContentInfo $contentInfo, APIVersionInfo $versionInfo = null, User $creator = null)
1007
    {
1008
        $contentInfo = $this->loadContentInfo($contentInfo->id);
1009
1010
        if ($versionInfo !== null) {
1011
            // Check that given $contentInfo and $versionInfo belong to the same content
1012
            if ($versionInfo->getContentInfo()->id != $contentInfo->id) {
1013
                throw new InvalidArgumentException(
1014
                    '$versionInfo',
1015
                    'VersionInfo does not belong to the same content as given ContentInfo'
1016
                );
1017
            }
1018
1019
            $versionInfo = $this->loadVersionInfoById($contentInfo->id, $versionInfo->versionNo);
1020
1021
            switch ($versionInfo->status) {
1022
                case VersionInfo::STATUS_PUBLISHED:
1023
                case VersionInfo::STATUS_ARCHIVED:
1024
                    break;
1025
1026
                default:
1027
                    // @todo: throw an exception here, to be defined
1028
                    throw new BadStateException(
1029
                        '$versionInfo',
1030
                        'Draft can not be created from a draft version'
1031
                    );
1032
            }
1033
1034
            $versionNo = $versionInfo->versionNo;
1035
        } elseif ($contentInfo->published) {
1036
            $versionNo = $contentInfo->currentVersionNo;
1037
        } else {
1038
            // @todo: throw an exception here, to be defined
1039
            throw new BadStateException(
1040
                '$contentInfo',
1041
                'Content is not published, draft can be created only from published or archived version'
1042
            );
1043
        }
1044
1045
        if ($creator === null) {
1046
            $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...
1047
        }
1048
1049
        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...
1050
            throw new UnauthorizedException('content', 'edit', array('contentId' => $contentInfo->id));
1051
        }
1052
1053
        $this->repository->beginTransaction();
1054
        try {
1055
            $spiContent = $this->persistenceHandler->contentHandler()->createDraftFromVersion(
1056
                $contentInfo->id,
1057
                $versionNo,
1058
                $creator->getUserId()
1059
            );
1060
            $this->repository->commit();
1061
        } catch (Exception $e) {
1062
            $this->repository->rollback();
1063
            throw $e;
1064
        }
1065
1066
        return $this->domainMapper->buildContentDomainObject($spiContent);
1067
    }
1068
1069
    /**
1070
     * Loads drafts for a user.
1071
     *
1072
     * If no user is given the drafts for the authenticated user a returned
1073
     *
1074
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to load the draft list
1075
     *
1076
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $user
1077
     *
1078
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo the drafts ({@link VersionInfo}) owned by the given user
1079
     */
1080
    public function loadContentDrafts(User $user = null)
1081
    {
1082
        if ($user === null) {
1083
            $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...
1084
        }
1085
1086
        // throw early if user has absolutely no access to versionread
1087
        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...
1088
            throw new UnauthorizedException('content', 'versionread');
1089
        }
1090
1091
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftsForUser($user->getUserId());
1092
        $versionInfoList = array();
1093 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...
1094
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1095
            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...
1096
                throw new UnauthorizedException('content', 'versionread', array('contentId' => $versionInfo->contentInfo->id));
1097
            }
1098
1099
            $versionInfoList[] = $versionInfo;
1100
        }
1101
1102
        return $versionInfoList;
1103
    }
1104
1105
    /**
1106
     * Translate a version.
1107
     *
1108
     * updates the destination version given in $translationInfo with the provided translated fields in $translationValues
1109
     *
1110
     * @example Examples/translation_5x.php
1111
     *
1112
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to update this version
1113
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the given destination version is not a draft
1114
     * @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.
1115
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1116
     *                                                                          or value is set for non-translatable field in language
1117
     *                                                                          other than main.
1118
     *
1119
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1120
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationValues $translationValues
1121
     * @param \eZ\Publish\API\Repository\Values\User\User $modifier If set, this user is taken as modifier of the version
1122
     *
1123
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the translated fields
1124
     *
1125
     * @since 5.0
1126
     */
1127
    public function translateVersion(TranslationInfo $translationInfo, APITranslationValues $translationValues, User $modifier = null)
1128
    {
1129
        throw new NotImplementedException(__METHOD__);
1130
    }
1131
1132
    /**
1133
     * Updates the fields of a draft.
1134
     *
1135
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version
1136
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1137
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid.
1138
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
1139
     *                                                                               or if a required field is missing / set to an empty value.
1140
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
1141
     *                                                                          or value is set for non-translatable field in language
1142
     *                                                                          other than main.
1143
     *
1144
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1145
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1146
     *
1147
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the updated fields
1148
     */
1149
    public function updateContent(APIVersionInfo $versionInfo, APIContentUpdateStruct $contentUpdateStruct)
1150
    {
1151
        $contentUpdateStruct = clone $contentUpdateStruct;
1152
1153
        /** @var $content \eZ\Publish\Core\Repository\Values\Content\Content */
1154
        $content = $this->loadContent(
1155
            $versionInfo->getContentInfo()->id,
1156
            null,
1157
            $versionInfo->versionNo
1158
        );
1159
        if (!$content->versionInfo->isDraft()) {
1160
            throw new BadStateException(
1161
                '$versionInfo',
1162
                'Version is not a draft and can not be updated'
1163
            );
1164
        }
1165
1166
        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...
1167
            throw new UnauthorizedException('content', 'edit', array('contentId' => $content->id));
1168
        }
1169
1170
        $mainLanguageCode = $content->contentInfo->mainLanguageCode;
1171
        $languageCodes = $this->getLanguageCodesForUpdate($contentUpdateStruct, $content);
1172
        $contentType = $this->repository->getContentTypeService()->loadContentType(
1173
            $content->contentInfo->contentTypeId
1174
        );
1175
        $fields = $this->mapFieldsForUpdate(
1176
            $contentUpdateStruct,
1177
            $contentType,
1178
            $mainLanguageCode
1179
        );
1180
1181
        $fieldValues = array();
1182
        $spiFields = array();
1183
        $allFieldErrors = array();
1184
        $inputRelations = array();
1185
        $locationIdToContentIdMapping = array();
1186
1187
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
1188
            /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */
1189
            $fieldType = $this->fieldTypeRegistry->getFieldType(
1190
                $fieldDefinition->fieldTypeIdentifier
1191
            );
1192
1193
            foreach ($languageCodes as $languageCode) {
1194
                $isCopied = $isEmpty = $isRetained = false;
1195
                $isLanguageNew = !in_array($languageCode, $content->versionInfo->languageCodes);
1196
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $mainLanguageCode;
1197
                $isFieldUpdated = isset($fields[$fieldDefinition->identifier][$valueLanguageCode]);
1198
                $isProcessed = isset($fieldValues[$fieldDefinition->identifier][$valueLanguageCode]);
1199
1200
                if (!$isFieldUpdated && !$isLanguageNew) {
1201
                    $isRetained = true;
1202
                    $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...
1203
                } elseif (!$isFieldUpdated && $isLanguageNew && !$fieldDefinition->isTranslatable) {
1204
                    $isCopied = true;
1205
                    $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...
1206
                } elseif ($isFieldUpdated) {
1207
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
1208
                } else {
1209
                    $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...
1210
                }
1211
1212
                $fieldValue = $fieldType->acceptValue($fieldValue);
1213
1214 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...
1215
                    $isEmpty = true;
1216
                    if ($fieldDefinition->isRequired) {
1217
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
1218
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
1219
                            null,
1220
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
1221
                            'empty'
1222
                        );
1223
                    }
1224
                } else {
1225
                    $fieldErrors = $fieldType->validate(
1226
                        $fieldDefinition,
1227
                        $fieldValue
1228
                    );
1229
                    if (!empty($fieldErrors)) {
1230
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
1231
                    }
1232
                }
1233
1234
                if (!empty($allFieldErrors)) {
1235
                    continue;
1236
                }
1237
1238
                $this->relationProcessor->appendFieldRelations(
1239
                    $inputRelations,
1240
                    $locationIdToContentIdMapping,
1241
                    $fieldType,
1242
                    $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...
1243
                    $fieldDefinition->id
1244
                );
1245
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
1246
1247
                if ($isRetained || $isCopied || ($isLanguageNew && $isEmpty) || $isProcessed) {
1248
                    continue;
1249
                }
1250
1251
                $spiFields[] = new SPIField(
1252
                    array(
1253
                        'id' => $isLanguageNew ?
1254
                            null :
1255
                            $content->getField($fieldDefinition->identifier, $languageCode)->id,
1256
                        'fieldDefinitionId' => $fieldDefinition->id,
1257
                        'type' => $fieldDefinition->fieldTypeIdentifier,
1258
                        'value' => $fieldType->toPersistenceValue($fieldValue),
1259
                        'languageCode' => $languageCode,
1260
                        'versionNo' => $versionInfo->versionNo,
1261
                    )
1262
                );
1263
            }
1264
        }
1265
1266
        if (!empty($allFieldErrors)) {
1267
            throw new ContentFieldValidationException($allFieldErrors);
1268
        }
1269
1270
        $spiContentUpdateStruct = new SPIContentUpdateStruct(
1271
            array(
1272
                'name' => $this->nameSchemaService->resolveNameSchema(
1273
                    $content,
1274
                    $fieldValues,
1275
                    $languageCodes,
1276
                    $contentType
1277
                ),
1278
                '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...
1279
                'fields' => $spiFields,
1280
                'modificationDate' => time(),
1281
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1282
                    $contentUpdateStruct->initialLanguageCode
1283
                )->id,
1284
            )
1285
        );
1286
        $existingRelations = $this->loadRelations($versionInfo);
1287
1288
        $this->repository->beginTransaction();
1289
        try {
1290
            $spiContent = $this->persistenceHandler->contentHandler()->updateContent(
1291
                $versionInfo->getContentInfo()->id,
1292
                $versionInfo->versionNo,
1293
                $spiContentUpdateStruct
1294
            );
1295
            $this->relationProcessor->processFieldRelations(
1296
                $inputRelations,
1297
                $spiContent->versionInfo->contentInfo->id,
1298
                $spiContent->versionInfo->versionNo,
1299
                $contentType,
1300
                $existingRelations
1301
            );
1302
            $this->repository->commit();
1303
        } catch (Exception $e) {
1304
            $this->repository->rollback();
1305
            throw $e;
1306
        }
1307
1308
        return $this->domainMapper->buildContentDomainObject(
1309
            $spiContent,
1310
            $contentType
1311
        );
1312
    }
1313
1314
    /**
1315
     * Returns all language codes used in given $fields.
1316
     *
1317
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value exists in initial language
1318
     *
1319
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1320
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1321
     *
1322
     * @return array
1323
     */
1324
    protected function getLanguageCodesForUpdate(APIContentUpdateStruct $contentUpdateStruct, APIContent $content)
1325
    {
1326
        if ($contentUpdateStruct->initialLanguageCode !== null) {
1327
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1328
                $contentUpdateStruct->initialLanguageCode
1329
            );
1330
        } else {
1331
            $contentUpdateStruct->initialLanguageCode = $content->contentInfo->mainLanguageCode;
1332
        }
1333
1334
        $languageCodes = array_fill_keys($content->versionInfo->languageCodes, true);
1335
        $languageCodes[$contentUpdateStruct->initialLanguageCode] = true;
1336
1337 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...
1338
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1339
                continue;
1340
            }
1341
1342
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1343
                $field->languageCode
1344
            );
1345
            $languageCodes[$field->languageCode] = true;
1346
        }
1347
1348
        return array_keys($languageCodes);
1349
    }
1350
1351
    /**
1352
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
1353
     *
1354
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1355
     *                                                                          or value is set for non-translatable field in language
1356
     *                                                                          other than main
1357
     *
1358
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1359
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1360
     * @param string $mainLanguageCode
1361
     *
1362
     * @return array
1363
     */
1364
    protected function mapFieldsForUpdate(
1365
        APIContentUpdateStruct $contentUpdateStruct,
1366
        ContentType $contentType,
1367
        $mainLanguageCode
1368
    ) {
1369
        $fields = array();
1370
1371
        foreach ($contentUpdateStruct->fields as $field) {
1372
            $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier);
1373
1374
            if ($fieldDefinition === null) {
1375
                throw new ContentValidationException(
1376
                    "Field definition '%identifier%' does not exist in given ContentType",
1377
                    ['%identifier%' => $field->fieldDefIdentifier]
1378
                );
1379
            }
1380
1381
            if ($field->languageCode === null) {
1382
                if ($fieldDefinition->isTranslatable) {
1383
                    $languageCode = $contentUpdateStruct->initialLanguageCode;
1384
                } else {
1385
                    $languageCode = $mainLanguageCode;
1386
                }
1387
                $field = $this->cloneField($field, array('languageCode' => $languageCode));
1388
            }
1389
1390 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...
1391
                throw new ContentValidationException(
1392
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
1393
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
1394
                );
1395
            }
1396
1397
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
1398
        }
1399
1400
        return $fields;
1401
    }
1402
1403
    /**
1404
     * Publishes a content version.
1405
     *
1406
     * Publishes a content version and deletes archive versions if they overflow max archive versions.
1407
     * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future.
1408
     *
1409
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version
1410
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1411
     *
1412
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1413
     *
1414
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1415
     */
1416
    public function publishVersion(APIVersionInfo $versionInfo)
1417
    {
1418
        $content = $this->internalLoadContent(
1419
            $versionInfo->contentInfo->id,
1420
            null,
1421
            $versionInfo->versionNo
1422
        );
1423
1424
        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...
1425
            throw new UnauthorizedException('content', 'publish', array('contentId' => $content->id));
1426
        }
1427
1428
        $this->repository->beginTransaction();
1429
        try {
1430
            $content = $this->internalPublishVersion($content->getVersionInfo());
1431
            $this->repository->commit();
1432
        } catch (Exception $e) {
1433
            $this->repository->rollback();
1434
            throw $e;
1435
        }
1436
1437
        return $content;
1438
    }
1439
1440
    /**
1441
     * Publishes a content version.
1442
     *
1443
     * Publishes a content version and deletes archive versions if they overflow max archive versions.
1444
     * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future.
1445
     *
1446
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1447
     *
1448
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1449
     * @param int|null $publicationDate If null existing date is kept if there is one, otherwise current time is used.
1450
     *
1451
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1452
     */
1453
    protected function internalPublishVersion(APIVersionInfo $versionInfo, $publicationDate = null)
1454
    {
1455
        if (!$versionInfo->isDraft()) {
1456
            throw new BadStateException('$versionInfo', 'Only versions in draft status can be published.');
1457
        }
1458
1459
        $currentTime = time();
1460
        if ($publicationDate === null && $versionInfo->versionNo === 1) {
1461
            $publicationDate = $currentTime;
1462
        }
1463
1464
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
1465
        $metadataUpdateStruct->publicationDate = $publicationDate;
1466
        $metadataUpdateStruct->modificationDate = $currentTime;
1467
1468
        $contentId = $versionInfo->getContentInfo()->id;
1469
        $spiContent = $this->persistenceHandler->contentHandler()->publish(
1470
            $contentId,
1471
            $versionInfo->versionNo,
1472
            $metadataUpdateStruct
1473
        );
1474
1475
        $content = $this->domainMapper->buildContentDomainObject($spiContent);
1476
1477
        $this->publishUrlAliasesForContent($content);
1478
1479
        // Delete version archive overflow if any, limit is 0-50 (however 0 will mean 1 if content is unpublished)
1480
        $archiveList = $this->persistenceHandler->contentHandler()->listVersions(
1481
            $contentId,
1482
            APIVersionInfo::STATUS_ARCHIVED,
1483
            100 // Limited to avoid publishing taking to long, besides SE limitations this is why limit is max 50
1484
        );
1485
1486
        $maxVersionArchiveCount = max(0, min(50, $this->settings['default_version_archive_limit']));
1487
        while (!empty($archiveList) && count($archiveList) > $maxVersionArchiveCount) {
1488
            /** @var \eZ\Publish\SPI\Persistence\Content\VersionInfo $archiveVersion */
1489
            $archiveVersion = array_shift($archiveList);
1490
            $this->persistenceHandler->contentHandler()->deleteVersion(
1491
                $contentId,
1492
                $archiveVersion->versionNo
1493
            );
1494
        }
1495
1496
        return $content;
1497
    }
1498
1499
    /**
1500
     * Removes the given version.
1501
     *
1502
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is in
1503
     *         published state or is the last version of the Content
1504
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version
1505
     *
1506
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1507
     */
1508
    public function deleteVersion(APIVersionInfo $versionInfo)
1509
    {
1510
        if ($versionInfo->isPublished()) {
1511
            throw new BadStateException(
1512
                '$versionInfo',
1513
                'Version is published and can not be removed'
1514
            );
1515
        }
1516
1517 View Code Duplication
        if (!$this->repository->canUser('content', 'versionremove', $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...
1518
            throw new UnauthorizedException(
1519
                'content',
1520
                'versionremove',
1521
                array('contentId' => $versionInfo->contentInfo->id, 'versionNo' => $versionInfo->versionNo)
1522
            );
1523
        }
1524
1525
        $versionList = $this->persistenceHandler->contentHandler()->listVersions(
1526
            $versionInfo->contentInfo->id,
1527
            null,
1528
            2
1529
        );
1530
1531
        if (count($versionList) === 1) {
1532
            throw new BadStateException(
1533
                '$versionInfo',
1534
                'Version is the last version of the Content and can not be removed'
1535
            );
1536
        }
1537
1538
        $this->repository->beginTransaction();
1539
        try {
1540
            $this->persistenceHandler->contentHandler()->deleteVersion(
1541
                $versionInfo->getContentInfo()->id,
1542
                $versionInfo->versionNo
1543
            );
1544
            $this->repository->commit();
1545
        } catch (Exception $e) {
1546
            $this->repository->rollback();
1547
            throw $e;
1548
        }
1549
    }
1550
1551
    /**
1552
     * Loads all versions for the given content.
1553
     *
1554
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions
1555
     *
1556
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1557
     *
1558
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] Sorted by creation date
1559
     */
1560
    public function loadVersions(ContentInfo $contentInfo)
1561
    {
1562
        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...
1563
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentInfo->id));
1564
        }
1565
1566
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->listVersions($contentInfo->id);
1567
1568
        $versions = array();
1569 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...
1570
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1571
            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...
1572
                throw new UnauthorizedException('content', 'versionread', array('versionId' => $versionInfo->id));
1573
            }
1574
1575
            $versions[] = $versionInfo;
1576
        }
1577
1578
        return $versions;
1579
    }
1580
1581
    /**
1582
     * Copies the content to a new location. If no version is given,
1583
     * all versions are copied, otherwise only the given version.
1584
     *
1585
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location
1586
     *
1587
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1588
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to
1589
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1590
     *
1591
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1592
     */
1593
    public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, APIVersionInfo $versionInfo = null)
1594
    {
1595
        $destinationLocation = $this->repository->getLocationService()->loadLocation(
1596
            $destinationLocationCreateStruct->parentLocationId
1597
        );
1598 View Code Duplication
        if (!$this->repository->canUser('content', 'create', $contentInfo, [$destinationLocation])) {
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...
1599
            throw new UnauthorizedException(
1600
                'content',
1601
                'create',
1602
                [
1603
                    'parentLocationId' => $destinationLocationCreateStruct->parentLocationId,
1604
                    'sectionId' => $contentInfo->sectionId,
1605
                ]
1606
            );
1607
        }
1608
1609
        $defaultObjectStates = $this->getDefaultObjectStates();
1610
1611
        $this->repository->beginTransaction();
1612
        try {
1613
            $spiContent = $this->persistenceHandler->contentHandler()->copy(
1614
                $contentInfo->id,
1615
                $versionInfo ? $versionInfo->versionNo : null
1616
            );
1617
1618
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
1619
                $this->persistenceHandler->objectStateHandler()->setContentState(
1620
                    $spiContent->versionInfo->contentInfo->id,
1621
                    $objectStateGroupId,
1622
                    $objectState->id
1623
                );
1624
            }
1625
1626
            $content = $this->internalPublishVersion(
1627
                $this->domainMapper->buildVersionInfoDomainObject($spiContent->versionInfo),
1628
                $spiContent->versionInfo->creationDate
1629
            );
1630
1631
            $this->repository->getLocationService()->createLocation(
1632
                $content->getVersionInfo()->getContentInfo(),
1633
                $destinationLocationCreateStruct
1634
            );
1635
            $this->repository->commit();
1636
        } catch (Exception $e) {
1637
            $this->repository->rollback();
1638
            throw $e;
1639
        }
1640
1641
        return $this->internalLoadContent($content->id);
1642
    }
1643
1644
    /**
1645
     * Loads all outgoing relations for the given version.
1646
     *
1647
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1648
     *
1649
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1650
     *
1651
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1652
     */
1653
    public function loadRelations(APIVersionInfo $versionInfo)
1654
    {
1655
        if ($versionInfo->isPublished()) {
1656
            $function = 'read';
1657
        } else {
1658
            $function = 'versionread';
1659
        }
1660
1661
        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...
1662
            throw new UnauthorizedException('content', $function);
1663
        }
1664
1665
        $contentInfo = $versionInfo->getContentInfo();
1666
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1667
            $contentInfo->id,
1668
            $versionInfo->versionNo
1669
        );
1670
1671
        /** @var $relations \eZ\Publish\API\Repository\Values\Content\Relation[] */
1672
        $relations = array();
1673 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...
1674
            $destinationContentInfo = $this->internalLoadContentInfo($spiRelation->destinationContentId);
1675
            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...
1676
                continue;
1677
            }
1678
1679
            $relations[] = $this->domainMapper->buildRelationDomainObject(
1680
                $spiRelation,
1681
                $contentInfo,
1682
                $destinationContentInfo
1683
            );
1684
        }
1685
1686
        return $relations;
1687
    }
1688
1689
    /**
1690
     * Loads all incoming relations for a content object.
1691
     *
1692
     * The relations come only from published versions of the source content objects
1693
     *
1694
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1695
     *
1696
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1697
     *
1698
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1699
     */
1700
    public function loadReverseRelations(ContentInfo $contentInfo)
1701
    {
1702
        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...
1703
            throw new UnauthorizedException('content', 'reverserelatedlist', array('contentId' => $contentInfo->id));
1704
        }
1705
1706
        $spiRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations(
1707
            $contentInfo->id
1708
        );
1709
1710
        $returnArray = array();
1711 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...
1712
            $sourceContentInfo = $this->internalLoadContentInfo($spiRelation->sourceContentId);
1713
            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...
1714
                continue;
1715
            }
1716
1717
            $returnArray[] = $this->domainMapper->buildRelationDomainObject(
1718
                $spiRelation,
1719
                $sourceContentInfo,
1720
                $contentInfo
1721
            );
1722
        }
1723
1724
        return $returnArray;
1725
    }
1726
1727
    /**
1728
     * Adds a relation of type common.
1729
     *
1730
     * The source of the relation is the content and version
1731
     * referenced by $versionInfo.
1732
     *
1733
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version
1734
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1735
     *
1736
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1737
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation
1738
     *
1739
     * @return \eZ\Publish\API\Repository\Values\Content\Relation the newly created relation
1740
     */
1741
    public function addRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1742
    {
1743
        $sourceVersion = $this->loadVersionInfoById(
1744
            $sourceVersion->contentInfo->id,
1745
            $sourceVersion->versionNo
1746
        );
1747
1748
        if (!$sourceVersion->isDraft()) {
1749
            throw new BadStateException(
1750
                '$sourceVersion',
1751
                'Relations of type common can only be added to versions of status draft'
1752
            );
1753
        }
1754
1755
        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...
1756
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1757
        }
1758
1759
        $sourceContentInfo = $sourceVersion->getContentInfo();
1760
1761
        $this->repository->beginTransaction();
1762
        try {
1763
            $spiRelation = $this->persistenceHandler->contentHandler()->addRelation(
1764
                new SPIRelationCreateStruct(
1765
                    array(
1766
                        'sourceContentId' => $sourceContentInfo->id,
1767
                        'sourceContentVersionNo' => $sourceVersion->versionNo,
1768
                        'sourceFieldDefinitionId' => null,
1769
                        'destinationContentId' => $destinationContent->id,
1770
                        'type' => APIRelation::COMMON,
1771
                    )
1772
                )
1773
            );
1774
            $this->repository->commit();
1775
        } catch (Exception $e) {
1776
            $this->repository->rollback();
1777
            throw $e;
1778
        }
1779
1780
        return $this->domainMapper->buildRelationDomainObject($spiRelation, $sourceContentInfo, $destinationContent);
1781
    }
1782
1783
    /**
1784
     * Removes a relation of type COMMON from a draft.
1785
     *
1786
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version
1787
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1788
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination
1789
     *
1790
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1791
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent
1792
     */
1793
    public function deleteRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1794
    {
1795
        $sourceVersion = $this->loadVersionInfoById(
1796
            $sourceVersion->contentInfo->id,
1797
            $sourceVersion->versionNo
1798
        );
1799
1800
        if (!$sourceVersion->isDraft()) {
1801
            throw new BadStateException(
1802
                '$sourceVersion',
1803
                'Relations of type common can only be removed from versions of status draft'
1804
            );
1805
        }
1806
1807
        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...
1808
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1809
        }
1810
1811
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1812
            $sourceVersion->getContentInfo()->id,
1813
            $sourceVersion->versionNo,
1814
            APIRelation::COMMON
1815
        );
1816
1817
        if (empty($spiRelations)) {
1818
            throw new InvalidArgumentException(
1819
                '$sourceVersion',
1820
                'There are no relations of type COMMON for the given destination'
1821
            );
1822
        }
1823
1824
        // there should be only one relation of type COMMON for each destination,
1825
        // but in case there were ever more then one, we will remove them all
1826
        // @todo: alternatively, throw BadStateException?
1827
        $this->repository->beginTransaction();
1828
        try {
1829
            foreach ($spiRelations as $spiRelation) {
1830
                if ($spiRelation->destinationContentId == $destinationContent->id) {
1831
                    $this->persistenceHandler->contentHandler()->removeRelation(
1832
                        $spiRelation->id,
1833
                        APIRelation::COMMON
1834
                    );
1835
                }
1836
            }
1837
            $this->repository->commit();
1838
        } catch (Exception $e) {
1839
            $this->repository->rollback();
1840
            throw $e;
1841
        }
1842
    }
1843
1844
    /**
1845
     * Adds translation information to the content object.
1846
     *
1847
     * @example Examples/translation_5x.php
1848
     *
1849
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed add a translation info
1850
     *
1851
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1852
     *
1853
     * @since 5.0
1854
     */
1855
    public function addTranslationInfo(TranslationInfo $translationInfo)
1856
    {
1857
        throw new NotImplementedException(__METHOD__);
1858
    }
1859
1860
    /**
1861
     * lists the translations done on this content object.
1862
     *
1863
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed read translation infos
1864
     *
1865
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1866
     * @param array $filter
1867
     *
1868
     * @todo TBD - filter by source version destination version and languages
1869
     *
1870
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo[]
1871
     *
1872
     * @since 5.0
1873
     */
1874
    public function loadTranslationInfos(ContentInfo $contentInfo, array $filter = array())
1875
    {
1876
        throw new NotImplementedException(__METHOD__);
1877
    }
1878
1879
    /**
1880
     * Remove Content Object translation from all Versions (including archived ones) of a Content Object.
1881
     *
1882
     * NOTE: this operation is risky and permanent, so user interface (ideally CLI) should provide
1883
     *       a warning before performing it.
1884
     *
1885
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the specified translation
1886
     *         is the only one a Version has or it is the main translation of a Content Object.
1887
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed
1888
     *         to delete the content (in one of the locations of the given Content Object).
1889
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument
1890
     *         is invalid for the given content.
1891
     *
1892
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1893
     * @param string $languageCode
1894
     *
1895
     * @since 6.11
1896
     */
1897
    public function removeTranslation(ContentInfo $contentInfo, $languageCode)
1898
    {
1899
        if ($contentInfo->mainLanguageCode === $languageCode) {
1900
            throw new BadStateException(
1901
                '$languageCode',
1902
                'Specified translation is the main translation of the Content Object'
1903
            );
1904
        }
1905
        // before actual removal, collect information on Versions
1906
        $versions = [];
1907
        $singleLangVersions = [];
1908
        foreach ($this->loadVersions($contentInfo) as $versionInfo) {
1909
            // check if user is authorized to delete Version
1910
            if (!$this->repository->canUser('content', 'remove', $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...
1911
                throw new UnauthorizedException(
1912
                    'content',
1913
                    'remove',
1914
                    [
1915
                        'contentId' => $contentInfo->id,
1916
                        'versionNo' => $versionInfo->versionNo,
1917
                    ]
1918
                );
1919
            }
1920
            // check if the specified translation exists for the Version
1921
            if (!in_array($languageCode, $versionInfo->languageCodes)) {
1922
                // if translation does not exist, simply ignore Version (see InvalidArgumentException later on)
1923
                continue;
1924
            }
1925
            // check if the specified translation is the only one
1926
            if (count($versionInfo->languageCodes) < 2) {
1927
                $singleLangVersions[] = $versionInfo->versionNo;
1928
            } else {
1929
                // otherwise add Version to the list of valid Versions to be processed
1930
                $versions[] = $versionInfo->versionNo;
1931
            }
1932
        }
1933
1934
        if (!empty($singleLangVersions)) {
1935
            $verList = implode(', ', $singleLangVersions);
1936
            throw new BadStateException(
1937
                '$languageCode',
1938
                "The Version(s): {$verList} of the ContentId={$contentInfo->id} have only one language {$languageCode}. Remove these Version(s) before proceeding.\n" .
1939
                "Hint: Command 'ezplatform:remove-content-translation' handles this for you, look into it to see how this can be handled in custom code."
1940
            );
1941
        }
1942
1943
        // if there are no Versions with the given translation, $languageCode arg is invalid
1944
        if (empty($versions)) {
1945
            throw new InvalidArgumentException(
1946
                '$languageCode',
1947
                sprintf(
1948
                    'Specified translation does not exist in the Content Object(id=%d)',
1949
                     $contentInfo->id
1950
                )
1951
            );
1952
        }
1953
1954
        $this->repository->beginTransaction();
1955
        try {
1956
            $this->persistenceHandler->contentHandler()->removeTranslationFromContent($contentInfo->id, $languageCode);
1957
            $locationIds = array_map(
1958
                function (Location $location) {
1959
                    return $location->id;
0 ignored issues
show
Documentation introduced by
The property $id is declared protected in eZ\Publish\API\Repository\Values\Content\Location. 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...
1960
                },
1961
                $this->repository->getLocationService()->loadLocations($contentInfo)
1962
            );
1963
            $this->persistenceHandler->urlAliasHandler()->translationRemoved($locationIds, $languageCode);
1964
            $this->repository->commit();
1965
        } catch (Exception $e) {
1966
            $this->repository->rollback();
1967
            // cover generic unexpected exception to fulfill API promise on @throws
1968
            throw new BadStateException('$contentInfo', 'Translation removal failed', $e);
1969
        }
1970
    }
1971
1972
    /**
1973
     * Delete specified Translation from a Content Draft.
1974
     *
1975
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the specified Translation
1976
     *         is the only one the Content Draft has or it is the main Translation of a Content Object.
1977
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed
1978
     *         to edit the Content (in one of the locations of the given Content Object).
1979
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument
1980
     *         is invalid for the given Draft.
1981
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if specified Version was not found
1982
     *
1983
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo Content Version Draft
1984
     * @param string $languageCode Language code of the Translation to be removed
1985
     *
1986
     * @return \eZ\Publish\API\Repository\Values\Content\Content Content Draft w/o the specified Translation
1987
     *
1988
     * @since 6.12
1989
     */
1990
    public function deleteTranslationFromDraft(APIVersionInfo $versionInfo, $languageCode)
1991
    {
1992
        if (!$versionInfo->isDraft()) {
1993
            throw new BadStateException(
1994
                '$versionInfo',
1995
                'Version is not a draft, so Translations cannot be modified. Create a Draft before proceeding'
1996
            );
1997
        }
1998
1999
        if ($versionInfo->contentInfo->mainLanguageCode === $languageCode) {
2000
            throw new BadStateException(
2001
                '$languageCode',
2002
                'Specified Translation is the main Translation of the Content Object. Change it before proceeding.'
2003
            );
2004
        }
2005
2006
        if (!$this->repository->canUser('content', 'edit', $versionInfo->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...
2007
            throw new UnauthorizedException(
2008
                'content', 'edit', ['contentId' => $versionInfo->contentInfo->id]
2009
            );
2010
        }
2011
2012
        if (!in_array($languageCode, $versionInfo->languageCodes)) {
2013
            throw new InvalidArgumentException(
2014
                '$languageCode',
2015
                sprintf(
2016
                    'The Version (ContentId=%d, VersionNo=%d) is not translated into %s',
2017
                    $versionInfo->contentInfo->id,
2018
                    $versionInfo->versionNo,
2019
                    $languageCode
2020
                )
2021
            );
2022
        }
2023
2024
        if (count($versionInfo->languageCodes) === 1) {
2025
            throw new BadStateException(
2026
                '$languageCode',
2027
                'Specified Translation is the only one Content Object Version has'
2028
            );
2029
        }
2030
2031
        $this->repository->beginTransaction();
2032
        try {
2033
            $spiContent = $this->persistenceHandler->contentHandler()->deleteTranslationFromDraft(
2034
                $versionInfo->contentInfo->id,
2035
                $versionInfo->versionNo,
2036
                $languageCode
2037
            );
2038
            $this->repository->commit();
2039
2040
            return $this->domainMapper->buildContentDomainObject($spiContent);
2041
        } catch (APINotFoundException $e) {
2042
            // avoid wrapping expected NotFoundException in BadStateException handled below
2043
            $this->repository->rollback();
2044
            throw $e;
2045
        } catch (Exception $e) {
2046
            $this->repository->rollback();
2047
            // cover generic unexpected exception to fulfill API promise on @throws
2048
            throw new BadStateException('$contentInfo', 'Translation removal failed', $e);
2049
        }
2050
    }
2051
2052
    /**
2053
     * Instantiates a new content create struct object.
2054
     *
2055
     * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable
2056
     *
2057
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
2058
     * @param string $mainLanguageCode
2059
     *
2060
     * @return \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct
2061
     */
2062
    public function newContentCreateStruct(ContentType $contentType, $mainLanguageCode)
2063
    {
2064
        return new ContentCreateStruct(
2065
            array(
2066
                'contentType' => $contentType,
2067
                'mainLanguageCode' => $mainLanguageCode,
2068
                'alwaysAvailable' => $contentType->defaultAlwaysAvailable,
2069
            )
2070
        );
2071
    }
2072
2073
    /**
2074
     * Instantiates a new content meta data update struct.
2075
     *
2076
     * @return \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct
2077
     */
2078
    public function newContentMetadataUpdateStruct()
2079
    {
2080
        return new ContentMetadataUpdateStruct();
2081
    }
2082
2083
    /**
2084
     * Instantiates a new content update struct.
2085
     *
2086
     * @return \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct
2087
     */
2088
    public function newContentUpdateStruct()
2089
    {
2090
        return new ContentUpdateStruct();
2091
    }
2092
2093
    /**
2094
     * Instantiates a new TranslationInfo object.
2095
     *
2096
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo
2097
     */
2098
    public function newTranslationInfo()
2099
    {
2100
        return new TranslationInfo();
2101
    }
2102
2103
    /**
2104
     * Instantiates a Translation object.
2105
     *
2106
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationValues
2107
     */
2108
    public function newTranslationValues()
2109
    {
2110
        return new TranslationValues();
2111
    }
2112
}
2113