Completed
Push — version_list_ordering ( 7460a7 )
by André
80:56 queued 37:52
created

ContentService::mapFieldsForUpdate()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 38
Code Lines 23

Duplication

Lines 6
Ratio 15.79 %

Importance

Changes 0
Metric Value
cc 7
eloc 23
nc 8
nop 3
dl 6
loc 38
rs 6.7272
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
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Repository;
12
13
use eZ\Publish\API\Repository\ContentService as ContentServiceInterface;
14
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
15
use eZ\Publish\SPI\Persistence\Handler;
16
use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct as APIContentUpdateStruct;
17
use eZ\Publish\API\Repository\Values\ContentType\ContentType;
18
use eZ\Publish\API\Repository\Values\Content\TranslationInfo;
19
use eZ\Publish\API\Repository\Values\Content\TranslationValues as APITranslationValues;
20
use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct;
21
use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct;
22
use eZ\Publish\API\Repository\Values\Content\Content as APIContent;
23
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
24
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
25
use eZ\Publish\API\Repository\Values\User\User;
26
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
27
use eZ\Publish\API\Repository\Values\Content\Field;
28
use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation;
29
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
30
use eZ\Publish\Core\Base\Exceptions\BadStateException;
31
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
32
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
33
use eZ\Publish\Core\Base\Exceptions\ContentValidationException;
34
use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException;
35
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
36
use eZ\Publish\Core\FieldType\ValidationError;
37
use eZ\Publish\Core\Repository\Values\Content\VersionInfo;
38
use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct;
39
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
40
use eZ\Publish\Core\Repository\Values\Content\TranslationValues;
41
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct;
42
use eZ\Publish\SPI\Persistence\Content\CreateStruct as SPIContentCreateStruct;
43
use eZ\Publish\SPI\Persistence\Content\UpdateStruct as SPIContentUpdateStruct;
44
use eZ\Publish\SPI\Persistence\Content\Field as SPIField;
45
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as SPIRelationCreateStruct;
46
use Exception;
47
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
48
49
/**
50
 * This class provides service methods for managing content.
51
 *
52
 * @example Examples/content.php
53
 */
54
class ContentService implements ContentServiceInterface
55
{
56
    /**
57
     * @var \eZ\Publish\Core\Repository\Repository
58
     */
59
    protected $repository;
60
61
    /**
62
     * @var \eZ\Publish\SPI\Persistence\Handler
63
     */
64
    protected $persistenceHandler;
65
66
    /**
67
     * @var array
68
     */
69
    protected $settings;
70
71
    /**
72
     * @var \eZ\Publish\Core\Repository\Helper\DomainMapper
73
     */
74
    protected $domainMapper;
75
76
    /**
77
     * @var \eZ\Publish\Core\Repository\Helper\RelationProcessor
78
     */
79
    protected $relationProcessor;
80
81
    /**
82
     * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService
83
     */
84
    protected $nameSchemaService;
85
86
    /**
87
     * @var \eZ\Publish\Core\Repository\Helper\FieldTypeRegistry
88
     */
89
    protected $fieldTypeRegistry;
90
91
    /**
92
     * Setups service with reference to repository object that created it & corresponding handler.
93
     *
94
     * @param \eZ\Publish\API\Repository\Repository $repository
95
     * @param \eZ\Publish\SPI\Persistence\Handler $handler
96
     * @param \eZ\Publish\Core\Repository\Helper\DomainMapper $domainMapper
97
     * @param \eZ\Publish\Core\Repository\Helper\RelationProcessor $relationProcessor
98
     * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService
99
     * @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...
100
     * @param array $settings
101
     */
102 View Code Duplication
    public function __construct(
103
        RepositoryInterface $repository,
104
        Handler $handler,
105
        Helper\DomainMapper $domainMapper,
106
        Helper\RelationProcessor $relationProcessor,
107
        Helper\NameSchemaService $nameSchemaService,
108
        Helper\FieldTypeRegistry $fieldTypeRegistry,
109
        array $settings = array()
110
    ) {
111
        $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...
112
        $this->persistenceHandler = $handler;
113
        $this->domainMapper = $domainMapper;
114
        $this->relationProcessor = $relationProcessor;
115
        $this->nameSchemaService = $nameSchemaService;
116
        $this->fieldTypeRegistry = $fieldTypeRegistry;
117
        // Union makes sure default settings are ignored if provided in argument
118
        $this->settings = $settings + array(
119
            //'defaultSetting' => array(),
120
        );
121
    }
122
123
    /**
124
     * Loads a content info object.
125
     *
126
     * To load fields use loadContent
127
     *
128
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content
129
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist
130
     *
131
     * @param int $contentId
132
     *
133
     * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo
134
     */
135 View Code Duplication
    public function loadContentInfo($contentId)
136
    {
137
        $contentInfo = $this->internalLoadContentInfo($contentId);
138
        if (!$this->repository->canUser('content', 'read', $contentInfo)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

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

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

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

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

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

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

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

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

Loading history...
259
            throw new UnauthorizedException('content', $function, array('contentId' => $contentId));
260
        }
261
262
        return $versionInfo;
263
    }
264
265
    /**
266
     * Loads content in a version for the given content info object.
267
     *
268
     * If no version number is given, the method returns the current version
269
     *
270
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if version with the given number does not exist
271
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
272
     *
273
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
274
     * @param array $languages A language filter for fields. If not given all languages are returned
275
     * @param int $versionNo the version number. If not given the current version is returned
276
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
277
     *
278
     * @return \eZ\Publish\API\Repository\Values\Content\Content
279
     */
280
    public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
281
    {
282
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
283
        if ($useAlwaysAvailable && !$contentInfo->alwaysAvailable) {
284
            $useAlwaysAvailable = false;
285
        }
286
287
        return $this->loadContent(
288
            $contentInfo->id,
289
            $languages,
290
            $versionNo,
291
            $useAlwaysAvailable
292
        );
293
    }
294
295
    /**
296
     * Loads content in the version given by version info.
297
     *
298
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
299
     *
300
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
301
     * @param array $languages A language filter for fields. If not given all languages are returned
302
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
303
     *
304
     * @return \eZ\Publish\API\Repository\Values\Content\Content
305
     */
306
    public function loadContentByVersionInfo(APIVersionInfo $versionInfo, array $languages = null, $useAlwaysAvailable = true)
307
    {
308
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
309
        if ($useAlwaysAvailable && !$versionInfo->getContentInfo()->alwaysAvailable) {
310
            $useAlwaysAvailable = false;
311
        }
312
313
        return $this->loadContent(
314
            $versionInfo->getContentInfo()->id,
315
            $languages,
316
            $versionInfo->versionNo,
317
            $useAlwaysAvailable
318
        );
319
    }
320
321
    /**
322
     * Loads content in a version of the given content object.
323
     *
324
     * If no version number is given, the method returns the current version
325
     *
326
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist
327
     * @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
328
     *
329
     * @param int $contentId
330
     * @param array|null $languages A language filter for fields. If not given all languages are returned
331
     * @param int|null $versionNo the version number. If not given the current version is returned
332
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
333
     *
334
     * @return \eZ\Publish\API\Repository\Values\Content\Content
335
     */
336 View Code Duplication
    public function loadContent($contentId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
337
    {
338
        $content = $this->internalLoadContent($contentId, $languages, $versionNo, false, $useAlwaysAvailable);
339
340
        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...
341
            throw new UnauthorizedException('content', 'read', array('contentId' => $contentId));
342
        }
343
344
        if (
345
            $content->getVersionInfo()->status !== APIVersionInfo::STATUS_PUBLISHED
346
            && !$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...
347
        ) {
348
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentId, 'versionNo' => $versionNo));
349
        }
350
351
        return $content;
352
    }
353
354
    /**
355
     * Loads content in a version of the given content object.
356
     *
357
     * If no version number is given, the method returns the current version
358
     *
359
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist
360
     *
361
     * @param mixed $id
362
     * @param array|null $languages A language filter for fields. If not given all languages are returned
363
     * @param int|null $versionNo the version number. If not given the current version is returned
364
     * @param bool $isRemoteId
365
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
366
     *
367
     * @return \eZ\Publish\API\Repository\Values\Content\Content
368
     */
369
    public function internalLoadContent($id, array $languages = null, $versionNo = null, $isRemoteId = false, $useAlwaysAvailable = true)
370
    {
371
        try {
372
            // Get Content ID if lookup by remote ID
373
            if ($isRemoteId) {
374
                $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($id);
375
                $id = $spiContentInfo->id;
376
            }
377
378
            // Get current version if $versionNo is not defined
379
            if ($versionNo === null) {
380
                if (!isset($spiContentInfo)) {
381
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
382
                }
383
384
                $versionNo = $spiContentInfo->currentVersionNo;
385
            }
386
387
            $loadLanguages = $languages;
388
            $alwaysAvailableLanguageCode = null;
389
            // Set main language on $languages filter if not empty (all) and $useAlwaysAvailable being true
390
            if (!empty($loadLanguages) && $useAlwaysAvailable) {
391
                if (!isset($spiContentInfo)) {
392
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
393
                }
394
395
                if ($spiContentInfo->alwaysAvailable) {
396
                    $loadLanguages[] = $alwaysAvailableLanguageCode = $spiContentInfo->mainLanguageCode;
397
                    $loadLanguages = array_unique($loadLanguages);
398
                }
399
            }
400
401
            $spiContent = $this->persistenceHandler->contentHandler()->load(
402
                $id,
403
                $versionNo,
404
                $loadLanguages
0 ignored issues
show
Bug introduced by
It seems like $loadLanguages defined by $languages on line 387 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...
405
            );
406
        } catch (APINotFoundException $e) {
407
            throw new NotFoundException(
408
                'Content',
409
                array(
410
                    $isRemoteId ? 'remoteId' : 'id' => $id,
411
                    'languages' => $languages,
412
                    'versionNo' => $versionNo,
413
                ),
414
                $e
415
            );
416
        }
417
418
        return $this->domainMapper->buildContentDomainObject(
419
            $spiContent,
420
            null,
421
            empty($languages) ? null : $languages,
422
            $alwaysAvailableLanguageCode
423
        );
424
    }
425
426
    /**
427
     * Loads content in a version for the content object reference by the given remote id.
428
     *
429
     * If no version is given, the method returns the current version
430
     *
431
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist
432
     * @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
433
     *
434
     * @param string $remoteId
435
     * @param array $languages A language filter for fields. If not given all languages are returned
436
     * @param int $versionNo the version number. If not given the current version is returned
437
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
438
     *
439
     * @return \eZ\Publish\API\Repository\Values\Content\Content
440
     */
441 View Code Duplication
    public function loadContentByRemoteId($remoteId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
442
    {
443
        $content = $this->internalLoadContent($remoteId, $languages, $versionNo, true, $useAlwaysAvailable);
444
445
        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...
446
            throw new UnauthorizedException('content', 'read', array('remoteId' => $remoteId));
447
        }
448
449
        if (
450
            $content->getVersionInfo()->status !== APIVersionInfo::STATUS_PUBLISHED
451
            && !$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...
452
        ) {
453
            throw new UnauthorizedException('content', 'versionread', array('remoteId' => $remoteId, 'versionNo' => $versionNo));
454
        }
455
456
        return $content;
457
    }
458
459
    /**
460
     * Creates a new content draft assigned to the authenticated user.
461
     *
462
     * If a different userId is given in $contentCreateStruct it is assigned to the given user
463
     * but this required special rights for the authenticated user
464
     * (this is useful for content staging where the transfer process does not
465
     * have to authenticate with the user which created the content object in the source server).
466
     * The user has to publish the draft if it should be visible.
467
     * In 4.x at least one location has to be provided in the location creation array.
468
     *
469
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location
470
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the provided remoteId exists in the system, required properties on
471
     *                                                                        struct are missing or invalid, or if multiple locations are under the
472
     *                                                                        same parent.
473
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
474
     *                                                                               or if a required field is missing / set to an empty value.
475
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
476
     *                                                                          or value is set for non-translatable field in language
477
     *                                                                          other than main.
478
     *
479
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
480
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs For each location parent under which a location should be created for the content
481
     *
482
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
483
     */
484
    public function createContent(APIContentCreateStruct $contentCreateStruct, array $locationCreateStructs = array())
485
    {
486
        if ($contentCreateStruct->mainLanguageCode === null) {
487
            throw new InvalidArgumentException('$contentCreateStruct', "'mainLanguageCode' property must be set");
488
        }
489
490
        if ($contentCreateStruct->contentType === null) {
491
            throw new InvalidArgumentException('$contentCreateStruct', "'contentType' property must be set");
492
        }
493
494
        $contentCreateStruct = clone $contentCreateStruct;
495
496
        if ($contentCreateStruct->ownerId === null) {
497
            $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...
498
        }
499
500
        if ($contentCreateStruct->alwaysAvailable === null) {
501
            $contentCreateStruct->alwaysAvailable = false;
502
        }
503
504
        $contentCreateStruct->contentType = $this->repository->getContentTypeService()->loadContentType(
505
            $contentCreateStruct->contentType->id
506
        );
507
508
        if (empty($contentCreateStruct->sectionId)) {
509
            if (isset($locationCreateStructs[0])) {
510
                $location = $this->repository->getLocationService()->loadLocation(
511
                    $locationCreateStructs[0]->parentLocationId
512
                );
513
                $contentCreateStruct->sectionId = $location->contentInfo->sectionId;
514
            } else {
515
                $contentCreateStruct->sectionId = 1;
516
            }
517
        }
518
519
        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...
520
            throw new UnauthorizedException(
521
                'content',
522
                'create',
523
                array(
524
                    'parentLocationId' => isset($locationCreateStructs[0]) ?
525
                            $locationCreateStructs[0]->parentLocationId :
526
                            null,
527
                    'sectionId' => $contentCreateStruct->sectionId,
528
                )
529
            );
530
        }
531
532
        if (!empty($contentCreateStruct->remoteId)) {
533
            try {
534
                $this->loadContentByRemoteId($contentCreateStruct->remoteId);
535
536
                throw new InvalidArgumentException(
537
                    '$contentCreateStruct',
538
                    "Another content with remoteId '{$contentCreateStruct->remoteId}' exists"
539
                );
540
            } catch (APINotFoundException $e) {
541
                // Do nothing
542
            }
543
        } else {
544
            $contentCreateStruct->remoteId = $this->domainMapper->getUniqueHash($contentCreateStruct);
545
        }
546
547
        $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...
548
549
        $languageCodes = $this->getLanguageCodesForCreate($contentCreateStruct);
550
        $fields = $this->mapFieldsForCreate($contentCreateStruct);
551
552
        $fieldValues = array();
553
        $spiFields = array();
554
        $allFieldErrors = array();
555
        $inputRelations = array();
556
        $locationIdToContentIdMapping = array();
557
558
        foreach ($contentCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) {
559
            /** @var $fieldType \eZ\Publish\Core\FieldType\FieldType */
560
            $fieldType = $this->fieldTypeRegistry->getFieldType(
561
                $fieldDefinition->fieldTypeIdentifier
562
            );
563
564
            foreach ($languageCodes as $languageCode) {
565
                $isEmptyValue = false;
566
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $contentCreateStruct->mainLanguageCode;
567
                $isLanguageMain = $languageCode === $contentCreateStruct->mainLanguageCode;
568
                if (isset($fields[$fieldDefinition->identifier][$valueLanguageCode])) {
569
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
570
                } else {
571
                    $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...
572
                }
573
574
                $fieldValue = $fieldType->acceptValue($fieldValue);
575
576 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...
577
                    $isEmptyValue = true;
578
                    if ($fieldDefinition->isRequired) {
579
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
580
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
581
                            null,
582
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
583
                            'empty'
584
                        );
585
                    }
586
                } else {
587
                    $fieldErrors = $fieldType->validate(
588
                        $fieldDefinition,
589
                        $fieldValue
590
                    );
591
                    if (!empty($fieldErrors)) {
592
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
593
                    }
594
                }
595
596
                if (!empty($allFieldErrors)) {
597
                    continue;
598
                }
599
600
                $this->relationProcessor->appendFieldRelations(
601
                    $inputRelations,
602
                    $locationIdToContentIdMapping,
603
                    $fieldType,
604
                    $fieldValue,
605
                    $fieldDefinition->id
606
                );
607
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
608
609
                // Only non-empty value for: translatable field or in main language
610
                if (
611
                    (!$isEmptyValue && $fieldDefinition->isTranslatable) ||
612
                    (!$isEmptyValue && $isLanguageMain)
613
                ) {
614
                    $spiFields[] = new SPIField(
615
                        array(
616
                            'id' => null,
617
                            'fieldDefinitionId' => $fieldDefinition->id,
618
                            'type' => $fieldDefinition->fieldTypeIdentifier,
619
                            'value' => $fieldType->toPersistenceValue($fieldValue),
620
                            'languageCode' => $languageCode,
621
                            'versionNo' => null,
622
                        )
623
                    );
624
                }
625
            }
626
        }
627
628
        if (!empty($allFieldErrors)) {
629
            throw new ContentFieldValidationException($allFieldErrors);
630
        }
631
632
        $spiContentCreateStruct = new SPIContentCreateStruct(
633
            array(
634
                'name' => $this->nameSchemaService->resolve(
635
                    $contentCreateStruct->contentType->nameSchema,
636
                    $contentCreateStruct->contentType,
637
                    $fieldValues,
638
                    $languageCodes
639
                ),
640
                'typeId' => $contentCreateStruct->contentType->id,
641
                'sectionId' => $contentCreateStruct->sectionId,
642
                'ownerId' => $contentCreateStruct->ownerId,
643
                'locations' => $spiLocationCreateStructs,
644
                'fields' => $spiFields,
645
                'alwaysAvailable' => $contentCreateStruct->alwaysAvailable,
646
                'remoteId' => $contentCreateStruct->remoteId,
647
                'modified' => isset($contentCreateStruct->modificationDate) ? $contentCreateStruct->modificationDate->getTimestamp() : time(),
648
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
649
                    $contentCreateStruct->mainLanguageCode
650
                )->id,
651
            )
652
        );
653
654
        $defaultObjectStates = $this->getDefaultObjectStates();
655
656
        $this->repository->beginTransaction();
657
        try {
658
            $spiContent = $this->persistenceHandler->contentHandler()->create($spiContentCreateStruct);
659
            $this->relationProcessor->processFieldRelations(
660
                $inputRelations,
661
                $spiContent->versionInfo->contentInfo->id,
662
                $spiContent->versionInfo->versionNo,
663
                $contentCreateStruct->contentType
664
            );
665
666
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
667
                $this->persistenceHandler->objectStateHandler()->setContentState(
668
                    $spiContent->versionInfo->contentInfo->id,
669
                    $objectStateGroupId,
670
                    $objectState->id
671
                );
672
            }
673
674
            $this->repository->commit();
675
        } catch (Exception $e) {
676
            $this->repository->rollback();
677
            throw $e;
678
        }
679
680
        return $this->domainMapper->buildContentDomainObject($spiContent);
681
    }
682
683
    /**
684
     * Returns an array of default content states with content state group id as key.
685
     *
686
     * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[]
687
     */
688
    protected function getDefaultObjectStates()
689
    {
690
        $defaultObjectStatesMap = array();
691
        $objectStateHandler = $this->persistenceHandler->objectStateHandler();
692
693
        foreach ($objectStateHandler->loadAllGroups() as $objectStateGroup) {
694
            foreach ($objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) {
695
                // Only register the first object state which is the default one.
696
                $defaultObjectStatesMap[$objectStateGroup->id] = $objectState;
697
                break;
698
            }
699
        }
700
701
        return $defaultObjectStatesMap;
702
    }
703
704
    /**
705
     * Returns all language codes used in given $fields.
706
     *
707
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value is set in main language
708
     *
709
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
710
     *
711
     * @return string[]
712
     */
713
    protected function getLanguageCodesForCreate(APIContentCreateStruct $contentCreateStruct)
714
    {
715
        $languageCodes = array();
716
717 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...
718
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
719
                continue;
720
            }
721
722
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
723
                $field->languageCode
724
            );
725
            $languageCodes[$field->languageCode] = true;
726
        }
727
728
        if (!isset($languageCodes[$contentCreateStruct->mainLanguageCode])) {
729
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
730
                $contentCreateStruct->mainLanguageCode
731
            );
732
            $languageCodes[$contentCreateStruct->mainLanguageCode] = true;
733
        }
734
735
        return array_keys($languageCodes);
736
    }
737
738
    /**
739
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
740
     *
741
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
742
     *                                                                          or value is set for non-translatable field in language
743
     *                                                                          other than main
744
     *
745
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
746
     *
747
     * @return array
748
     */
749
    protected function mapFieldsForCreate(APIContentCreateStruct $contentCreateStruct)
750
    {
751
        $fields = array();
752
753
        foreach ($contentCreateStruct->fields as $field) {
754
            $fieldDefinition = $contentCreateStruct->contentType->getFieldDefinition($field->fieldDefIdentifier);
755
756
            if ($fieldDefinition === null) {
757
                throw new ContentValidationException(
758
                    "Field definition '%identifier%' does not exist in given ContentType",
759
                    ['%identifier%' => $field->fieldDefIdentifier]
760
                );
761
            }
762
763
            if ($field->languageCode === null) {
764
                $field = $this->cloneField(
765
                    $field,
766
                    array('languageCode' => $contentCreateStruct->mainLanguageCode)
767
                );
768
            }
769
770 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...
771
                throw new ContentValidationException(
772
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
773
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
774
                );
775
            }
776
777
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
778
        }
779
780
        return $fields;
781
    }
782
783
    /**
784
     * Clones $field with overriding specific properties from given $overrides array.
785
     *
786
     * @param Field $field
787
     * @param array $overrides
788
     *
789
     * @return Field
790
     */
791
    private function cloneField(Field $field, array $overrides = array())
792
    {
793
        $fieldData = array_merge(
794
            array(
795
                'id' => $field->id,
796
                '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...
797
                'languageCode' => $field->languageCode,
798
                'fieldDefIdentifier' => $field->fieldDefIdentifier,
799
            ),
800
            $overrides
801
        );
802
803
        return new Field($fieldData);
804
    }
805
806
    /**
807
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
808
     *
809
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
810
     *
811
     * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct[]
812
     */
813
    protected function buildSPILocationCreateStructs(array $locationCreateStructs)
814
    {
815
        $spiLocationCreateStructs = array();
816
        $parentLocationIdSet = array();
817
        $mainLocation = true;
818
819
        foreach ($locationCreateStructs as $locationCreateStruct) {
820
            if (isset($parentLocationIdSet[$locationCreateStruct->parentLocationId])) {
821
                throw new InvalidArgumentException(
822
                    '$locationCreateStructs',
823
                    "Multiple LocationCreateStructs with the same parent Location '{$locationCreateStruct->parentLocationId}' are given"
824
                );
825
            }
826
827
            $parentLocationIdSet[$locationCreateStruct->parentLocationId] = true;
828
            $parentLocation = $this->repository->getLocationService()->loadLocation(
829
                $locationCreateStruct->parentLocationId
830
            );
831
832
            $spiLocationCreateStructs[] = $this->domainMapper->buildSPILocationCreateStruct(
833
                $locationCreateStruct,
834
                $parentLocation,
835
                $mainLocation,
836
                // For Content draft contentId and contentVersionNo are set in ContentHandler upon draft creation
837
                null,
838
                null
839
            );
840
841
            // First Location in the list will be created as main Location
842
            $mainLocation = false;
843
        }
844
845
        return $spiLocationCreateStructs;
846
    }
847
848
    /**
849
     * Updates the metadata.
850
     *
851
     * (see {@link ContentMetadataUpdateStruct}) of a content object - to update fields use updateContent
852
     *
853
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data
854
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists
855
     *
856
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
857
     * @param \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct
858
     *
859
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content with the updated attributes
860
     */
861
    public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct)
862
    {
863
        $propertyCount = 0;
864
        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...
865
            if (isset($contentMetadataUpdateStruct->$propertyName)) {
866
                $propertyCount += 1;
867
            }
868
        }
869
        if ($propertyCount === 0) {
870
            throw new InvalidArgumentException(
871
                '$contentMetadataUpdateStruct',
872
                'At least one property must be set'
873
            );
874
        }
875
876
        $loadedContentInfo = $this->loadContentInfo($contentInfo->id);
877
878
        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...
879
            throw new UnauthorizedException('content', 'edit', array('contentId' => $loadedContentInfo->id));
880
        }
881
882
        if (isset($contentMetadataUpdateStruct->remoteId)) {
883
            try {
884
                $existingContentInfo = $this->loadContentInfoByRemoteId($contentMetadataUpdateStruct->remoteId);
885
886
                if ($existingContentInfo->id !== $loadedContentInfo->id) {
887
                    throw new InvalidArgumentException(
888
                        '$contentMetadataUpdateStruct',
889
                        "Another content with remoteId '{$contentMetadataUpdateStruct->remoteId}' exists"
890
                    );
891
                }
892
            } catch (APINotFoundException $e) {
893
                // Do nothing
894
            }
895
        }
896
897
        $this->repository->beginTransaction();
898
        try {
899
            if ($propertyCount > 1 || !isset($contentMetadataUpdateStruct->mainLocationId)) {
900
                $this->persistenceHandler->contentHandler()->updateMetadata(
901
                    $loadedContentInfo->id,
902
                    new SPIMetadataUpdateStruct(
903
                        array(
904
                            'ownerId' => $contentMetadataUpdateStruct->ownerId,
905
                            'publicationDate' => isset($contentMetadataUpdateStruct->publishedDate) ?
906
                                $contentMetadataUpdateStruct->publishedDate->getTimestamp() :
907
                                null,
908
                            'modificationDate' => isset($contentMetadataUpdateStruct->modificationDate) ?
909
                                $contentMetadataUpdateStruct->modificationDate->getTimestamp() :
910
                                null,
911
                            'mainLanguageId' => isset($contentMetadataUpdateStruct->mainLanguageCode) ?
912
                                $this->repository->getContentLanguageService()->loadLanguage(
913
                                    $contentMetadataUpdateStruct->mainLanguageCode
914
                                )->id :
915
                                null,
916
                            'alwaysAvailable' => $contentMetadataUpdateStruct->alwaysAvailable,
917
                            'remoteId' => $contentMetadataUpdateStruct->remoteId,
918
                        )
919
                    )
920
                );
921
            }
922
923
            // Change main location
924
            if (isset($contentMetadataUpdateStruct->mainLocationId)
925
                && $loadedContentInfo->mainLocationId !== $contentMetadataUpdateStruct->mainLocationId) {
926
                $this->persistenceHandler->locationHandler()->changeMainLocation(
927
                    $loadedContentInfo->id,
928
                    $contentMetadataUpdateStruct->mainLocationId
929
                );
930
            }
931
932
            // Republish URL aliases to update always-available flag
933
            if (isset($contentMetadataUpdateStruct->alwaysAvailable)
934
                && $loadedContentInfo->alwaysAvailable !== $contentMetadataUpdateStruct->alwaysAvailable) {
935
                $content = $this->loadContent($loadedContentInfo->id);
936
                $this->publishUrlAliasesForContent($content, false);
937
            }
938
939
            $this->repository->commit();
940
        } catch (Exception $e) {
941
            $this->repository->rollback();
942
            throw $e;
943
        }
944
945
        return isset($content) ? $content : $this->loadContent($loadedContentInfo->id);
946
    }
947
948
    /**
949
     * Publishes URL aliases for all locations of a given content.
950
     *
951
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
952
     * @param bool $updatePathIdentificationString this parameter is legacy storage specific for updating
953
     *                      ezcontentobject_tree.path_identification_string, it is ignored by other storage engines
954
     */
955
    protected function publishUrlAliasesForContent(APIContent $content, $updatePathIdentificationString = true)
956
    {
957
        $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
958
        $locations = $this->repository->getLocationService()->loadLocations(
959
            $content->getVersionInfo()->getContentInfo()
960
        );
961
        foreach ($locations as $location) {
962
            foreach ($urlAliasNames as $languageCode => $name) {
963
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
964
                    $location->id,
965
                    $location->parentLocationId,
966
                    $name,
967
                    $languageCode,
968
                    $content->contentInfo->alwaysAvailable,
969
                    $updatePathIdentificationString ? $languageCode === $content->contentInfo->mainLanguageCode : false
970
                );
971
            }
972
        }
973
    }
974
975
    /**
976
     * Deletes a content object including all its versions and locations including their subtrees.
977
     *
978
     * @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)
979
     *
980
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
981
     *
982
     * @return mixed[] Affected Location Id's
983
     */
984
    public function deleteContent(ContentInfo $contentInfo)
985
    {
986
        $contentInfo = $this->internalLoadContentInfo($contentInfo->id);
987
988
        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...
989
            throw new UnauthorizedException('content', 'remove', array('contentId' => $contentInfo->id));
990
        }
991
992
        $affectedLocations = [];
993
        $this->repository->beginTransaction();
994
        try {
995
            // Load Locations first as deleting Content also deletes belonging Locations
996
            $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($contentInfo->id);
997
            $this->persistenceHandler->contentHandler()->deleteContent($contentInfo->id);
998
            foreach ($spiLocations as $spiLocation) {
999
                $this->persistenceHandler->urlAliasHandler()->locationDeleted($spiLocation->id);
1000
                $affectedLocations[] = $spiLocation->id;
1001
            }
1002
            $this->repository->commit();
1003
        } catch (Exception $e) {
1004
            $this->repository->rollback();
1005
            throw $e;
1006
        }
1007
1008
        return $affectedLocations;
1009
    }
1010
1011
    /**
1012
     * Creates a draft from a published or archived version.
1013
     *
1014
     * If no version is given, the current published version is used.
1015
     * 4.x: The draft is created with the initialLanguage code of the source version or if not present with the main language.
1016
     * It can be changed on updating the version.
1017
     *
1018
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft
1019
     *
1020
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1021
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1022
     * @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
1023
     *
1024
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
1025
     */
1026
    public function createContentDraft(ContentInfo $contentInfo, APIVersionInfo $versionInfo = null, User $creator = null)
1027
    {
1028
        $contentInfo = $this->loadContentInfo($contentInfo->id);
1029
1030
        if ($versionInfo !== null) {
1031
            // Check that given $contentInfo and $versionInfo belong to the same content
1032
            if ($versionInfo->getContentInfo()->id != $contentInfo->id) {
1033
                throw new InvalidArgumentException(
1034
                    '$versionInfo',
1035
                    'VersionInfo does not belong to the same content as given ContentInfo'
1036
                );
1037
            }
1038
1039
            $versionInfo = $this->loadVersionInfoById($contentInfo->id, $versionInfo->versionNo);
1040
1041
            switch ($versionInfo->status) {
1042
                case VersionInfo::STATUS_PUBLISHED:
1043
                case VersionInfo::STATUS_ARCHIVED:
1044
                    break;
1045
1046
                default:
1047
                    // @todo: throw an exception here, to be defined
1048
                    throw new BadStateException(
1049
                        '$versionInfo',
1050
                        'Draft can not be created from a draft version'
1051
                    );
1052
            }
1053
1054
            $versionNo = $versionInfo->versionNo;
1055
        } elseif ($contentInfo->published) {
1056
            $versionNo = $contentInfo->currentVersionNo;
1057
        } else {
1058
            // @todo: throw an exception here, to be defined
1059
            throw new BadStateException(
1060
                '$contentInfo',
1061
                'Content is not published, draft can be created only from published or archived version'
1062
            );
1063
        }
1064
1065
        if ($creator === null) {
1066
            $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...
1067
        }
1068
1069
        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...
1070
            throw new UnauthorizedException('content', 'edit', array('contentId' => $contentInfo->id));
1071
        }
1072
1073
        $this->repository->beginTransaction();
1074
        try {
1075
            $spiContent = $this->persistenceHandler->contentHandler()->createDraftFromVersion(
1076
                $contentInfo->id,
1077
                $versionNo,
1078
                $creator->getUserId()
1079
            );
1080
            $this->repository->commit();
1081
        } catch (Exception $e) {
1082
            $this->repository->rollback();
1083
            throw $e;
1084
        }
1085
1086
        return $this->domainMapper->buildContentDomainObject($spiContent);
1087
    }
1088
1089
    /**
1090
     * Loads drafts for a user.
1091
     *
1092
     * If no user is given the drafts for the authenticated user a returned
1093
     *
1094
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to load the draft list
1095
     *
1096
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $user
1097
     *
1098
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo the drafts ({@link VersionInfo}) owned by the given user
1099
     */
1100
    public function loadContentDrafts(User $user = null)
1101
    {
1102
        if ($user === null) {
1103
            $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...
1104
        }
1105
1106
        // throw early if user has absolutely no access to versionread
1107
        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...
1108
            throw new UnauthorizedException('content', 'versionread');
1109
        }
1110
1111
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftsForUser($user->getUserId());
1112
        $versionInfoList = array();
1113 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...
1114
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1115
            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...
1116
                throw new UnauthorizedException('content', 'versionread', array('contentId' => $versionInfo->contentInfo->id));
1117
            }
1118
1119
            $versionInfoList[] = $versionInfo;
1120
        }
1121
1122
        return $versionInfoList;
1123
    }
1124
1125
    /**
1126
     * Translate a version.
1127
     *
1128
     * updates the destination version given in $translationInfo with the provided translated fields in $translationValues
1129
     *
1130
     * @example Examples/translation_5x.php
1131
     *
1132
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to update this version
1133
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the given destination version is not a draft
1134
     * @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.
1135
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1136
     *                                                                          or value is set for non-translatable field in language
1137
     *                                                                          other than main.
1138
     *
1139
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1140
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationValues $translationValues
1141
     * @param \eZ\Publish\API\Repository\Values\User\User $modifier If set, this user is taken as modifier of the version
1142
     *
1143
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the translated fields
1144
     *
1145
     * @since 5.0
1146
     */
1147
    public function translateVersion(TranslationInfo $translationInfo, APITranslationValues $translationValues, User $modifier = null)
1148
    {
1149
        throw new NotImplementedException(__METHOD__);
1150
    }
1151
1152
    /**
1153
     * Updates the fields of a draft.
1154
     *
1155
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version
1156
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1157
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid.
1158
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
1159
     *                                                                               or if a required field is missing / set to an empty value.
1160
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
1161
     *                                                                          or value is set for non-translatable field in language
1162
     *                                                                          other than main.
1163
     *
1164
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1165
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1166
     *
1167
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the updated fields
1168
     */
1169
    public function updateContent(APIVersionInfo $versionInfo, APIContentUpdateStruct $contentUpdateStruct)
1170
    {
1171
        $contentUpdateStruct = clone $contentUpdateStruct;
1172
1173
        /** @var $content \eZ\Publish\Core\Repository\Values\Content\Content */
1174
        $content = $this->loadContent(
1175
            $versionInfo->getContentInfo()->id,
1176
            null,
1177
            $versionInfo->versionNo
1178
        );
1179
        if ($content->versionInfo->status !== APIVersionInfo::STATUS_DRAFT) {
1180
            throw new BadStateException(
1181
                '$versionInfo',
1182
                'Version is not a draft and can not be updated'
1183
            );
1184
        }
1185
1186
        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...
1187
            throw new UnauthorizedException('content', 'edit', array('contentId' => $content->id));
1188
        }
1189
1190
        $mainLanguageCode = $content->contentInfo->mainLanguageCode;
1191
        $languageCodes = $this->getLanguageCodesForUpdate($contentUpdateStruct, $content);
1192
        $contentType = $this->repository->getContentTypeService()->loadContentType(
1193
            $content->contentInfo->contentTypeId
1194
        );
1195
        $fields = $this->mapFieldsForUpdate(
1196
            $contentUpdateStruct,
1197
            $contentType,
1198
            $mainLanguageCode
1199
        );
1200
1201
        $fieldValues = array();
1202
        $spiFields = array();
1203
        $allFieldErrors = array();
1204
        $inputRelations = array();
1205
        $locationIdToContentIdMapping = array();
1206
1207
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
1208
            /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */
1209
            $fieldType = $this->fieldTypeRegistry->getFieldType(
1210
                $fieldDefinition->fieldTypeIdentifier
1211
            );
1212
1213
            foreach ($languageCodes as $languageCode) {
1214
                $isCopied = $isEmpty = $isRetained = false;
1215
                $isLanguageNew = !in_array($languageCode, $content->versionInfo->languageCodes);
1216
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $mainLanguageCode;
1217
                $isFieldUpdated = isset($fields[$fieldDefinition->identifier][$valueLanguageCode]);
1218
                $isProcessed = isset($fieldValues[$fieldDefinition->identifier][$valueLanguageCode]);
1219
1220
                if (!$isFieldUpdated && !$isLanguageNew) {
1221
                    $isRetained = true;
1222
                    $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...
1223
                } elseif (!$isFieldUpdated && $isLanguageNew && !$fieldDefinition->isTranslatable) {
1224
                    $isCopied = true;
1225
                    $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...
1226
                } elseif ($isFieldUpdated) {
1227
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
1228
                } else {
1229
                    $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...
1230
                }
1231
1232
                $fieldValue = $fieldType->acceptValue($fieldValue);
1233
1234 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...
1235
                    $isEmpty = true;
1236
                    if ($fieldDefinition->isRequired) {
1237
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
1238
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
1239
                            null,
1240
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
1241
                            'empty'
1242
                        );
1243
                    }
1244
                } else {
1245
                    $fieldErrors = $fieldType->validate(
1246
                        $fieldDefinition,
1247
                        $fieldValue
1248
                    );
1249
                    if (!empty($fieldErrors)) {
1250
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
1251
                    }
1252
                }
1253
1254
                if (!empty($allFieldErrors)) {
1255
                    continue;
1256
                }
1257
1258
                $this->relationProcessor->appendFieldRelations(
1259
                    $inputRelations,
1260
                    $locationIdToContentIdMapping,
1261
                    $fieldType,
1262
                    $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...
1263
                    $fieldDefinition->id
1264
                );
1265
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
1266
1267
                if ($isRetained || $isCopied || ($isLanguageNew && $isEmpty) || $isProcessed) {
1268
                    continue;
1269
                }
1270
1271
                $spiFields[] = new SPIField(
1272
                    array(
1273
                        'id' => $isLanguageNew ?
1274
                            null :
1275
                            $content->getField($fieldDefinition->identifier, $languageCode)->id,
1276
                        'fieldDefinitionId' => $fieldDefinition->id,
1277
                        'type' => $fieldDefinition->fieldTypeIdentifier,
1278
                        'value' => $fieldType->toPersistenceValue($fieldValue),
1279
                        'languageCode' => $languageCode,
1280
                        'versionNo' => $versionInfo->versionNo,
1281
                    )
1282
                );
1283
            }
1284
        }
1285
1286
        if (!empty($allFieldErrors)) {
1287
            throw new ContentFieldValidationException($allFieldErrors);
1288
        }
1289
1290
        $spiContentUpdateStruct = new SPIContentUpdateStruct(
1291
            array(
1292
                'name' => $this->nameSchemaService->resolveNameSchema(
1293
                    $content,
1294
                    $fieldValues,
1295
                    $languageCodes,
1296
                    $contentType
1297
                ),
1298
                '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...
1299
                'fields' => $spiFields,
1300
                'modificationDate' => time(),
1301
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1302
                    $contentUpdateStruct->initialLanguageCode
1303
                )->id,
1304
            )
1305
        );
1306
        $existingRelations = $this->loadRelations($versionInfo);
1307
1308
        $this->repository->beginTransaction();
1309
        try {
1310
            $spiContent = $this->persistenceHandler->contentHandler()->updateContent(
1311
                $versionInfo->getContentInfo()->id,
1312
                $versionInfo->versionNo,
1313
                $spiContentUpdateStruct
1314
            );
1315
            $this->relationProcessor->processFieldRelations(
1316
                $inputRelations,
1317
                $spiContent->versionInfo->contentInfo->id,
1318
                $spiContent->versionInfo->versionNo,
1319
                $contentType,
1320
                $existingRelations
1321
            );
1322
            $this->repository->commit();
1323
        } catch (Exception $e) {
1324
            $this->repository->rollback();
1325
            throw $e;
1326
        }
1327
1328
        return $this->domainMapper->buildContentDomainObject(
1329
            $spiContent,
1330
            $contentType
1331
        );
1332
    }
1333
1334
    /**
1335
     * Returns all language codes used in given $fields.
1336
     *
1337
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value exists in initial language
1338
     *
1339
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1340
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1341
     *
1342
     * @return array
1343
     */
1344
    protected function getLanguageCodesForUpdate(APIContentUpdateStruct $contentUpdateStruct, APIContent $content)
1345
    {
1346
        if ($contentUpdateStruct->initialLanguageCode !== null) {
1347
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1348
                $contentUpdateStruct->initialLanguageCode
1349
            );
1350
        } else {
1351
            $contentUpdateStruct->initialLanguageCode = $content->contentInfo->mainLanguageCode;
1352
        }
1353
1354
        $languageCodes = array_fill_keys($content->versionInfo->languageCodes, true);
1355
        $languageCodes[$contentUpdateStruct->initialLanguageCode] = true;
1356
1357 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...
1358
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1359
                continue;
1360
            }
1361
1362
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1363
                $field->languageCode
1364
            );
1365
            $languageCodes[$field->languageCode] = true;
1366
        }
1367
1368
        return array_keys($languageCodes);
1369
    }
1370
1371
    /**
1372
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
1373
     *
1374
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1375
     *                                                                          or value is set for non-translatable field in language
1376
     *                                                                          other than main
1377
     *
1378
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1379
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1380
     * @param string $mainLanguageCode
1381
     *
1382
     * @return array
1383
     */
1384
    protected function mapFieldsForUpdate(
1385
        APIContentUpdateStruct $contentUpdateStruct,
1386
        ContentType $contentType,
1387
        $mainLanguageCode
1388
    ) {
1389
        $fields = array();
1390
1391
        foreach ($contentUpdateStruct->fields as $field) {
1392
            $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier);
1393
1394
            if ($fieldDefinition === null) {
1395
                throw new ContentValidationException(
1396
                    "Field definition '%identifier%' does not exist in given ContentType",
1397
                    ['%identifier%' => $field->fieldDefIdentifier]
1398
                );
1399
            }
1400
1401
            if ($field->languageCode === null) {
1402
                if ($fieldDefinition->isTranslatable) {
1403
                    $languageCode = $contentUpdateStruct->initialLanguageCode;
1404
                } else {
1405
                    $languageCode = $mainLanguageCode;
1406
                }
1407
                $field = $this->cloneField($field, array('languageCode' => $languageCode));
1408
            }
1409
1410 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...
1411
                throw new ContentValidationException(
1412
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
1413
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
1414
                );
1415
            }
1416
1417
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
1418
        }
1419
1420
        return $fields;
1421
    }
1422
1423
    /**
1424
     * Publishes a content version.
1425
     *
1426
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version
1427
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1428
     *
1429
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1430
     *
1431
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1432
     */
1433
    public function publishVersion(APIVersionInfo $versionInfo)
1434
    {
1435
        $content = $this->internalLoadContent(
1436
            $versionInfo->contentInfo->id,
1437
            null,
1438
            $versionInfo->versionNo
1439
        );
1440
1441
        if (!$content->getVersionInfo()->getContentInfo()->published) {
1442
            if (!$this->repository->canUser('content', 'create', $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...
1443
                throw new UnauthorizedException('content', 'create', array('contentId' => $content->id));
1444
            }
1445
        } elseif (!$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...
1446
            throw new UnauthorizedException('content', 'edit', array('contentId' => $content->id));
1447
        }
1448
1449
        $this->repository->beginTransaction();
1450
        try {
1451
            $content = $this->internalPublishVersion($content->getVersionInfo());
1452
            $this->repository->commit();
1453
        } catch (Exception $e) {
1454
            $this->repository->rollback();
1455
            throw $e;
1456
        }
1457
1458
        return $content;
1459
    }
1460
1461
    /**
1462
     * Publishes a content version.
1463
     *
1464
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1465
     *
1466
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1467
     * @param int|null $publicationDate If null existing date is kept if there is one, otherwise current time is used.
1468
     *
1469
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1470
     */
1471
    protected function internalPublishVersion(APIVersionInfo $versionInfo, $publicationDate = null)
1472
    {
1473
        if ($versionInfo->status !== APIVersionInfo::STATUS_DRAFT) {
1474
            throw new BadStateException('$versionInfo', 'Only versions in draft status can be published.');
1475
        }
1476
1477
        $currentTime = time();
1478
        if ($publicationDate === null && $versionInfo->versionNo === 1) {
1479
            $publicationDate = $currentTime;
1480
        }
1481
1482
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
1483
        $metadataUpdateStruct->publicationDate = $publicationDate;
1484
        $metadataUpdateStruct->modificationDate = $currentTime;
1485
1486
        $spiContent = $this->persistenceHandler->contentHandler()->publish(
1487
            $versionInfo->getContentInfo()->id,
1488
            $versionInfo->versionNo,
1489
            $metadataUpdateStruct
1490
        );
1491
        $content = $this->domainMapper->buildContentDomainObject($spiContent);
1492
1493
        $this->publishUrlAliasesForContent($content);
1494
1495
        return $content;
1496
    }
1497
1498
    /**
1499
     * Removes the given version.
1500
     *
1501
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is in
1502
     *         published state or is the last version of the Content
1503
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version
1504
     *
1505
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1506
     */
1507
    public function deleteVersion(APIVersionInfo $versionInfo)
1508
    {
1509
        if ($versionInfo->status === APIVersionInfo::STATUS_PUBLISHED) {
1510
            throw new BadStateException(
1511
                '$versionInfo',
1512
                'Version is published and can not be removed'
1513
            );
1514
        }
1515
1516 View Code Duplication
        if (!$this->repository->canUser('content', 'versionremove', $versionInfo)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

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

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

Loading history...
1517
            throw new UnauthorizedException(
1518
                'content',
1519
                'versionremove',
1520
                array('contentId' => $versionInfo->contentInfo->id, 'versionNo' => $versionInfo->versionNo)
1521
            );
1522
        }
1523
1524
        $versionList = $this->persistenceHandler->contentHandler()->listVersions(
1525
            $versionInfo->contentInfo->id
1526
        );
1527
1528
        if (count($versionList) === 1) {
1529
            throw new BadStateException(
1530
                '$versionInfo',
1531
                'Version is the last version of the Content and can not be removed'
1532
            );
1533
        }
1534
1535
        $this->repository->beginTransaction();
1536
        try {
1537
            $this->persistenceHandler->contentHandler()->deleteVersion(
1538
                $versionInfo->getContentInfo()->id,
1539
                $versionInfo->versionNo
1540
            );
1541
            $this->repository->commit();
1542
        } catch (Exception $e) {
1543
            $this->repository->rollback();
1544
            throw $e;
1545
        }
1546
    }
1547
1548
    /**
1549
     * Loads all versions for the given content.
1550
     *
1551
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions
1552
     *
1553
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1554
     *
1555
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] Sorted by creation date
1556
     */
1557
    public function loadVersions(ContentInfo $contentInfo)
1558
    {
1559
        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...
1560
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentInfo->id));
1561
        }
1562
1563
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->listVersions($contentInfo->id);
1564
1565
        $versions = array();
1566 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...
1567
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1568
            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...
1569
                throw new UnauthorizedException('content', 'versionread', array('versionId' => $versionInfo->id));
1570
            }
1571
1572
            $versions[] = $versionInfo;
1573
        }
1574
1575
        return $versions;
1576
    }
1577
1578
    /**
1579
     * Copies the content to a new location. If no version is given,
1580
     * all versions are copied, otherwise only the given version.
1581
     *
1582
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location
1583
     *
1584
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1585
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to
1586
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1587
     *
1588
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1589
     */
1590
    public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, APIVersionInfo $versionInfo = null)
1591
    {
1592 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...
1593
            throw new UnauthorizedException(
1594
                'content',
1595
                'create',
1596
                array(
1597
                    'parentLocationId' => $destinationLocationCreateStruct->parentLocationId,
1598
                    'sectionId' => $contentInfo->sectionId,
1599
                )
1600
            );
1601
        }
1602
1603
        $defaultObjectStates = $this->getDefaultObjectStates();
1604
1605
        $this->repository->beginTransaction();
1606
        try {
1607
            $spiContent = $this->persistenceHandler->contentHandler()->copy(
1608
                $contentInfo->id,
1609
                $versionInfo ? $versionInfo->versionNo : null
1610
            );
1611
1612
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
1613
                $this->persistenceHandler->objectStateHandler()->setContentState(
1614
                    $spiContent->versionInfo->contentInfo->id,
1615
                    $objectStateGroupId,
1616
                    $objectState->id
1617
                );
1618
            }
1619
1620
            $content = $this->internalPublishVersion(
1621
                $this->domainMapper->buildVersionInfoDomainObject($spiContent->versionInfo),
1622
                $spiContent->versionInfo->creationDate
1623
            );
1624
1625
            $this->repository->getLocationService()->createLocation(
1626
                $content->getVersionInfo()->getContentInfo(),
1627
                $destinationLocationCreateStruct
1628
            );
1629
            $this->repository->commit();
1630
        } catch (Exception $e) {
1631
            $this->repository->rollback();
1632
            throw $e;
1633
        }
1634
1635
        return $this->internalLoadContent($content->id);
1636
    }
1637
1638
    /**
1639
     * Loads all outgoing relations for the given version.
1640
     *
1641
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1642
     *
1643
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1644
     *
1645
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1646
     */
1647
    public function loadRelations(APIVersionInfo $versionInfo)
1648
    {
1649
        if ($versionInfo->status === APIVersionInfo::STATUS_PUBLISHED) {
1650
            $function = 'read';
1651
        } else {
1652
            $function = 'versionread';
1653
        }
1654
1655
        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...
1656
            throw new UnauthorizedException('content', $function);
1657
        }
1658
1659
        $contentInfo = $versionInfo->getContentInfo();
1660
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1661
            $contentInfo->id,
1662
            $versionInfo->versionNo
1663
        );
1664
1665
        /** @var $relations \eZ\Publish\API\Repository\Values\Content\Relation[] */
1666
        $relations = array();
1667 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...
1668
            $destinationContentInfo = $this->internalLoadContentInfo($spiRelation->destinationContentId);
1669
            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...
1670
                continue;
1671
            }
1672
1673
            $relations[] = $this->domainMapper->buildRelationDomainObject(
1674
                $spiRelation,
1675
                $contentInfo,
1676
                $destinationContentInfo
1677
            );
1678
        }
1679
1680
        return $relations;
1681
    }
1682
1683
    /**
1684
     * Loads all incoming relations for a content object.
1685
     *
1686
     * The relations come only from published versions of the source content objects
1687
     *
1688
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1689
     *
1690
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1691
     *
1692
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1693
     */
1694
    public function loadReverseRelations(ContentInfo $contentInfo)
1695
    {
1696
        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...
1697
            throw new UnauthorizedException('content', 'reverserelatedlist', array('contentId' => $contentInfo->id));
1698
        }
1699
1700
        $spiRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations(
1701
            $contentInfo->id
1702
        );
1703
1704
        $returnArray = array();
1705 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...
1706
            $sourceContentInfo = $this->internalLoadContentInfo($spiRelation->sourceContentId);
1707
            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...
1708
                continue;
1709
            }
1710
1711
            $returnArray[] = $this->domainMapper->buildRelationDomainObject(
1712
                $spiRelation,
1713
                $sourceContentInfo,
1714
                $contentInfo
1715
            );
1716
        }
1717
1718
        return $returnArray;
1719
    }
1720
1721
    /**
1722
     * Adds a relation of type common.
1723
     *
1724
     * The source of the relation is the content and version
1725
     * referenced by $versionInfo.
1726
     *
1727
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version
1728
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1729
     *
1730
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1731
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation
1732
     *
1733
     * @return \eZ\Publish\API\Repository\Values\Content\Relation the newly created relation
1734
     */
1735
    public function addRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1736
    {
1737
        $sourceVersion = $this->loadVersionInfoById(
1738
            $sourceVersion->contentInfo->id,
1739
            $sourceVersion->versionNo
1740
        );
1741
1742
        if ($sourceVersion->status !== APIVersionInfo::STATUS_DRAFT) {
1743
            throw new BadStateException(
1744
                '$sourceVersion',
1745
                'Relations of type common can only be added to versions of status draft'
1746
            );
1747
        }
1748
1749
        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...
1750
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1751
        }
1752
1753
        $sourceContentInfo = $sourceVersion->getContentInfo();
1754
1755
        $this->repository->beginTransaction();
1756
        try {
1757
            $spiRelation = $this->persistenceHandler->contentHandler()->addRelation(
1758
                new SPIRelationCreateStruct(
1759
                    array(
1760
                        'sourceContentId' => $sourceContentInfo->id,
1761
                        'sourceContentVersionNo' => $sourceVersion->versionNo,
1762
                        'sourceFieldDefinitionId' => null,
1763
                        'destinationContentId' => $destinationContent->id,
1764
                        'type' => APIRelation::COMMON,
1765
                    )
1766
                )
1767
            );
1768
            $this->repository->commit();
1769
        } catch (Exception $e) {
1770
            $this->repository->rollback();
1771
            throw $e;
1772
        }
1773
1774
        return $this->domainMapper->buildRelationDomainObject($spiRelation, $sourceContentInfo, $destinationContent);
1775
    }
1776
1777
    /**
1778
     * Removes a relation of type COMMON from a draft.
1779
     *
1780
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version
1781
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1782
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination
1783
     *
1784
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1785
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent
1786
     */
1787
    public function deleteRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1788
    {
1789
        $sourceVersion = $this->loadVersionInfoById(
1790
            $sourceVersion->contentInfo->id,
1791
            $sourceVersion->versionNo
1792
        );
1793
1794
        if ($sourceVersion->status !== APIVersionInfo::STATUS_DRAFT) {
1795
            throw new BadStateException(
1796
                '$sourceVersion',
1797
                'Relations of type common can only be removed from versions of status draft'
1798
            );
1799
        }
1800
1801
        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...
1802
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1803
        }
1804
1805
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1806
            $sourceVersion->getContentInfo()->id,
1807
            $sourceVersion->versionNo,
1808
            APIRelation::COMMON
1809
        );
1810
1811
        if (empty($spiRelations)) {
1812
            throw new InvalidArgumentException(
1813
                '$sourceVersion',
1814
                'There are no relations of type COMMON for the given destination'
1815
            );
1816
        }
1817
1818
        // there should be only one relation of type COMMON for each destination,
1819
        // but in case there were ever more then one, we will remove them all
1820
        // @todo: alternatively, throw BadStateException?
1821
        $this->repository->beginTransaction();
1822
        try {
1823
            foreach ($spiRelations as $spiRelation) {
1824
                if ($spiRelation->destinationContentId == $destinationContent->id) {
1825
                    $this->persistenceHandler->contentHandler()->removeRelation(
1826
                        $spiRelation->id,
1827
                        APIRelation::COMMON
1828
                    );
1829
                }
1830
            }
1831
            $this->repository->commit();
1832
        } catch (Exception $e) {
1833
            $this->repository->rollback();
1834
            throw $e;
1835
        }
1836
    }
1837
1838
    /**
1839
     * Adds translation information to the content object.
1840
     *
1841
     * @example Examples/translation_5x.php
1842
     *
1843
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed add a translation info
1844
     *
1845
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1846
     *
1847
     * @since 5.0
1848
     */
1849
    public function addTranslationInfo(TranslationInfo $translationInfo)
1850
    {
1851
        throw new NotImplementedException(__METHOD__);
1852
    }
1853
1854
    /**
1855
     * lists the translations done on this content object.
1856
     *
1857
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed read translation infos
1858
     *
1859
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1860
     * @param array $filter
1861
     *
1862
     * @todo TBD - filter by source version destination version and languages
1863
     *
1864
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo[]
1865
     *
1866
     * @since 5.0
1867
     */
1868
    public function loadTranslationInfos(ContentInfo $contentInfo, array $filter = array())
1869
    {
1870
        throw new NotImplementedException(__METHOD__);
1871
    }
1872
1873
    /**
1874
     * Instantiates a new content create struct object.
1875
     *
1876
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1877
     * @param string $mainLanguageCode
1878
     *
1879
     * @return \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct
1880
     */
1881
    public function newContentCreateStruct(ContentType $contentType, $mainLanguageCode)
1882
    {
1883
        return new ContentCreateStruct(
1884
            array(
1885
                'contentType' => $contentType,
1886
                'mainLanguageCode' => $mainLanguageCode,
1887
            )
1888
        );
1889
    }
1890
1891
    /**
1892
     * Instantiates a new content meta data update struct.
1893
     *
1894
     * @return \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct
1895
     */
1896
    public function newContentMetadataUpdateStruct()
1897
    {
1898
        return new ContentMetadataUpdateStruct();
1899
    }
1900
1901
    /**
1902
     * Instantiates a new content update struct.
1903
     *
1904
     * @return \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct
1905
     */
1906
    public function newContentUpdateStruct()
1907
    {
1908
        return new ContentUpdateStruct();
1909
    }
1910
1911
    /**
1912
     * Instantiates a new TranslationInfo object.
1913
     *
1914
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo
1915
     */
1916
    public function newTranslationInfo()
1917
    {
1918
        return new TranslationInfo();
1919
    }
1920
1921
    /**
1922
     * Instantiates a Translation object.
1923
     *
1924
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationValues
1925
     */
1926
    public function newTranslationValues()
1927
    {
1928
        return new TranslationValues();
1929
    }
1930
}
1931