Completed
Push — 6.7 ( eacd1f...2a3684 )
by
unknown
25:16
created

ContentService::getLanguageCodesForUpdate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

class Alien {}

class Dalek extends Alien {}

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

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

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

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

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

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

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

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

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

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

Loading history...
259
            throw new UnauthorizedException('content', $function, array('contentId' => $contentId));
260
        }
261
262
        return $versionInfo;
263
    }
264
265
    /**
266
     * Loads content in a version for the given content info object.
267
     *
268
     * If no version number is given, the method returns the current version
269
     *
270
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if version with the given number does not exist
271
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
272
     *
273
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
274
     * @param array $languages A language filter for fields. If not given all languages are returned
275
     * @param int $versionNo the version number. If not given the current version is returned
276
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
277
     *
278
     * @return \eZ\Publish\API\Repository\Values\Content\Content
279
     */
280
    public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
281
    {
282
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
283
        if ($useAlwaysAvailable && !$contentInfo->alwaysAvailable) {
284
            $useAlwaysAvailable = false;
285
        }
286
287
        return $this->loadContent(
288
            $contentInfo->id,
289
            $languages,
290
            $versionNo,
291
            $useAlwaysAvailable
292
        );
293
    }
294
295
    /**
296
     * Loads content in the version given by version info.
297
     *
298
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version
299
     *
300
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
301
     * @param array $languages A language filter for fields. If not given all languages are returned
302
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
303
     *
304
     * @return \eZ\Publish\API\Repository\Values\Content\Content
305
     */
306
    public function loadContentByVersionInfo(APIVersionInfo $versionInfo, array $languages = null, $useAlwaysAvailable = true)
307
    {
308
        // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled
309
        if ($useAlwaysAvailable && !$versionInfo->getContentInfo()->alwaysAvailable) {
310
            $useAlwaysAvailable = false;
311
        }
312
313
        return $this->loadContent(
314
            $versionInfo->getContentInfo()->id,
315
            $languages,
316
            $versionInfo->versionNo,
317
            $useAlwaysAvailable
318
        );
319
    }
320
321
    /**
322
     * Loads content in a version of the given content object.
323
     *
324
     * If no version number is given, the method returns the current version
325
     *
326
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist
327
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions
328
     *
329
     * @param int $contentId
330
     * @param array|null $languages A language filter for fields. If not given all languages are returned
331
     * @param int|null $versionNo the version number. If not given the current version is returned
332
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
333
     *
334
     * @return \eZ\Publish\API\Repository\Values\Content\Content
335
     */
336 View Code Duplication
    public function loadContent($contentId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
337
    {
338
        $content = $this->internalLoadContent($contentId, $languages, $versionNo, false, $useAlwaysAvailable);
339
340
        if (!$this->repository->canUser('content', 'read', $content)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

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

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

Loading history...
341
            throw new UnauthorizedException('content', 'read', array('contentId' => $contentId));
342
        }
343
344
        if (
345
            $content->getVersionInfo()->status !== APIVersionInfo::STATUS_PUBLISHED
346
            && !$this->repository->canUser('content', 'versionread', $content)
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

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

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

Loading history...
347
        ) {
348
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentId, 'versionNo' => $versionNo));
349
        }
350
351
        return $content;
352
    }
353
354
    /**
355
     * Loads content in a version of the given content object.
356
     *
357
     * If no version number is given, the method returns the current version
358
     *
359
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist
360
     *
361
     * @param mixed $id
362
     * @param array|null $languages A language filter for fields. If not given all languages are returned
363
     * @param int|null $versionNo the version number. If not given the current version is returned
364
     * @param bool $isRemoteId
365
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
366
     *
367
     * @return \eZ\Publish\API\Repository\Values\Content\Content
368
     */
369
    public function internalLoadContent($id, array $languages = null, $versionNo = null, $isRemoteId = false, $useAlwaysAvailable = true)
370
    {
371
        try {
372
            // Get Content ID if lookup by remote ID
373
            if ($isRemoteId) {
374
                $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($id);
375
                $id = $spiContentInfo->id;
376
            }
377
378
            // Get current version if $versionNo is not defined
379
            if ($versionNo === null) {
380
                if (!isset($spiContentInfo)) {
381
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
382
                }
383
384
                $versionNo = $spiContentInfo->currentVersionNo;
385
            }
386
387
            $loadLanguages = $languages;
388
            $alwaysAvailableLanguageCode = null;
389
            // Set main language on $languages filter if not empty (all) and $useAlwaysAvailable being true
390
            if (!empty($loadLanguages) && $useAlwaysAvailable) {
391
                if (!isset($spiContentInfo)) {
392
                    $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id);
393
                }
394
395
                if ($spiContentInfo->alwaysAvailable) {
396
                    $loadLanguages[] = $alwaysAvailableLanguageCode = $spiContentInfo->mainLanguageCode;
397
                    $loadLanguages = array_unique($loadLanguages);
398
                }
399
            }
400
401
            $spiContent = $this->persistenceHandler->contentHandler()->load(
402
                $id,
403
                $versionNo,
404
                $loadLanguages
0 ignored issues
show
Bug introduced by
It seems like $loadLanguages defined by $languages on line 387 can also be of type array; however, eZ\Publish\SPI\Persistence\Content\Handler::load() does only seem to accept null|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
405
            );
406
        } catch (APINotFoundException $e) {
407
            throw new NotFoundException(
408
                'Content',
409
                array(
410
                    $isRemoteId ? 'remoteId' : 'id' => $id,
411
                    'languages' => $languages,
412
                    'versionNo' => $versionNo,
413
                ),
414
                $e
415
            );
416
        }
417
418
        return $this->domainMapper->buildContentDomainObject(
419
            $spiContent,
420
            null,
421
            empty($languages) ? null : $languages,
422
            $alwaysAvailableLanguageCode
423
        );
424
    }
425
426
    /**
427
     * Loads content in a version for the content object reference by the given remote id.
428
     *
429
     * If no version is given, the method returns the current version
430
     *
431
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist
432
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions
433
     *
434
     * @param string $remoteId
435
     * @param array $languages A language filter for fields. If not given all languages are returned
436
     * @param int $versionNo the version number. If not given the current version is returned
437
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true
438
     *
439
     * @return \eZ\Publish\API\Repository\Values\Content\Content
440
     */
441 View Code Duplication
    public function loadContentByRemoteId($remoteId, array $languages = null, $versionNo = null, $useAlwaysAvailable = true)
442
    {
443
        $content = $this->internalLoadContent($remoteId, $languages, $versionNo, true, $useAlwaysAvailable);
444
445
        if (!$this->repository->canUser('content', 'read', $content)) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

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

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

Loading history...
446
            throw new UnauthorizedException('content', 'read', array('remoteId' => $remoteId));
447
        }
448
449
        if (
450
            $content->getVersionInfo()->status !== APIVersionInfo::STATUS_PUBLISHED
451
            && !$this->repository->canUser('content', 'versionread', $content)
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

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

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

Loading history...
452
        ) {
453
            throw new UnauthorizedException('content', 'versionread', array('remoteId' => $remoteId, 'versionNo' => $versionNo));
454
        }
455
456
        return $content;
457
    }
458
459
    /**
460
     * Bulk-load Content items by the list of ContentInfo Value Objects.
461
     *
462
     * Note: it does not throw exceptions on load, just ignores erroneous Content item.
463
     * Moreover, since the method works on pre-loaded ContentInfo list, it is assumed that user is
464
     * allowed to access every Content on the list.
465
     *
466
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo[] $contentInfoList
467
     * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on
468
     *                            returned value object. If not given all languages are returned.
469
     * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true,
470
     *                                 unless all languages have been asked for.
471
     *
472
     * @return \eZ\Publish\API\Repository\Values\Content\Content[] list of Content items with Content Ids as keys
473
     */
474
    public function loadContentListByContentInfo(
475
        array $contentInfoList,
476
        array $languages = [],
477
        $useAlwaysAvailable = true
478
    ) {
479
        $loadAllLanguages = $languages === Language::ALL;
480
        $contentIds = [];
481
        $translations = $languages;
482
        foreach ($contentInfoList as $contentInfo) {
483
            $contentIds[] = $contentInfo->id;
484
            // Unless we are told to load all languages, we add main language to translations so they are loaded too
485
            // Might in some case load more languages then intended, but prioritised handling will pick right one
486
            if (!$loadAllLanguages && $useAlwaysAvailable && $contentInfo->alwaysAvailable) {
487
                $translations[] = $contentInfo->mainLanguageCode;
488
            }
489
        }
490
        $translations = array_unique($translations);
491
492
        $spiContentList = $this->persistenceHandler->contentHandler()->loadContentList(
493
            $contentIds,
494
            $translations
495
        );
496
        $contentList = [];
497
        foreach ($spiContentList as $contentId => $spiContent) {
498
            $contentInfo = $spiContent->versionInfo->contentInfo;
499
            $contentList[$contentId] = $this->domainMapper->buildContentDomainObject(
500
                $spiContent,
501
                null,
502
                $languages,
503
                $contentInfo->alwaysAvailable ? $contentInfo->mainLanguageCode : null
504
            );
505
        }
506
507
        return $contentList;
508
    }
509
510
    /**
511
     * Creates a new content draft assigned to the authenticated user.
512
     *
513
     * If a different userId is given in $contentCreateStruct it is assigned to the given user
514
     * but this required special rights for the authenticated user
515
     * (this is useful for content staging where the transfer process does not
516
     * have to authenticate with the user which created the content object in the source server).
517
     * The user has to publish the draft if it should be visible.
518
     * In 4.x at least one location has to be provided in the location creation array.
519
     *
520
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location
521
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the provided remoteId exists in the system, required properties on
522
     *                                                                        struct are missing or invalid, or if multiple locations are under the
523
     *                                                                        same parent.
524
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
525
     *                                                                               or if a required field is missing / set to an empty value.
526
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
527
     *                                                                          or value is set for non-translatable field in language
528
     *                                                                          other than main.
529
     *
530
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
531
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs For each location parent under which a location should be created for the content
532
     *
533
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
534
     */
535
    public function createContent(APIContentCreateStruct $contentCreateStruct, array $locationCreateStructs = array())
536
    {
537
        if ($contentCreateStruct->mainLanguageCode === null) {
538
            throw new InvalidArgumentException('$contentCreateStruct', "'mainLanguageCode' property must be set");
539
        }
540
541
        if ($contentCreateStruct->contentType === null) {
542
            throw new InvalidArgumentException('$contentCreateStruct', "'contentType' property must be set");
543
        }
544
545
        $contentCreateStruct = clone $contentCreateStruct;
546
547
        if ($contentCreateStruct->ownerId === null) {
548
            $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...
549
        }
550
551
        if ($contentCreateStruct->alwaysAvailable === null) {
552
            $contentCreateStruct->alwaysAvailable = false;
553
        }
554
555
        $contentCreateStruct->contentType = $this->repository->getContentTypeService()->loadContentType(
556
            $contentCreateStruct->contentType->id
557
        );
558
559
        if (empty($contentCreateStruct->sectionId)) {
560
            if (isset($locationCreateStructs[0])) {
561
                $location = $this->repository->getLocationService()->loadLocation(
562
                    $locationCreateStructs[0]->parentLocationId
563
                );
564
                $contentCreateStruct->sectionId = $location->contentInfo->sectionId;
565
            } else {
566
                $contentCreateStruct->sectionId = 1;
567
            }
568
        }
569
570
        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...
571
            throw new UnauthorizedException(
572
                'content',
573
                'create',
574
                array(
575
                    'parentLocationId' => isset($locationCreateStructs[0]) ?
576
                            $locationCreateStructs[0]->parentLocationId :
577
                            null,
578
                    'sectionId' => $contentCreateStruct->sectionId,
579
                )
580
            );
581
        }
582
583
        if (!empty($contentCreateStruct->remoteId)) {
584
            try {
585
                $this->loadContentByRemoteId($contentCreateStruct->remoteId);
586
587
                throw new InvalidArgumentException(
588
                    '$contentCreateStruct',
589
                    "Another content with remoteId '{$contentCreateStruct->remoteId}' exists"
590
                );
591
            } catch (APINotFoundException $e) {
592
                // Do nothing
593
            }
594
        } else {
595
            $contentCreateStruct->remoteId = $this->domainMapper->getUniqueHash($contentCreateStruct);
596
        }
597
598
        $spiLocationCreateStructs = $this->buildSPILocationCreateStructs($locationCreateStructs);
599
600
        $languageCodes = $this->getLanguageCodesForCreate($contentCreateStruct);
601
        $fields = $this->mapFieldsForCreate($contentCreateStruct);
602
603
        $fieldValues = array();
604
        $spiFields = array();
605
        $allFieldErrors = array();
606
        $inputRelations = array();
607
        $locationIdToContentIdMapping = array();
608
609
        foreach ($contentCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) {
610
            /** @var $fieldType \eZ\Publish\Core\FieldType\FieldType */
611
            $fieldType = $this->fieldTypeRegistry->getFieldType(
612
                $fieldDefinition->fieldTypeIdentifier
613
            );
614
615
            foreach ($languageCodes as $languageCode) {
616
                $isEmptyValue = false;
617
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $contentCreateStruct->mainLanguageCode;
618
                $isLanguageMain = $languageCode === $contentCreateStruct->mainLanguageCode;
619
                if (isset($fields[$fieldDefinition->identifier][$valueLanguageCode])) {
620
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
621
                } else {
622
                    $fieldValue = $fieldDefinition->defaultValue;
623
                }
624
625
                $fieldValue = $fieldType->acceptValue($fieldValue);
626
627 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...
628
                    $isEmptyValue = true;
629
                    if ($fieldDefinition->isRequired) {
630
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
631
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
632
                            null,
633
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
634
                            'empty'
635
                        );
636
                    }
637
                } else {
638
                    $fieldErrors = $fieldType->validate(
639
                        $fieldDefinition,
640
                        $fieldValue
641
                    );
642
                    if (!empty($fieldErrors)) {
643
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
644
                    }
645
                }
646
647
                if (!empty($allFieldErrors)) {
648
                    continue;
649
                }
650
651
                $this->relationProcessor->appendFieldRelations(
652
                    $inputRelations,
653
                    $locationIdToContentIdMapping,
654
                    $fieldType,
655
                    $fieldValue,
656
                    $fieldDefinition->id
657
                );
658
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
659
660
                // Only non-empty value for: translatable field or in main language
661
                if (
662
                    (!$isEmptyValue && $fieldDefinition->isTranslatable) ||
663
                    (!$isEmptyValue && $isLanguageMain)
664
                ) {
665
                    $spiFields[] = new SPIField(
666
                        array(
667
                            'id' => null,
668
                            'fieldDefinitionId' => $fieldDefinition->id,
669
                            'type' => $fieldDefinition->fieldTypeIdentifier,
670
                            'value' => $fieldType->toPersistenceValue($fieldValue),
671
                            'languageCode' => $languageCode,
672
                            'versionNo' => null,
673
                        )
674
                    );
675
                }
676
            }
677
        }
678
679
        if (!empty($allFieldErrors)) {
680
            throw new ContentFieldValidationException($allFieldErrors);
681
        }
682
683
        $spiContentCreateStruct = new SPIContentCreateStruct(
684
            array(
685
                'name' => $this->nameSchemaService->resolve(
686
                    $contentCreateStruct->contentType->nameSchema,
687
                    $contentCreateStruct->contentType,
688
                    $fieldValues,
689
                    $languageCodes
690
                ),
691
                'typeId' => $contentCreateStruct->contentType->id,
692
                'sectionId' => $contentCreateStruct->sectionId,
693
                'ownerId' => $contentCreateStruct->ownerId,
694
                'locations' => $spiLocationCreateStructs,
695
                'fields' => $spiFields,
696
                'alwaysAvailable' => $contentCreateStruct->alwaysAvailable,
697
                'remoteId' => $contentCreateStruct->remoteId,
698
                'modified' => isset($contentCreateStruct->modificationDate) ? $contentCreateStruct->modificationDate->getTimestamp() : time(),
699
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
700
                    $contentCreateStruct->mainLanguageCode
701
                )->id,
702
            )
703
        );
704
705
        $defaultObjectStates = $this->getDefaultObjectStates();
706
707
        $this->repository->beginTransaction();
708
        try {
709
            $spiContent = $this->persistenceHandler->contentHandler()->create($spiContentCreateStruct);
710
            $this->relationProcessor->processFieldRelations(
711
                $inputRelations,
712
                $spiContent->versionInfo->contentInfo->id,
713
                $spiContent->versionInfo->versionNo,
714
                $contentCreateStruct->contentType
715
            );
716
717
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
718
                $this->persistenceHandler->objectStateHandler()->setContentState(
719
                    $spiContent->versionInfo->contentInfo->id,
720
                    $objectStateGroupId,
721
                    $objectState->id
722
                );
723
            }
724
725
            $this->repository->commit();
726
        } catch (Exception $e) {
727
            $this->repository->rollback();
728
            throw $e;
729
        }
730
731
        return $this->domainMapper->buildContentDomainObject($spiContent);
732
    }
733
734
    /**
735
     * Returns an array of default content states with content state group id as key.
736
     *
737
     * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[]
738
     */
739
    protected function getDefaultObjectStates()
740
    {
741
        $defaultObjectStatesMap = array();
742
        $objectStateHandler = $this->persistenceHandler->objectStateHandler();
743
744
        foreach ($objectStateHandler->loadAllGroups() as $objectStateGroup) {
745
            foreach ($objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) {
746
                // Only register the first object state which is the default one.
747
                $defaultObjectStatesMap[$objectStateGroup->id] = $objectState;
748
                break;
749
            }
750
        }
751
752
        return $defaultObjectStatesMap;
753
    }
754
755
    /**
756
     * Returns all language codes used in given $fields.
757
     *
758
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value is set in main language
759
     *
760
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
761
     *
762
     * @return string[]
763
     */
764
    protected function getLanguageCodesForCreate(APIContentCreateStruct $contentCreateStruct)
765
    {
766
        $languageCodes = array();
767
768
        foreach ($contentCreateStruct->fields as $field) {
769
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
770
                continue;
771
            }
772
773
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
774
                $field->languageCode
775
            );
776
            $languageCodes[$field->languageCode] = true;
777
        }
778
779
        if (!isset($languageCodes[$contentCreateStruct->mainLanguageCode])) {
780
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
781
                $contentCreateStruct->mainLanguageCode
782
            );
783
            $languageCodes[$contentCreateStruct->mainLanguageCode] = true;
784
        }
785
786
        return array_keys($languageCodes);
787
    }
788
789
    /**
790
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
791
     *
792
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
793
     *                                                                          or value is set for non-translatable field in language
794
     *                                                                          other than main
795
     *
796
     * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct
797
     *
798
     * @return array
799
     */
800
    protected function mapFieldsForCreate(APIContentCreateStruct $contentCreateStruct)
801
    {
802
        $fields = array();
803
804
        foreach ($contentCreateStruct->fields as $field) {
805
            $fieldDefinition = $contentCreateStruct->contentType->getFieldDefinition($field->fieldDefIdentifier);
806
807
            if ($fieldDefinition === null) {
808
                throw new ContentValidationException(
809
                    "Field definition '%identifier%' does not exist in given ContentType",
810
                    ['%identifier%' => $field->fieldDefIdentifier]
811
                );
812
            }
813
814
            if ($field->languageCode === null) {
815
                $field = $this->cloneField(
816
                    $field,
817
                    array('languageCode' => $contentCreateStruct->mainLanguageCode)
818
                );
819
            }
820
821 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...
822
                throw new ContentValidationException(
823
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
824
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
825
                );
826
            }
827
828
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
829
        }
830
831
        return $fields;
832
    }
833
834
    /**
835
     * Clones $field with overriding specific properties from given $overrides array.
836
     *
837
     * @param Field $field
838
     * @param array $overrides
839
     *
840
     * @return Field
841
     */
842
    private function cloneField(Field $field, array $overrides = array())
843
    {
844
        $fieldData = array_merge(
845
            array(
846
                'id' => $field->id,
847
                'value' => $field->value,
848
                'languageCode' => $field->languageCode,
849
                'fieldDefIdentifier' => $field->fieldDefIdentifier,
850
            ),
851
            $overrides
852
        );
853
854
        return new Field($fieldData);
855
    }
856
857
    /**
858
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
859
     *
860
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
861
     *
862
     * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct[]
863
     */
864
    protected function buildSPILocationCreateStructs(array $locationCreateStructs)
865
    {
866
        $spiLocationCreateStructs = array();
867
        $parentLocationIdSet = array();
868
        $mainLocation = true;
869
870
        foreach ($locationCreateStructs as $locationCreateStruct) {
871
            if (isset($parentLocationIdSet[$locationCreateStruct->parentLocationId])) {
872
                throw new InvalidArgumentException(
873
                    '$locationCreateStructs',
874
                    "Multiple LocationCreateStructs with the same parent Location '{$locationCreateStruct->parentLocationId}' are given"
875
                );
876
            }
877
878
            $parentLocationIdSet[$locationCreateStruct->parentLocationId] = true;
879
            $parentLocation = $this->repository->getLocationService()->loadLocation(
880
                $locationCreateStruct->parentLocationId
881
            );
882
883
            $spiLocationCreateStructs[] = $this->domainMapper->buildSPILocationCreateStruct(
884
                $locationCreateStruct,
885
                $parentLocation,
886
                $mainLocation,
887
                // For Content draft contentId and contentVersionNo are set in ContentHandler upon draft creation
888
                null,
889
                null
890
            );
891
892
            // First Location in the list will be created as main Location
893
            $mainLocation = false;
894
        }
895
896
        return $spiLocationCreateStructs;
897
    }
898
899
    /**
900
     * Updates the metadata.
901
     *
902
     * (see {@link ContentMetadataUpdateStruct}) of a content object - to update fields use updateContent
903
     *
904
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data
905
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists
906
     *
907
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
908
     * @param \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct
909
     *
910
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content with the updated attributes
911
     */
912
    public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct)
913
    {
914
        $propertyCount = 0;
915
        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...
916
            if (isset($contentMetadataUpdateStruct->$propertyName)) {
917
                $propertyCount += 1;
918
            }
919
        }
920
        if ($propertyCount === 0) {
921
            throw new InvalidArgumentException(
922
                '$contentMetadataUpdateStruct',
923
                'At least one property must be set'
924
            );
925
        }
926
927
        $loadedContentInfo = $this->loadContentInfo($contentInfo->id);
928
929
        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...
930
            throw new UnauthorizedException('content', 'edit', array('contentId' => $loadedContentInfo->id));
931
        }
932
933
        if (isset($contentMetadataUpdateStruct->remoteId)) {
934
            try {
935
                $existingContentInfo = $this->loadContentInfoByRemoteId($contentMetadataUpdateStruct->remoteId);
936
937
                if ($existingContentInfo->id !== $loadedContentInfo->id) {
938
                    throw new InvalidArgumentException(
939
                        '$contentMetadataUpdateStruct',
940
                        "Another content with remoteId '{$contentMetadataUpdateStruct->remoteId}' exists"
941
                    );
942
                }
943
            } catch (APINotFoundException $e) {
944
                // Do nothing
945
            }
946
        }
947
948
        $this->repository->beginTransaction();
949
        try {
950
            if ($propertyCount > 1 || !isset($contentMetadataUpdateStruct->mainLocationId)) {
951
                $this->persistenceHandler->contentHandler()->updateMetadata(
952
                    $loadedContentInfo->id,
953
                    new SPIMetadataUpdateStruct(
954
                        array(
955
                            'ownerId' => $contentMetadataUpdateStruct->ownerId,
956
                            'publicationDate' => isset($contentMetadataUpdateStruct->publishedDate) ?
957
                                $contentMetadataUpdateStruct->publishedDate->getTimestamp() :
958
                                null,
959
                            'modificationDate' => isset($contentMetadataUpdateStruct->modificationDate) ?
960
                                $contentMetadataUpdateStruct->modificationDate->getTimestamp() :
961
                                null,
962
                            'mainLanguageId' => isset($contentMetadataUpdateStruct->mainLanguageCode) ?
963
                                $this->repository->getContentLanguageService()->loadLanguage(
964
                                    $contentMetadataUpdateStruct->mainLanguageCode
965
                                )->id :
966
                                null,
967
                            'alwaysAvailable' => $contentMetadataUpdateStruct->alwaysAvailable,
968
                            'remoteId' => $contentMetadataUpdateStruct->remoteId,
969
                        )
970
                    )
971
                );
972
            }
973
974
            // Change main location
975
            if (isset($contentMetadataUpdateStruct->mainLocationId)
976
                && $loadedContentInfo->mainLocationId !== $contentMetadataUpdateStruct->mainLocationId) {
977
                $this->persistenceHandler->locationHandler()->changeMainLocation(
978
                    $loadedContentInfo->id,
979
                    $contentMetadataUpdateStruct->mainLocationId
980
                );
981
            }
982
983
            // Republish URL aliases to update always-available flag
984
            if (isset($contentMetadataUpdateStruct->alwaysAvailable)
985
                && $loadedContentInfo->alwaysAvailable !== $contentMetadataUpdateStruct->alwaysAvailable) {
986
                $content = $this->loadContent($loadedContentInfo->id);
987
                $this->publishUrlAliasesForContent($content, false);
988
            }
989
990
            $this->repository->commit();
991
        } catch (Exception $e) {
992
            $this->repository->rollback();
993
            throw $e;
994
        }
995
996
        return isset($content) ? $content : $this->loadContent($loadedContentInfo->id);
997
    }
998
999
    /**
1000
     * Publishes URL aliases for all locations of a given content.
1001
     *
1002
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1003
     * @param bool $updatePathIdentificationString this parameter is legacy storage specific for updating
1004
     *                      ezcontentobject_tree.path_identification_string, it is ignored by other storage engines
1005
     */
1006
    protected function publishUrlAliasesForContent(APIContent $content, $updatePathIdentificationString = true)
1007
    {
1008
        $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content);
1009
        $locations = $this->repository->getLocationService()->loadLocations(
1010
            $content->getVersionInfo()->getContentInfo()
1011
        );
1012
        foreach ($locations as $location) {
1013 View Code Duplication
            foreach ($urlAliasNames as $languageCode => $name) {
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...
1014
                $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation(
1015
                    $location->id,
1016
                    $location->parentLocationId,
1017
                    $name,
1018
                    $languageCode,
1019
                    $content->contentInfo->alwaysAvailable,
1020
                    $updatePathIdentificationString ? $languageCode === $content->contentInfo->mainLanguageCode : false
1021
                );
1022
            }
1023
        }
1024
    }
1025
1026
    /**
1027
     * Deletes a content object including all its versions and locations including their subtrees.
1028
     *
1029
     * @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)
1030
     *
1031
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1032
     *
1033
     * @return mixed[] Affected Location Id's
1034
     */
1035
    public function deleteContent(ContentInfo $contentInfo)
1036
    {
1037
        $contentInfo = $this->internalLoadContentInfo($contentInfo->id);
1038
1039
        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...
1040
            throw new UnauthorizedException('content', 'remove', array('contentId' => $contentInfo->id));
1041
        }
1042
1043
        $affectedLocations = [];
1044
        $this->repository->beginTransaction();
1045
        try {
1046
            // Load Locations first as deleting Content also deletes belonging Locations
1047
            $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($contentInfo->id);
1048
            $this->persistenceHandler->contentHandler()->deleteContent($contentInfo->id);
1049
            foreach ($spiLocations as $spiLocation) {
1050
                $this->persistenceHandler->urlAliasHandler()->locationDeleted($spiLocation->id);
1051
                $affectedLocations[] = $spiLocation->id;
1052
            }
1053
            $this->repository->commit();
1054
        } catch (Exception $e) {
1055
            $this->repository->rollback();
1056
            throw $e;
1057
        }
1058
1059
        return $affectedLocations;
1060
    }
1061
1062
    /**
1063
     * Creates a draft from a published or archived version.
1064
     *
1065
     * If no version is given, the current published version is used.
1066
     * 4.x: The draft is created with the initialLanguage code of the source version or if not present with the main language.
1067
     * It can be changed on updating the version.
1068
     *
1069
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft
1070
     *
1071
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1072
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1073
     * @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
1074
     *
1075
     * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft
1076
     */
1077
    public function createContentDraft(ContentInfo $contentInfo, APIVersionInfo $versionInfo = null, User $creator = null)
1078
    {
1079
        $contentInfo = $this->loadContentInfo($contentInfo->id);
1080
1081
        if ($versionInfo !== null) {
1082
            // Check that given $contentInfo and $versionInfo belong to the same content
1083
            if ($versionInfo->getContentInfo()->id != $contentInfo->id) {
1084
                throw new InvalidArgumentException(
1085
                    '$versionInfo',
1086
                    'VersionInfo does not belong to the same content as given ContentInfo'
1087
                );
1088
            }
1089
1090
            $versionInfo = $this->loadVersionInfoById($contentInfo->id, $versionInfo->versionNo);
1091
1092
            switch ($versionInfo->status) {
1093
                case VersionInfo::STATUS_PUBLISHED:
1094
                case VersionInfo::STATUS_ARCHIVED:
1095
                    break;
1096
1097
                default:
1098
                    // @todo: throw an exception here, to be defined
1099
                    throw new BadStateException(
1100
                        '$versionInfo',
1101
                        'Draft can not be created from a draft version'
1102
                    );
1103
            }
1104
1105
            $versionNo = $versionInfo->versionNo;
1106
        } elseif ($contentInfo->published) {
1107
            $versionNo = $contentInfo->currentVersionNo;
1108
        } else {
1109
            // @todo: throw an exception here, to be defined
1110
            throw new BadStateException(
1111
                '$contentInfo',
1112
                'Content is not published, draft can be created only from published or archived version'
1113
            );
1114
        }
1115
1116
        if ($creator === null) {
1117
            $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...
1118
        }
1119
1120
        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...
1121
            throw new UnauthorizedException('content', 'edit', array('contentId' => $contentInfo->id));
1122
        }
1123
1124
        $this->repository->beginTransaction();
1125
        try {
1126
            $spiContent = $this->persistenceHandler->contentHandler()->createDraftFromVersion(
1127
                $contentInfo->id,
1128
                $versionNo,
1129
                $creator->getUserId()
1130
            );
1131
            $this->repository->commit();
1132
        } catch (Exception $e) {
1133
            $this->repository->rollback();
1134
            throw $e;
1135
        }
1136
1137
        return $this->domainMapper->buildContentDomainObject($spiContent);
1138
    }
1139
1140
    /**
1141
     * Loads drafts for a user.
1142
     *
1143
     * If no user is given the drafts for the authenticated user a returned
1144
     *
1145
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to load the draft list
1146
     *
1147
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $user
1148
     *
1149
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo the drafts ({@link VersionInfo}) owned by the given user
1150
     */
1151
    public function loadContentDrafts(User $user = null)
1152
    {
1153
        if ($user === null) {
1154
            $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...
1155
        }
1156
1157
        // throw early if user has absolutely no access to versionread
1158
        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...
1159
            throw new UnauthorizedException('content', 'versionread');
1160
        }
1161
1162
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftsForUser($user->getUserId());
1163
        $versionInfoList = array();
1164 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...
1165
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1166
            // @todo: Change this to filter returned drafts by permissions instead of throwing
1167
            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...
1168
                throw new UnauthorizedException('content', 'versionread', array('contentId' => $versionInfo->contentInfo->id));
1169
            }
1170
1171
            $versionInfoList[] = $versionInfo;
1172
        }
1173
1174
        return $versionInfoList;
1175
    }
1176
1177
    /**
1178
     * Translate a version.
1179
     *
1180
     * updates the destination version given in $translationInfo with the provided translated fields in $translationValues
1181
     *
1182
     * @example Examples/translation_5x.php
1183
     *
1184
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to update this version
1185
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the given destination version is not a draft
1186
     * @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.
1187
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1188
     *                                                                          or value is set for non-translatable field in language
1189
     *                                                                          other than main.
1190
     *
1191
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1192
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationValues $translationValues
1193
     * @param \eZ\Publish\API\Repository\Values\User\User $modifier If set, this user is taken as modifier of the version
1194
     *
1195
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the translated fields
1196
     *
1197
     * @since 5.0
1198
     */
1199
    public function translateVersion(TranslationInfo $translationInfo, APITranslationValues $translationValues, User $modifier = null)
1200
    {
1201
        throw new NotImplementedException(__METHOD__);
1202
    }
1203
1204
    /**
1205
     * Updates the fields of a draft.
1206
     *
1207
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version
1208
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1209
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid.
1210
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid,
1211
     *                                                                               or if a required field is missing / set to an empty value.
1212
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType,
1213
     *                                                                          or value is set for non-translatable field in language
1214
     *                                                                          other than main.
1215
     *
1216
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1217
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1218
     *
1219
     * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the updated fields
1220
     */
1221
    public function updateContent(APIVersionInfo $versionInfo, APIContentUpdateStruct $contentUpdateStruct)
1222
    {
1223
        $contentUpdateStruct = clone $contentUpdateStruct;
1224
1225
        /** @var $content \eZ\Publish\Core\Repository\Values\Content\Content */
1226
        $content = $this->loadContent(
1227
            $versionInfo->getContentInfo()->id,
1228
            null,
1229
            $versionInfo->versionNo
1230
        );
1231
        if ($content->versionInfo->status !== APIVersionInfo::STATUS_DRAFT) {
1232
            throw new BadStateException(
1233
                '$versionInfo',
1234
                'Version is not a draft and can not be updated'
1235
            );
1236
        }
1237
1238
        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...
1239
            throw new UnauthorizedException('content', 'edit', array('contentId' => $content->id));
1240
        }
1241
1242
        $mainLanguageCode = $content->contentInfo->mainLanguageCode;
1243
        if ($contentUpdateStruct->initialLanguageCode === null) {
1244
            $contentUpdateStruct->initialLanguageCode = $mainLanguageCode;
1245
        }
1246
1247
        $allLanguageCodes = $this->getLanguageCodesForUpdate($contentUpdateStruct, $content);
1248
        foreach ($allLanguageCodes as $languageCode) {
1249
            $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode($languageCode);
1250
        }
1251
1252
        $updatedLanguageCodes = $this->getUpdatedLanguageCodes($contentUpdateStruct);
1253
        $contentType = $this->repository->getContentTypeService()->loadContentType(
1254
            $content->contentInfo->contentTypeId
1255
        );
1256
        $fields = $this->mapFieldsForUpdate(
1257
            $contentUpdateStruct,
1258
            $contentType,
1259
            $mainLanguageCode
1260
        );
1261
1262
        $fieldValues = array();
1263
        $spiFields = array();
1264
        $allFieldErrors = array();
1265
        $inputRelations = array();
1266
        $locationIdToContentIdMapping = array();
1267
1268
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
1269
            /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */
1270
            $fieldType = $this->fieldTypeRegistry->getFieldType(
1271
                $fieldDefinition->fieldTypeIdentifier
1272
            );
1273
1274
            foreach ($allLanguageCodes as $languageCode) {
1275
                $isCopied = $isEmpty = $isRetained = false;
1276
                $isLanguageNew = !in_array($languageCode, $content->versionInfo->languageCodes);
1277
                $isLanguageUpdated = in_array($languageCode, $updatedLanguageCodes);
1278
                $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $mainLanguageCode;
1279
                $isFieldUpdated = isset($fields[$fieldDefinition->identifier][$valueLanguageCode]);
1280
                $isProcessed = isset($fieldValues[$fieldDefinition->identifier][$valueLanguageCode]);
1281
1282
                if (!$isFieldUpdated && !$isLanguageNew) {
1283
                    $isRetained = true;
1284
                    $fieldValue = $content->getField($fieldDefinition->identifier, $valueLanguageCode)->value;
1285
                } elseif (!$isFieldUpdated && $isLanguageNew && !$fieldDefinition->isTranslatable) {
1286
                    $isCopied = true;
1287
                    $fieldValue = $content->getField($fieldDefinition->identifier, $valueLanguageCode)->value;
1288
                } elseif ($isFieldUpdated) {
1289
                    $fieldValue = $fields[$fieldDefinition->identifier][$valueLanguageCode]->value;
1290
                } else {
1291
                    $fieldValue = $fieldDefinition->defaultValue;
1292
                }
1293
1294
                $fieldValue = $fieldType->acceptValue($fieldValue);
1295
1296 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...
1297
                    $isEmpty = true;
1298
                    if ($isLanguageUpdated && $fieldDefinition->isRequired) {
1299
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(
1300
                            "Value for required field definition '%identifier%' with language '%languageCode%' is empty",
1301
                            null,
1302
                            ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode],
1303
                            'empty'
1304
                        );
1305
                    }
1306
                } elseif ($isLanguageUpdated) {
1307
                    $fieldErrors = $fieldType->validate(
1308
                        $fieldDefinition,
1309
                        $fieldValue
1310
                    );
1311
                    if (!empty($fieldErrors)) {
1312
                        $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors;
1313
                    }
1314
                }
1315
1316
                if (!empty($allFieldErrors)) {
1317
                    continue;
1318
                }
1319
1320
                $this->relationProcessor->appendFieldRelations(
1321
                    $inputRelations,
1322
                    $locationIdToContentIdMapping,
1323
                    $fieldType,
1324
                    $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...
1325
                    $fieldDefinition->id
1326
                );
1327
                $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue;
1328
1329
                if ($isRetained || $isCopied || ($isLanguageNew && $isEmpty) || $isProcessed) {
1330
                    continue;
1331
                }
1332
1333
                $spiFields[] = new SPIField(
1334
                    array(
1335
                        'id' => $isLanguageNew ?
1336
                            null :
1337
                            $content->getField($fieldDefinition->identifier, $languageCode)->id,
1338
                        'fieldDefinitionId' => $fieldDefinition->id,
1339
                        'type' => $fieldDefinition->fieldTypeIdentifier,
1340
                        'value' => $fieldType->toPersistenceValue($fieldValue),
1341
                        'languageCode' => $languageCode,
1342
                        'versionNo' => $versionInfo->versionNo,
1343
                    )
1344
                );
1345
            }
1346
        }
1347
1348
        if (!empty($allFieldErrors)) {
1349
            throw new ContentFieldValidationException($allFieldErrors);
1350
        }
1351
1352
        $spiContentUpdateStruct = new SPIContentUpdateStruct(
1353
            array(
1354
                'name' => $this->nameSchemaService->resolveNameSchema(
1355
                    $content,
1356
                    $fieldValues,
1357
                    $allLanguageCodes,
1358
                    $contentType
1359
                ),
1360
                '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...
1361
                'fields' => $spiFields,
1362
                'modificationDate' => time(),
1363
                'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode(
1364
                    $contentUpdateStruct->initialLanguageCode
1365
                )->id,
1366
            )
1367
        );
1368
        $existingRelations = $this->loadRelations($versionInfo);
1369
1370
        $this->repository->beginTransaction();
1371
        try {
1372
            $spiContent = $this->persistenceHandler->contentHandler()->updateContent(
1373
                $versionInfo->getContentInfo()->id,
1374
                $versionInfo->versionNo,
1375
                $spiContentUpdateStruct
1376
            );
1377
            $this->relationProcessor->processFieldRelations(
1378
                $inputRelations,
1379
                $spiContent->versionInfo->contentInfo->id,
1380
                $spiContent->versionInfo->versionNo,
1381
                $contentType,
1382
                $existingRelations
1383
            );
1384
            $this->repository->commit();
1385
        } catch (Exception $e) {
1386
            $this->repository->rollback();
1387
            throw $e;
1388
        }
1389
1390
        return $this->domainMapper->buildContentDomainObject(
1391
            $spiContent,
1392
            $contentType
1393
        );
1394
    }
1395
1396
    /**
1397
     * Returns only updated language codes.
1398
     *
1399
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1400
     *
1401
     * @return array
1402
     */
1403 View Code Duplication
    private function getUpdatedLanguageCodes(APIContentUpdateStruct $contentUpdateStruct)
1404
    {
1405
        $languageCodes = [
1406
            $contentUpdateStruct->initialLanguageCode => true,
1407
        ];
1408
1409
        foreach ($contentUpdateStruct->fields as $field) {
1410
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1411
                continue;
1412
            }
1413
1414
            $languageCodes[$field->languageCode] = true;
1415
        }
1416
1417
        return array_keys($languageCodes);
1418
    }
1419
1420
    /**
1421
     * Returns all language codes used in given $fields.
1422
     *
1423
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if no field value exists in initial language
1424
     *
1425
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1426
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1427
     *
1428
     * @return array
1429
     */
1430
    protected function getLanguageCodesForUpdate(APIContentUpdateStruct $contentUpdateStruct, APIContent $content)
1431
    {
1432
        $languageCodes = array_fill_keys($content->versionInfo->languageCodes, true);
1433
        $languageCodes[$contentUpdateStruct->initialLanguageCode] = true;
1434
1435
        $updatedLanguageCodes = $this->getUpdatedLanguageCodes($contentUpdateStruct);
1436
        foreach ($updatedLanguageCodes as $languageCode) {
1437
            $languageCodes[$languageCode] = true;
1438
        }
1439
1440
        return array_keys($languageCodes);
1441
    }
1442
1443
    /**
1444
     * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode].
1445
     *
1446
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType
1447
     *                                                                          or value is set for non-translatable field in language
1448
     *                                                                          other than main
1449
     *
1450
     * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct
1451
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1452
     * @param string $mainLanguageCode
1453
     *
1454
     * @return array
1455
     */
1456
    protected function mapFieldsForUpdate(
1457
        APIContentUpdateStruct $contentUpdateStruct,
1458
        ContentType $contentType,
1459
        $mainLanguageCode
1460
    ) {
1461
        $fields = array();
1462
1463
        foreach ($contentUpdateStruct->fields as $field) {
1464
            $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier);
1465
1466
            if ($fieldDefinition === null) {
1467
                throw new ContentValidationException(
1468
                    "Field definition '%identifier%' does not exist in given ContentType",
1469
                    ['%identifier%' => $field->fieldDefIdentifier]
1470
                );
1471
            }
1472
1473
            if ($field->languageCode === null) {
1474
                if ($fieldDefinition->isTranslatable) {
1475
                    $languageCode = $contentUpdateStruct->initialLanguageCode;
1476
                } else {
1477
                    $languageCode = $mainLanguageCode;
1478
                }
1479
                $field = $this->cloneField($field, array('languageCode' => $languageCode));
1480
            }
1481
1482 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...
1483
                throw new ContentValidationException(
1484
                    "A value is set for non translatable field definition '%identifier%' with language '%languageCode%'",
1485
                    ['%identifier%' => $field->fieldDefIdentifier, '%languageCode%' => $field->languageCode]
1486
                );
1487
            }
1488
1489
            $fields[$field->fieldDefIdentifier][$field->languageCode] = $field;
1490
        }
1491
1492
        return $fields;
1493
    }
1494
1495
    /**
1496
     * Publishes a content version.
1497
     *
1498
     * Publishes a content version and deletes archive versions if they overflow max archive versions.
1499
     * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future.
1500
     *
1501
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version
1502
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1503
     *
1504
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1505
     *
1506
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1507
     */
1508
    public function publishVersion(APIVersionInfo $versionInfo)
1509
    {
1510
        $content = $this->internalLoadContent(
1511
            $versionInfo->contentInfo->id,
1512
            null,
1513
            $versionInfo->versionNo
1514
        );
1515
1516
        if (!$content->getVersionInfo()->getContentInfo()->published) {
1517
            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...
1518
                throw new UnauthorizedException('content', 'create', array('contentId' => $content->id));
1519
            }
1520
        } 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...
1521
            throw new UnauthorizedException('content', 'edit', array('contentId' => $content->id));
1522
        }
1523
1524
        $this->repository->beginTransaction();
1525
        try {
1526
            $content = $this->internalPublishVersion($content->getVersionInfo());
1527
            $this->repository->commit();
1528
        } catch (Exception $e) {
1529
            $this->repository->rollback();
1530
            throw $e;
1531
        }
1532
1533
        return $content;
1534
    }
1535
1536
    /**
1537
     * Publishes a content version.
1538
     *
1539
     * Publishes a content version and deletes archive versions if they overflow max archive versions.
1540
     * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future.
1541
     *
1542
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1543
     *
1544
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1545
     * @param int|null $publicationDate If null existing date is kept if there is one, otherwise current time is used.
1546
     *
1547
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1548
     */
1549
    protected function internalPublishVersion(APIVersionInfo $versionInfo, $publicationDate = null)
1550
    {
1551
        if ($versionInfo->status !== APIVersionInfo::STATUS_DRAFT) {
1552
            throw new BadStateException('$versionInfo', 'Only versions in draft status can be published.');
1553
        }
1554
1555
        $currentTime = time();
1556
        if ($publicationDate === null && $versionInfo->versionNo === 1) {
1557
            $publicationDate = $currentTime;
1558
        }
1559
1560
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
1561
        $metadataUpdateStruct->publicationDate = $publicationDate;
1562
        $metadataUpdateStruct->modificationDate = $currentTime;
1563
1564
        $contentId = $versionInfo->getContentInfo()->id;
1565
        $spiContent = $this->persistenceHandler->contentHandler()->publish(
1566
            $contentId,
1567
            $versionInfo->versionNo,
1568
            $metadataUpdateStruct
1569
        );
1570
1571
        $content = $this->domainMapper->buildContentDomainObject($spiContent);
1572
1573
        $this->publishUrlAliasesForContent($content);
1574
1575
        // Delete version archive overflow if any, limit is 0-50 (however 0 will mean 1 if content is unpublished)
1576
        $archiveList = $this->persistenceHandler->contentHandler()->listVersions(
1577
            $contentId,
1578
            APIVersionInfo::STATUS_ARCHIVED,
1579
            100 // Limited to avoid publishing taking to long, besides SE limitations this is why limit is max 50
1580
        );
1581
1582
        $maxVersionArchiveCount = max(0, min(50, $this->settings['default_version_archive_limit']));
1583
        while (!empty($archiveList) && count($archiveList) > $maxVersionArchiveCount) {
1584
            /** @var \eZ\Publish\SPI\Persistence\Content\VersionInfo $archiveVersion */
1585
            $archiveVersion = array_shift($archiveList);
1586
            $this->persistenceHandler->contentHandler()->deleteVersion(
1587
                $contentId,
1588
                $archiveVersion->versionNo
1589
            );
1590
        }
1591
1592
        return $content;
1593
    }
1594
1595
    /**
1596
     * Removes the given version.
1597
     *
1598
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is in
1599
     *         published state or is the last version of the Content
1600
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version
1601
     *
1602
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1603
     */
1604
    public function deleteVersion(APIVersionInfo $versionInfo)
1605
    {
1606
        if ($versionInfo->status === APIVersionInfo::STATUS_PUBLISHED) {
1607
            throw new BadStateException(
1608
                '$versionInfo',
1609
                'Version is published and can not be removed'
1610
            );
1611
        }
1612
1613 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...
1614
            throw new UnauthorizedException(
1615
                'content',
1616
                'versionremove',
1617
                array('contentId' => $versionInfo->contentInfo->id, 'versionNo' => $versionInfo->versionNo)
1618
            );
1619
        }
1620
1621
        $versionList = $this->persistenceHandler->contentHandler()->listVersions(
1622
            $versionInfo->contentInfo->id,
1623
            null,
1624
            2
1625
        );
1626
1627
        if (count($versionList) === 1) {
1628
            throw new BadStateException(
1629
                '$versionInfo',
1630
                'Version is the last version of the Content and can not be removed'
1631
            );
1632
        }
1633
1634
        $this->repository->beginTransaction();
1635
        try {
1636
            $this->persistenceHandler->contentHandler()->deleteVersion(
1637
                $versionInfo->getContentInfo()->id,
1638
                $versionInfo->versionNo
1639
            );
1640
            $this->repository->commit();
1641
        } catch (Exception $e) {
1642
            $this->repository->rollback();
1643
            throw $e;
1644
        }
1645
    }
1646
1647
    /**
1648
     * Loads all versions for the given content.
1649
     *
1650
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions
1651
     *
1652
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1653
     *
1654
     * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] Sorted by creation date
1655
     */
1656
    public function loadVersions(ContentInfo $contentInfo)
1657
    {
1658
        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...
1659
            throw new UnauthorizedException('content', 'versionread', array('contentId' => $contentInfo->id));
1660
        }
1661
1662
        $spiVersionInfoList = $this->persistenceHandler->contentHandler()->listVersions($contentInfo->id);
1663
1664
        $versions = array();
1665 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...
1666
            $versionInfo = $this->domainMapper->buildVersionInfoDomainObject($spiVersionInfo);
1667
            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...
1668
                throw new UnauthorizedException('content', 'versionread', array('versionId' => $versionInfo->id));
1669
            }
1670
1671
            $versions[] = $versionInfo;
1672
        }
1673
1674
        return $versions;
1675
    }
1676
1677
    /**
1678
     * Copies the content to a new location. If no version is given,
1679
     * all versions are copied, otherwise only the given version.
1680
     *
1681
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location
1682
     *
1683
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1684
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to
1685
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1686
     *
1687
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1688
     */
1689
    public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, APIVersionInfo $versionInfo = null)
1690
    {
1691
        $destinationLocation = $this->repository->getLocationService()->loadLocation(
1692
            $destinationLocationCreateStruct->parentLocationId
1693
        );
1694 View Code Duplication
        if (!$this->repository->canUser('content', 'create', $contentInfo, [$destinationLocation])) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Check if user has access to a given action on a given value object. Indicates if the current user is allowed to perform an action given by the function on the given
objects.

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

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

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

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

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

Loading history...
1695
            throw new UnauthorizedException(
1696
                'content',
1697
                'create',
1698
                [
1699
                    'parentLocationId' => $destinationLocationCreateStruct->parentLocationId,
1700
                    'sectionId' => $contentInfo->sectionId,
1701
                ]
1702
            );
1703
        }
1704
1705
        $defaultObjectStates = $this->getDefaultObjectStates();
1706
1707
        $this->repository->beginTransaction();
1708
        try {
1709
            $spiContent = $this->persistenceHandler->contentHandler()->copy(
1710
                $contentInfo->id,
1711
                $versionInfo ? $versionInfo->versionNo : null
1712
            );
1713
1714
            foreach ($defaultObjectStates as $objectStateGroupId => $objectState) {
1715
                $this->persistenceHandler->objectStateHandler()->setContentState(
1716
                    $spiContent->versionInfo->contentInfo->id,
1717
                    $objectStateGroupId,
1718
                    $objectState->id
1719
                );
1720
            }
1721
1722
            $content = $this->internalPublishVersion(
1723
                $this->domainMapper->buildVersionInfoDomainObject($spiContent->versionInfo),
1724
                $spiContent->versionInfo->creationDate
1725
            );
1726
1727
            $this->repository->getLocationService()->createLocation(
1728
                $content->getVersionInfo()->getContentInfo(),
1729
                $destinationLocationCreateStruct
1730
            );
1731
            $this->repository->commit();
1732
        } catch (Exception $e) {
1733
            $this->repository->rollback();
1734
            throw $e;
1735
        }
1736
1737
        return $this->internalLoadContent($content->id);
1738
    }
1739
1740
    /**
1741
     * Loads all outgoing relations for the given version.
1742
     *
1743
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1744
     *
1745
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
1746
     *
1747
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1748
     */
1749
    public function loadRelations(APIVersionInfo $versionInfo)
1750
    {
1751
        if ($versionInfo->status === APIVersionInfo::STATUS_PUBLISHED) {
1752
            $function = 'read';
1753
        } else {
1754
            $function = 'versionread';
1755
        }
1756
1757
        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...
1758
            throw new UnauthorizedException('content', $function);
1759
        }
1760
1761
        $contentInfo = $versionInfo->getContentInfo();
1762
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1763
            $contentInfo->id,
1764
            $versionInfo->versionNo
1765
        );
1766
1767
        /** @var $relations \eZ\Publish\API\Repository\Values\Content\Relation[] */
1768
        $relations = array();
1769 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...
1770
            $destinationContentInfo = $this->internalLoadContentInfo($spiRelation->destinationContentId);
1771
            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...
1772
                continue;
1773
            }
1774
1775
            $relations[] = $this->domainMapper->buildRelationDomainObject(
1776
                $spiRelation,
1777
                $contentInfo,
1778
                $destinationContentInfo
1779
            );
1780
        }
1781
1782
        return $relations;
1783
    }
1784
1785
    /**
1786
     * Loads all incoming relations for a content object.
1787
     *
1788
     * The relations come only from published versions of the source content objects
1789
     *
1790
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version
1791
     *
1792
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1793
     *
1794
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
1795
     */
1796
    public function loadReverseRelations(ContentInfo $contentInfo)
1797
    {
1798
        if (!$this->repository->canUser('content', 'reverserelatedlist', $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...
1799
            throw new UnauthorizedException('content', 'reverserelatedlist', array('contentId' => $contentInfo->id));
1800
        }
1801
1802
        $spiRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations(
1803
            $contentInfo->id
1804
        );
1805
1806
        $returnArray = array();
1807 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...
1808
            $sourceContentInfo = $this->internalLoadContentInfo($spiRelation->sourceContentId);
1809
            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...
1810
                continue;
1811
            }
1812
1813
            $returnArray[] = $this->domainMapper->buildRelationDomainObject(
1814
                $spiRelation,
1815
                $sourceContentInfo,
1816
                $contentInfo
1817
            );
1818
        }
1819
1820
        return $returnArray;
1821
    }
1822
1823
    /**
1824
     * Adds a relation of type common.
1825
     *
1826
     * The source of the relation is the content and version
1827
     * referenced by $versionInfo.
1828
     *
1829
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version
1830
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1831
     *
1832
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1833
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation
1834
     *
1835
     * @return \eZ\Publish\API\Repository\Values\Content\Relation the newly created relation
1836
     */
1837
    public function addRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1838
    {
1839
        $sourceVersion = $this->loadVersionInfoById(
1840
            $sourceVersion->contentInfo->id,
1841
            $sourceVersion->versionNo
1842
        );
1843
1844
        if ($sourceVersion->status !== APIVersionInfo::STATUS_DRAFT) {
1845
            throw new BadStateException(
1846
                '$sourceVersion',
1847
                'Relations of type common can only be added to versions of status draft'
1848
            );
1849
        }
1850
1851
        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...
1852
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1853
        }
1854
1855
        $sourceContentInfo = $sourceVersion->getContentInfo();
1856
1857
        $this->repository->beginTransaction();
1858
        try {
1859
            $spiRelation = $this->persistenceHandler->contentHandler()->addRelation(
1860
                new SPIRelationCreateStruct(
1861
                    array(
1862
                        'sourceContentId' => $sourceContentInfo->id,
1863
                        'sourceContentVersionNo' => $sourceVersion->versionNo,
1864
                        'sourceFieldDefinitionId' => null,
1865
                        'destinationContentId' => $destinationContent->id,
1866
                        'type' => APIRelation::COMMON,
1867
                    )
1868
                )
1869
            );
1870
            $this->repository->commit();
1871
        } catch (Exception $e) {
1872
            $this->repository->rollback();
1873
            throw $e;
1874
        }
1875
1876
        return $this->domainMapper->buildRelationDomainObject($spiRelation, $sourceContentInfo, $destinationContent);
1877
    }
1878
1879
    /**
1880
     * Removes a relation of type COMMON from a draft.
1881
     *
1882
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version
1883
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft
1884
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination
1885
     *
1886
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion
1887
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent
1888
     */
1889
    public function deleteRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent)
1890
    {
1891
        $sourceVersion = $this->loadVersionInfoById(
1892
            $sourceVersion->contentInfo->id,
1893
            $sourceVersion->versionNo
1894
        );
1895
1896
        if ($sourceVersion->status !== APIVersionInfo::STATUS_DRAFT) {
1897
            throw new BadStateException(
1898
                '$sourceVersion',
1899
                'Relations of type common can only be removed from versions of status draft'
1900
            );
1901
        }
1902
1903
        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...
1904
            throw new UnauthorizedException('content', 'edit', array('contentId' => $sourceVersion->contentInfo->id));
1905
        }
1906
1907
        $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations(
1908
            $sourceVersion->getContentInfo()->id,
1909
            $sourceVersion->versionNo,
1910
            APIRelation::COMMON
1911
        );
1912
1913
        if (empty($spiRelations)) {
1914
            throw new InvalidArgumentException(
1915
                '$sourceVersion',
1916
                'There are no relations of type COMMON for the given destination'
1917
            );
1918
        }
1919
1920
        // there should be only one relation of type COMMON for each destination,
1921
        // but in case there were ever more then one, we will remove them all
1922
        // @todo: alternatively, throw BadStateException?
1923
        $this->repository->beginTransaction();
1924
        try {
1925
            foreach ($spiRelations as $spiRelation) {
1926
                if ($spiRelation->destinationContentId == $destinationContent->id) {
1927
                    $this->persistenceHandler->contentHandler()->removeRelation(
1928
                        $spiRelation->id,
1929
                        APIRelation::COMMON
1930
                    );
1931
                }
1932
            }
1933
            $this->repository->commit();
1934
        } catch (Exception $e) {
1935
            $this->repository->rollback();
1936
            throw $e;
1937
        }
1938
    }
1939
1940
    /**
1941
     * Adds translation information to the content object.
1942
     *
1943
     * @example Examples/translation_5x.php
1944
     *
1945
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed add a translation info
1946
     *
1947
     * @param \eZ\Publish\API\Repository\Values\Content\TranslationInfo $translationInfo
1948
     *
1949
     * @since 5.0
1950
     */
1951
    public function addTranslationInfo(TranslationInfo $translationInfo)
1952
    {
1953
        throw new NotImplementedException(__METHOD__);
1954
    }
1955
1956
    /**
1957
     * lists the translations done on this content object.
1958
     *
1959
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed read translation infos
1960
     *
1961
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
1962
     * @param array $filter
1963
     *
1964
     * @todo TBD - filter by source version destination version and languages
1965
     *
1966
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo[]
1967
     *
1968
     * @since 5.0
1969
     */
1970
    public function loadTranslationInfos(ContentInfo $contentInfo, array $filter = array())
1971
    {
1972
        throw new NotImplementedException(__METHOD__);
1973
    }
1974
1975
    /**
1976
     * Instantiates a new content create struct object.
1977
     *
1978
     * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable
1979
     *
1980
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1981
     * @param string $mainLanguageCode
1982
     *
1983
     * @return \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct
1984
     */
1985
    public function newContentCreateStruct(ContentType $contentType, $mainLanguageCode)
1986
    {
1987
        return new ContentCreateStruct(
1988
            array(
1989
                'contentType' => $contentType,
1990
                'mainLanguageCode' => $mainLanguageCode,
1991
                'alwaysAvailable' => $contentType->defaultAlwaysAvailable,
1992
            )
1993
        );
1994
    }
1995
1996
    /**
1997
     * Instantiates a new content meta data update struct.
1998
     *
1999
     * @return \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct
2000
     */
2001
    public function newContentMetadataUpdateStruct()
2002
    {
2003
        return new ContentMetadataUpdateStruct();
2004
    }
2005
2006
    /**
2007
     * Instantiates a new content update struct.
2008
     *
2009
     * @return \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct
2010
     */
2011
    public function newContentUpdateStruct()
2012
    {
2013
        return new ContentUpdateStruct();
2014
    }
2015
2016
    /**
2017
     * Instantiates a new TranslationInfo object.
2018
     *
2019
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationInfo
2020
     */
2021
    public function newTranslationInfo()
2022
    {
2023
        return new TranslationInfo();
2024
    }
2025
2026
    /**
2027
     * Instantiates a Translation object.
2028
     *
2029
     * @return \eZ\Publish\API\Repository\Values\Content\TranslationValues
2030
     */
2031
    public function newTranslationValues()
2032
    {
2033
        return new TranslationValues();
2034
    }
2035
}
2036