Completed
Push — master ( 3650d6...6d4b31 )
by André
20:00
created

ContentService::buildSPILocationCreateStructs()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

Loading history...
98
     * @param array $settings
99
     */
100
    public function __construct(
101
        RepositoryInterface $repository,
102
        Handler $handler,
103
        Helper\DomainMapper $domainMapper,
104
        Helper\RelationProcessor $relationProcessor,
105
        Helper\NameSchemaService $nameSchemaService,
106
        Helper\FieldTypeRegistry $fieldTypeRegistry,
107
        array $settings = array()
108
    ) {
109
        $this->repository = $repository;
0 ignored issues
show
Documentation Bug introduced by
$repository is of type object<eZ\Publish\API\Repository\Repository>, but the property $repository was declared to be of type object<eZ\Publish\Core\Repository\Repository>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
110
        $this->persistenceHandler = $handler;
111
        $this->domainMapper = $domainMapper;
112
        $this->relationProcessor = $relationProcessor;
113
        $this->nameSchemaService = $nameSchemaService;
114
        $this->fieldTypeRegistry = $fieldTypeRegistry;
115
        // Union makes sure default settings are ignored if provided in argument
116
        $this->settings = $settings + array(
117
            // Version archive limit (0-50), only enforced on publish, not on un-publish.
118
            'default_version_archive_limit' => 5,
119
        );
120
    }
121
122
    /**
123
     * Loads a content info object.
124
     *
125
     * To load fields use loadContent
126
     *
127
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content
128
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist
129
     *
130
     * @param int $contentId
131
     *
132
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
133
     */
134 View Code Duplication
    public function loadContentInfo($contentId)
135
    {
136
        $contentInfo = $this->internalLoadContentInfo($contentId);
137
        if (!$this->repository->canUser('content', 'read', $contentInfo)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
138
            throw new UnauthorizedException('content', 'read', array('contentId' => $contentId));
139
        }
140
141
        return $contentInfo;
142
    }
143
144
    /**
145
     * Loads a content info object.
146
     *
147
     * To load fields use loadContent
148
     *
149
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist
150
     *
151
     * @param mixed $id
152
     * @param bool $isRemoteId
153
     *
154
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
155
     */
156
    public function internalLoadContentInfo($id, $isRemoteId = false)
157
    {
158
        try {
159
            $method = $isRemoteId ? 'loadContentInfoByRemoteId' : 'loadContentInfo';
160
161
            return $this->domainMapper->buildContentInfoDomainObject(
162
                $this->persistenceHandler->contentHandler()->$method($id)
163
            );
164
        } catch (APINotFoundException $e) {
165
            throw new NotFoundException(
166
                'Content',
167
                $id,
168
                $e
169
            );
170
        }
171
    }
172
173
    /**
174
     * Loads a content info object for the given remoteId.
175
     *
176
     * To load fields use loadContent
177
     *
178
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content
179
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given remote id does not exist
180
     *
181
     * @param string $remoteId
182
     *
183
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
184
     */
185 View Code Duplication
    public function loadContentInfoByRemoteId($remoteId)
186
    {
187
        $contentInfo = $this->internalLoadContentInfo($remoteId, true);
188
189
        if (!$this->repository->canUser('content', 'read', $contentInfo)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
190
            throw new UnauthorizedException('content', 'read', array('remoteId' => $remoteId));
191
        }
192
193
        return $contentInfo;
194
    }
195
196
    /**
197
     * Loads a version info of the given content object.
198
     *
199
     * If no version number is given, the method returns the current version
200
     *
201
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist
202
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
203
     *
204
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
205
     * @param int $versionNo the version number. If not given the current version is returned.
206
     *
207
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo
208
     */
209
    public function loadVersionInfo(ContentInfo $contentInfo, $versionNo = null)
210
    {
211
        return $this->loadVersionInfoById($contentInfo->id, $versionNo);
212
    }
213
214
    /**
215
     * Loads a version info of the given content object id.
216
     *
217
     * If no version number is given, the method returns the current version
218
     *
219
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist
220
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
221
     *
222
     * @param mixed $contentId
223
     * @param int $versionNo the version number. If not given the current version is returned.
224
     *
225
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo
226
     */
227
    public function loadVersionInfoById($contentId, $versionNo = null)
228
    {
229
        if ($versionNo === null) {
230
            $versionNo = $this->loadContentInfo($contentId)->currentVersionNo;
231
        }
232
233
        try {
234
            $spiVersionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo(
235
                $contentId,
236
                $versionNo
237
            );
238
        } catch (APINotFoundException $e) {
239
            throw new NotFoundException(
240
                'VersionInfo',
241
                array(
242
                    'contentId' => $contentId,
243
                    'versionNo' => $versionNo,
244
                ),
245
                $e
246
            );
247
        }
248
249
        $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
250
251
        if ($versionInfo->status === APIVersionInfo::STATUS_PUBLISHED) {
252
            $function = 'read';
253
        } else {
254
            $function = 'versionread';
255
        }
256
257
        if (!$this->repository->canUser('content', $function, $versionInfo)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

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