Completed
Push — master ( 9749fb...c9d304 )
by André
26:06 queued 10:47
created

ContentService::loadVersions()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 11

Duplication

Lines 8
Ratio 40 %

Importance

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

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

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

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

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

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

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

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

class Alien {}

class Dalek extends Alien {}

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

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

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

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

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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