Completed
Push — EZP-30997 ( 014d9d...2cfc5a )
by
unknown
16:02
created

testLoadContentByRemoteIdWithSecondParameter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the ContentServiceTest 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\API\Repository\Tests;
10
11
use eZ\Publish\API\Repository\Exceptions\BadStateException;
12
use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException;
13
use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException;
14
use eZ\Publish\API\Repository\Values\Content\Content;
15
use eZ\Publish\API\Repository\Exceptions\UnauthorizedException;
16
use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct;
17
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
18
use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct;
19
use eZ\Publish\API\Repository\Values\Content\Field;
20
use eZ\Publish\API\Repository\Values\Content\Location;
21
use eZ\Publish\API\Repository\Values\Content\DraftList\Item\UnauthorizedContentDraftListItem;
22
use eZ\Publish\API\Repository\Values\Content\URLAlias;
23
use eZ\Publish\API\Repository\Values\Content\Relation;
24
use eZ\Publish\API\Repository\Values\Content\VersionInfo;
25
use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation;
26
use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation;
27
use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation;
28
use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation;
29
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
30
use DOMDocument;
31
use Exception;
32
use eZ\Publish\API\Repository\Values\User\User;
33
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException as CoreUnauthorizedException;
34
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
35
use InvalidArgumentException;
36
37
/**
38
 * Test case for operations in the ContentService using in memory storage.
39
 *
40
 * @see \eZ\Publish\API\Repository\ContentService
41
 * @group content
42
 */
43
class ContentServiceTest extends BaseContentServiceTest
44
{
45
    private const ADMINISTRATORS_USER_GROUP_NAME = 'Administrators';
46
    private const ADMINISTRATORS_USER_GROUP_ID = 12;
47
    private const ADMINISTRATORS_USER_GROUP_LOCATION_ID = 13;
48
49
    private const WRITERS_USER_GROUP_NAME = 'Writers';
50
51
    private const MEMBERS_USER_GROUP_ID = 11;
52
53
    private const MEDIA_CONTENT_ID = 41;
54
55
    private const MEDIA_REMOTE_ID = 'a6e35cbcb7cd6ae4b691f3eee30cd262';
56
    private const DEMO_DESIGN_REMOTE_ID = '8b8b22fe3c6061ed500fbd2b377b885f';
57
58
    private const FORUM_IDENTIFIER = 'forum';
59
60
    private const ENG_US = 'eng-US';
61
    private const GER_DE = 'ger-DE';
62
    private const ENG_GB = 'eng-GB';
63
64
    /** @var \eZ\Publish\API\Repository\PermissionResolver */
65
    private $permissionResolver;
66
67
    /** @var \eZ\Publish\API\Repository\ContentService */
68
    private $contentService;
69
70
    /** @var \eZ\Publish\API\Repository\LocationService */
71
    private $locationService;
72
73
    public function setUp(): void
74
    {
75
        parent::setUp();
76
77
        $repository = $this->getRepository();
78
        $this->permissionResolver = $repository->getPermissionResolver();
79
        $this->contentService = $repository->getContentService();
80
        $this->locationService = $repository->getLocationService();
81
    }
82
83
    /**
84
     * Test for the newContentCreateStruct() method.
85
     *
86
     * @see \eZ\Publish\API\Repository\ContentService::newContentCreateStruct()
87
     * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier
88
     * @group user
89
     * @group field-type
90
     */
91
    public function testNewContentCreateStruct()
92
    {
93
        $contentTypeService = $this->getRepository()->getContentTypeService();
94
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
95
96
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
97
98
        $this->assertInstanceOf(ContentCreateStruct::class, $contentCreate);
99
    }
100
101
    /**
102
     * Test for the createContent() method.
103
     *
104
     * @return \eZ\Publish\API\Repository\Values\Content\Content
105
     *
106
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
107
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentCreateStruct
108
     * @group user
109
     * @group field-type
110
     */
111
    public function testCreateContent()
112
    {
113
        if ($this->isVersion4()) {
114
            $this->markTestSkipped('This test requires eZ Publish 5');
115
        }
116
117
        $contentTypeService = $this->getRepository()->getContentTypeService();
118
119
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
120
121
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
122
        $contentCreate->setField('name', 'My awesome forum');
123
124
        $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
125
        $contentCreate->alwaysAvailable = true;
126
127
        $content = $this->contentService->createContent($contentCreate);
128
129
        $this->assertInstanceOf(Content::class, $content);
130
131
        return $content;
132
    }
133
134
    /**
135
     * Test for the createContent() method.
136
     *
137
     * Tests made for issue #EZP-20955 where Anonymous user is granted access to create content
138
     * and should have access to do that.
139
     *
140
     * @return \eZ\Publish\API\Repository\Values\Content\Content
141
     *
142
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
143
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentCreateStruct
144
     * @group user
145
     * @group field-type
146
     */
147
    public function testCreateContentAndPublishWithPrivilegedAnonymousUser()
148
    {
149
        if ($this->isVersion4()) {
150
            $this->markTestSkipped('This test requires eZ Publish 5');
151
        }
152
153
        $anonymousUserId = $this->generateId('user', 10);
154
155
        $repository = $this->getRepository();
156
        $contentTypeService = $this->getRepository()->getContentTypeService();
157
        $roleService = $repository->getRoleService();
158
159
        // Give Anonymous user role additional rights
160
        $role = $roleService->loadRoleByIdentifier('Anonymous');
161
        $roleDraft = $roleService->createRoleDraft($role);
162
        $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'create');
163
        $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]]));
164
        $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]]));
165
        $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]]));
166
        $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct);
167
168
        $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'publish');
169
        $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]]));
170
        $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]]));
171
        $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]]));
172
        $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct);
173
        $roleService->publishRoleDraft($roleDraft);
174
175
        // Set Anonymous user as current
176
        $repository->getPermissionResolver()->setCurrentUserReference($repository->getUserService()->loadUser($anonymousUserId));
177
178
        // Create a new content object:
179
        $contentCreate = $this->contentService->newContentCreateStruct(
180
            $contentTypeService->loadContentTypeByIdentifier('folder'),
181
            self::ENG_GB
182
        );
183
184
        $contentCreate->setField('name', 'Folder 1');
185
186
        $content = $this->contentService->createContent(
187
            $contentCreate,
188
            [$this->locationService->newLocationCreateStruct(2)]
189
        );
190
191
        $this->contentService->publishVersion(
192
            $content->getVersionInfo()
193
        );
194
    }
195
196
    /**
197
     * Test for the createContent() method.
198
     *
199
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
200
     *
201
     * @return \eZ\Publish\API\Repository\Values\Content\Content
202
     *
203
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
204
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
205
     */
206
    public function testCreateContentSetsContentInfo($content)
207
    {
208
        $this->assertInstanceOf(ContentInfo::class, $content->contentInfo);
209
210
        return $content;
211
    }
212
213
    /**
214
     * Test for the createContent() method.
215
     *
216
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
217
     *
218
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
219
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentSetsContentInfo
220
     */
221
    public function testCreateContentSetsExpectedContentInfo($content)
222
    {
223
        $this->assertEquals(
224
            [
225
                $content->id,
226
                28, // id of content type "forum"
227
                true,
228
                1,
229
                'abcdef0123456789abcdef0123456789',
230
                self::ENG_US,
231
                $this->getRepository()->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

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...
232
                false,
233
                null,
234
                // Main Location id for unpublished Content should be null
235
                null,
236
            ],
237
            [
238
                $content->contentInfo->id,
239
                $content->contentInfo->contentTypeId,
240
                $content->contentInfo->alwaysAvailable,
241
                $content->contentInfo->currentVersionNo,
242
                $content->contentInfo->remoteId,
243
                $content->contentInfo->mainLanguageCode,
244
                $content->contentInfo->ownerId,
245
                $content->contentInfo->published,
246
                $content->contentInfo->publishedDate,
247
                $content->contentInfo->mainLocationId,
248
            ]
249
        );
250
    }
251
252
    /**
253
     * Test for the createContent() method.
254
     *
255
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
256
     *
257
     * @return \eZ\Publish\API\Repository\Values\Content\Content
258
     *
259
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
260
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
261
     */
262
    public function testCreateContentSetsVersionInfo($content)
263
    {
264
        $this->assertInstanceOf(VersionInfo::class, $content->getVersionInfo());
265
266
        return $content;
267
    }
268
269
    /**
270
     * Test for the createContent() method.
271
     *
272
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
273
     *
274
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
275
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentSetsVersionInfo
276
     */
277
    public function testCreateContentSetsExpectedVersionInfo($content)
278
    {
279
        $this->assertEquals(
280
            [
281
                'status' => VersionInfo::STATUS_DRAFT,
282
                'versionNo' => 1,
283
                'creatorId' => $this->getRepository()->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

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...
284
                'initialLanguageCode' => self::ENG_US,
285
            ],
286
            [
287
                'status' => $content->getVersionInfo()->status,
288
                'versionNo' => $content->getVersionInfo()->versionNo,
289
                'creatorId' => $content->getVersionInfo()->creatorId,
290
                'initialLanguageCode' => $content->getVersionInfo()->initialLanguageCode,
291
            ]
292
        );
293
        $this->assertTrue($content->getVersionInfo()->isDraft());
294
        $this->assertFalse($content->getVersionInfo()->isPublished());
295
        $this->assertFalse($content->getVersionInfo()->isArchived());
296
    }
297
298
    /**
299
     * Test for the createContent() method.
300
     *
301
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
302
     *
303
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
304
     * @depends testCreateContent
305
     */
306
    public function testCreateContentSetsExpectedContentType($content)
307
    {
308
        $contentType = $content->getContentType();
309
310
        $this->assertEquals(
311
            [
312
                $contentType->id,
313
                // Won't match as it's set to true in createContentDraftVersion1()
314
                //$contentType->defaultAlwaysAvailable,
315
                //$contentType->defaultSortField,
316
                //$contentType->defaultSortOrder,
317
            ],
318
            [
319
                $content->contentInfo->contentTypeId,
320
                //$content->contentInfo->alwaysAvailable,
321
                //$location->sortField,
322
                //$location->sortOrder,
323
            ]
324
        );
325
    }
326
327
    /**
328
     * Test for the createContent() method.
329
     *
330
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
331
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
332
     */
333
    public function testCreateContentThrowsInvalidArgumentException()
334
    {
335
        if ($this->isVersion4()) {
336
            $this->markTestSkipped('This test requires eZ Publish 5');
337
        }
338
339
        $contentTypeService = $this->getRepository()->getContentTypeService();
340
341
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
342
343
        $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
344
        $contentCreate1->setField('name', 'An awesome Sidelfingen forum');
345
346
        $contentCreate1->remoteId = 'abcdef0123456789abcdef0123456789';
347
        $contentCreate1->alwaysAvailable = true;
348
349
        $draft = $this->contentService->createContent($contentCreate1);
350
        $this->contentService->publishVersion($draft->versionInfo);
351
352
        $contentCreate2 = $this->contentService->newContentCreateStruct($contentType, self::ENG_GB);
353
        $contentCreate2->setField('name', 'An awesome Bielefeld forum');
354
355
        $contentCreate2->remoteId = 'abcdef0123456789abcdef0123456789';
356
        $contentCreate2->alwaysAvailable = false;
357
358
        $this->expectException(APIInvalidArgumentException::class);
359
        $this->contentService->createContent($contentCreate2);
360
    }
361
362
    /**
363
     * Test for the createContent() method.
364
     *
365
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
366
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
367
     */
368
    public function testCreateContentThrowsInvalidArgumentExceptionOnFieldTypeNotAccept()
369
    {
370
        $contentTypeService = $this->getRepository()->getContentTypeService();
371
372
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
373
374
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
375
        // The name field does only accept strings and null as its values
376
        $contentCreate->setField('name', new \stdClass());
377
378
        $this->expectException(APIInvalidArgumentException::class);
379
        $this->contentService->createContent($contentCreate);
380
    }
381
382
    /**
383
     * Test for the createContent() method.
384
     *
385
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
386
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
387
     */
388
    public function testCreateContentThrowsContentFieldValidationException()
389
    {
390
        $contentTypeService = $this->getRepository()->getContentTypeService();
391
392
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
393
394
        $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
395
        $contentCreate1->setField('name', 'An awesome Sidelfingen folder');
396
        // Violates string length constraint
397
        $contentCreate1->setField('short_name', str_repeat('a', 200));
398
399
        $this->expectException(ContentFieldValidationException::class);
400
401
        // Throws ContentFieldValidationException, since short_name does not pass validation of the string length validator
402
        $this->contentService->createContent($contentCreate1);
403
    }
404
405
    /**
406
     * Test for the createContent() method.
407
     *
408
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
409
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
410
     */
411
    public function testCreateContentRequiredFieldMissing()
412
    {
413
        $contentTypeService = $this->getRepository()->getContentTypeService();
414
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
415
416
        $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
417
        // Required field "name" is not set
418
419
        $this->expectException(ContentFieldValidationException::class);
420
421
        // Throws a ContentFieldValidationException, since a required field is missing
422
        $this->contentService->createContent($contentCreate1);
423
    }
424
425
    /**
426
     * Test for the createContent() method.
427
     *
428
     * NOTE: We have bidirectional dependencies between the ContentService and
429
     * the LocationService, so that we cannot use PHPUnit's test dependencies
430
     * here.
431
     *
432
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
433
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation
434
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationByRemoteId
435
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
436
     * @group user
437
     */
438
    public function testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately()
439
    {
440
        $this->createContentDraftVersion1();
441
442
        $this->expectException(NotFoundException::class);
443
444
        // The location will not have been created, yet, so this throws an exception
445
        $this->locationService->loadLocationByRemoteId('0123456789abcdef0123456789abcdef');
446
    }
447
448
    /**
449
     * Test for the createContent() method.
450
     *
451
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
452
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
453
     */
454
    public function testCreateContentThrowsInvalidArgumentExceptionWithLocationCreateParameter()
455
    {
456
        $parentLocationId = $this->generateId('location', 56);
457
        // $parentLocationId is a valid location ID
458
459
        $contentTypeService = $this->getRepository()->getContentTypeService();
460
461
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
462
463
        // Configure new locations
464
        $locationCreate1 = $this->locationService->newLocationCreateStruct($parentLocationId);
465
466
        $locationCreate1->priority = 23;
467
        $locationCreate1->hidden = true;
468
        $locationCreate1->remoteId = '0123456789abcdef0123456789aaaaaa';
469
        $locationCreate1->sortField = Location::SORT_FIELD_NODE_ID;
470
        $locationCreate1->sortOrder = Location::SORT_ORDER_DESC;
471
472
        $locationCreate2 = $this->locationService->newLocationCreateStruct($parentLocationId);
473
474
        $locationCreate2->priority = 42;
475
        $locationCreate2->hidden = true;
476
        $locationCreate2->remoteId = '0123456789abcdef0123456789bbbbbb';
477
        $locationCreate2->sortField = Location::SORT_FIELD_NODE_ID;
478
        $locationCreate2->sortOrder = Location::SORT_ORDER_DESC;
479
480
        // Configure new content object
481
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
482
483
        $contentCreate->setField('name', 'A awesome Sindelfingen forum');
484
        $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
485
        $contentCreate->alwaysAvailable = true;
486
487
        // Create new content object under the specified location
488
        $draft = $this->contentService->createContent(
489
            $contentCreate,
490
            [$locationCreate1]
491
        );
492
        $this->contentService->publishVersion($draft->versionInfo);
493
494
        $this->expectException(APIInvalidArgumentException::class);
495
        // Content remoteId already exists,
496
        $this->contentService->createContent(
497
            $contentCreate,
498
            [$locationCreate2]
499
        );
500
    }
501
502
    /**
503
     * Test for the loadContentInfo() method.
504
     *
505
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfo()
506
     * @group user
507
     */
508
    public function testLoadContentInfo()
509
    {
510
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
511
512
        // Load the ContentInfo for "Media" folder
513
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
514
515
        $this->assertInstanceOf(ContentInfo::class, $contentInfo);
516
517
        return $contentInfo;
518
    }
519
520
    /**
521
     * Test for the returned value of the loadContentInfo() method.
522
     *
523
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
524
     * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo
525
     *
526
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
527
     */
528
    public function testLoadContentInfoSetsExpectedContentInfo(ContentInfo $contentInfo)
529
    {
530
        $this->assertPropertiesCorrectUnsorted(
531
            $this->getExpectedMediaContentInfoProperties(),
532
            $contentInfo
533
        );
534
    }
535
536
    /**
537
     * Test for the loadContentInfo() method.
538
     *
539
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfo()
540
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
541
     */
542
    public function testLoadContentInfoThrowsNotFoundException()
543
    {
544
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
545
546
        $this->expectException(NotFoundException::class);
547
548
        $this->contentService->loadContentInfo($nonExistentContentId);
549
    }
550
551
    /**
552
     * Test for the loadContentInfoList() method.
553
     *
554
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoList()
555
     */
556
    public function testLoadContentInfoList()
557
    {
558
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
559
        $list = $this->contentService->loadContentInfoList([$mediaFolderId]);
560
561
        $this->assertCount(1, $list);
562
        $this->assertEquals([$mediaFolderId], array_keys($list), 'Array key was not content id');
563
        $this->assertInstanceOf(
564
            ContentInfo::class,
565
            $list[$mediaFolderId]
566
        );
567
    }
568
569
    /**
570
     * Test for the loadContentInfoList() method.
571
     *
572
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoList()
573
     * @depends testLoadContentInfoList
574
     */
575
    public function testLoadContentInfoListSkipsNotFoundItems()
576
    {
577
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
578
        $list = $this->contentService->loadContentInfoList([$nonExistentContentId]);
579
580
        $this->assertCount(0, $list);
581
    }
582
583
    /**
584
     * Test for the loadContentInfoByRemoteId() method.
585
     *
586
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId()
587
     */
588
    public function testLoadContentInfoByRemoteId()
589
    {
590
        // Load the ContentInfo for "Media" folder
591
        $contentInfo = $this->contentService->loadContentInfoByRemoteId('faaeb9be3bd98ed09f606fc16d144eca');
592
593
        $this->assertInstanceOf(ContentInfo::class, $contentInfo);
594
595
        return $contentInfo;
596
    }
597
598
    /**
599
     * Test for the returned value of the loadContentInfoByRemoteId() method.
600
     *
601
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoByRemoteId
602
     * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId
603
     *
604
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
605
     */
606
    public function testLoadContentInfoByRemoteIdSetsExpectedContentInfo(ContentInfo $contentInfo)
607
    {
608
        $this->assertPropertiesCorrectUnsorted(
609
            [
610
                'id' => 10,
611
                'contentTypeId' => 4,
612
                'name' => 'Anonymous User',
613
                'sectionId' => 2,
614
                'currentVersionNo' => 2,
615
                'published' => true,
616
                'ownerId' => 14,
617
                'modificationDate' => $this->createDateTime(1072180405),
618
                'publishedDate' => $this->createDateTime(1033920665),
619
                'alwaysAvailable' => 1,
620
                'remoteId' => 'faaeb9be3bd98ed09f606fc16d144eca',
621
                'mainLanguageCode' => self::ENG_US,
622
                'mainLocationId' => 45,
623
            ],
624
            $contentInfo
625
        );
626
    }
627
628
    /**
629
     * Test for the loadContentInfoByRemoteId() method.
630
     *
631
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId()
632
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoByRemoteId
633
     */
634
    public function testLoadContentInfoByRemoteIdThrowsNotFoundException()
635
    {
636
        $this->expectException(NotFoundException::class);
637
638
        $this->contentService->loadContentInfoByRemoteId('abcdefghijklmnopqrstuvwxyz0123456789');
639
    }
640
641
    /**
642
     * Test for the loadVersionInfo() method.
643
     *
644
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo()
645
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
646
     * @group user
647
     */
648
    public function testLoadVersionInfo()
649
    {
650
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
651
        // $mediaFolderId contains the ID of the "Media" folder
652
653
        // Load the ContentInfo for "Media" folder
654
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
655
656
        // Now load the current version info of the "Media" folder
657
        $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
658
659
        $this->assertInstanceOf(
660
            VersionInfo::class,
661
            $versionInfo
662
        );
663
    }
664
665
    /**
666
     * Test for the loadVersionInfoById() method.
667
     *
668
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById()
669
     */
670
    public function testLoadVersionInfoById()
671
    {
672
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
673
        // $mediaFolderId contains the ID of the "Media" folder
674
675
        // Load the VersionInfo for "Media" folder
676
        $versionInfo = $this->contentService->loadVersionInfoById($mediaFolderId);
677
678
        $this->assertInstanceOf(
679
            VersionInfo::class,
680
            $versionInfo
681
        );
682
683
        return $versionInfo;
684
    }
685
686
    /**
687
     * Test for the returned value of the loadVersionInfoById() method.
688
     *
689
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById
690
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
691
     *
692
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
693
     */
694
    public function testLoadVersionInfoByIdSetsExpectedVersionInfo(VersionInfo $versionInfo)
695
    {
696
        $this->assertPropertiesCorrect(
697
            [
698
                'names' => [
699
                    self::ENG_US => 'Media',
700
                ],
701
                'contentInfo' => new ContentInfo($this->getExpectedMediaContentInfoProperties()),
702
                'id' => 472,
703
                'versionNo' => 1,
704
                'modificationDate' => $this->createDateTime(1060695457),
705
                'creatorId' => 14,
706
                'creationDate' => $this->createDateTime(1060695450),
707
                'status' => VersionInfo::STATUS_PUBLISHED,
708
                'initialLanguageCode' => self::ENG_US,
709
                'languageCodes' => [
710
                    self::ENG_US,
711
                ],
712
            ],
713
            $versionInfo
714
        );
715
        $this->assertTrue($versionInfo->isPublished());
716
        $this->assertFalse($versionInfo->isDraft());
717
        $this->assertFalse($versionInfo->isArchived());
718
    }
719
720
    /**
721
     * Test for the loadVersionInfoById() method.
722
     *
723
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById()
724
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById
725
     */
726
    public function testLoadVersionInfoByIdThrowsNotFoundException()
727
    {
728
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
729
730
        $this->expectException(NotFoundException::class);
731
732
        $this->contentService->loadVersionInfoById($nonExistentContentId);
733
    }
734
735
    /**
736
     * Test for the loadContentByContentInfo() method.
737
     *
738
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo()
739
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
740
     */
741
    public function testLoadContentByContentInfo()
742
    {
743
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
744
        // $mediaFolderId contains the ID of the "Media" folder
745
746
        // Load the ContentInfo for "Media" folder
747
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
748
749
        // Now load the current content version for the info instance
750
        $content = $this->contentService->loadContentByContentInfo($contentInfo);
751
752
        $this->assertInstanceOf(
753
            Content::class,
754
            $content
755
        );
756
    }
757
758
    /**
759
     * Test for the loadContentByVersionInfo() method.
760
     *
761
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo()
762
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
763
     */
764
    public function testLoadContentByVersionInfo()
765
    {
766
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
767
        // $mediaFolderId contains the ID of the "Media" folder
768
769
        // Load the ContentInfo for "Media" folder
770
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
771
772
        // Load the current VersionInfo
773
        $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
774
775
        // Now load the current content version for the info instance
776
        $content = $this->contentService->loadContentByVersionInfo($versionInfo);
777
778
        $this->assertInstanceOf(
779
            Content::class,
780
            $content
781
        );
782
    }
783
784
    /**
785
     * Test for the loadContent() method.
786
     *
787
     * @see \eZ\Publish\API\Repository\ContentService::loadContent()
788
     * @group user
789
     * @group field-type
790
     */
791
    public function testLoadContent()
792
    {
793
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
794
        // $mediaFolderId contains the ID of the "Media" folder
795
796
        // Load the Content for "Media" folder, any language and current version
797
        $content = $this->contentService->loadContent($mediaFolderId);
798
799
        $this->assertInstanceOf(
800
            Content::class,
801
            $content
802
        );
803
    }
804
805
    /**
806
     * Test for the loadContent() method.
807
     *
808
     * @see \eZ\Publish\API\Repository\ContentService::loadContent()
809
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
810
     */
811
    public function testLoadContentThrowsNotFoundException()
812
    {
813
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
814
815
        $this->expectException(NotFoundException::class);
816
817
        $this->contentService->loadContent($nonExistentContentId);
818
    }
819
820
    /**
821
     * Data provider for testLoadContentByRemoteId().
822
     *
823
     * @return array
824
     */
825
    public function contentRemoteIdVersionLanguageProvider()
826
    {
827
        return [
828
            ['f5c88a2209584891056f987fd965b0ba', null, null],
829
            ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], null],
830
            ['f5c88a2209584891056f987fd965b0ba', null, 1],
831
            ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], 1],
832
            [self::MEDIA_REMOTE_ID, null, null],
833
            [self::MEDIA_REMOTE_ID, [self::ENG_US], null],
834
            [self::MEDIA_REMOTE_ID, null, 1],
835
            [self::MEDIA_REMOTE_ID, [self::ENG_US], 1],
836
        ];
837
    }
838
839
    /**
840
     * Test for the loadContentByRemoteId() method.
841
     *
842
     * @covers \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId
843
     * @dataProvider contentRemoteIdVersionLanguageProvider
844
     *
845
     * @param string $remoteId
846
     * @param array|null $languages
847
     * @param int $versionNo
848
     */
849
    public function testLoadContentByRemoteId($remoteId, $languages, $versionNo)
850
    {
851
        $content = $this->contentService->loadContentByRemoteId($remoteId, $languages, $versionNo);
852
853
        $this->assertInstanceOf(
854
            Content::class,
855
            $content
856
        );
857
858
        $this->assertEquals($remoteId, $content->contentInfo->remoteId);
859
        if ($languages !== null) {
860
            $this->assertEquals($languages, $content->getVersionInfo()->languageCodes);
861
        }
862
        $this->assertEquals($versionNo ?: 1, $content->getVersionInfo()->versionNo);
863
    }
864
865
    /**
866
     * Test for the loadContentByRemoteId() method.
867
     *
868
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId()
869
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteId
870
     */
871
    public function testLoadContentByRemoteIdThrowsNotFoundException()
872
    {
873
        $this->expectException(NotFoundException::class);
874
875
        // This call will fail with a "NotFoundException", because no content object exists for the given remoteId
876
        $this->contentService->loadContentByRemoteId('a1b1c1d1e1f1a2b2c2d2e2f2a3b3c3d3');
877
    }
878
879
    /**
880
     * Test for the publishVersion() method.
881
     *
882
     * @return \eZ\Publish\API\Repository\Values\Content\Content
883
     *
884
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
885
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
886
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
887
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
888
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
889
     * @group user
890
     * @group field-type
891
     */
892
    public function testPublishVersion()
893
    {
894
        $time = time();
895
        $content = $this->createContentVersion1();
896
897
        $this->assertInstanceOf(Content::class, $content);
898
        $this->assertTrue($content->contentInfo->published);
899
        $this->assertEquals(VersionInfo::STATUS_PUBLISHED, $content->versionInfo->status);
900
        $this->assertGreaterThanOrEqual($time, $content->contentInfo->publishedDate->getTimestamp());
901
        $this->assertGreaterThanOrEqual($time, $content->contentInfo->modificationDate->getTimestamp());
902
        $this->assertTrue($content->versionInfo->isPublished());
903
        $this->assertFalse($content->versionInfo->isDraft());
904
        $this->assertFalse($content->versionInfo->isArchived());
905
906
        return $content;
907
    }
908
909
    /**
910
     * Test for the publishVersion() method.
911
     *
912
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
913
     *
914
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
915
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
916
     */
917
    public function testPublishVersionSetsExpectedContentInfo($content)
918
    {
919
        $this->assertEquals(
920
            [
921
                $content->id,
922
                true,
923
                1,
924
                'abcdef0123456789abcdef0123456789',
925
                self::ENG_US,
926
                $this->getRepository()->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

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...
927
                true,
928
            ],
929
            [
930
                $content->contentInfo->id,
931
                $content->contentInfo->alwaysAvailable,
932
                $content->contentInfo->currentVersionNo,
933
                $content->contentInfo->remoteId,
934
                $content->contentInfo->mainLanguageCode,
935
                $content->contentInfo->ownerId,
936
                $content->contentInfo->published,
937
            ]
938
        );
939
940
        $this->assertNotNull($content->contentInfo->mainLocationId);
941
        $date = new \DateTime('1984/01/01');
942
        $this->assertGreaterThan(
943
            $date->getTimestamp(),
944
            $content->contentInfo->publishedDate->getTimestamp()
945
        );
946
    }
947
948
    /**
949
     * Test for the publishVersion() method.
950
     *
951
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
952
     *
953
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
954
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
955
     */
956
    public function testPublishVersionSetsExpectedVersionInfo($content)
957
    {
958
        $this->assertEquals(
959
            [
960
                $this->getRepository()->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

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...
961
                self::ENG_US,
962
                VersionInfo::STATUS_PUBLISHED,
963
                1,
964
            ],
965
            [
966
                $content->getVersionInfo()->creatorId,
967
                $content->getVersionInfo()->initialLanguageCode,
968
                $content->getVersionInfo()->status,
969
                $content->getVersionInfo()->versionNo,
970
            ]
971
        );
972
973
        $date = new \DateTime('1984/01/01');
974
        $this->assertGreaterThan(
975
            $date->getTimestamp(),
976
            $content->getVersionInfo()->modificationDate->getTimestamp()
977
        );
978
979
        $this->assertNotNull($content->getVersionInfo()->modificationDate);
980
        $this->assertTrue($content->getVersionInfo()->isPublished());
981
        $this->assertFalse($content->getVersionInfo()->isDraft());
982
        $this->assertFalse($content->getVersionInfo()->isArchived());
983
    }
984
985
    /**
986
     * Test for the publishVersion() method.
987
     *
988
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
989
     *
990
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
991
     * @depends testPublishVersion
992
     */
993
    public function testPublishVersionSetsExpectedContentType($content)
994
    {
995
        $contentType = $content->getContentType();
996
997
        $this->assertEquals(
998
            [
999
                $contentType->id,
1000
                // won't be a match as it's set to true in createContentDraftVersion1()
1001
                //$contentType->defaultAlwaysAvailable,
1002
                //$contentType->defaultSortField,
1003
                //$contentType->defaultSortOrder,
1004
            ],
1005
            [
1006
                $content->contentInfo->contentTypeId,
1007
                //$content->contentInfo->alwaysAvailable,
1008
                //$location->sortField,
1009
                //$location->sortOrder,
1010
            ]
1011
        );
1012
    }
1013
1014
    /**
1015
     * Test for the publishVersion() method.
1016
     *
1017
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1018
     *
1019
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1020
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
1021
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1022
     */
1023
    public function testPublishVersionCreatesLocationsDefinedOnCreate()
1024
    {
1025
        $content = $this->createContentVersion1();
1026
1027
        $location = $this->locationService->loadLocationByRemoteId(
1028
            '0123456789abcdef0123456789abcdef'
1029
        );
1030
1031
        $this->assertEquals(
1032
            $location->getContentInfo(),
1033
            $content->getVersionInfo()->getContentInfo()
1034
        );
1035
1036
        return [$content, $location];
1037
    }
1038
1039
    /**
1040
     * Test for the publishVersion() method.
1041
     *
1042
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1043
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionCreatesLocationsDefinedOnCreate
1044
     */
1045
    public function testCreateContentWithLocationCreateParameterCreatesExpectedLocation(array $testData)
1046
    {
1047
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */
1048
        /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */
1049
        list($content, $location) = $testData;
1050
1051
        $parentLocationId = $this->generateId('location', 56);
1052
        $parentLocation = $this->getRepository()->getLocationService()->loadLocation($parentLocationId);
1053
        $mainLocationId = $content->getVersionInfo()->getContentInfo()->mainLocationId;
1054
1055
        $this->assertPropertiesCorrect(
1056
            [
1057
                'id' => $mainLocationId,
1058
                'priority' => 23,
1059
                'hidden' => true,
1060
                'invisible' => true,
1061
                'remoteId' => '0123456789abcdef0123456789abcdef',
1062
                'parentLocationId' => $parentLocationId,
1063
                'pathString' => $parentLocation->pathString . $mainLocationId . '/',
1064
                'depth' => $parentLocation->depth + 1,
1065
                'sortField' => Location::SORT_FIELD_NODE_ID,
1066
                'sortOrder' => Location::SORT_ORDER_DESC,
1067
            ],
1068
            $location
1069
        );
1070
    }
1071
1072
    /**
1073
     * Test for the publishVersion() method.
1074
     *
1075
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1076
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1077
     */
1078
    public function testPublishVersionThrowsBadStateException()
1079
    {
1080
        $draft = $this->createContentDraftVersion1();
1081
1082
        // Publish the content draft
1083
        $this->contentService->publishVersion($draft->getVersionInfo());
1084
1085
        $this->expectException(BadStateException::class);
1086
1087
        // This call will fail with a "BadStateException", because the version is already published.
1088
        $this->contentService->publishVersion($draft->getVersionInfo());
1089
    }
1090
1091
    /**
1092
     * Test that publishVersion() does not affect publishedDate (assuming previous version exists).
1093
     *
1094
     * @covers \eZ\Publish\API\Repository\ContentService::publishVersion
1095
     */
1096
    public function testPublishVersionDoesNotChangePublishedDate()
1097
    {
1098
        $publishedContent = $this->createContentVersion1();
1099
1100
        // force timestamps to differ
1101
        sleep(1);
1102
1103
        $contentDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
1104
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1105
        $contentUpdateStruct->setField('name', 'New name');
1106
        $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct);
1107
        $republishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
1108
1109
        $this->assertEquals(
1110
            $publishedContent->contentInfo->publishedDate->getTimestamp(),
1111
            $republishedContent->contentInfo->publishedDate->getTimestamp()
1112
        );
1113
        $this->assertGreaterThan(
1114
            $publishedContent->contentInfo->modificationDate->getTimestamp(),
1115
            $republishedContent->contentInfo->modificationDate->getTimestamp()
1116
        );
1117
    }
1118
1119
    /**
1120
     * Test for the createContentDraft() method.
1121
     *
1122
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1123
     *
1124
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1125
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1126
     * @group user
1127
     */
1128
    public function testCreateContentDraft()
1129
    {
1130
        $content = $this->createContentVersion1();
1131
1132
        // Now we create a new draft from the published content
1133
        $draftedContent = $this->contentService->createContentDraft($content->contentInfo);
1134
1135
        $this->assertInstanceOf(
1136
            Content::class,
1137
            $draftedContent
1138
        );
1139
1140
        return $draftedContent;
1141
    }
1142
1143
    /**
1144
     * Test for the createContentDraft() method.
1145
     *
1146
     * Test that editor has access to edit own draft.
1147
     * Note: Editors have access to version_read, which is needed to load content drafts.
1148
     *
1149
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1150
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1151
     * @group user
1152
     */
1153
    public function testCreateContentDraftAndLoadAccess()
1154
    {
1155
        $user = $this->createUserVersion1();
1156
1157
        // Set new editor as user
1158
        $this->permissionResolver->setCurrentUserReference($user);
1159
1160
        // Create draft
1161
        $draft = $this->createContentDraftVersion1(2, 'folder');
1162
1163
        // Try to load the draft
1164
        $loadedDraft = $this->contentService->loadContent($draft->id);
1165
1166
        $this->assertEquals($draft->id, $loadedDraft->id);
1167
    }
1168
1169
    /**
1170
     * Test for the createContentDraft() method.
1171
     *
1172
     * @param \eZ\Publish\API\Repository\Values\Content\Content $draft
1173
     *
1174
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1175
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1176
     */
1177
    public function testCreateContentDraftSetsExpectedProperties($draft)
1178
    {
1179
        $this->assertEquals(
1180
            [
1181
                'fieldCount' => 2,
1182
                'relationCount' => 0,
1183
            ],
1184
            [
1185
                'fieldCount' => count($draft->getFields()),
1186
                'relationCount' => count($this->getRepository()->getContentService()->loadRelations($draft->getVersionInfo())),
1187
            ]
1188
        );
1189
    }
1190
1191
    /**
1192
     * Test for the createContentDraft() method.
1193
     *
1194
     * @param \eZ\Publish\API\Repository\Values\Content\Content $draft
1195
     *
1196
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1197
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1198
     */
1199
    public function testCreateContentDraftSetsContentInfo($draft)
1200
    {
1201
        $contentInfo = $draft->contentInfo;
1202
1203
        $this->assertEquals(
1204
            [
1205
                $draft->id,
1206
                true,
1207
                1,
1208
                self::ENG_US,
1209
                $this->getRepository()->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

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...
1210
                'abcdef0123456789abcdef0123456789',
1211
                1,
1212
            ],
1213
            [
1214
                $contentInfo->id,
1215
                $contentInfo->alwaysAvailable,
1216
                $contentInfo->currentVersionNo,
1217
                $contentInfo->mainLanguageCode,
1218
                $contentInfo->ownerId,
1219
                $contentInfo->remoteId,
1220
                $contentInfo->sectionId,
1221
            ]
1222
        );
1223
    }
1224
1225
    /**
1226
     * Test for the createContentDraft() method.
1227
     *
1228
     * @param \eZ\Publish\API\Repository\Values\Content\Content $draft
1229
     *
1230
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1231
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1232
     */
1233
    public function testCreateContentDraftSetsVersionInfo($draft)
1234
    {
1235
        $versionInfo = $draft->getVersionInfo();
1236
1237
        $this->assertEquals(
1238
            [
1239
                'creatorId' => $this->getRepository()->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

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...
1240
                'initialLanguageCode' => self::ENG_US,
1241
                'languageCodes' => [0 => self::ENG_US],
1242
                'status' => VersionInfo::STATUS_DRAFT,
1243
                'versionNo' => 2,
1244
            ],
1245
            [
1246
                'creatorId' => $versionInfo->creatorId,
1247
                'initialLanguageCode' => $versionInfo->initialLanguageCode,
1248
                'languageCodes' => $versionInfo->languageCodes,
1249
                'status' => $versionInfo->status,
1250
                'versionNo' => $versionInfo->versionNo,
1251
            ]
1252
        );
1253
        $this->assertTrue($versionInfo->isDraft());
1254
        $this->assertFalse($versionInfo->isPublished());
1255
        $this->assertFalse($versionInfo->isArchived());
1256
    }
1257
1258
    /**
1259
     * Test for the createContentDraft() method.
1260
     *
1261
     * @param \eZ\Publish\API\Repository\Values\Content\Content $draft
1262
     *
1263
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1264
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1265
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
1266
     */
1267
    public function testCreateContentDraftLoadVersionInfoStillLoadsPublishedVersion($draft)
0 ignored issues
show
Unused Code introduced by
The parameter $draft is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1268
    {
1269
        $content = $this->createContentVersion1();
1270
1271
        // Now we create a new draft from the published content
1272
        $this->contentService->createContentDraft($content->contentInfo);
1273
1274
        // This call will still load the published version
1275
        $versionInfoPublished = $this->contentService->loadVersionInfo($content->contentInfo);
1276
1277
        $this->assertEquals(1, $versionInfoPublished->versionNo);
1278
    }
1279
1280
    /**
1281
     * Test for the createContentDraft() method.
1282
     *
1283
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1284
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
1285
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1286
     */
1287
    public function testCreateContentDraftLoadContentStillLoadsPublishedVersion()
1288
    {
1289
        $content = $this->createContentVersion1();
1290
1291
        // Now we create a new draft from the published content
1292
        $this->contentService->createContentDraft($content->contentInfo);
1293
1294
        // This call will still load the published content version
1295
        $contentPublished = $this->contentService->loadContent($content->id);
1296
1297
        $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo);
1298
    }
1299
1300
    /**
1301
     * Test for the createContentDraft() method.
1302
     *
1303
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1304
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteId
1305
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1306
     */
1307
    public function testCreateContentDraftLoadContentByRemoteIdStillLoadsPublishedVersion()
1308
    {
1309
        $content = $this->createContentVersion1();
1310
1311
        // Now we create a new draft from the published content
1312
        $this->contentService->createContentDraft($content->contentInfo);
1313
1314
        // This call will still load the published content version
1315
        $contentPublished = $this->contentService->loadContentByRemoteId('abcdef0123456789abcdef0123456789');
1316
1317
        $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo);
1318
    }
1319
1320
    /**
1321
     * Test for the createContentDraft() method.
1322
     *
1323
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1324
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo
1325
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1326
     */
1327
    public function testCreateContentDraftLoadContentByContentInfoStillLoadsPublishedVersion()
1328
    {
1329
        $content = $this->createContentVersion1();
1330
1331
        // Now we create a new draft from the published content
1332
        $this->contentService->createContentDraft($content->contentInfo);
1333
1334
        // This call will still load the published content version
1335
        $contentPublished = $this->contentService->loadContentByContentInfo($content->contentInfo);
1336
1337
        $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo);
1338
    }
1339
1340
    /**
1341
     * Test for the newContentUpdateStruct() method.
1342
     *
1343
     * @covers \eZ\Publish\API\Repository\ContentService::newContentUpdateStruct
1344
     * @group user
1345
     */
1346
    public function testNewContentUpdateStruct()
1347
    {
1348
        $updateStruct = $this->contentService->newContentUpdateStruct();
1349
1350
        $this->assertInstanceOf(
1351
            ContentUpdateStruct::class,
1352
            $updateStruct
1353
        );
1354
1355
        $this->assertPropertiesCorrect(
1356
            [
1357
                'initialLanguageCode' => null,
1358
                'fields' => [],
1359
            ],
1360
            $updateStruct
1361
        );
1362
    }
1363
1364
    /**
1365
     * Test for the updateContent() method.
1366
     *
1367
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1368
     *
1369
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1370
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentUpdateStruct
1371
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1372
     * @group user
1373
     * @group field-type
1374
     */
1375
    public function testUpdateContent()
1376
    {
1377
        $draftVersion2 = $this->createUpdatedDraftVersion2();
1378
1379
        $this->assertInstanceOf(
1380
            Content::class,
1381
            $draftVersion2
1382
        );
1383
1384
        $this->assertEquals(
1385
            $this->generateId('user', 10),
1386
            $draftVersion2->versionInfo->creatorId,
1387
            'creatorId is not properly set on new Version'
1388
        );
1389
1390
        return $draftVersion2;
1391
    }
1392
1393
    /**
1394
     * Test for the updateContent_WithDifferentUser() method.
1395
     *
1396
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1397
     *
1398
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1399
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentUpdateStruct
1400
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1401
     * @group user
1402
     * @group field-type
1403
     */
1404
    public function testUpdateContentWithDifferentUser()
1405
    {
1406
        $arrayWithDraftVersion2 = $this->createUpdatedDraftVersion2NotAdmin();
1407
1408
        $this->assertInstanceOf(
1409
            Content::class,
1410
            $arrayWithDraftVersion2[0]
1411
        );
1412
1413
        $this->assertEquals(
1414
            $this->generateId('user', $arrayWithDraftVersion2[1]),
1415
            $arrayWithDraftVersion2[0]->versionInfo->creatorId,
1416
            'creatorId is not properly set on new Version'
1417
        );
1418
1419
        return $arrayWithDraftVersion2[0];
1420
    }
1421
1422
    /**
1423
     * Test for the updateContent() method.
1424
     *
1425
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1426
     *
1427
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1428
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1429
     */
1430
    public function testUpdateContentSetsExpectedFields($content)
1431
    {
1432
        $actual = $this->normalizeFields($content->getFields());
1433
1434
        $expected = [
1435
            new Field(
1436
                [
1437
                    'id' => 0,
1438
                    'value' => true,
1439
                    'languageCode' => self::ENG_GB,
1440
                    'fieldDefIdentifier' => 'description',
1441
                    'fieldTypeIdentifier' => 'ezrichtext',
1442
                ]
1443
            ),
1444
            new Field(
1445
                [
1446
                    'id' => 0,
1447
                    'value' => true,
1448
                    'languageCode' => self::ENG_US,
1449
                    'fieldDefIdentifier' => 'description',
1450
                    'fieldTypeIdentifier' => 'ezrichtext',
1451
                ]
1452
            ),
1453
            new Field(
1454
                [
1455
                    'id' => 0,
1456
                    'value' => true,
1457
                    'languageCode' => self::ENG_GB,
1458
                    'fieldDefIdentifier' => 'name',
1459
                    'fieldTypeIdentifier' => 'ezstring',
1460
                ]
1461
            ),
1462
            new Field(
1463
                [
1464
                    'id' => 0,
1465
                    'value' => true,
1466
                    'languageCode' => self::ENG_US,
1467
                    'fieldDefIdentifier' => 'name',
1468
                    'fieldTypeIdentifier' => 'ezstring',
1469
                ]
1470
            ),
1471
        ];
1472
1473
        $this->assertEquals($expected, $actual);
1474
    }
1475
1476
    /**
1477
     * Test for the updateContent() method.
1478
     *
1479
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1480
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1481
     */
1482
    public function testUpdateContentThrowsBadStateException()
1483
    {
1484
        $content = $this->createContentVersion1();
1485
1486
        // Now create an update struct and modify some fields
1487
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1488
        $contentUpdateStruct->setField('title', 'An awesome² story about ezp.');
1489
        $contentUpdateStruct->setField('title', 'An awesome²³ story about ezp.', self::ENG_GB);
1490
1491
        $contentUpdateStruct->initialLanguageCode = self::ENG_US;
1492
1493
        $this->expectException(BadStateException::class);
1494
1495
        // This call will fail with a "BadStateException", because $publishedContent is not a draft.
1496
        $this->contentService->updateContent(
1497
            $content->getVersionInfo(),
1498
            $contentUpdateStruct
1499
        );
1500
    }
1501
1502
    /**
1503
     * Test for the updateContent() method.
1504
     *
1505
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1506
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1507
     */
1508
    public function testUpdateContentThrowsInvalidArgumentExceptionWhenFieldTypeDoesNotAccept()
1509
    {
1510
        $draft = $this->createContentDraftVersion1();
1511
1512
        // Now create an update struct and modify some fields
1513
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1514
        // The name field does not accept a stdClass object as its input
1515
        $contentUpdateStruct->setField('name', new \stdClass(), self::ENG_US);
1516
1517
        $this->expectException(APIInvalidArgumentException::class);
1518
        // is not accepted
1519
        $this->contentService->updateContent(
1520
            $draft->getVersionInfo(),
1521
            $contentUpdateStruct
1522
        );
1523
    }
1524
1525
    /**
1526
     * Test for the updateContent() method.
1527
     *
1528
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1529
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1530
     */
1531
    public function testUpdateContentWhenMandatoryFieldIsEmpty()
1532
    {
1533
        $draft = $this->createContentDraftVersion1();
1534
1535
        // Now create an update struct and set a mandatory field to null
1536
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1537
        $contentUpdateStruct->setField('name', null);
1538
1539
        // Don't set this, then the above call without languageCode will fail
1540
        $contentUpdateStruct->initialLanguageCode = self::ENG_US;
1541
1542
        $this->expectException(ContentFieldValidationException::class);
1543
1544
        // This call will fail with a "ContentFieldValidationException", because the mandatory "name" field is empty.
1545
        $this->contentService->updateContent(
1546
            $draft->getVersionInfo(),
1547
            $contentUpdateStruct
1548
        );
1549
    }
1550
1551
    /**
1552
     * Test for the updateContent() method.
1553
     *
1554
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1555
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1556
     */
1557
    public function testUpdateContentThrowsContentFieldValidationException()
1558
    {
1559
        $contentTypeService = $this->getRepository()->getContentTypeService();
1560
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
1561
1562
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
1563
        $contentCreate->setField('name', 'An awesome Sidelfingen folder');
1564
1565
        $draft = $this->contentService->createContent($contentCreate);
1566
1567
        $contentUpdate = $this->contentService->newContentUpdateStruct();
1568
        // Violates string length constraint
1569
        $contentUpdate->setField('short_name', str_repeat('a', 200), self::ENG_US);
1570
1571
        $this->expectException(ContentFieldValidationException::class);
1572
1573
        // Throws ContentFieldValidationException because the string length validation of the field "short_name" fails
1574
        $this->contentService->updateContent($draft->getVersionInfo(), $contentUpdate);
1575
    }
1576
1577
    /**
1578
     * Test for the updateContent() method.
1579
     *
1580
     * @covers \eZ\Publish\API\Repository\ContentService::updateContent()
1581
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1582
     */
1583
    public function testUpdateContentValidatorIgnoresRequiredFieldsOfNotUpdatedLanguages()
1584
    {
1585
        $contentTypeService = $this->getRepository()->getContentTypeService();
1586
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
1587
1588
        // Create multilangual content
1589
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
1590
        $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_US);
1591
        $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_GB);
1592
1593
        $contentDraft = $this->contentService->createContent($contentCreate);
1594
1595
        // 2. Update content type definition
1596
        $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType);
1597
1598
        $fieldDefinition = $contentType->getFieldDefinition('description');
1599
        $fieldDefinitionUpdate = $contentTypeService->newFieldDefinitionUpdateStruct();
1600
        $fieldDefinitionUpdate->identifier = 'description';
1601
        $fieldDefinitionUpdate->isRequired = true;
1602
1603
        $contentTypeService->updateFieldDefinition(
1604
            $contentTypeDraft,
1605
            $fieldDefinition,
1606
            $fieldDefinitionUpdate
1607
        );
1608
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1609
1610
        // 3. Update only eng-US translation
1611
        $description = new DOMDocument();
1612
        $description->loadXML(<<<XML
1613
<?xml version="1.0" encoding="UTF-8"?>
1614
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0">
1615
    <para>Lorem ipsum dolor</para>
1616
</section>
1617
XML
1618
        );
1619
1620
        $contentUpdate = $this->contentService->newContentUpdateStruct();
1621
        $contentUpdate->setField('name', 'An awesome Sidelfingen folder (updated)', self::ENG_US);
1622
        $contentUpdate->setField('description', $description);
1623
1624
        $this->contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdate);
1625
    }
1626
1627
    /**
1628
     * Test for the updateContent() method.
1629
     *
1630
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1631
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1632
     */
1633
    public function testUpdateContentWithNotUpdatingMandatoryField()
1634
    {
1635
        $draft = $this->createContentDraftVersion1();
1636
1637
        // Now create an update struct which does not overwrite mandatory
1638
        // fields
1639
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1640
        $contentUpdateStruct->setField(
1641
            'description',
1642
            '<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/>'
1643
        );
1644
1645
        // Don't set this, then the above call without languageCode will fail
1646
        $contentUpdateStruct->initialLanguageCode = self::ENG_US;
1647
1648
        // This will only update the "description" field in the "eng-US" language
1649
        $updatedDraft = $this->contentService->updateContent(
1650
            $draft->getVersionInfo(),
1651
            $contentUpdateStruct
1652
        );
1653
1654
        foreach ($updatedDraft->getFields() as $field) {
1655
            if ($field->languageCode === self::ENG_US && $field->fieldDefIdentifier === 'name' && $field->value !== null) {
1656
                // Found field
1657
                return;
1658
            }
1659
        }
1660
        $this->fail(
1661
            'Field with identifier "name" in language "eng-US" could not be found or has empty value.'
1662
        );
1663
    }
1664
1665
    /**
1666
     * Test for the createContentDraft() method.
1667
     *
1668
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft($contentInfo, $versionInfo)
1669
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1670
     */
1671
    public function testCreateContentDraftWithSecondParameter()
1672
    {
1673
        $contentVersion2 = $this->createContentVersion2();
1674
1675
        // Now we create a new draft from the initial version
1676
        $draftedContentReloaded = $this->contentService->createContentDraft(
1677
            $contentVersion2->contentInfo,
1678
            $contentVersion2->getVersionInfo()
1679
        );
1680
1681
        $this->assertEquals(3, $draftedContentReloaded->getVersionInfo()->versionNo);
1682
    }
1683
1684
    /**
1685
     * Test for the createContentDraft() method with third parameter.
1686
     *
1687
     * @covers \eZ\Publish\Core\Repository\ContentService::createContentDraft
1688
     */
1689
    public function testCreateContentDraftWithThirdParameter()
1690
    {
1691
        $content = $this->contentService->loadContent(4);
1692
        $user = $this->createUserVersion1();
1693
1694
        $draftContent = $this->contentService->createContentDraft(
1695
            $content->contentInfo,
1696
            $content->getVersionInfo(),
1697
            $user
1698
        );
1699
1700
        $this->assertInstanceOf(
1701
            Content::class,
1702
            $draftContent
1703
        );
1704
    }
1705
1706
    /**
1707
     * Test for the publishVersion() method.
1708
     *
1709
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1710
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1711
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1712
     */
1713
    public function testPublishVersionFromContentDraft()
1714
    {
1715
        $contentVersion2 = $this->createContentVersion2();
1716
1717
        $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo);
1718
1719
        $this->assertEquals(
1720
            [
1721
                'status' => VersionInfo::STATUS_PUBLISHED,
1722
                'versionNo' => 2,
1723
            ],
1724
            [
1725
                'status' => $versionInfo->status,
1726
                'versionNo' => $versionInfo->versionNo,
1727
            ]
1728
        );
1729
        $this->assertTrue($versionInfo->isPublished());
1730
        $this->assertFalse($versionInfo->isDraft());
1731
        $this->assertFalse($versionInfo->isArchived());
1732
    }
1733
1734
    /**
1735
     * Test for the publishVersion() method.
1736
     *
1737
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1738
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1739
     */
1740
    public function testPublishVersionFromContentDraftArchivesOldVersion()
1741
    {
1742
        $contentVersion2 = $this->createContentVersion2();
1743
1744
        $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1);
1745
1746
        $this->assertEquals(
1747
            [
1748
                'status' => VersionInfo::STATUS_ARCHIVED,
1749
                'versionNo' => 1,
1750
            ],
1751
            [
1752
                'status' => $versionInfo->status,
1753
                'versionNo' => $versionInfo->versionNo,
1754
            ]
1755
        );
1756
        $this->assertTrue($versionInfo->isArchived());
1757
        $this->assertFalse($versionInfo->isDraft());
1758
        $this->assertFalse($versionInfo->isPublished());
1759
    }
1760
1761
    /**
1762
     * Test for the publishVersion() method.
1763
     *
1764
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1765
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1766
     */
1767
    public function testPublishVersionFromContentDraftUpdatesContentInfoCurrentVersion()
1768
    {
1769
        $contentVersion2 = $this->createContentVersion2();
1770
1771
        $this->assertEquals(2, $contentVersion2->contentInfo->currentVersionNo);
1772
    }
1773
1774
    /**
1775
     * Test for the publishVersion() method.
1776
     *
1777
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1778
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1779
     */
1780
    public function testPublishVersionFromOldContentDraftArchivesNewerVersionNo()
1781
    {
1782
        $content = $this->createContentVersion1();
1783
1784
        // Create a new draft with versionNo = 2
1785
        $draftedContentVersion2 = $this->contentService->createContentDraft($content->contentInfo);
1786
1787
        // Create another new draft with versionNo = 3
1788
        $draftedContentVersion3 = $this->contentService->createContentDraft($content->contentInfo);
1789
1790
        // Publish draft with versionNo = 3
1791
        $this->contentService->publishVersion($draftedContentVersion3->getVersionInfo());
1792
1793
        // Publish the first draft with versionNo = 2
1794
        // currentVersionNo is now 2, versionNo 3 will be archived
1795
        $publishedDraft = $this->contentService->publishVersion($draftedContentVersion2->getVersionInfo());
1796
1797
        $this->assertEquals(2, $publishedDraft->contentInfo->currentVersionNo);
1798
    }
1799
1800
    /**
1801
     * Test for the publishVersion() method, and that it creates limited archives.
1802
     *
1803
     * @todo Adapt this when per content type archive limited is added on repository Content Type model.
1804
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1805
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1806
     */
1807
    public function testPublishVersionNotCreatingUnlimitedArchives()
1808
    {
1809
        $content = $this->createContentVersion1();
1810
1811
        // load first to make sure list gets updated also (cache)
1812
        $versionInfoList = $this->contentService->loadVersions($content->contentInfo);
1813
        $this->assertEquals(1, count($versionInfoList));
1814
        $this->assertEquals(1, $versionInfoList[0]->versionNo);
1815
1816
        // Create a new draft with versionNo = 2
1817
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1818
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1819
1820
        // Create a new draft with versionNo = 3
1821
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1822
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1823
1824
        // Create a new draft with versionNo = 4
1825
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1826
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1827
1828
        // Create a new draft with versionNo = 5
1829
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1830
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1831
1832
        // Create a new draft with versionNo = 6
1833
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1834
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1835
1836
        // Create a new draft with versionNo = 7
1837
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1838
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1839
1840
        $versionInfoList = $this->contentService->loadVersions($content->contentInfo);
1841
1842
        $this->assertEquals(6, count($versionInfoList));
1843
        $this->assertEquals(2, $versionInfoList[0]->versionNo);
1844
        $this->assertEquals(7, $versionInfoList[5]->versionNo);
1845
1846
        $this->assertEquals(
1847
            [
1848
                VersionInfo::STATUS_ARCHIVED,
1849
                VersionInfo::STATUS_ARCHIVED,
1850
                VersionInfo::STATUS_ARCHIVED,
1851
                VersionInfo::STATUS_ARCHIVED,
1852
                VersionInfo::STATUS_ARCHIVED,
1853
                VersionInfo::STATUS_PUBLISHED,
1854
            ],
1855
            [
1856
                $versionInfoList[0]->status,
1857
                $versionInfoList[1]->status,
1858
                $versionInfoList[2]->status,
1859
                $versionInfoList[3]->status,
1860
                $versionInfoList[4]->status,
1861
                $versionInfoList[5]->status,
1862
            ]
1863
        );
1864
    }
1865
1866
    /**
1867
     * Test for the newContentMetadataUpdateStruct() method.
1868
     *
1869
     * @covers \eZ\Publish\API\Repository\ContentService::newContentMetadataUpdateStruct
1870
     * @group user
1871
     */
1872
    public function testNewContentMetadataUpdateStruct()
1873
    {
1874
        // Creates a new metadata update struct
1875
        $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
1876
1877
        foreach ($metadataUpdate as $propertyName => $propertyValue) {
0 ignored issues
show
Bug introduced by
The expression $metadataUpdate of type object<eZ\Publish\API\Re...ntMetadataUpdateStruct> is not traversable.
Loading history...
1878
            $this->assertNull($propertyValue, "Property '{$propertyName}' initial value should be null'");
1879
        }
1880
1881
        $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222';
1882
        $metadataUpdate->mainLanguageCode = self::ENG_GB;
1883
        $metadataUpdate->alwaysAvailable = false;
1884
1885
        $this->assertInstanceOf(
1886
            ContentMetadataUpdateStruct::class,
1887
            $metadataUpdate
1888
        );
1889
    }
1890
1891
    /**
1892
     * Test for the updateContentMetadata() method.
1893
     *
1894
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1895
     *
1896
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
1897
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1898
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentMetadataUpdateStruct
1899
     * @group user
1900
     */
1901
    public function testUpdateContentMetadata()
1902
    {
1903
        $content = $this->createContentVersion1();
1904
1905
        // Creates a metadata update struct
1906
        $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
1907
1908
        $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222';
1909
        $metadataUpdate->mainLanguageCode = self::ENG_GB;
1910
        $metadataUpdate->alwaysAvailable = false;
1911
        $metadataUpdate->publishedDate = $this->createDateTime(441759600); // 1984/01/01
1912
        $metadataUpdate->modificationDate = $this->createDateTime(441759600); // 1984/01/01
1913
1914
        // Update the metadata of the published content object
1915
        $content = $this->contentService->updateContentMetadata(
1916
            $content->contentInfo,
1917
            $metadataUpdate
1918
        );
1919
1920
        $this->assertInstanceOf(
1921
            Content::class,
1922
            $content
1923
        );
1924
1925
        return $content;
1926
    }
1927
1928
    /**
1929
     * Test for the updateContentMetadata() method.
1930
     *
1931
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1932
     *
1933
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
1934
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
1935
     */
1936
    public function testUpdateContentMetadataSetsExpectedProperties($content)
1937
    {
1938
        $contentInfo = $content->contentInfo;
1939
1940
        $this->assertEquals(
1941
            [
1942
                'remoteId' => 'aaaabbbbccccddddeeeeffff11112222',
1943
                'sectionId' => $this->generateId('section', 1),
1944
                'alwaysAvailable' => false,
1945
                'currentVersionNo' => 1,
1946
                'mainLanguageCode' => self::ENG_GB,
1947
                'modificationDate' => $this->createDateTime(441759600),
1948
                'ownerId' => $this->getRepository()->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

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...
1949
                'published' => true,
1950
                'publishedDate' => $this->createDateTime(441759600),
1951
            ],
1952
            [
1953
                'remoteId' => $contentInfo->remoteId,
1954
                'sectionId' => $contentInfo->sectionId,
1955
                'alwaysAvailable' => $contentInfo->alwaysAvailable,
1956
                'currentVersionNo' => $contentInfo->currentVersionNo,
1957
                'mainLanguageCode' => $contentInfo->mainLanguageCode,
1958
                'modificationDate' => $contentInfo->modificationDate,
1959
                'ownerId' => $contentInfo->ownerId,
1960
                'published' => $contentInfo->published,
1961
                'publishedDate' => $contentInfo->publishedDate,
1962
            ]
1963
        );
1964
    }
1965
1966
    /**
1967
     * Test for the updateContentMetadata() method.
1968
     *
1969
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1970
     *
1971
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
1972
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
1973
     */
1974
    public function testUpdateContentMetadataNotUpdatesContentVersion($content)
1975
    {
1976
        $this->assertEquals(1, $content->getVersionInfo()->versionNo);
1977
    }
1978
1979
    /**
1980
     * Test for the updateContentMetadata() method.
1981
     *
1982
     * @covers \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
1983
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
1984
     */
1985
    public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnDuplicateRemoteId()
1986
    {
1987
        $content = $this->createContentVersion1();
1988
1989
        // Creates a metadata update struct
1990
        $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
1991
        $metadataUpdate->remoteId = self::MEDIA_REMOTE_ID;
1992
1993
        $this->expectException(APIInvalidArgumentException::class);
1994
        // specified remoteId is already used by the "Media" page.
1995
        $this->contentService->updateContentMetadata(
1996
            $content->contentInfo,
1997
            $metadataUpdate
1998
        );
1999
    }
2000
2001
    /**
2002
     * Test for the updateContentMetadata() method.
2003
     *
2004
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContentMetadata
2005
     */
2006
    public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnNoMetadataPropertiesSet()
2007
    {
2008
        $contentInfo = $this->contentService->loadContentInfo(4);
2009
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
2010
2011
        $this->expectException(APIInvalidArgumentException::class);
2012
        $this->contentService->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct);
2013
    }
2014
2015
    /**
2016
     * Test for the deleteContent() method.
2017
     *
2018
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
2019
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2020
     */
2021
    public function testDeleteContent()
2022
    {
2023
        $contentVersion2 = $this->createContentVersion2();
2024
2025
        // Load the locations for this content object
2026
        $locations = $this->locationService->loadLocations($contentVersion2->contentInfo);
2027
2028
        // This will delete the content, all versions and the associated locations
2029
        $this->contentService->deleteContent($contentVersion2->contentInfo);
2030
2031
        $this->expectException(NotFoundException::class);
2032
2033
        foreach ($locations as $location) {
2034
            $this->locationService->loadLocation($location->id);
2035
        }
2036
    }
2037
2038
    /**
2039
     * Test for the deleteContent() method.
2040
     *
2041
     * Test for issue EZP-21057:
2042
     * "contentService: Unable to delete a content with an empty file attribute"
2043
     *
2044
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
2045
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2046
     */
2047
    public function testDeleteContentWithEmptyBinaryField()
2048
    {
2049
        $contentVersion = $this->createContentVersion1EmptyBinaryField();
2050
2051
        // Load the locations for this content object
2052
        $locations = $this->locationService->loadLocations($contentVersion->contentInfo);
2053
2054
        // This will delete the content, all versions and the associated locations
2055
        $this->contentService->deleteContent($contentVersion->contentInfo);
2056
2057
        $this->expectException(NotFoundException::class);
2058
2059
        foreach ($locations as $location) {
2060
            $this->locationService->loadLocation($location->id);
2061
        }
2062
    }
2063
2064
    public function testCountContentDraftsReturnsZeroByDefault(): void
2065
    {
2066
        $this->assertSame(0, $this->contentService->countContentDrafts());
2067
    }
2068
2069
    public function testCountContentDrafts(): void
2070
    {
2071
        // Create 5 drafts
2072
        $this->createContentDrafts(5);
2073
2074
        $this->assertSame(5, $this->contentService->countContentDrafts());
2075
    }
2076
2077
    public function testCountContentDraftsForUsers(): void
2078
    {
2079
        $newUser = $this->createUserWithPolicies(
2080
            'new_user',
2081
            [
2082
                ['module' => 'content', 'function' => 'create'],
2083
                ['module' => 'content', 'function' => 'read'],
2084
                ['module' => 'content', 'function' => 'publish'],
2085
                ['module' => 'content', 'function' => 'edit'],
2086
            ]
2087
        );
2088
2089
        $previousUser = $this->permissionResolver->getCurrentUserReference();
2090
2091
        // Set new editor as user
2092
        $this->permissionResolver->setCurrentUserReference($newUser);
2093
2094
        // Create a content draft as newUser
2095
        $publishedContent = $this->createContentVersion1();
2096
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2097
2098
        // Reset to previous current user
2099
        $this->permissionResolver->setCurrentUserReference($previousUser);
2100
2101
        // Now $contentDrafts for the previous current user and the new user
2102
        $newUserDrafts = $this->contentService->countContentDrafts($newUser);
2103
        $previousUserDrafts = $this->contentService->countContentDrafts();
2104
2105
        $this->assertSame(1, $newUserDrafts);
2106
        $this->assertSame(0, $previousUserDrafts);
2107
    }
2108
2109
    /**
2110
     * Test for the loadContentDrafts() method.
2111
     *
2112
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2113
     */
2114
    public function testLoadContentDraftsReturnsEmptyArrayByDefault()
2115
    {
2116
        $contentDrafts = $this->contentService->loadContentDrafts();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...ce::loadContentDrafts() has been deprecated with message: Please use {@see loadContentDraftList()} instead to avoid risking loading too much data.

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...
2117
2118
        $this->assertSame([], $contentDrafts);
2119
    }
2120
2121
    /**
2122
     * Test for the loadContentDrafts() method.
2123
     *
2124
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2125
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
2126
     */
2127
    public function testLoadContentDrafts()
2128
    {
2129
        // "Media" content object
2130
        $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
2131
2132
        // "eZ Publish Demo Design ..." content object
2133
        $demoDesignContentInfo = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
2134
2135
        // Create some drafts
2136
        $this->contentService->createContentDraft($mediaContentInfo);
2137
        $this->contentService->createContentDraft($demoDesignContentInfo);
2138
2139
        // Now $contentDrafts should contain two drafted versions
2140
        $draftedVersions = $this->contentService->loadContentDrafts();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...ce::loadContentDrafts() has been deprecated with message: Please use {@see loadContentDraftList()} instead to avoid risking loading too much data.

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...
2141
2142
        $actual = [
2143
            $draftedVersions[0]->status,
2144
            $draftedVersions[0]->getContentInfo()->remoteId,
2145
            $draftedVersions[1]->status,
2146
            $draftedVersions[1]->getContentInfo()->remoteId,
2147
        ];
2148
        sort($actual, SORT_STRING);
2149
2150
        $this->assertEquals(
2151
            [
2152
                VersionInfo::STATUS_DRAFT,
2153
                VersionInfo::STATUS_DRAFT,
2154
                self::DEMO_DESIGN_REMOTE_ID,
2155
                self::MEDIA_REMOTE_ID,
2156
            ],
2157
            $actual
2158
        );
2159
    }
2160
2161
    /**
2162
     * Test for the loadContentDrafts() method.
2163
     *
2164
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts($user)
2165
     */
2166
    public function testLoadContentDraftsWithFirstParameter()
2167
    {
2168
        $user = $this->createUserVersion1();
2169
2170
        // Get current user
2171
        $oldCurrentUser = $this->permissionResolver->getCurrentUserReference();
2172
2173
        // Set new editor as user
2174
        $this->permissionResolver->setCurrentUserReference($user);
2175
2176
        // "Media" content object
2177
        $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
2178
2179
        // Create a content draft
2180
        $this->contentService->createContentDraft($mediaContentInfo);
2181
2182
        // Reset to previous current user
2183
        $this->permissionResolver->setCurrentUserReference($oldCurrentUser);
2184
2185
        // Now $contentDrafts for the previous current user and the new user
2186
        $newCurrentUserDrafts = $this->contentService->loadContentDrafts($user);
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...ce::loadContentDrafts() has been deprecated with message: Please use {@see loadContentDraftList()} instead to avoid risking loading too much data.

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...
2187
        $oldCurrentUserDrafts = $this->contentService->loadContentDrafts();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...ce::loadContentDrafts() has been deprecated with message: Please use {@see loadContentDraftList()} instead to avoid risking loading too much data.

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...
2188
2189
        $this->assertSame([], $oldCurrentUserDrafts);
2190
2191
        $this->assertEquals(
2192
            [
2193
                VersionInfo::STATUS_DRAFT,
2194
                self::MEDIA_REMOTE_ID,
2195
            ],
2196
            [
2197
                $newCurrentUserDrafts[0]->status,
2198
                $newCurrentUserDrafts[0]->getContentInfo()->remoteId,
2199
            ]
2200
        );
2201
        $this->assertTrue($newCurrentUserDrafts[0]->isDraft());
2202
        $this->assertFalse($newCurrentUserDrafts[0]->isArchived());
2203
        $this->assertFalse($newCurrentUserDrafts[0]->isPublished());
2204
    }
2205
2206
    /**
2207
     * Test for the loadContentDraftList() method.
2208
     *
2209
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2210
     */
2211
    public function testLoadContentDraftListWithPaginationParameters()
2212
    {
2213
        // Create some drafts
2214
        $publishedContent = $this->createContentVersion1();
2215
        $draftContentA = $this->contentService->createContentDraft($publishedContent->contentInfo);
2216
        $draftContentB = $this->contentService->createContentDraft($draftContentA->contentInfo);
2217
        $draftContentC = $this->contentService->createContentDraft($draftContentB->contentInfo);
2218
        $draftContentD = $this->contentService->createContentDraft($draftContentC->contentInfo);
2219
        $draftContentE = $this->contentService->createContentDraft($draftContentD->contentInfo);
2220
2221
        $draftsOnPage1 = $this->contentService->loadContentDraftList(null, 0, 2);
2222
        $draftsOnPage2 = $this->contentService->loadContentDraftList(null, 2, 2);
2223
2224
        $this->assertSame(5, $draftsOnPage1->totalCount);
2225
        $this->assertSame(5, $draftsOnPage2->totalCount);
2226
        $this->assertEquals($draftContentE->getVersionInfo(), $draftsOnPage1->items[0]->getVersionInfo());
2227
        $this->assertEquals($draftContentD->getVersionInfo(), $draftsOnPage1->items[1]->getVersionInfo());
2228
        $this->assertEquals($draftContentC->getVersionInfo(), $draftsOnPage2->items[0]->getVersionInfo());
2229
        $this->assertEquals($draftContentB->getVersionInfo(), $draftsOnPage2->items[1]->getVersionInfo());
2230
    }
2231
2232
    /**
2233
     * Test for the loadContentDraftList() method.
2234
     *
2235
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts($user)
2236
     */
2237
    public function testLoadContentDraftListWithForUserWithLimitation()
2238
    {
2239
        $oldUser = $this->permissionResolver->getCurrentUserReference();
2240
2241
        $parentContent = $this->createFolder(['eng-US' => 'parentFolder'], 2);
2242
        $content = $this->createFolder(['eng-US' => 'parentFolder'], $parentContent->contentInfo->mainLocationId);
2243
2244
        // User has limitation to read versions only for `$content`, not for `$parentContent`
2245
        $newUser = $this->createUserWithVersionReadLimitations([$content->contentInfo->mainLocationId]);
2246
2247
        $this->permissionResolver->setCurrentUserReference($newUser);
2248
2249
        $contentDraftUnauthorized = $this->contentService->createContentDraft($parentContent->contentInfo);
2250
        $contentDraftA = $this->contentService->createContentDraft($content->contentInfo);
2251
        $contentDraftB = $this->contentService->createContentDraft($content->contentInfo);
2252
2253
        $newUserDraftList = $this->contentService->loadContentDraftList($newUser, 0);
2254
        $this->assertSame(3, $newUserDraftList->totalCount);
2255
        $this->assertEquals($contentDraftB->getVersionInfo(), $newUserDraftList->items[0]->getVersionInfo());
2256
        $this->assertEquals($contentDraftA->getVersionInfo(), $newUserDraftList->items[1]->getVersionInfo());
2257
        $this->assertEquals(
2258
            new UnauthorizedContentDraftListItem('content', 'versionread', ['contentId' => $contentDraftUnauthorized->id]),
2259
            $newUserDraftList->items[2]
2260
        );
2261
2262
        // Reset to previous user
2263
        $this->permissionResolver->setCurrentUserReference($oldUser);
2264
2265
        $oldUserDraftList = $this->contentService->loadContentDraftList();
2266
2267
        $this->assertSame(0, $oldUserDraftList->totalCount);
2268
        $this->assertSame([], $oldUserDraftList->items);
2269
    }
2270
2271
    /**
2272
     * Test for the loadContentDraftList() method.
2273
     *
2274
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2275
     */
2276
    public function testLoadAllContentDrafts()
2277
    {
2278
        // Create more drafts then default pagination limit
2279
        $this->createContentDrafts(12);
2280
2281
        $this->assertCount(12, $this->contentService->loadContentDraftList());
2282
    }
2283
2284
    /**
2285
     * Test for the loadVersionInfo() method.
2286
     *
2287
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo)
2288
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2289
     */
2290
    public function testLoadVersionInfoWithSecondParameter()
2291
    {
2292
        $publishedContent = $this->createContentVersion1();
2293
2294
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2295
2296
        // Will return the VersionInfo of the $draftContent
2297
        $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2);
2298
2299
        $this->assertEquals(2, $versionInfo->versionNo);
2300
2301
        // Check that ContentInfo contained in VersionInfo has correct main Location id set
2302
        $this->assertEquals(
2303
            $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId,
2304
            $versionInfo->getContentInfo()->mainLocationId
2305
        );
2306
    }
2307
2308
    /**
2309
     * Test for the loadVersionInfo() method.
2310
     *
2311
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo)
2312
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoWithSecondParameter
2313
     */
2314
    public function testLoadVersionInfoThrowsNotFoundExceptionWithSecondParameter()
2315
    {
2316
        $draft = $this->createContentDraftVersion1();
2317
2318
        $this->expectException(NotFoundException::class);
2319
2320
        // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object.
2321
        $this->contentService->loadVersionInfo($draft->contentInfo, 2);
2322
    }
2323
2324
    /**
2325
     * Test for the loadVersionInfoById() method.
2326
     *
2327
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo)
2328
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoWithSecondParameter
2329
     */
2330
    public function testLoadVersionInfoByIdWithSecondParameter()
2331
    {
2332
        $publishedContent = $this->createContentVersion1();
2333
2334
        $draftContent = $this->contentService->createContentDraft($publishedContent->contentInfo);
2335
2336
        // Will return the VersionInfo of the $draftContent
2337
        $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2);
2338
2339
        $this->assertEquals(2, $versionInfo->versionNo);
2340
2341
        // Check that ContentInfo contained in VersionInfo has correct main Location id set
2342
        $this->assertEquals(
2343
            $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId,
2344
            $versionInfo->getContentInfo()->mainLocationId
2345
        );
2346
2347
        return [
2348
            'versionInfo' => $versionInfo,
2349
            'draftContent' => $draftContent,
2350
        ];
2351
    }
2352
2353
    /**
2354
     * Test for the returned value of the loadVersionInfoById() method.
2355
     *
2356
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoByIdWithSecondParameter
2357
     * @covers \eZ\Publish\API\Repository\ContentService::loadVersionInfoById
2358
     *
2359
     * @param array $data
2360
     */
2361
    public function testLoadVersionInfoByIdWithSecondParameterSetsExpectedVersionInfo(array $data)
2362
    {
2363
        /** @var VersionInfo $versionInfo */
2364
        $versionInfo = $data['versionInfo'];
2365
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $draftContent */
2366
        $draftContent = $data['draftContent'];
2367
2368
        $this->assertPropertiesCorrect(
2369
            [
2370
                'names' => [
2371
                    self::ENG_US => 'An awesome forum',
2372
                ],
2373
                'contentInfo' => new ContentInfo([
2374
                    'id' => $draftContent->contentInfo->id,
2375
                    'contentTypeId' => 28,
2376
                    'name' => 'An awesome forum',
2377
                    'sectionId' => 1,
2378
                    'currentVersionNo' => 1,
2379
                    'published' => true,
2380
                    'ownerId' => 14,
2381
                    // this Content Object is created at the test runtime
2382
                    'modificationDate' => $versionInfo->contentInfo->modificationDate,
2383
                    'publishedDate' => $versionInfo->contentInfo->publishedDate,
2384
                    'alwaysAvailable' => 1,
2385
                    'remoteId' => 'abcdef0123456789abcdef0123456789',
2386
                    'mainLanguageCode' => self::ENG_US,
2387
                    'mainLocationId' => $draftContent->contentInfo->mainLocationId,
2388
                    'status' => ContentInfo::STATUS_PUBLISHED,
2389
                ]),
2390
                'id' => $draftContent->versionInfo->id,
2391
                'versionNo' => 2,
2392
                'creatorId' => 14,
2393
                'status' => 0,
2394
                'initialLanguageCode' => self::ENG_US,
2395
                'languageCodes' => [
2396
                    self::ENG_US,
2397
                ],
2398
            ],
2399
            $versionInfo
2400
        );
2401
    }
2402
2403
    /**
2404
     * Test for the loadVersionInfoById() method.
2405
     *
2406
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo)
2407
     */
2408
    public function testLoadVersionInfoByIdThrowsNotFoundExceptionWithSecondParameter()
2409
    {
2410
        $content = $this->createContentVersion1();
2411
2412
        $this->expectException(NotFoundException::class);
2413
2414
        // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object.
2415
        $this->contentService->loadVersionInfoById($content->id, 2);
2416
    }
2417
2418
    /**
2419
     * Test for the loadContentByVersionInfo() method.
2420
     *
2421
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo($versionInfo, $languages)
2422
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2423
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByVersionInfo
2424
     */
2425
    public function testLoadContentByVersionInfoWithSecondParameter()
2426
    {
2427
        $sectionId = $this->generateId('section', 1);
2428
        $contentTypeService = $this->getRepository()->getContentTypeService();
2429
2430
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
2431
2432
        $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
2433
2434
        $contentCreateStruct->setField('name', 'Sindelfingen forum²');
2435
2436
        $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB);
2437
2438
        $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789';
2439
        // $sectionId contains the ID of section 1
2440
        $contentCreateStruct->sectionId = $sectionId;
2441
        $contentCreateStruct->alwaysAvailable = true;
2442
2443
        // Create a new content draft
2444
        $content = $this->contentService->createContent($contentCreateStruct);
2445
2446
        // Now publish this draft
2447
        $publishedContent = $this->contentService->publishVersion($content->getVersionInfo());
2448
2449
        // Will return a content instance with fields in "eng-US"
2450
        $reloadedContent = $this->contentService->loadContentByVersionInfo(
2451
            $publishedContent->getVersionInfo(),
2452
            [
2453
                self::ENG_GB,
2454
            ],
2455
            false
2456
        );
2457
2458
        $actual = [];
2459
        foreach ($reloadedContent->getFields() as $field) {
2460
            $actual[] = new Field(
2461
                [
2462
                    'id' => 0,
2463
                    'value' => $field->value !== null, // Actual value tested by FieldType integration tests
2464
                    'languageCode' => $field->languageCode,
2465
                    'fieldDefIdentifier' => $field->fieldDefIdentifier,
2466
                ]
2467
            );
2468
        }
2469
        usort(
2470
            $actual,
2471
            function ($field1, $field2) {
2472
                if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) {
2473
                    return strcasecmp($field1->languageCode, $field2->languageCode);
2474
                }
2475
2476
                return $return;
2477
            }
2478
        );
2479
2480
        $expected = [
2481
            new Field(
2482
                [
2483
                    'id' => 0,
2484
                    'value' => true,
2485
                    'languageCode' => self::ENG_GB,
2486
                    'fieldDefIdentifier' => 'description',
2487
                ]
2488
            ),
2489
            new Field(
2490
                [
2491
                    'id' => 0,
2492
                    'value' => true,
2493
                    'languageCode' => self::ENG_GB,
2494
                    'fieldDefIdentifier' => 'name',
2495
                ]
2496
            ),
2497
        ];
2498
2499
        $this->assertEquals($expected, $actual);
2500
    }
2501
2502
    /**
2503
     * Test for the loadContentByContentInfo() method.
2504
     *
2505
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages)
2506
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo
2507
     */
2508
    public function testLoadContentByContentInfoWithLanguageParameters()
2509
    {
2510
        $sectionId = $this->generateId('section', 1);
2511
        $contentTypeService = $this->getRepository()->getContentTypeService();
2512
2513
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
2514
2515
        $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
2516
2517
        $contentCreateStruct->setField('name', 'Sindelfingen forum²');
2518
2519
        $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB);
2520
2521
        $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789';
2522
        // $sectionId contains the ID of section 1
2523
        $contentCreateStruct->sectionId = $sectionId;
2524
        $contentCreateStruct->alwaysAvailable = true;
2525
2526
        // Create a new content draft
2527
        $content = $this->contentService->createContent($contentCreateStruct);
2528
2529
        // Now publish this draft
2530
        $publishedContent = $this->contentService->publishVersion($content->getVersionInfo());
2531
2532
        // Will return a content instance with fields in "eng-US"
2533
        $reloadedContent = $this->contentService->loadContentByContentInfo(
2534
            $publishedContent->contentInfo,
2535
            [
2536
                self::ENG_US,
2537
            ],
2538
            null,
2539
            false
2540
        );
2541
2542
        $actual = $this->normalizeFields($reloadedContent->getFields());
2543
2544
        $expected = [
2545
            new Field(
2546
                [
2547
                    'id' => 0,
2548
                    'value' => true,
2549
                    'languageCode' => self::ENG_US,
2550
                    'fieldDefIdentifier' => 'description',
2551
                    'fieldTypeIdentifier' => 'ezrichtext',
2552
                ]
2553
            ),
2554
            new Field(
2555
                [
2556
                    'id' => 0,
2557
                    'value' => true,
2558
                    'languageCode' => self::ENG_US,
2559
                    'fieldDefIdentifier' => 'name',
2560
                    'fieldTypeIdentifier' => 'ezstring',
2561
                ]
2562
            ),
2563
        ];
2564
2565
        $this->assertEquals($expected, $actual);
2566
2567
        // Will return a content instance with fields in "eng-GB" (versions prior to 6.0.0-beta9 returned "eng-US" also)
2568
        $reloadedContent = $this->contentService->loadContentByContentInfo(
2569
            $publishedContent->contentInfo,
2570
            [
2571
                self::ENG_GB,
2572
            ],
2573
            null,
2574
            true
2575
        );
2576
2577
        $actual = $this->normalizeFields($reloadedContent->getFields());
2578
2579
        $expected = [
2580
            new Field(
2581
                [
2582
                    'id' => 0,
2583
                    'value' => true,
2584
                    'languageCode' => self::ENG_GB,
2585
                    'fieldDefIdentifier' => 'description',
2586
                    'fieldTypeIdentifier' => 'ezrichtext',
2587
                ]
2588
            ),
2589
            new Field(
2590
                [
2591
                    'id' => 0,
2592
                    'value' => true,
2593
                    'languageCode' => self::ENG_GB,
2594
                    'fieldDefIdentifier' => 'name',
2595
                    'fieldTypeIdentifier' => 'ezstring',
2596
                ]
2597
            ),
2598
        ];
2599
2600
        $this->assertEquals($expected, $actual);
2601
2602
        // Will return a content instance with fields in main language "eng-US", as "fre-FR" does not exists
2603
        $reloadedContent = $this->contentService->loadContentByContentInfo(
2604
            $publishedContent->contentInfo,
2605
            [
2606
                'fre-FR',
2607
            ],
2608
            null,
2609
            true
2610
        );
2611
2612
        $actual = $this->normalizeFields($reloadedContent->getFields());
2613
2614
        $expected = [
2615
            new Field(
2616
                [
2617
                    'id' => 0,
2618
                    'value' => true,
2619
                    'languageCode' => self::ENG_US,
2620
                    'fieldDefIdentifier' => 'description',
2621
                    'fieldTypeIdentifier' => 'ezrichtext',
2622
                ]
2623
            ),
2624
            new Field(
2625
                [
2626
                    'id' => 0,
2627
                    'value' => true,
2628
                    'languageCode' => self::ENG_US,
2629
                    'fieldDefIdentifier' => 'name',
2630
                    'fieldTypeIdentifier' => 'ezstring',
2631
                ]
2632
            ),
2633
        ];
2634
2635
        $this->assertEquals($expected, $actual);
2636
    }
2637
2638
    /**
2639
     * Test for the loadContentByContentInfo() method.
2640
     *
2641
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo)
2642
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo
2643
     */
2644
    public function testLoadContentByContentInfoWithVersionNumberParameter()
2645
    {
2646
        $publishedContent = $this->createContentVersion1();
2647
2648
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2649
2650
        // This content instance is identical to $draftContent
2651
        $draftContentReloaded = $this->contentService->loadContentByContentInfo(
2652
            $publishedContent->contentInfo,
2653
            null,
2654
            2
2655
        );
2656
2657
        $this->assertEquals(
2658
            2,
2659
            $draftContentReloaded->getVersionInfo()->versionNo
2660
        );
2661
2662
        // Check that ContentInfo contained in reloaded draft Content has correct main Location id set
2663
        $this->assertEquals(
2664
            $publishedContent->versionInfo->contentInfo->mainLocationId,
2665
            $draftContentReloaded->versionInfo->contentInfo->mainLocationId
2666
        );
2667
    }
2668
2669
    /**
2670
     * Test for the loadContentByContentInfo() method.
2671
     *
2672
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo)
2673
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfoWithVersionNumberParameter
2674
     */
2675
    public function testLoadContentByContentInfoThrowsNotFoundExceptionWithVersionNumberParameter()
2676
    {
2677
        $content = $this->createContentVersion1();
2678
2679
        $this->expectException(NotFoundException::class);
2680
2681
        // This call will fail with a "NotFoundException", because no content with versionNo = 2 exists.
2682
        $this->contentService->loadContentByContentInfo($content->contentInfo, null, 2);
2683
    }
2684
2685
    /**
2686
     * Test for the loadContent() method.
2687
     *
2688
     * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages)
2689
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2690
     */
2691
    public function testLoadContentWithPrioritizedLanguages()
2692
    {
2693
        $draft = $this->createMultipleLanguageDraftVersion1();
2694
2695
        // This draft contains those fields localized with "eng-GB"
2696
        $draftLocalized = $this->contentService->loadContent($draft->id, [self::ENG_GB], null, false);
2697
2698
        $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB);
2699
2700
        return $draftLocalized;
2701
    }
2702
2703
    /**
2704
     * Test for the loadContent() method using undefined translation.
2705
     *
2706
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithPrioritizedLanguages
2707
     *
2708
     * @param \eZ\Publish\API\Repository\Values\Content\Content $contentDraft
2709
     */
2710
    public function testLoadContentWithPrioritizedLanguagesThrowsNotFoundException(Content $contentDraft)
2711
    {
2712
        $this->expectException(NotFoundException::class);
2713
2714
        $this->contentService->loadContent($contentDraft->id, [self::GER_DE], null, false);
2715
    }
2716
2717
    /**
2718
     * Test for the loadContent() method.
2719
     *
2720
     * @see \eZ\Publish\API\Repository\ContentService::loadContent
2721
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithPrioritizedLanguages
2722
     */
2723
    public function testLoadContentPassTroughPrioritizedLanguagesToContentType(Content $content): void
2724
    {
2725
        $contentTypeService = $this->getRepository()->getContentTypeService();
2726
2727
        $contentType = $contentTypeService->loadContentType(
2728
            $content->contentInfo->contentTypeId,
2729
            [self::ENG_GB]
2730
        );
2731
2732
        $this->assertEquals($contentType, $content->getContentType());
2733
    }
2734
2735
    /**
2736
     * Test for the loadContent() method.
2737
     *
2738
     * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages, $versionNo)
2739
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2740
     */
2741
    public function testLoadContentWithThirdParameter()
2742
    {
2743
        $publishedContent = $this->createContentVersion1();
2744
2745
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2746
2747
        // This content instance is identical to $draftContent
2748
        $draftContentReloaded = $this->contentService->loadContent($publishedContent->id, null, 2);
2749
2750
        $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo);
2751
2752
        // Check that ContentInfo contained in reloaded draft Content has correct main Location id set
2753
        $this->assertEquals(
2754
            $publishedContent->versionInfo->contentInfo->mainLocationId,
2755
            $draftContentReloaded->versionInfo->contentInfo->mainLocationId
2756
        );
2757
    }
2758
2759
    /**
2760
     * Test for the loadContent() method.
2761
     *
2762
     * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages, $versionNo)
2763
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithThirdParameter
2764
     */
2765
    public function testLoadContentThrowsNotFoundExceptionWithThirdParameter()
2766
    {
2767
        $content = $this->createContentVersion1();
2768
2769
        $this->expectException(NotFoundException::class);
2770
2771
        // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists.
2772
        $this->contentService->loadContent($content->id, null, 2);
2773
    }
2774
2775
    /**
2776
     * Test for the loadContentByRemoteId() method.
2777
     *
2778
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages)
2779
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2780
     */
2781
    public function testLoadContentByRemoteIdWithSecondParameter()
2782
    {
2783
        $draft = $this->createMultipleLanguageDraftVersion1();
2784
2785
        $this->contentService->publishVersion($draft->versionInfo);
2786
2787
        // This draft contains those fields localized with "eng-GB"
2788
        $draftLocalized = $this->contentService->loadContentByRemoteId(
2789
            $draft->contentInfo->remoteId,
2790
            [self::ENG_GB],
2791
            null,
2792
            false
2793
        );
2794
2795
        $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB);
2796
    }
2797
2798
    /**
2799
     * Test for the loadContentByRemoteId() method.
2800
     *
2801
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo)
2802
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2803
     */
2804
    public function testLoadContentByRemoteIdWithThirdParameter()
2805
    {
2806
        $publishedContent = $this->createContentVersion1();
2807
2808
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2809
2810
        // This content instance is identical to $draftContent
2811
        $draftContentReloaded = $this->contentService->loadContentByRemoteId(
2812
            $publishedContent->contentInfo->remoteId,
2813
            null,
2814
            2
2815
        );
2816
2817
        $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo);
2818
2819
        // Check that ContentInfo contained in reloaded draft Content has correct main Location id set
2820
        $this->assertEquals(
2821
            $publishedContent->versionInfo->contentInfo->mainLocationId,
2822
            $draftContentReloaded->versionInfo->contentInfo->mainLocationId
2823
        );
2824
    }
2825
2826
    /**
2827
     * Test for the loadContentByRemoteId() method.
2828
     *
2829
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo)
2830
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteIdWithThirdParameter
2831
     */
2832
    public function testLoadContentByRemoteIdThrowsNotFoundExceptionWithThirdParameter()
2833
    {
2834
        $content = $this->createContentVersion1();
2835
2836
        $this->expectException(NotFoundException::class);
2837
2838
        // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists.
2839
        $this->contentService->loadContentByRemoteId(
2840
            $content->contentInfo->remoteId,
2841
            null,
2842
            2
2843
        );
2844
    }
2845
2846
    /**
2847
     * Test that retrieval of translated name field respects prioritized language list.
2848
     *
2849
     * @dataProvider getPrioritizedLanguageList
2850
     * @param string[]|null $languageCodes
2851
     */
2852
    public function testLoadContentWithPrioritizedLanguagesList($languageCodes)
2853
    {
2854
        $content = $this->createContentVersion2();
2855
2856
        $content = $this->contentService->loadContent($content->id, $languageCodes);
2857
2858
        $expectedName = $content->getVersionInfo()->getName(
2859
            isset($languageCodes[0]) ? $languageCodes[0] : null
2860
        );
2861
        $nameValue = $content->getFieldValue('name');
2862
        /** @var \eZ\Publish\Core\FieldType\TextLine\Value $nameValue */
2863
        self::assertEquals($expectedName, $nameValue->text);
2864
        self::assertEquals($expectedName, $content->getVersionInfo()->getName());
2865
        // Also check value on shortcut method on content
2866
        self::assertEquals($expectedName, $content->getName());
2867
    }
2868
2869
    /**
2870
     * @return array
2871
     */
2872
    public function getPrioritizedLanguageList()
2873
    {
2874
        return [
2875
            [[self::ENG_US]],
2876
            [[self::ENG_GB]],
2877
            [[self::ENG_GB, self::ENG_US]],
2878
            [[self::ENG_US, self::ENG_GB]],
2879
        ];
2880
    }
2881
2882
    /**
2883
     * Test for the deleteVersion() method.
2884
     *
2885
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
2886
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
2887
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2888
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2889
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
2890
     */
2891
    public function testDeleteVersion()
2892
    {
2893
        $content = $this->createContentVersion1();
2894
2895
        // Create new draft, because published or last version of the Content can't be deleted
2896
        $draft = $this->contentService->createContentDraft(
2897
            $content->getVersionInfo()->getContentInfo()
2898
        );
2899
2900
        // Delete the previously created draft
2901
        $this->contentService->deleteVersion($draft->getVersionInfo());
2902
2903
        $versions = $this->contentService->loadVersions($content->getVersionInfo()->getContentInfo());
2904
2905
        $this->assertCount(1, $versions);
2906
        $this->assertEquals(
2907
            $content->getVersionInfo()->id,
2908
            $versions[0]->id
2909
        );
2910
    }
2911
2912
    /**
2913
     * Test for the deleteVersion() method.
2914
     *
2915
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
2916
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
2917
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2918
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2919
     */
2920
    public function testDeleteVersionThrowsBadStateExceptionOnPublishedVersion()
2921
    {
2922
        $content = $this->createContentVersion1();
2923
2924
        $this->expectException(BadStateException::class);
2925
2926
        // This call will fail with a "BadStateException", because the content version is currently published.
2927
        $this->contentService->deleteVersion($content->getVersionInfo());
2928
    }
2929
2930
    /**
2931
     * Test for the deleteVersion() method.
2932
     *
2933
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
2934
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
2935
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2936
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2937
     */
2938
    public function testDeleteVersionWorksIfOnlyVersionIsDraft()
2939
    {
2940
        $draft = $this->createContentDraftVersion1();
2941
2942
        $this->contentService->deleteVersion($draft->getVersionInfo());
2943
2944
        $this->expectException(NotFoundException::class);
2945
2946
        // This call will fail with a "NotFound", because we allow to delete content if remaining version is draft.
2947
        // Can normally only happen if there where always only a draft to begin with, simplifies UI edit API usage.
2948
        $this->contentService->loadContent($draft->id);
2949
    }
2950
2951
    /**
2952
     * Test for the loadVersions() method.
2953
     *
2954
     * @see \eZ\Publish\API\Repository\ContentService::loadVersions()
2955
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2956
     *
2957
     * @return VersionInfo[]
2958
     */
2959
    public function testLoadVersions()
2960
    {
2961
        $contentVersion2 = $this->createContentVersion2();
2962
2963
        // Load versions of this ContentInfo instance
2964
        $versions = $this->contentService->loadVersions($contentVersion2->contentInfo);
2965
2966
        $expectedVersionsOrder = [
2967
            $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1),
2968
            $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 2),
2969
        ];
2970
2971
        $this->assertEquals($expectedVersionsOrder, $versions);
2972
2973
        return $versions;
2974
    }
2975
2976
    /**
2977
     * Test for the loadVersions() method.
2978
     *
2979
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersions
2980
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersions
2981
     *
2982
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo[] $versions
2983
     */
2984
    public function testLoadVersionsSetsExpectedVersionInfo(array $versions)
2985
    {
2986
        $this->assertCount(2, $versions);
2987
2988
        $expectedVersions = [
2989
            [
2990
                'versionNo' => 1,
2991
                'creatorId' => 14,
2992
                'status' => VersionInfo::STATUS_ARCHIVED,
2993
                'initialLanguageCode' => self::ENG_US,
2994
                'languageCodes' => [self::ENG_US],
2995
            ],
2996
            [
2997
                'versionNo' => 2,
2998
                'creatorId' => 10,
2999
                'status' => VersionInfo::STATUS_PUBLISHED,
3000
                'initialLanguageCode' => self::ENG_US,
3001
                'languageCodes' => [self::ENG_US, self::ENG_GB],
3002
            ],
3003
        ];
3004
3005
        $this->assertPropertiesCorrect($expectedVersions[0], $versions[0]);
3006
        $this->assertPropertiesCorrect($expectedVersions[1], $versions[1]);
3007
        $this->assertEquals(
3008
            $versions[0]->creationDate->getTimestamp(),
3009
            $versions[1]->creationDate->getTimestamp(),
3010
            'Creation time did not match within delta of 2 seconds',
3011
            2
3012
        );
3013
        $this->assertEquals(
3014
            $versions[0]->modificationDate->getTimestamp(),
3015
            $versions[1]->modificationDate->getTimestamp(),
3016
            'Creation time did not match within delta of 2 seconds',
3017
            2
3018
        );
3019
        $this->assertTrue($versions[0]->isArchived());
3020
        $this->assertFalse($versions[0]->isDraft());
3021
        $this->assertFalse($versions[0]->isPublished());
3022
3023
        $this->assertTrue($versions[1]->isPublished());
3024
        $this->assertFalse($versions[1]->isDraft());
3025
        $this->assertFalse($versions[1]->isArchived());
3026
    }
3027
3028
    /**
3029
     * Test for the copyContent() method.
3030
     *
3031
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
3032
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
3033
     * @group field-type
3034
     */
3035
    public function testCopyContent()
3036
    {
3037
        $parentLocationId = $this->generateId('location', 56);
3038
3039
        $contentVersion2 = $this->createMultipleLanguageContentVersion2();
3040
3041
        // Configure new target location
3042
        $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId);
3043
3044
        $targetLocationCreate->priority = 42;
3045
        $targetLocationCreate->hidden = true;
3046
        $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789';
3047
        $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID;
3048
        $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC;
3049
3050
        // Copy content with all versions and drafts
3051
        $contentCopied = $this->contentService->copyContent(
3052
            $contentVersion2->contentInfo,
3053
            $targetLocationCreate
3054
        );
3055
3056
        $this->assertInstanceOf(
3057
            Content::class,
3058
            $contentCopied
3059
        );
3060
3061
        $this->assertNotEquals(
3062
            $contentVersion2->contentInfo->remoteId,
3063
            $contentCopied->contentInfo->remoteId
3064
        );
3065
3066
        $this->assertNotEquals(
3067
            $contentVersion2->id,
3068
            $contentCopied->id
3069
        );
3070
3071
        $this->assertEquals(
3072
            2,
3073
            count($this->contentService->loadVersions($contentCopied->contentInfo))
3074
        );
3075
3076
        $this->assertEquals(2, $contentCopied->getVersionInfo()->versionNo);
3077
3078
        $this->assertAllFieldsEquals($contentCopied->getFields());
3079
3080
        $this->assertDefaultContentStates($contentCopied->contentInfo);
3081
3082
        $this->assertNotNull(
3083
            $contentCopied->contentInfo->mainLocationId,
3084
            'Expected main location to be set given we provided a LocationCreateStruct'
3085
        );
3086
    }
3087
3088
    /**
3089
     * Test for the copyContent() method with ezsettings.default.content.retain_owner_on_copy set to false
3090
     * See settings/test/integration_legacy.yml for service override.
3091
     *
3092
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
3093
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
3094
     * @group field-type
3095
     */
3096
    public function testCopyContentWithNewOwner()
3097
    {
3098
        $parentLocationId = $this->generateId('location', 56);
3099
3100
        $userService = $this->getRepository()->getUserService();
3101
3102
        $newOwner = $this->createUser('new_owner', 'foo', 'bar');
3103
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $contentVersion2 */
3104
        $contentVersion2 = $this->createContentDraftVersion1(
3105
            $parentLocationId,
3106
            self::FORUM_IDENTIFIER,
3107
            'name',
3108
            $newOwner
3109
        );
3110
3111
        // Configure new target location
3112
        $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId);
3113
3114
        $targetLocationCreate->priority = 42;
3115
        $targetLocationCreate->hidden = true;
3116
        $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789';
3117
        $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID;
3118
        $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC;
3119
3120
        // Copy content with all versions and drafts
3121
        $contentCopied = $this->contentService->copyContent(
3122
            $contentVersion2->contentInfo,
3123
            $targetLocationCreate
3124
        );
3125
3126
        $this->assertEquals(
3127
            $newOwner->id,
3128
            $contentVersion2->contentInfo->ownerId
3129
        );
3130
        $this->assertEquals(
3131
            $userService->loadUserByLogin('admin')->getUserId(),
3132
            $contentCopied->contentInfo->ownerId
3133
        );
3134
    }
3135
3136
    /**
3137
     * Test for the copyContent() method.
3138
     *
3139
     * @see \eZ\Publish\API\Repository\ContentService::copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo)
3140
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent
3141
     */
3142
    public function testCopyContentWithGivenVersion()
3143
    {
3144
        $parentLocationId = $this->generateId('location', 56);
3145
3146
        $contentVersion2 = $this->createContentVersion2();
3147
3148
        // Configure new target location
3149
        $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId);
3150
3151
        $targetLocationCreate->priority = 42;
3152
        $targetLocationCreate->hidden = true;
3153
        $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789';
3154
        $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID;
3155
        $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC;
3156
3157
        // Copy only the initial version
3158
        $contentCopied = $this->contentService->copyContent(
3159
            $contentVersion2->contentInfo,
3160
            $targetLocationCreate,
3161
            $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1)
3162
        );
3163
3164
        $this->assertInstanceOf(
3165
            Content::class,
3166
            $contentCopied
3167
        );
3168
3169
        $this->assertNotEquals(
3170
            $contentVersion2->contentInfo->remoteId,
3171
            $contentCopied->contentInfo->remoteId
3172
        );
3173
3174
        $this->assertNotEquals(
3175
            $contentVersion2->id,
3176
            $contentCopied->id
3177
        );
3178
3179
        $this->assertEquals(
3180
            1,
3181
            count($this->contentService->loadVersions($contentCopied->contentInfo))
3182
        );
3183
3184
        $this->assertEquals(1, $contentCopied->getVersionInfo()->versionNo);
3185
3186
        $this->assertNotNull(
3187
            $contentCopied->contentInfo->mainLocationId,
3188
            'Expected main location to be set given we provided a LocationCreateStruct'
3189
        );
3190
    }
3191
3192
    /**
3193
     * Test for the addRelation() method.
3194
     *
3195
     * @return \eZ\Publish\API\Repository\Values\Content\Content
3196
     *
3197
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3198
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
3199
     */
3200
    public function testAddRelation()
3201
    {
3202
        $draft = $this->createContentDraftVersion1();
3203
3204
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3205
3206
        // Create relation between new content object and "Media" page
3207
        $relation = $this->contentService->addRelation(
3208
            $draft->getVersionInfo(),
3209
            $media
3210
        );
3211
3212
        $this->assertInstanceOf(
3213
            '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Relation',
3214
            $relation
3215
        );
3216
3217
        return $this->contentService->loadRelations($draft->getVersionInfo());
3218
    }
3219
3220
    /**
3221
     * Test for the addRelation() method.
3222
     *
3223
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3224
     *
3225
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3226
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3227
     */
3228
    public function testAddRelationAddsRelationToContent($relations)
3229
    {
3230
        $this->assertEquals(
3231
            1,
3232
            count($relations)
3233
        );
3234
    }
3235
3236
    /**
3237
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3238
     */
3239
    protected function assertExpectedRelations($relations)
3240
    {
3241
        $this->assertEquals(
3242
            [
3243
                'type' => Relation::COMMON,
3244
                'sourceFieldDefinitionIdentifier' => null,
3245
                'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3246
                'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3247
            ],
3248
            [
3249
                'type' => $relations[0]->type,
3250
                'sourceFieldDefinitionIdentifier' => $relations[0]->sourceFieldDefinitionIdentifier,
3251
                'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3252
                'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3253
            ]
3254
        );
3255
    }
3256
3257
    /**
3258
     * Test for the addRelation() method.
3259
     *
3260
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3261
     *
3262
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3263
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3264
     */
3265
    public function testAddRelationSetsExpectedRelations($relations)
3266
    {
3267
        $this->assertExpectedRelations($relations);
3268
    }
3269
3270
    /**
3271
     * Test for the createContentDraft() method.
3272
     *
3273
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
3274
     *
3275
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
3276
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelationSetsExpectedRelations
3277
     */
3278
    public function testCreateContentDraftWithRelations()
3279
    {
3280
        $draft = $this->createContentDraftVersion1();
3281
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3282
3283
        // Create relation between new content object and "Media" page
3284
        $this->contentService->addRelation(
3285
            $draft->getVersionInfo(),
3286
            $media
3287
        );
3288
3289
        $content = $this->contentService->publishVersion($draft->versionInfo);
3290
        $newDraft = $this->contentService->createContentDraft($content->contentInfo);
3291
3292
        return $this->contentService->loadRelations($newDraft->getVersionInfo());
3293
    }
3294
3295
    /**
3296
     * Test for the createContentDraft() method.
3297
     *
3298
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3299
     *
3300
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
3301
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraftWithRelations
3302
     */
3303
    public function testCreateContentDraftWithRelationsCreatesRelations($relations)
3304
    {
3305
        $this->assertEquals(
3306
            1,
3307
            count($relations)
3308
        );
3309
3310
        return $relations;
3311
    }
3312
3313
    /**
3314
     * Test for the createContentDraft() method.
3315
     *
3316
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3317
     *
3318
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraftWithRelationsCreatesRelations
3319
     */
3320
    public function testCreateContentDraftWithRelationsCreatesExpectedRelations($relations)
3321
    {
3322
        $this->assertExpectedRelations($relations);
3323
    }
3324
3325
    /**
3326
     * Test for the addRelation() method.
3327
     *
3328
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3329
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3330
     */
3331
    public function testAddRelationThrowsBadStateException()
3332
    {
3333
        $content = $this->createContentVersion1();
3334
3335
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3336
3337
        $this->expectException(BadStateException::class);
3338
3339
        // This call will fail with a "BadStateException", because content is published and not a draft.
3340
        $this->contentService->addRelation(
3341
            $content->getVersionInfo(),
3342
            $media
3343
        );
3344
    }
3345
3346
    /**
3347
     * Test for the loadRelations() method.
3348
     *
3349
     * @see \eZ\Publish\API\Repository\ContentService::loadRelations()
3350
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3351
     */
3352
    public function testLoadRelations()
3353
    {
3354
        $draft = $this->createContentDraftVersion1();
3355
3356
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3357
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3358
3359
        // Create relation between new content object and "Media" page
3360
        $this->contentService->addRelation(
3361
            $draft->getVersionInfo(),
3362
            $media
3363
        );
3364
3365
        // Create another relation with the "Demo Design" page
3366
        $this->contentService->addRelation(
3367
            $draft->getVersionInfo(),
3368
            $demoDesign
3369
        );
3370
3371
        $relations = $this->contentService->loadRelations($draft->getVersionInfo());
3372
3373
        usort(
3374
            $relations,
3375
            function ($rel1, $rel2) {
3376
                return strcasecmp(
3377
                    $rel2->getDestinationContentInfo()->remoteId,
3378
                    $rel1->getDestinationContentInfo()->remoteId
3379
                );
3380
            }
3381
        );
3382
3383
        $this->assertEquals(
3384
            [
3385
                [
3386
                    'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3387
                    'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3388
                ],
3389
                [
3390
                    'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3391
                    'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3392
                ],
3393
            ],
3394
            [
3395
                [
3396
                    'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3397
                    'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3398
                ],
3399
                [
3400
                    'sourceContentInfo' => $relations[1]->sourceContentInfo->remoteId,
3401
                    'destinationContentInfo' => $relations[1]->destinationContentInfo->remoteId,
3402
                ],
3403
            ]
3404
        );
3405
    }
3406
3407
    /**
3408
     * Test for the loadRelations() method.
3409
     *
3410
     * @see \eZ\Publish\API\Repository\ContentService::loadRelations()
3411
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3412
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations
3413
     */
3414
    public function testLoadRelationsSkipsArchivedContent()
3415
    {
3416
        $trashService = $this->getRepository()->getTrashService();
3417
3418
        $draft = $this->createContentDraftVersion1();
3419
3420
        // Load other content objects
3421
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3422
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3423
3424
        // Create relation between new content object and "Media" page
3425
        $this->contentService->addRelation(
3426
            $draft->getVersionInfo(),
3427
            $media
3428
        );
3429
3430
        // Create another relation with the "Demo Design" page
3431
        $this->contentService->addRelation(
3432
            $draft->getVersionInfo(),
3433
            $demoDesign
3434
        );
3435
3436
        $demoDesignLocation = $this->locationService->loadLocation($demoDesign->mainLocationId);
3437
3438
        // Trashing Content's last Location will change its status to archived,
3439
        // in this case relation towards it will not be loaded.
3440
        $trashService->trash($demoDesignLocation);
3441
3442
        // Load all relations
3443
        $relations = $this->contentService->loadRelations($draft->getVersionInfo());
3444
3445
        $this->assertCount(1, $relations);
3446
        $this->assertEquals(
3447
            [
3448
                [
3449
                    'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3450
                    'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3451
                ],
3452
            ],
3453
            [
3454
                [
3455
                    'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3456
                    'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3457
                ],
3458
            ]
3459
        );
3460
    }
3461
3462
    /**
3463
     * Test for the loadRelations() method.
3464
     *
3465
     * @see \eZ\Publish\API\Repository\ContentService::loadRelations()
3466
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3467
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations
3468
     */
3469
    public function testLoadRelationsSkipsDraftContent()
3470
    {
3471
        $draft = $this->createContentDraftVersion1();
3472
3473
        // Load other content objects
3474
        $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID);
3475
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3476
3477
        // Create draft of "Media" page
3478
        $mediaDraft = $this->contentService->createContentDraft($media->contentInfo);
3479
3480
        // Create relation between "Media" page and new content object draft.
3481
        // This relation will not be loaded before the draft is published.
3482
        $this->contentService->addRelation(
3483
            $mediaDraft->getVersionInfo(),
3484
            $draft->getVersionInfo()->getContentInfo()
3485
        );
3486
3487
        // Create another relation with the "Demo Design" page
3488
        $this->contentService->addRelation(
3489
            $mediaDraft->getVersionInfo(),
3490
            $demoDesign
3491
        );
3492
3493
        $relations = $this->contentService->loadRelations($mediaDraft->getVersionInfo());
3494
3495
        $this->assertCount(1, $relations);
3496
        $this->assertEquals(
3497
            [
3498
                [
3499
                    'sourceContentInfo' => self::MEDIA_REMOTE_ID,
3500
                    'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3501
                ],
3502
            ],
3503
            [
3504
                [
3505
                    'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3506
                    'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3507
                ],
3508
            ]
3509
        );
3510
    }
3511
3512
    /**
3513
     * Test for the countReverseRelations() method.
3514
     *
3515
     * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations
3516
     */
3517
    public function testCountReverseRelations(): void
3518
    {
3519
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3520
            $this->contentService->createContentDraft(
3521
                $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3522
            ),
3523
            $this->contentService->createContentDraft(
3524
                $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3525
            ),
3526
        ]);
3527
3528
        $contentInfo = $contentWithReverseRelations->content->getVersionInfo()->getContentInfo();
3529
3530
        $this->assertEquals(2, $this->contentService->countReverseRelations($contentInfo));
3531
    }
3532
3533
    /**
3534
     * Test for the countReverseRelations() method.
3535
     *
3536
     * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations
3537
     */
3538
    public function testCountReverseRelationsReturnsZeroByDefault(): void
3539
    {
3540
        $draft = $this->createContentDraftVersion1();
3541
3542
        $this->assertSame(0, $this->contentService->countReverseRelations($draft->getVersionInfo()->getContentInfo()));
3543
    }
3544
3545
    /**
3546
     * Test for the countReverseRelations() method.
3547
     *
3548
     * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations
3549
     */
3550
    public function testCountReverseRelationsForUnauthorizedUser(): void
3551
    {
3552
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3553
            $this->contentService->createContentDraft(
3554
                $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3555
            ),
3556
        ]);
3557
        $mediaUser = $this->createMediaUserVersion1();
3558
        $this->permissionResolver->setCurrentUserReference($mediaUser);
3559
3560
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3561
3562
        $this->assertSame(0, $this->contentService->countReverseRelations($contentInfo));
3563
    }
3564
3565
    /**
3566
     * Test for the loadReverseRelations() method.
3567
     *
3568
     * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations()
3569
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3570
     */
3571
    public function testLoadReverseRelations()
3572
    {
3573
        $versionInfo = $this->createContentVersion1()->getVersionInfo();
3574
        $contentInfo = $versionInfo->getContentInfo();
3575
3576
        // Create some drafts
3577
        $mediaDraft = $this->contentService->createContentDraft(
3578
            $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID)
3579
        );
3580
        $demoDesignDraft = $this->contentService->createContentDraft(
3581
            $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID)
3582
        );
3583
3584
        // Create relation between new content object and "Media" page
3585
        $relation1 = $this->contentService->addRelation(
3586
            $mediaDraft->getVersionInfo(),
3587
            $contentInfo
3588
        );
3589
3590
        // Create another relation with the "Demo Design" page
3591
        $relation2 = $this->contentService->addRelation(
3592
            $demoDesignDraft->getVersionInfo(),
3593
            $contentInfo
3594
        );
3595
3596
        // Publish drafts, so relations become active
3597
        $this->contentService->publishVersion($mediaDraft->getVersionInfo());
3598
        $this->contentService->publishVersion($demoDesignDraft->getVersionInfo());
3599
3600
        $relations = $this->contentService->loadRelations($versionInfo);
3601
        $reverseRelations = $this->contentService->loadReverseRelations($contentInfo);
3602
3603
        $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id);
3604
        $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id);
3605
3606
        $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id);
3607
        $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id);
3608
3609
        $this->assertEquals(0, count($relations));
3610
        $this->assertEquals(2, count($reverseRelations));
3611
3612
        usort(
3613
            $reverseRelations,
3614
            function ($rel1, $rel2) {
3615
                return strcasecmp(
3616
                    $rel2->getSourceContentInfo()->remoteId,
3617
                    $rel1->getSourceContentInfo()->remoteId
3618
                );
3619
            }
3620
        );
3621
3622
        $this->assertEquals(
3623
            [
3624
                [
3625
                    'sourceContentInfo' => self::MEDIA_REMOTE_ID,
3626
                    'destinationContentInfo' => 'abcdef0123456789abcdef0123456789',
3627
                ],
3628
                [
3629
                    'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3630
                    'destinationContentInfo' => 'abcdef0123456789abcdef0123456789',
3631
                ],
3632
            ],
3633
            [
3634
                [
3635
                    'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId,
3636
                    'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId,
3637
                ],
3638
                [
3639
                    'sourceContentInfo' => $reverseRelations[1]->sourceContentInfo->remoteId,
3640
                    'destinationContentInfo' => $reverseRelations[1]->destinationContentInfo->remoteId,
3641
                ],
3642
            ]
3643
        );
3644
    }
3645
3646
    /**
3647
     * Test for the loadReverseRelations() method.
3648
     *
3649
     * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations()
3650
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3651
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadReverseRelations
3652
     */
3653
    public function testLoadReverseRelationsSkipsArchivedContent()
3654
    {
3655
        $trashService = $this->getRepository()->getTrashService();
3656
3657
        $versionInfo = $this->createContentVersion1()->getVersionInfo();
3658
        $contentInfo = $versionInfo->getContentInfo();
3659
3660
        // Create some drafts
3661
        $mediaDraft = $this->contentService->createContentDraft(
3662
            $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID)
3663
        );
3664
        $demoDesignDraft = $this->contentService->createContentDraft(
3665
            $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID)
3666
        );
3667
3668
        // Create relation between new content object and "Media" page
3669
        $relation1 = $this->contentService->addRelation(
3670
            $mediaDraft->getVersionInfo(),
3671
            $contentInfo
3672
        );
3673
3674
        // Create another relation with the "Demo Design" page
3675
        $relation2 = $this->contentService->addRelation(
3676
            $demoDesignDraft->getVersionInfo(),
3677
            $contentInfo
3678
        );
3679
3680
        // Publish drafts, so relations become active
3681
        $this->contentService->publishVersion($mediaDraft->getVersionInfo());
3682
        $this->contentService->publishVersion($demoDesignDraft->getVersionInfo());
3683
3684
        $demoDesignLocation = $this->locationService->loadLocation($demoDesignDraft->contentInfo->mainLocationId);
3685
3686
        // Trashing Content's last Location will change its status to archived,
3687
        // in this case relation from it will not be loaded.
3688
        $trashService->trash($demoDesignLocation);
3689
3690
        // Load all relations
3691
        $relations = $this->contentService->loadRelations($versionInfo);
3692
        $reverseRelations = $this->contentService->loadReverseRelations($contentInfo);
3693
3694
        $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id);
3695
        $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id);
3696
3697
        $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id);
3698
        $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id);
3699
3700
        $this->assertEquals(0, count($relations));
3701
        $this->assertEquals(1, count($reverseRelations));
3702
3703
        $this->assertEquals(
3704
            [
3705
                [
3706
                    'sourceContentInfo' => self::MEDIA_REMOTE_ID,
3707
                    'destinationContentInfo' => 'abcdef0123456789abcdef0123456789',
3708
                ],
3709
            ],
3710
            [
3711
                [
3712
                    'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId,
3713
                    'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId,
3714
                ],
3715
            ]
3716
        );
3717
    }
3718
3719
    /**
3720
     * Test for the loadReverseRelations() method.
3721
     *
3722
     * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations()
3723
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3724
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadReverseRelations
3725
     */
3726
    public function testLoadReverseRelationsSkipsDraftContent()
3727
    {
3728
        // Load "Media" page Content
3729
        $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID);
3730
3731
        // Create some drafts
3732
        $newDraftVersionInfo = $this->createContentDraftVersion1()->getVersionInfo();
3733
        $demoDesignDraft = $this->contentService->createContentDraft(
3734
            $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID)
3735
        );
3736
3737
        // Create relation between "Media" page and new content object
3738
        $relation1 = $this->contentService->addRelation(
3739
            $newDraftVersionInfo,
3740
            $media->contentInfo
3741
        );
3742
3743
        // Create another relation with the "Demo Design" page
3744
        $relation2 = $this->contentService->addRelation(
3745
            $demoDesignDraft->getVersionInfo(),
3746
            $media->contentInfo
3747
        );
3748
3749
        // Publish drafts, so relations become active
3750
        $this->contentService->publishVersion($demoDesignDraft->getVersionInfo());
3751
        // We will not publish new Content draft, therefore relation from it
3752
        // will not be loaded as reverse relation for "Media" page
3753
3754
        $relations = $this->contentService->loadRelations($media->versionInfo);
3755
        $reverseRelations = $this->contentService->loadReverseRelations($media->contentInfo);
3756
3757
        $this->assertEquals($media->contentInfo->id, $relation1->getDestinationContentInfo()->id);
3758
        $this->assertEquals($newDraftVersionInfo->contentInfo->id, $relation1->getSourceContentInfo()->id);
3759
3760
        $this->assertEquals($media->contentInfo->id, $relation2->getDestinationContentInfo()->id);
3761
        $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id);
3762
3763
        $this->assertEquals(0, count($relations));
3764
        $this->assertEquals(1, count($reverseRelations));
3765
3766
        $this->assertEquals(
3767
            [
3768
                [
3769
                    'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3770
                    'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3771
                ],
3772
            ],
3773
            [
3774
                [
3775
                    'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId,
3776
                    'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId,
3777
                ],
3778
            ]
3779
        );
3780
    }
3781
3782
    /**
3783
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3784
     */
3785
    public function testLoadReverseRelationList(): void
3786
    {
3787
        $draft1 = $this->contentService->createContentDraft(
3788
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3789
        );
3790
        $draft2 = $this->contentService->createContentDraft(
3791
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3792
        );
3793
        $draft3 = $this->contentService->createContentDraft(
3794
            $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo
3795
        );
3796
3797
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3798
            $draft1,
3799
            $draft2,
3800
            $draft3,
3801
        ]);
3802
3803
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3804
3805
        $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo);
3806
3807
        $this->assertSame(3, $reverseRelationList->totalCount);
3808
        $this->assertEquals(
3809
            $contentWithReverseRelations->reverseRelations[2]->contentInfo,
3810
            $reverseRelationList->items[0]->getRelation()->sourceContentInfo
3811
        );
3812
        $this->assertEquals(
3813
            $contentWithReverseRelations->reverseRelations[1]->contentInfo,
3814
            $reverseRelationList->items[1]->getRelation()->sourceContentInfo
3815
        );
3816
        $this->assertEquals(
3817
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3818
            $reverseRelationList->items[2]->getRelation()->sourceContentInfo
3819
        );
3820
    }
3821
3822
    /**
3823
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3824
     */
3825
    public function testLoadReverseRelationListWithPagination(): void
3826
    {
3827
        $draft1 = $this->contentService->createContentDraft(
3828
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3829
        );
3830
        $draft2 = $this->contentService->createContentDraft(
3831
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3832
        );
3833
        $draft3 = $this->contentService->createContentDraft(
3834
            $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo
3835
        );
3836
3837
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3838
            $draft1,
3839
            $draft2,
3840
            $draft3,
3841
        ]);
3842
3843
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3844
3845
        $reverseRelationPage1 = $this->contentService->loadReverseRelationList($contentInfo, 0, 2);
3846
        $reverseRelationPage2 = $this->contentService->loadReverseRelationList($contentInfo, 2, 2);
3847
        $this->assertSame(3, $reverseRelationPage1->totalCount);
3848
        $this->assertSame(3, $reverseRelationPage2->totalCount);
3849
        $this->assertEquals(
3850
            $contentWithReverseRelations->reverseRelations[2]->contentInfo,
3851
            $reverseRelationPage1->items[0]->getRelation()->sourceContentInfo
3852
        );
3853
        $this->assertEquals(
3854
            $contentWithReverseRelations->reverseRelations[1]->contentInfo,
3855
            $reverseRelationPage1->items[1]->getRelation()->sourceContentInfo
3856
        );
3857
        $this->assertEquals(
3858
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3859
            $reverseRelationPage2->items[0]->getRelation()->sourceContentInfo
3860
        );
3861
    }
3862
3863
    /**
3864
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3865
     */
3866
    public function testLoadReverseRelationListSkipsArchivedContent(): void
3867
    {
3868
        $trashService = $this->getRepository()->getTrashService();
3869
3870
        $draft1 = $this->contentService->createContentDraft(
3871
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3872
        );
3873
        $draft2 = $this->contentService->createContentDraft(
3874
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3875
        );
3876
        $draft3 = $this->contentService->createContentDraft(
3877
            $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo
3878
        );
3879
3880
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3881
            $draft1,
3882
            $draft2,
3883
            $draft3,
3884
        ]);
3885
3886
        $locationToTrash = $this->locationService->loadLocation($draft3->contentInfo->mainLocationId);
3887
3888
        // Trashing Content's last Location will change its status to archived, in this case relation from it will not be loaded.
3889
        $trashService->trash($locationToTrash);
3890
3891
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3892
        $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo);
3893
3894
        $this->assertSame(2, $reverseRelationList->totalCount);
3895
        $this->assertEquals(
3896
            $contentWithReverseRelations->reverseRelations[1]->contentInfo,
3897
            $reverseRelationList->items[0]->getRelation()->sourceContentInfo
3898
        );
3899
        $this->assertEquals(
3900
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3901
            $reverseRelationList->items[1]->getRelation()->sourceContentInfo
3902
        );
3903
    }
3904
3905
    /**
3906
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3907
     */
3908
    public function testLoadReverseRelationListSkipsDraftContent()
3909
    {
3910
        $draft1 = $this->contentService->createContentDraft(
3911
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3912
        );
3913
3914
        $contentWithReverseRelations = $this->createContentWithReverseRelations([$draft1]);
3915
3916
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3917
3918
        // create a relation, but without publishing it
3919
        $draft2 = $this->contentService->createContentDraft(
3920
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3921
        );
3922
        $this->contentService->addRelation(
3923
            $draft2->getVersionInfo(),
3924
            $contentInfo
3925
        );
3926
3927
        $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo);
3928
3929
        $this->assertSame(1, $reverseRelationList->totalCount);
3930
        $this->assertEquals(
3931
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3932
            $reverseRelationList->items[0]->getRelation()->sourceContentInfo
3933
        );
3934
    }
3935
3936
    /**
3937
     * Test for the deleteRelation() method.
3938
     *
3939
     * @see \eZ\Publish\API\Repository\ContentService::deleteRelation()
3940
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations
3941
     */
3942
    public function testDeleteRelation()
3943
    {
3944
        $draft = $this->createContentDraftVersion1();
3945
3946
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3947
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3948
3949
        // Establish some relations
3950
        $this->contentService->addRelation($draft->getVersionInfo(), $media);
3951
        $this->contentService->addRelation($draft->getVersionInfo(), $demoDesign);
3952
3953
        // Delete one of the currently created relations
3954
        $this->contentService->deleteRelation($draft->getVersionInfo(), $media);
3955
3956
        // The relations array now contains only one element
3957
        $relations = $this->contentService->loadRelations($draft->getVersionInfo());
3958
3959
        $this->assertEquals(1, count($relations));
3960
    }
3961
3962
    /**
3963
     * Test for the deleteRelation() method.
3964
     *
3965
     * @see \eZ\Publish\API\Repository\ContentService::deleteRelation()
3966
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteRelation
3967
     */
3968
    public function testDeleteRelationThrowsBadStateException()
3969
    {
3970
        $content = $this->createContentVersion1();
3971
3972
        // Load the destination object
3973
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3974
3975
        // Create a new draft
3976
        $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo);
3977
3978
        // Add a relation
3979
        $this->contentService->addRelation($draftVersion2->getVersionInfo(), $media);
3980
3981
        // Publish new version
3982
        $contentVersion2 = $this->contentService->publishVersion(
3983
            $draftVersion2->getVersionInfo()
3984
        );
3985
3986
        $this->expectException(BadStateException::class);
3987
3988
        // This call will fail with a "BadStateException", because content is published and not a draft.
3989
        $this->contentService->deleteRelation(
3990
            $contentVersion2->getVersionInfo(),
3991
            $media
3992
        );
3993
    }
3994
3995
    /**
3996
     * Test for the deleteRelation() method.
3997
     *
3998
     * @see \eZ\Publish\API\Repository\ContentService::deleteRelation()
3999
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteRelation
4000
     */
4001
    public function testDeleteRelationThrowsInvalidArgumentException()
4002
    {
4003
        $draft = $this->createContentDraftVersion1();
4004
4005
        // Load the destination object
4006
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
4007
4008
        // This call will fail with a "InvalidArgumentException", because no relation exists between $draft and $media.
4009
        $this->expectException(APIInvalidArgumentException::class);
4010
        $this->contentService->deleteRelation(
4011
            $draft->getVersionInfo(),
4012
            $media
4013
        );
4014
    }
4015
4016
    /**
4017
     * Test for the createContent() method.
4018
     *
4019
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
4020
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4021
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4022
     */
4023
    public function testCreateContentInTransactionWithRollback()
4024
    {
4025
        if ($this->isVersion4()) {
4026
            $this->markTestSkipped('This test requires eZ Publish 5');
4027
        }
4028
4029
        $repository = $this->getRepository();
4030
4031
        $contentTypeService = $this->getRepository()->getContentTypeService();
4032
4033
        // Start a transaction
4034
        $repository->beginTransaction();
4035
4036
        try {
4037
            $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
4038
4039
            // Get a content create struct and set mandatory properties
4040
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
4041
            $contentCreate->setField('name', 'Sindelfingen forum');
4042
4043
            $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
4044
            $contentCreate->alwaysAvailable = true;
4045
4046
            // Create a new content object
4047
            $contentId = $this->contentService->createContent($contentCreate)->id;
4048
        } catch (Exception $e) {
4049
            // Cleanup hanging transaction on error
4050
            $repository->rollback();
4051
            throw $e;
4052
        }
4053
4054
        // Rollback all changes
4055
        $repository->rollback();
4056
4057
        try {
4058
            // This call will fail with a "NotFoundException"
4059
            $this->contentService->loadContent($contentId);
4060
        } catch (NotFoundException $e) {
4061
            // This is expected
4062
            return;
4063
        }
4064
4065
        $this->fail('Content object still exists after rollback.');
4066
    }
4067
4068
    /**
4069
     * Test for the createContent() method.
4070
     *
4071
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
4072
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4073
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4074
     */
4075
    public function testCreateContentInTransactionWithCommit()
4076
    {
4077
        if ($this->isVersion4()) {
4078
            $this->markTestSkipped('This test requires eZ Publish 5');
4079
        }
4080
4081
        $repository = $this->getRepository();
4082
4083
        $contentTypeService = $repository->getContentTypeService();
4084
4085
        // Start a transaction
4086
        $repository->beginTransaction();
4087
4088
        try {
4089
            $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
4090
4091
            // Get a content create struct and set mandatory properties
4092
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
4093
            $contentCreate->setField('name', 'Sindelfingen forum');
4094
4095
            $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
4096
            $contentCreate->alwaysAvailable = true;
4097
4098
            // Create a new content object
4099
            $contentId = $this->contentService->createContent($contentCreate)->id;
4100
4101
            // Commit changes
4102
            $repository->commit();
4103
        } catch (Exception $e) {
4104
            // Cleanup hanging transaction on error
4105
            $repository->rollback();
4106
            throw $e;
4107
        }
4108
4109
        // Load the new content object
4110
        $content = $this->contentService->loadContent($contentId);
4111
4112
        $this->assertEquals($contentId, $content->id);
4113
    }
4114
4115
    /**
4116
     * Test for the createContent() method.
4117
     *
4118
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
4119
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
4120
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentThrowsNotFoundException
4121
     */
4122
    public function testCreateContentWithLocationCreateParameterInTransactionWithRollback()
4123
    {
4124
        $repository = $this->getRepository();
4125
4126
        // Start a transaction
4127
        $repository->beginTransaction();
4128
4129
        try {
4130
            $draft = $this->createContentDraftVersion1();
4131
        } catch (Exception $e) {
4132
            // Cleanup hanging transaction on error
4133
            $repository->rollback();
4134
            throw $e;
4135
        }
4136
4137
        $contentId = $draft->id;
4138
4139
        // Roleback the transaction
4140
        $repository->rollback();
4141
4142
        try {
4143
            // This call will fail with a "NotFoundException"
4144
            $this->contentService->loadContent($contentId);
4145
        } catch (NotFoundException $e) {
4146
            return;
4147
        }
4148
4149
        $this->fail('Can still load content object after rollback.');
4150
    }
4151
4152
    /**
4153
     * Test for the createContent() method.
4154
     *
4155
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
4156
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
4157
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentThrowsNotFoundException
4158
     */
4159
    public function testCreateContentWithLocationCreateParameterInTransactionWithCommit()
4160
    {
4161
        $repository = $this->getRepository();
4162
4163
        // Start a transaction
4164
        $repository->beginTransaction();
4165
4166
        try {
4167
            $draft = $this->createContentDraftVersion1();
4168
4169
            $contentId = $draft->id;
4170
4171
            // Roleback the transaction
4172
            $repository->commit();
4173
        } catch (Exception $e) {
4174
            // Cleanup hanging transaction on error
4175
            $repository->rollback();
4176
            throw $e;
4177
        }
4178
4179
        // Load the new content object
4180
        $content = $this->contentService->loadContent($contentId);
4181
4182
        $this->assertEquals($contentId, $content->id);
4183
    }
4184
4185
    /**
4186
     * Test for the createContentDraft() method.
4187
     *
4188
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
4189
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
4190
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4191
     */
4192
    public function testCreateContentDraftInTransactionWithRollback()
4193
    {
4194
        $repository = $this->getRepository();
4195
4196
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4197
4198
        // Load the user group content object
4199
        $content = $this->contentService->loadContent($contentId);
4200
4201
        // Start a new transaction
4202
        $repository->beginTransaction();
4203
4204
        try {
4205
            // Create a new draft
4206
            $drafted = $this->contentService->createContentDraft($content->contentInfo);
4207
4208
            // Store version number for later reuse
4209
            $versionNo = $drafted->versionInfo->versionNo;
4210
        } catch (Exception $e) {
4211
            // Cleanup hanging transaction on error
4212
            $repository->rollback();
4213
            throw $e;
4214
        }
4215
4216
        // Rollback
4217
        $repository->rollback();
4218
4219
        try {
4220
            // This call will fail with a "NotFoundException"
4221
            $this->contentService->loadContent($contentId, null, $versionNo);
4222
        } catch (NotFoundException $e) {
4223
            return;
4224
        }
4225
4226
        $this->fail('Can still load content draft after rollback');
4227
    }
4228
4229
    /**
4230
     * Test for the createContentDraft() method.
4231
     *
4232
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
4233
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
4234
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4235
     */
4236
    public function testCreateContentDraftInTransactionWithCommit()
4237
    {
4238
        $repository = $this->getRepository();
4239
4240
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4241
4242
        // Load the user group content object
4243
        $content = $this->contentService->loadContent($contentId);
4244
4245
        // Start a new transaction
4246
        $repository->beginTransaction();
4247
4248
        try {
4249
            // Create a new draft
4250
            $drafted = $this->contentService->createContentDraft($content->contentInfo);
4251
4252
            // Store version number for later reuse
4253
            $versionNo = $drafted->versionInfo->versionNo;
4254
4255
            // Commit all changes
4256
            $repository->commit();
4257
        } catch (Exception $e) {
4258
            // Cleanup hanging transaction on error
4259
            $repository->rollback();
4260
            throw $e;
4261
        }
4262
4263
        $content = $this->contentService->loadContent($contentId, null, $versionNo);
4264
4265
        $this->assertEquals(
4266
            $versionNo,
4267
            $content->getVersionInfo()->versionNo
4268
        );
4269
    }
4270
4271
    /**
4272
     * Test for the publishVersion() method.
4273
     *
4274
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
4275
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
4276
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4277
     */
4278
    public function testPublishVersionInTransactionWithRollback()
4279
    {
4280
        $repository = $this->getRepository();
4281
4282
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4283
4284
        // Load the user group content object
4285
        $content = $this->contentService->loadContent($contentId);
4286
4287
        // Start a new transaction
4288
        $repository->beginTransaction();
4289
4290
        try {
4291
            $draftVersion = $this->contentService->createContentDraft($content->contentInfo)->getVersionInfo();
4292
4293
            // Publish a new version
4294
            $content = $this->contentService->publishVersion($draftVersion);
4295
4296
            // Store version number for later reuse
4297
            $versionNo = $content->versionInfo->versionNo;
4298
        } catch (Exception $e) {
4299
            // Cleanup hanging transaction on error
4300
            $repository->rollback();
4301
            throw $e;
4302
        }
4303
4304
        // Rollback
4305
        $repository->rollback();
4306
4307
        try {
4308
            // This call will fail with a "NotFoundException"
4309
            $this->contentService->loadContent($contentId, null, $versionNo);
4310
        } catch (NotFoundException $e) {
4311
            return;
4312
        }
4313
4314
        $this->fail('Can still load content draft after rollback');
4315
    }
4316
4317
    /**
4318
     * Test for the publishVersion() method.
4319
     *
4320
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
4321
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
4322
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
4323
     */
4324
    public function testPublishVersionInTransactionWithCommit()
4325
    {
4326
        $repository = $this->getRepository();
4327
4328
        // Load the user group content object
4329
        $template = $this->contentService->loadContent(self::ADMINISTRATORS_USER_GROUP_ID);
4330
4331
        // Start a new transaction
4332
        $repository->beginTransaction();
4333
4334
        try {
4335
            // Publish a new version
4336
            $content = $this->contentService->publishVersion(
4337
                $this->contentService->createContentDraft($template->contentInfo)->getVersionInfo()
4338
            );
4339
4340
            // Store version number for later reuse
4341
            $versionNo = $content->versionInfo->versionNo;
4342
4343
            // Commit all changes
4344
            $repository->commit();
4345
        } catch (Exception $e) {
4346
            // Cleanup hanging transaction on error
4347
            $repository->rollback();
4348
            throw $e;
4349
        }
4350
4351
        // Load current version info
4352
        $versionInfo = $this->contentService->loadVersionInfo($content->contentInfo);
4353
4354
        $this->assertEquals($versionNo, $versionInfo->versionNo);
4355
    }
4356
4357
    /**
4358
     * Test for the updateContent() method.
4359
     *
4360
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
4361
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
4362
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4363
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4364
     */
4365
    public function testUpdateContentInTransactionWithRollback()
4366
    {
4367
        $repository = $this->getRepository();
4368
4369
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4370
4371
        // Create a new user group draft
4372
        $draft = $this->contentService->createContentDraft(
4373
            $this->contentService->loadContentInfo($contentId)
4374
        );
4375
4376
        // Get an update struct and change the group name
4377
        $contentUpdate = $this->contentService->newContentUpdateStruct();
4378
        $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US);
4379
4380
        // Start a transaction
4381
        $repository->beginTransaction();
4382
4383
        try {
4384
            // Update the group name
4385
            $draft = $this->contentService->updateContent(
4386
                $draft->getVersionInfo(),
4387
                $contentUpdate
4388
            );
4389
4390
            // Publish updated version
4391
            $this->contentService->publishVersion($draft->getVersionInfo());
4392
        } catch (Exception $e) {
4393
            // Cleanup hanging transaction on error
4394
            $repository->rollback();
4395
            throw $e;
4396
        }
4397
4398
        // Rollback all changes.
4399
        $repository->rollback();
4400
4401
        // Name will still be "Administrator users"
4402
        $name = $this->contentService->loadContent($contentId)->getFieldValue('name');
4403
4404
        $this->assertEquals('Administrator users', $name);
4405
    }
4406
4407
    /**
4408
     * Test for the updateContent() method.
4409
     *
4410
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
4411
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
4412
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4413
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4414
     */
4415
    public function testUpdateContentInTransactionWithCommit()
4416
    {
4417
        $repository = $this->getRepository();
4418
4419
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4420
4421
        // Create a new user group draft
4422
        $draft = $this->contentService->createContentDraft(
4423
            $this->contentService->loadContentInfo($contentId)
4424
        );
4425
4426
        // Get an update struct and change the group name
4427
        $contentUpdate = $this->contentService->newContentUpdateStruct();
4428
        $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US);
4429
4430
        // Start a transaction
4431
        $repository->beginTransaction();
4432
4433
        try {
4434
            // Update the group name
4435
            $draft = $this->contentService->updateContent(
4436
                $draft->getVersionInfo(),
4437
                $contentUpdate
4438
            );
4439
4440
            // Publish updated version
4441
            $this->contentService->publishVersion($draft->getVersionInfo());
4442
4443
            // Commit all changes.
4444
            $repository->commit();
4445
        } catch (Exception $e) {
4446
            // Cleanup hanging transaction on error
4447
            $repository->rollback();
4448
            throw $e;
4449
        }
4450
4451
        // Name is now "Administrators"
4452
        $name = $this->contentService->loadContent($contentId)->getFieldValue('name', self::ENG_US);
4453
4454
        $this->assertEquals(self::ADMINISTRATORS_USER_GROUP_NAME, $name);
4455
    }
4456
4457
    /**
4458
     * Test for the updateContentMetadata() method.
4459
     *
4460
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
4461
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
4462
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4463
     */
4464
    public function testUpdateContentMetadataInTransactionWithRollback()
4465
    {
4466
        $repository = $this->getRepository();
4467
4468
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4469
4470
        // Load a ContentInfo object
4471
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4472
4473
        // Store remoteId for later testing
4474
        $remoteId = $contentInfo->remoteId;
4475
4476
        // Start a transaction
4477
        $repository->beginTransaction();
4478
4479
        try {
4480
            // Get metadata update struct and change remoteId
4481
            $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
4482
            $metadataUpdate->remoteId = md5(microtime(true));
4483
4484
            // Update the metadata of the published content object
4485
            $this->contentService->updateContentMetadata(
4486
                $contentInfo,
4487
                $metadataUpdate
4488
            );
4489
        } catch (Exception $e) {
4490
            // Cleanup hanging transaction on error
4491
            $repository->rollback();
4492
            throw $e;
4493
        }
4494
4495
        // Rollback all changes.
4496
        $repository->rollback();
4497
4498
        // Load current remoteId
4499
        $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId;
4500
4501
        $this->assertEquals($remoteId, $remoteIdReloaded);
4502
    }
4503
4504
    /**
4505
     * Test for the updateContentMetadata() method.
4506
     *
4507
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
4508
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
4509
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4510
     */
4511
    public function testUpdateContentMetadataInTransactionWithCommit()
4512
    {
4513
        $repository = $this->getRepository();
4514
4515
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4516
4517
        // Load a ContentInfo object
4518
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4519
4520
        // Store remoteId for later testing
4521
        $remoteId = $contentInfo->remoteId;
4522
4523
        // Start a transaction
4524
        $repository->beginTransaction();
4525
4526
        try {
4527
            // Get metadata update struct and change remoteId
4528
            $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
4529
            $metadataUpdate->remoteId = md5(microtime(true));
4530
4531
            // Update the metadata of the published content object
4532
            $this->contentService->updateContentMetadata(
4533
                $contentInfo,
4534
                $metadataUpdate
4535
            );
4536
4537
            // Commit all changes.
4538
            $repository->commit();
4539
        } catch (Exception $e) {
4540
            // Cleanup hanging transaction on error
4541
            $repository->rollback();
4542
            throw $e;
4543
        }
4544
4545
        // Load current remoteId
4546
        $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId;
4547
4548
        $this->assertNotEquals($remoteId, $remoteIdReloaded);
4549
    }
4550
4551
    /**
4552
     * Test for the updateContentMetadata() method, and how cache + transactions play together.
4553
     *
4554
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
4555
     * @depends testUpdateContentMetadata
4556
     * @depends testLoadContentInfo
4557
     */
4558
    public function testUpdateContentMetadataCheckWithinTransaction()
4559
    {
4560
        $repository = $this->getRepository();
4561
        $contentService = $repository->getContentService();
4562
        $contentId = $this->generateId('object', 12);
4563
4564
        // Load a ContentInfo object, and warmup cache
4565
        $contentInfo = $contentService->loadContentInfo($contentId);
4566
4567
        // Store remoteId for later testing
4568
        $remoteId = $contentInfo->remoteId;
4569
4570
        // Start a transaction
4571
        $repository->beginTransaction();
4572
4573
        try {
4574
            // Get metadata update struct and change remoteId
4575
            $metadataUpdate = $contentService->newContentMetadataUpdateStruct();
4576
            $metadataUpdate->remoteId = md5(microtime(true));
4577
4578
            // Update the metadata of the published content object
4579
            $contentService->updateContentMetadata(
4580
                $contentInfo,
4581
                $metadataUpdate
4582
            );
4583
4584
            // Check that it's been updated
4585
            $remoteIdReloaded = $contentService->loadContentInfo($contentId)->remoteId;
4586
            $this->assertNotEquals($remoteId, $remoteIdReloaded);
4587
4588
            // Commit all changes.
4589
            $repository->commit();
4590
        } catch (Exception $e) {
4591
            // Cleanup hanging transaction on error
4592
            $repository->rollback();
4593
            throw $e;
4594
        }
4595
    }
4596
4597
    /**
4598
     * Test for the deleteVersion() method.
4599
     *
4600
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
4601
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4602
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4603
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts
4604
     */
4605
    public function testDeleteVersionInTransactionWithRollback()
4606
    {
4607
        $repository = $this->getRepository();
4608
4609
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4610
4611
        // Start a new transaction
4612
        $repository->beginTransaction();
4613
4614
        try {
4615
            // Create a new draft
4616
            $draft = $this->contentService->createContentDraft(
4617
                $this->contentService->loadContentInfo($contentId)
4618
            );
4619
4620
            $this->contentService->deleteVersion($draft->getVersionInfo());
4621
        } catch (Exception $e) {
4622
            // Cleanup hanging transaction on error
4623
            $repository->rollback();
4624
            throw $e;
4625
        }
4626
4627
        // Rollback all changes.
4628
        $repository->rollback();
4629
4630
        // This array will be empty
4631
        $drafts = $this->contentService->loadContentDrafts();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...ce::loadContentDrafts() has been deprecated with message: Please use {@see loadContentDraftList()} instead to avoid risking loading too much data.

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...
4632
4633
        $this->assertSame([], $drafts);
4634
    }
4635
4636
    /**
4637
     * Test for the deleteVersion() method.
4638
     *
4639
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
4640
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4641
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4642
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts
4643
     */
4644
    public function testDeleteVersionInTransactionWithCommit()
4645
    {
4646
        $repository = $this->getRepository();
4647
4648
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4649
4650
        // Start a new transaction
4651
        $repository->beginTransaction();
4652
4653
        try {
4654
            // Create a new draft
4655
            $draft = $this->contentService->createContentDraft(
4656
                $this->contentService->loadContentInfo($contentId)
4657
            );
4658
4659
            $this->contentService->deleteVersion($draft->getVersionInfo());
4660
4661
            // Commit all changes.
4662
            $repository->commit();
4663
        } catch (Exception $e) {
4664
            // Cleanup hanging transaction on error
4665
            $repository->rollback();
4666
            throw $e;
4667
        }
4668
4669
        // This array will contain no element
4670
        $drafts = $this->contentService->loadContentDrafts();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...ce::loadContentDrafts() has been deprecated with message: Please use {@see loadContentDraftList()} instead to avoid risking loading too much data.

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...
4671
4672
        $this->assertSame([], $drafts);
4673
    }
4674
4675
    /**
4676
     * Test for the deleteContent() method.
4677
     *
4678
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
4679
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent
4680
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4681
     */
4682
    public function testDeleteContentInTransactionWithRollback()
4683
    {
4684
        $repository = $this->getRepository();
4685
4686
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4687
4688
        // Load a ContentInfo instance
4689
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4690
4691
        // Start a new transaction
4692
        $repository->beginTransaction();
4693
4694
        try {
4695
            // Delete content object
4696
            $this->contentService->deleteContent($contentInfo);
4697
        } catch (Exception $e) {
4698
            // Cleanup hanging transaction on error
4699
            $repository->rollback();
4700
            throw $e;
4701
        }
4702
4703
        // Rollback all changes
4704
        $repository->rollback();
4705
4706
        // This call will return the original content object
4707
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4708
4709
        $this->assertEquals($contentId, $contentInfo->id);
4710
    }
4711
4712
    /**
4713
     * Test for the deleteContent() method.
4714
     *
4715
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
4716
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent
4717
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4718
     */
4719
    public function testDeleteContentInTransactionWithCommit()
4720
    {
4721
        $repository = $this->getRepository();
4722
4723
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4724
4725
        // Load a ContentInfo instance
4726
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4727
4728
        // Start a new transaction
4729
        $repository->beginTransaction();
4730
4731
        try {
4732
            // Delete content object
4733
            $this->contentService->deleteContent($contentInfo);
4734
4735
            // Commit all changes
4736
            $repository->commit();
4737
        } catch (Exception $e) {
4738
            // Cleanup hanging transaction on error
4739
            $repository->rollback();
4740
            throw $e;
4741
        }
4742
4743
        // Deleted content info is not found anymore
4744
        try {
4745
            $this->contentService->loadContentInfo($contentId);
4746
        } catch (NotFoundException $e) {
4747
            return;
4748
        }
4749
4750
        $this->fail('Can still load ContentInfo after commit.');
4751
    }
4752
4753
    /**
4754
     * Test for the copyContent() method.
4755
     *
4756
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
4757
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent
4758
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct
4759
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren
4760
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation
4761
     */
4762
    public function testCopyContentInTransactionWithRollback()
4763
    {
4764
        $repository = $this->getRepository();
4765
4766
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4767
        $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID);
4768
4769
        // Load content object to copy
4770
        $content = $this->contentService->loadContent($contentId);
4771
4772
        // Create new target location
4773
        $locationCreate = $this->locationService->newLocationCreateStruct($locationId);
4774
4775
        // Start a new transaction
4776
        $repository->beginTransaction();
4777
4778
        try {
4779
            // Copy content with all versions and drafts
4780
            $this->contentService->copyContent(
4781
                $content->contentInfo,
4782
                $locationCreate
4783
            );
4784
        } catch (Exception $e) {
4785
            // Cleanup hanging transaction on error
4786
            $repository->rollback();
4787
            throw $e;
4788
        }
4789
4790
        // Rollback all changes
4791
        $repository->rollback();
4792
4793
        $this->refreshSearch($repository);
4794
4795
        // This array will only contain a single admin user object
4796
        $locations = $this->locationService->loadLocationChildren(
4797
            $this->locationService->loadLocation($locationId)
4798
        )->locations;
4799
4800
        $this->assertEquals(1, count($locations));
4801
    }
4802
4803
    /**
4804
     * Test for the copyContent() method.
4805
     *
4806
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
4807
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent
4808
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct
4809
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren
4810
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation
4811
     */
4812
    public function testCopyContentInTransactionWithCommit()
4813
    {
4814
        $repository = $this->getRepository();
4815
4816
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4817
        $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID);
4818
4819
        // Load content object to copy
4820
        $content = $this->contentService->loadContent($contentId);
4821
4822
        // Create new target location
4823
        $locationCreate = $this->locationService->newLocationCreateStruct($locationId);
4824
4825
        // Start a new transaction
4826
        $repository->beginTransaction();
4827
4828
        try {
4829
            // Copy content with all versions and drafts
4830
            $this->contentService->copyContent(
4831
                $content->contentInfo,
4832
                $locationCreate
4833
            );
4834
4835
            // Commit all changes
4836
            $repository->commit();
4837
        } catch (Exception $e) {
4838
            // Cleanup hanging transaction on error
4839
            $repository->rollback();
4840
            throw $e;
4841
        }
4842
4843
        $this->refreshSearch($repository);
4844
4845
        // This will contain the admin user and the new child location
4846
        $locations = $this->locationService->loadLocationChildren(
4847
            $this->locationService->loadLocation($locationId)
4848
        )->locations;
4849
4850
        $this->assertEquals(2, count($locations));
4851
    }
4852
4853
    public function testURLAliasesCreatedForNewContent()
4854
    {
4855
        $urlAliasService = $this->getRepository()->getURLAliasService();
4856
4857
        $draft = $this->createContentDraftVersion1();
4858
4859
        // Automatically creates a new URLAlias for the content
4860
        $liveContent = $this->contentService->publishVersion($draft->getVersionInfo());
4861
4862
        $location = $this->locationService->loadLocation(
4863
            $liveContent->getVersionInfo()->getContentInfo()->mainLocationId
4864
        );
4865
4866
        $aliases = $urlAliasService->listLocationAliases($location, false);
4867
4868
        $this->assertAliasesCorrect(
4869
            [
4870
                '/Design/Plain-site/An-awesome-forum' => [
4871
                    'type' => URLAlias::LOCATION,
4872
                    'destination' => $location->id,
4873
                    'path' => '/Design/Plain-site/An-awesome-forum',
4874
                    'languageCodes' => [self::ENG_US],
4875
                    'isHistory' => false,
4876
                    'isCustom' => false,
4877
                    'forward' => false,
4878
                ],
4879
            ],
4880
            $aliases
4881
        );
4882
    }
4883
4884
    public function testURLAliasesCreatedForUpdatedContent()
4885
    {
4886
        $urlAliasService = $this->getRepository()->getURLAliasService();
4887
4888
        $draft = $this->createUpdatedDraftVersion2();
4889
4890
        $location = $this->locationService->loadLocation(
4891
            $draft->getVersionInfo()->getContentInfo()->mainLocationId
4892
        );
4893
4894
        // Load and assert URL aliases before publishing updated Content, so that
4895
        // SPI cache is warmed up and cache invalidation is also tested.
4896
        $aliases = $urlAliasService->listLocationAliases($location, false);
4897
4898
        $this->assertAliasesCorrect(
4899
            [
4900
                '/Design/Plain-site/An-awesome-forum' => [
4901
                    'type' => URLAlias::LOCATION,
4902
                    'destination' => $location->id,
4903
                    'path' => '/Design/Plain-site/An-awesome-forum',
4904
                    'languageCodes' => [self::ENG_US],
4905
                    'alwaysAvailable' => true,
4906
                    'isHistory' => false,
4907
                    'isCustom' => false,
4908
                    'forward' => false,
4909
                ],
4910
            ],
4911
            $aliases
4912
        );
4913
4914
        // Automatically marks old aliases for the content as history
4915
        // and creates new aliases, based on the changes
4916
        $liveContent = $this->contentService->publishVersion($draft->getVersionInfo());
4917
4918
        $location = $this->locationService->loadLocation(
4919
            $liveContent->getVersionInfo()->getContentInfo()->mainLocationId
4920
        );
4921
4922
        $aliases = $urlAliasService->listLocationAliases($location, false);
4923
4924
        $this->assertAliasesCorrect(
4925
            [
4926
                '/Design/Plain-site/An-awesome-forum2' => [
4927
                    'type' => URLAlias::LOCATION,
4928
                    'destination' => $location->id,
4929
                    'path' => '/Design/Plain-site/An-awesome-forum2',
4930
                    'languageCodes' => [self::ENG_US],
4931
                    'alwaysAvailable' => true,
4932
                    'isHistory' => false,
4933
                    'isCustom' => false,
4934
                    'forward' => false,
4935
                ],
4936
                '/Design/Plain-site/An-awesome-forum23' => [
4937
                    'type' => URLAlias::LOCATION,
4938
                    'destination' => $location->id,
4939
                    'path' => '/Design/Plain-site/An-awesome-forum23',
4940
                    'languageCodes' => [self::ENG_GB],
4941
                    'alwaysAvailable' => true,
4942
                    'isHistory' => false,
4943
                    'isCustom' => false,
4944
                    'forward' => false,
4945
                ],
4946
            ],
4947
            $aliases
4948
        );
4949
    }
4950
4951
    public function testCustomURLAliasesNotHistorizedOnUpdatedContent()
4952
    {
4953
        $urlAliasService = $this->getRepository()->getURLAliasService();
4954
4955
        $content = $this->createContentVersion1();
4956
4957
        // Create a custom URL alias
4958
        $urlAliasService->createUrlAlias(
4959
            $this->locationService->loadLocation(
4960
                $content->getVersionInfo()->getContentInfo()->mainLocationId
4961
            ),
4962
            '/my/fancy/story-about-ez-publish',
4963
            self::ENG_US
4964
        );
4965
4966
        $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo);
4967
4968
        $contentUpdate = $this->contentService->newContentUpdateStruct();
4969
        $contentUpdate->initialLanguageCode = self::ENG_US;
4970
        $contentUpdate->setField('name', 'Amazing Bielefeld forum');
4971
4972
        $draftVersion2 = $this->contentService->updateContent(
4973
            $draftVersion2->getVersionInfo(),
4974
            $contentUpdate
4975
        );
4976
4977
        // Only marks auto-generated aliases as history
4978
        // the custom one is left untouched
4979
        $liveContent = $this->contentService->publishVersion($draftVersion2->getVersionInfo());
4980
4981
        $location = $this->locationService->loadLocation(
4982
            $liveContent->getVersionInfo()->getContentInfo()->mainLocationId
4983
        );
4984
4985
        $aliases = $urlAliasService->listLocationAliases($location);
4986
4987
        $this->assertAliasesCorrect(
4988
            [
4989
                '/my/fancy/story-about-ez-publish' => [
4990
                    'type' => URLAlias::LOCATION,
4991
                    'destination' => $location->id,
4992
                    'path' => '/my/fancy/story-about-ez-publish',
4993
                    'languageCodes' => [self::ENG_US],
4994
                    'isHistory' => false,
4995
                    'isCustom' => true,
4996
                    'forward' => false,
4997
                    'alwaysAvailable' => false,
4998
                ],
4999
            ],
5000
            $aliases
5001
        );
5002
    }
5003
5004
    /**
5005
     * Test to ensure that old versions are not affected by updates to newer
5006
     * drafts.
5007
     */
5008
    public function testUpdatingDraftDoesNotUpdateOldVersions()
5009
    {
5010
        $contentVersion2 = $this->createContentVersion2();
5011
5012
        $loadedContent1 = $this->contentService->loadContent($contentVersion2->id, null, 1);
5013
        $loadedContent2 = $this->contentService->loadContent($contentVersion2->id, null, 2);
5014
5015
        $this->assertNotEquals(
5016
            $loadedContent1->getFieldValue('name', self::ENG_US),
5017
            $loadedContent2->getFieldValue('name', self::ENG_US)
5018
        );
5019
    }
5020
5021
    /**
5022
     * Test scenario with writer and publisher users.
5023
     * Writer can only create content. Publisher can publish this content.
5024
     */
5025
    public function testPublishWorkflow()
5026
    {
5027
        $this->createRoleWithPolicies('Publisher', [
5028
            ['module' => 'content', 'function' => 'read'],
5029
            ['module' => 'content', 'function' => 'create'],
5030
            ['module' => 'content', 'function' => 'publish'],
5031
        ]);
5032
5033
        $this->createRoleWithPolicies('Writer', [
5034
            ['module' => 'content', 'function' => 'read'],
5035
            ['module' => 'content', 'function' => 'create'],
5036
        ]);
5037
5038
        $writerUser = $this->createCustomUserWithLogin(
5039
            'writer',
5040
            '[email protected]',
5041
            self::WRITERS_USER_GROUP_NAME,
5042
            'Writer'
5043
        );
5044
5045
        $publisherUser = $this->createCustomUserWithLogin(
5046
            'publisher',
5047
            '[email protected]',
5048
            'Publishers',
5049
            'Publisher'
5050
        );
5051
5052
        $this->permissionResolver->setCurrentUserReference($writerUser);
5053
        $draft = $this->createContentDraftVersion1();
5054
5055
        $this->permissionResolver->setCurrentUserReference($publisherUser);
5056
        $content = $this->contentService->publishVersion($draft->versionInfo);
5057
5058
        $this->contentService->loadContent($content->id);
5059
    }
5060
5061
    /**
5062
     * Test publish / content policy is required to be able to publish content.
5063
     */
5064
    public function testPublishContentWithoutPublishPolicyThrowsException()
5065
    {
5066
        $this->createRoleWithPolicies('Writer', [
5067
            ['module' => 'content', 'function' => 'read'],
5068
            ['module' => 'content', 'function' => 'create'],
5069
            ['module' => 'content', 'function' => 'edit'],
5070
        ]);
5071
        $writerUser = $this->createCustomUserWithLogin(
5072
            'writer',
5073
            '[email protected]',
5074
            self::WRITERS_USER_GROUP_NAME,
5075
            'Writer'
5076
        );
5077
        $this->permissionResolver->setCurrentUserReference($writerUser);
5078
5079
        $this->expectException(CoreUnauthorizedException::class);
5080
        $this->expectExceptionMessageRegExp('/User does not have access to \'publish\' \'content\'/');
5081
5082
        $this->createContentVersion1();
5083
    }
5084
5085
    /**
5086
     * Test removal of the specific translation from all the Versions of a Content Object.
5087
     *
5088
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5089
     */
5090
    public function testDeleteTranslation()
5091
    {
5092
        $content = $this->createContentVersion2();
5093
5094
        // create multiple versions to exceed archive limit
5095
        for ($i = 0; $i < 5; ++$i) {
5096
            $contentDraft = $this->contentService->createContentDraft($content->contentInfo);
5097
            $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
5098
            $contentDraft = $this->contentService->updateContent(
5099
                $contentDraft->versionInfo,
5100
                $contentUpdateStruct
5101
            );
5102
            $this->contentService->publishVersion($contentDraft->versionInfo);
5103
        }
5104
5105
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB);
5106
5107
        $this->assertTranslationDoesNotExist(self::ENG_GB, $content->id);
5108
    }
5109
5110
    /**
5111
     * Test deleting a Translation which is initial for some Version, updates initialLanguageCode
5112
     * with mainLanguageCode (assuming they are different).
5113
     */
5114
    public function testDeleteTranslationUpdatesInitialLanguageCodeVersion()
5115
    {
5116
        $content = $this->createContentVersion2();
5117
        // create another, copied, version
5118
        $contentDraft = $this->contentService->updateContent(
5119
            $this->contentService->createContentDraft($content->contentInfo)->versionInfo,
5120
            $this->contentService->newContentUpdateStruct()
5121
        );
5122
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
5123
5124
        // remove first version with only one translation as it is not the subject of this test
5125
        $this->contentService->deleteVersion(
5126
            $this->contentService->loadVersionInfo($publishedContent->contentInfo, 1)
5127
        );
5128
5129
        // sanity check
5130
        self::assertEquals(self::ENG_US, $content->contentInfo->mainLanguageCode);
5131
        self::assertEquals(self::ENG_US, $content->versionInfo->initialLanguageCode);
5132
5133
        // update mainLanguageCode so it is different than initialLanguageCode for Version
5134
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
5135
        $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB;
5136
        $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct);
5137
5138
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US);
5139
5140
        $this->assertTranslationDoesNotExist(self::ENG_US, $content->id);
5141
    }
5142
5143
    /**
5144
     * Test removal of the specific translation properly updates languages of the URL alias.
5145
     *
5146
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5147
     */
5148
    public function testDeleteTranslationUpdatesUrlAlias()
5149
    {
5150
        $urlAliasService = $this->getRepository()->getURLAliasService();
5151
5152
        $content = $this->createContentVersion2();
5153
        $mainLocation = $this->locationService->loadLocation($content->contentInfo->mainLocationId);
5154
5155
        // create custom URL alias for Content main Location
5156
        $urlAliasService->createUrlAlias($mainLocation, '/my-custom-url', self::ENG_GB);
5157
5158
        // create secondary Location for Content
5159
        $secondaryLocation = $this->locationService->createLocation(
5160
            $content->contentInfo,
5161
            $this->locationService->newLocationCreateStruct(2)
5162
        );
5163
5164
        // create custom URL alias for Content secondary Location
5165
        $urlAliasService->createUrlAlias($secondaryLocation, '/my-secondary-url', self::ENG_GB);
5166
5167
        // delete Translation
5168
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB);
5169
5170
        foreach ([$mainLocation, $secondaryLocation] as $location) {
5171
            // check auto-generated URL aliases
5172
            foreach ($urlAliasService->listLocationAliases($location, false) as $alias) {
5173
                self::assertNotContains(self::ENG_GB, $alias->languageCodes);
5174
            }
5175
5176
            // check custom URL aliases
5177
            foreach ($urlAliasService->listLocationAliases($location) as $alias) {
5178
                self::assertNotContains(self::ENG_GB, $alias->languageCodes);
5179
            }
5180
        }
5181
    }
5182
5183
    /**
5184
     * Test removal of a main translation throws BadStateException.
5185
     *
5186
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5187
     */
5188
    public function testDeleteTranslationMainLanguageThrowsBadStateException()
5189
    {
5190
        $content = $this->createContentVersion2();
5191
5192
        // delete first version which has only one translation
5193
        $this->contentService->deleteVersion($this->contentService->loadVersionInfo($content->contentInfo, 1));
5194
5195
        // try to delete main translation
5196
        $this->expectException(BadStateException::class);
5197
        $this->expectExceptionMessage('Specified translation is the main translation of the Content Object');
5198
5199
        $this->contentService->deleteTranslation($content->contentInfo, $content->contentInfo->mainLanguageCode);
5200
    }
5201
5202
    /**
5203
     * Test removal of a Translation is possible when some archived Versions have only this Translation.
5204
     *
5205
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5206
     */
5207
    public function testDeleteTranslationDeletesSingleTranslationVersions()
5208
    {
5209
        // content created by the createContentVersion1 method has eng-US translation only.
5210
        $content = $this->createContentVersion1();
5211
5212
        // create new version and add eng-GB translation
5213
        $contentDraft = $this->contentService->createContentDraft($content->contentInfo);
5214
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
5215
        $contentUpdateStruct->setField('name', 'Awesome Board', self::ENG_GB);
5216
        $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct);
5217
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
5218
5219
        // update mainLanguageCode to avoid exception related to that
5220
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
5221
        $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB;
5222
5223
        $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct);
5224
5225
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US);
5226
5227
        $this->assertTranslationDoesNotExist(self::ENG_US, $content->id);
5228
    }
5229
5230
    /**
5231
     * Test removal of the translation by the user who is not allowed to delete a content
5232
     * throws UnauthorizedException.
5233
     *
5234
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5235
     */
5236
    public function testDeleteTranslationThrowsUnauthorizedException()
5237
    {
5238
        $content = $this->createContentVersion2();
5239
5240
        // create user that can read/create/edit but cannot delete content
5241
        $this->createRoleWithPolicies('Writer', [
5242
            ['module' => 'content', 'function' => 'read'],
5243
            ['module' => 'content', 'function' => 'versionread'],
5244
            ['module' => 'content', 'function' => 'create'],
5245
            ['module' => 'content', 'function' => 'edit'],
5246
        ]);
5247
        $writerUser = $this->createCustomUserWithLogin(
5248
            'writer',
5249
            '[email protected]',
5250
            self::WRITERS_USER_GROUP_NAME,
5251
            'Writer'
5252
        );
5253
        $this->permissionResolver->setCurrentUserReference($writerUser);
5254
5255
        $this->expectException(UnauthorizedException::class);
5256
        $this->expectExceptionMessage('User does not have access to \'remove\' \'content\'');
5257
5258
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB);
5259
    }
5260
5261
    /**
5262
     * Test removal of a non-existent translation throws InvalidArgumentException.
5263
     *
5264
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5265
     */
5266
    public function testDeleteTranslationThrowsInvalidArgumentException()
5267
    {
5268
        // content created by the createContentVersion1 method has eng-US translation only.
5269
        $content = $this->createContentVersion1();
5270
5271
        $this->expectException(APIInvalidArgumentException::class);
5272
        $this->expectExceptionMessage('Argument \'$languageCode\' is invalid: ger-DE does not exist in the Content item');
5273
5274
        $this->contentService->deleteTranslation($content->contentInfo, self::GER_DE);
5275
    }
5276
5277
    /**
5278
     * Test deleting a Translation from Draft.
5279
     *
5280
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5281
     */
5282
    public function testDeleteTranslationFromDraft()
5283
    {
5284
        $languageCode = self::ENG_GB;
5285
        $content = $this->createMultipleLanguageContentVersion2();
5286
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5287
        $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5288
        $content = $this->contentService->publishVersion($draft->versionInfo);
5289
5290
        $loadedContent = $this->contentService->loadContent($content->id);
5291
        self::assertNotContains($languageCode, $loadedContent->versionInfo->languageCodes);
5292
        self::assertEmpty($loadedContent->getFieldsByLanguage($languageCode));
5293
    }
5294
5295
    /**
5296
     * Get values for multilingual field.
5297
     *
5298
     * @return array
5299
     */
5300
    public function providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing()
5301
    {
5302
        return [
5303
            [
5304
                [self::ENG_US => 'US Name', self::ENG_GB => 'GB Name'],
5305
            ],
5306
            [
5307
                [self::ENG_US => 'Same Name', self::ENG_GB => 'Same Name'],
5308
            ],
5309
        ];
5310
    }
5311
5312
    /**
5313
     * Test deleting a Translation from Draft removes previously stored URL aliases for published Content.
5314
     *
5315
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5316
     *
5317
     * @dataProvider providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing
5318
     *
5319
     * @param string[] $fieldValues translated field values
5320
     *
5321
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
5322
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
5323
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
5324
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
5325
     */
5326
    public function testDeleteTranslationFromDraftRemovesUrlAliasOnPublishing(array $fieldValues)
5327
    {
5328
        $urlAliasService = $this->getRepository()->getURLAliasService();
5329
5330
        // set language code to be removed
5331
        $languageCode = self::ENG_GB;
5332
        $draft = $this->createMultilingualContentDraft(
5333
            'folder',
5334
            2,
5335
            self::ENG_US,
5336
            [
5337
                'name' => [
5338
                    self::ENG_GB => $fieldValues[self::ENG_GB],
5339
                    self::ENG_US => $fieldValues[self::ENG_US],
5340
                ],
5341
            ]
5342
        );
5343
        $content = $this->contentService->publishVersion($draft->versionInfo);
5344
5345
        // create secondary location
5346
        $this->locationService->createLocation(
5347
            $content->contentInfo,
5348
            $this->locationService->newLocationCreateStruct(5)
5349
        );
5350
5351
        // sanity check
5352
        $locations = $this->locationService->loadLocations($content->contentInfo);
5353
        self::assertCount(2, $locations, 'Sanity check: Expected to find 2 Locations');
5354
        foreach ($locations as $location) {
5355
            $urlAliasService->createUrlAlias($location, '/us-custom_' . $location->id, self::ENG_US);
5356
            $urlAliasService->createUrlAlias($location, '/gb-custom_' . $location->id, self::ENG_GB);
5357
5358
            // check default URL aliases
5359
            $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode);
5360
            self::assertNotEmpty($aliases, 'Sanity check: URL alias for the translation does not exist');
5361
5362
            // check custom URL aliases
5363
            $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode);
5364
            self::assertNotEmpty($aliases, 'Sanity check: Custom URL alias for the translation does not exist');
5365
        }
5366
5367
        // delete translation and publish new version
5368
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5369
        $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5370
        $this->contentService->publishVersion($draft->versionInfo);
5371
5372
        // check that aliases does not exist
5373
        foreach ($locations as $location) {
5374
            // check default URL aliases
5375
            $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode);
5376
            self::assertEmpty($aliases, 'URL alias for the deleted translation still exists');
5377
5378
            // check custom URL aliases
5379
            $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode);
5380
            self::assertEmpty($aliases, 'Custom URL alias for the deleted translation still exists');
5381
        }
5382
    }
5383
5384
    /**
5385
     * Test that URL aliases for deleted Translations are properly archived.
5386
     */
5387
    public function testDeleteTranslationFromDraftArchivesUrlAliasOnPublishing()
5388
    {
5389
        $urlAliasService = $this->getRepository()->getURLAliasService();
5390
5391
        $content = $this->contentService->publishVersion(
5392
            $this->createMultilingualContentDraft(
5393
                'folder',
5394
                2,
5395
                self::ENG_US,
5396
                [
5397
                    'name' => [
5398
                        self::ENG_GB => 'BritishEnglishContent',
5399
                        self::ENG_US => 'AmericanEnglishContent',
5400
                    ],
5401
                ]
5402
            )->versionInfo
5403
        );
5404
5405
        $unrelatedContent = $this->contentService->publishVersion(
5406
            $this->createMultilingualContentDraft(
5407
                'folder',
5408
                2,
5409
                self::ENG_US,
5410
                [
5411
                    'name' => [
5412
                        self::ENG_GB => 'AnotherBritishContent',
5413
                        self::ENG_US => 'AnotherAmericanContent',
5414
                    ],
5415
                ]
5416
            )->versionInfo
5417
        );
5418
5419
        $urlAlias = $urlAliasService->lookup('/BritishEnglishContent');
5420
        self::assertFalse($urlAlias->isHistory);
5421
        self::assertEquals($urlAlias->path, '/BritishEnglishContent');
5422
        self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId);
5423
5424
        $draft = $this->contentService->deleteTranslationFromDraft(
5425
            $this->contentService->createContentDraft($content->contentInfo)->versionInfo,
5426
            self::ENG_GB
5427
        );
5428
        $content = $this->contentService->publishVersion($draft->versionInfo);
5429
5430
        $urlAlias = $urlAliasService->lookup('/BritishEnglishContent');
5431
        self::assertTrue($urlAlias->isHistory);
5432
        self::assertEquals($urlAlias->path, '/BritishEnglishContent');
5433
        self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId);
5434
5435
        $unrelatedUrlAlias = $urlAliasService->lookup('/AnotherBritishContent');
5436
        self::assertFalse($unrelatedUrlAlias->isHistory);
5437
        self::assertEquals($unrelatedUrlAlias->path, '/AnotherBritishContent');
5438
        self::assertEquals($unrelatedUrlAlias->destination, $unrelatedContent->contentInfo->mainLocationId);
5439
    }
5440
5441
    /**
5442
     * Test deleting a Translation from Draft which has single Translation throws BadStateException.
5443
     *
5444
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5445
     */
5446
    public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnSingleTranslation()
5447
    {
5448
        // create Content with single Translation
5449
        $publishedContent = $this->contentService->publishVersion(
5450
            $this->createContentDraft(
5451
                self::FORUM_IDENTIFIER,
5452
                2,
5453
                ['name' => 'Eng-US Version name']
5454
            )->versionInfo
5455
        );
5456
5457
        // update mainLanguageCode to avoid exception related to trying to delete main Translation
5458
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
5459
        $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB;
5460
        $publishedContent = $this->contentService->updateContentMetadata(
5461
            $publishedContent->contentInfo,
5462
            $contentMetadataUpdateStruct
5463
        );
5464
5465
        // create single Translation Version from the first one
5466
        $draft = $this->contentService->createContentDraft(
5467
            $publishedContent->contentInfo,
5468
            $publishedContent->versionInfo
5469
        );
5470
5471
        $this->expectException(BadStateException::class);
5472
        $this->expectExceptionMessage('Specified Translation is the only one Content Object Version has');
5473
5474
        // attempt to delete Translation
5475
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, self::ENG_US);
5476
    }
5477
5478
    /**
5479
     * Test deleting the Main Translation from Draft throws BadStateException.
5480
     *
5481
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5482
     */
5483
    public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnMainTranslation()
5484
    {
5485
        $mainLanguageCode = self::ENG_US;
5486
        $draft = $this->createMultilingualContentDraft(
5487
            self::FORUM_IDENTIFIER,
5488
            2,
5489
            $mainLanguageCode,
5490
            [
5491
                'name' => [
5492
                    self::ENG_US => 'An awesome eng-US forum',
5493
                    self::ENG_GB => 'An awesome eng-GB forum',
5494
                ],
5495
            ]
5496
        );
5497
5498
        $this->expectException(BadStateException::class);
5499
        $this->expectExceptionMessage('Specified Translation is the main Translation of the Content Object');
5500
5501
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $mainLanguageCode);
5502
    }
5503
5504
    /**
5505
     * Test deleting the Translation from Published Version throws BadStateException.
5506
     *
5507
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5508
     */
5509
    public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnPublishedVersion()
5510
    {
5511
        $languageCode = self::ENG_US;
5512
        $content = $this->createMultipleLanguageContentVersion2();
5513
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5514
        $publishedContent = $this->contentService->publishVersion($draft->versionInfo);
5515
5516
        $this->expectException(BadStateException::class);
5517
        $this->expectExceptionMessage('Version is not a draft');
5518
5519
        $this->contentService->deleteTranslationFromDraft($publishedContent->versionInfo, $languageCode);
5520
    }
5521
5522
    /**
5523
     * Test deleting a Translation from Draft throws UnauthorizedException if user cannot edit Content.
5524
     *
5525
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5526
     */
5527
    public function testDeleteTranslationFromDraftThrowsUnauthorizedException()
5528
    {
5529
        $languageCode = self::ENG_GB;
5530
        $content = $this->createMultipleLanguageContentVersion2();
5531
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5532
5533
        // create user that can read/create/delete but cannot edit or content
5534
        $this->createRoleWithPolicies('Writer', [
5535
            ['module' => 'content', 'function' => 'read'],
5536
            ['module' => 'content', 'function' => 'versionread'],
5537
            ['module' => 'content', 'function' => 'create'],
5538
            ['module' => 'content', 'function' => 'delete'],
5539
        ]);
5540
        $writerUser = $this->createCustomUserWithLogin(
5541
            'user',
5542
            '[email protected]',
5543
            self::WRITERS_USER_GROUP_NAME,
5544
            'Writer'
5545
        );
5546
        $this->permissionResolver->setCurrentUserReference($writerUser);
5547
5548
        $this->expectException(UnauthorizedException::class);
5549
        $this->expectExceptionMessage('User does not have access to \'edit\' \'content\'');
5550
5551
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5552
    }
5553
5554
    /**
5555
     * Test deleting a non-existent Translation from Draft throws InvalidArgumentException.
5556
     *
5557
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5558
     */
5559
    public function testDeleteTranslationFromDraftThrowsInvalidArgumentException()
5560
    {
5561
        $languageCode = self::GER_DE;
5562
        $content = $this->createMultipleLanguageContentVersion2();
5563
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5564
        $this->expectException(APIInvalidArgumentException::class);
5565
        $this->expectExceptionMessageRegExp('/The Version \(ContentId=\d+, VersionNo=\d+\) is not translated into ger-DE/');
5566
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5567
    }
5568
5569
    /**
5570
     * Test loading list of Content items.
5571
     */
5572
    public function testLoadContentListByContentInfo()
5573
    {
5574
        $allLocationsCount = $this->locationService->getAllLocationsCount();
5575
        $contentInfoList = array_map(
5576
            function (Location $location) {
5577
                return $location->contentInfo;
5578
            },
5579
            $this->locationService->loadAllLocations(0, $allLocationsCount)
5580
        );
5581
5582
        $contentList = $this->contentService->loadContentListByContentInfo($contentInfoList);
5583
        self::assertCount(count($contentInfoList), $contentList);
5584
        foreach ($contentList as $content) {
5585
            try {
5586
                $loadedContent = $this->contentService->loadContent($content->id);
5587
                self::assertEquals($loadedContent, $content, "Failed to properly bulk-load Content {$content->id}");
5588
            } catch (NotFoundException $e) {
5589
                self::fail("Failed to load Content {$content->id}: {$e->getMessage()}");
5590
            } catch (UnauthorizedException $e) {
5591
                self::fail("Failed to load Content {$content->id}: {$e->getMessage()}");
5592
            }
5593
        }
5594
    }
5595
5596
    /**
5597
     * Test loading content versions after removing exactly two drafts.
5598
     *
5599
     * @see https://jira.ez.no/browse/EZP-30271
5600
     *
5601
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteVersion
5602
     */
5603
    public function testLoadVersionsAfterDeletingTwoDrafts()
5604
    {
5605
        $content = $this->createFolder([self::ENG_GB => 'Foo'], 2);
5606
5607
        // First update and publish
5608
        $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo1']);
5609
        $content = $this->contentService->publishVersion($modifiedContent->versionInfo);
5610
5611
        // Second update and publish
5612
        $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo2']);
5613
        $content = $this->contentService->publishVersion($modifiedContent->versionInfo);
5614
5615
        // Create drafts
5616
        $this->updateFolder($content, [self::ENG_GB => 'Foo3']);
5617
        $this->updateFolder($content, [self::ENG_GB => 'Foo4']);
5618
5619
        $versions = $this->contentService->loadVersions($content->contentInfo);
5620
5621
        foreach ($versions as $key => $version) {
5622
            if ($version->isDraft()) {
5623
                $this->contentService->deleteVersion($version);
5624
                unset($versions[$key]);
5625
            }
5626
        }
5627
5628
        $this->assertEquals($versions, $this->contentService->loadVersions($content->contentInfo));
5629
    }
5630
5631
    /**
5632
     * Tests loading list of content versions of status draft.
5633
     */
5634
    public function testLoadVersionsOfStatusDraft()
5635
    {
5636
        $content = $this->createContentVersion1();
5637
5638
        $this->contentService->createContentDraft($content->contentInfo);
5639
        $this->contentService->createContentDraft($content->contentInfo);
5640
        $this->contentService->createContentDraft($content->contentInfo);
5641
5642
        $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_DRAFT);
5643
5644
        $this->assertSame(\count($versions), 3);
5645
    }
5646
5647
    /**
5648
     * Tests loading list of content versions of status archived.
5649
     */
5650
    public function testLoadVersionsOfStatusArchived()
5651
    {
5652
        $content = $this->createContentVersion1();
5653
5654
        $draft1 = $this->contentService->createContentDraft($content->contentInfo);
5655
        $this->contentService->publishVersion($draft1->versionInfo);
5656
5657
        $draft2 = $this->contentService->createContentDraft($content->contentInfo);
5658
        $this->contentService->publishVersion($draft2->versionInfo);
5659
5660
        $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_ARCHIVED);
5661
5662
        $this->assertSame(\count($versions), 2);
5663
    }
5664
5665
    /**
5666
     * Asserts that all aliases defined in $expectedAliasProperties with the
5667
     * given properties are available in $actualAliases and not more.
5668
     *
5669
     * @param array $expectedAliasProperties
5670
     * @param array $actualAliases
5671
     */
5672
    private function assertAliasesCorrect(array $expectedAliasProperties, array $actualAliases)
5673
    {
5674
        foreach ($actualAliases as $actualAlias) {
5675
            if (!isset($expectedAliasProperties[$actualAlias->path])) {
5676
                $this->fail(
5677
                    sprintf(
5678
                        'Alias with path "%s" in languages "%s" not expected.',
5679
                        $actualAlias->path,
5680
                        implode(', ', $actualAlias->languageCodes)
5681
                    )
5682
                );
5683
            }
5684
5685
            foreach ($expectedAliasProperties[$actualAlias->path] as $propertyName => $propertyValue) {
5686
                $this->assertEquals(
5687
                    $propertyValue,
5688
                    $actualAlias->$propertyName,
5689
                    sprintf(
5690
                        'Property $%s incorrect on alias with path "%s" in languages "%s".',
5691
                        $propertyName,
5692
                        $actualAlias->path,
5693
                        implode(', ', $actualAlias->languageCodes)
5694
                    )
5695
                );
5696
            }
5697
5698
            unset($expectedAliasProperties[$actualAlias->path]);
5699
        }
5700
5701
        if (!empty($expectedAliasProperties)) {
5702
            $this->fail(
5703
                sprintf(
5704
                    'Missing expected aliases with paths "%s".',
5705
                    implode('", "', array_keys($expectedAliasProperties))
5706
                )
5707
            );
5708
        }
5709
    }
5710
5711
    /**
5712
     * Asserts that the given fields are equal to the default fields fixture.
5713
     *
5714
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields
5715
     */
5716
    private function assertAllFieldsEquals(array $fields)
5717
    {
5718
        $actual = $this->normalizeFields($fields);
5719
        $expected = $this->normalizeFields($this->createFieldsFixture());
5720
5721
        $this->assertEquals($expected, $actual);
5722
    }
5723
5724
    /**
5725
     * Asserts that the given fields are equal to a language filtered set of the
5726
     * default fields fixture.
5727
     *
5728
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields
5729
     * @param string $languageCode
5730
     */
5731
    private function assertLocaleFieldsEquals(array $fields, $languageCode)
5732
    {
5733
        $actual = $this->normalizeFields($fields);
5734
5735
        $expected = [];
5736
        foreach ($this->normalizeFields($this->createFieldsFixture()) as $field) {
5737
            if ($field->languageCode !== $languageCode) {
5738
                continue;
5739
            }
5740
            $expected[] = $field;
5741
        }
5742
5743
        $this->assertEquals($expected, $actual);
5744
    }
5745
5746
    /**
5747
     * This method normalizes a set of fields and returns a normalized set.
5748
     *
5749
     * Normalization means it resets the storage specific field id to zero and
5750
     * it sorts the field by their identifier and their language code. In
5751
     * addition, the field value is removed, since this one depends on the
5752
     * specific FieldType, which is tested in a dedicated integration test.
5753
     *
5754
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields
5755
     *
5756
     * @return \eZ\Publish\API\Repository\Values\Content\Field[]
5757
     */
5758
    private function normalizeFields(array $fields)
5759
    {
5760
        $normalized = [];
5761
        foreach ($fields as $field) {
5762
            $normalized[] = new Field(
5763
                [
5764
                    'id' => 0,
5765
                    'value' => $field->value !== null,
5766
                    'languageCode' => $field->languageCode,
5767
                    'fieldDefIdentifier' => $field->fieldDefIdentifier,
5768
                    'fieldTypeIdentifier' => $field->fieldTypeIdentifier,
5769
                ]
5770
            );
5771
        }
5772
        usort(
5773
            $normalized,
5774
            function ($field1, $field2) {
5775
                if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) {
5776
                    return strcasecmp($field1->languageCode, $field2->languageCode);
5777
                }
5778
5779
                return $return;
5780
            }
5781
        );
5782
5783
        return $normalized;
5784
    }
5785
5786
    /**
5787
     * Asserts that given Content has default ContentStates.
5788
     *
5789
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
5790
     */
5791
    private function assertDefaultContentStates(ContentInfo $contentInfo)
5792
    {
5793
        $objectStateService = $this->getRepository()->getObjectStateService();
5794
5795
        $objectStateGroups = $objectStateService->loadObjectStateGroups();
5796
5797
        foreach ($objectStateGroups as $objectStateGroup) {
5798
            $contentState = $objectStateService->getContentState($contentInfo, $objectStateGroup);
5799
            foreach ($objectStateService->loadObjectStates($objectStateGroup) as $objectState) {
5800
                // Only check the first object state which is the default one.
5801
                $this->assertEquals(
5802
                    $objectState,
5803
                    $contentState
5804
                );
5805
                break;
5806
            }
5807
        }
5808
    }
5809
5810
    /**
5811
     * Assert that given Content has no references to a translation specified by the $languageCode.
5812
     *
5813
     * @param string $languageCode
5814
     * @param int $contentId
5815
     */
5816
    private function assertTranslationDoesNotExist($languageCode, $contentId)
5817
    {
5818
        $content = $this->contentService->loadContent($contentId);
5819
5820
        foreach ($content->fields as $field) {
5821
            /** @var array $field */
5822
            self::assertArrayNotHasKey($languageCode, $field);
5823
            self::assertNotEquals($languageCode, $content->contentInfo->mainLanguageCode);
5824
            self::assertArrayNotHasKey($languageCode, $content->versionInfo->getNames());
5825
            self::assertNotEquals($languageCode, $content->versionInfo->initialLanguageCode);
5826
            self::assertNotContains($languageCode, $content->versionInfo->languageCodes);
5827
        }
5828
        foreach ($this->contentService->loadVersions($content->contentInfo) as $versionInfo) {
5829
            self::assertArrayNotHasKey($languageCode, $versionInfo->getNames());
5830
            self::assertNotEquals($languageCode, $versionInfo->contentInfo->mainLanguageCode);
5831
            self::assertNotEquals($languageCode, $versionInfo->initialLanguageCode);
5832
            self::assertNotContains($languageCode, $versionInfo->languageCodes);
5833
        }
5834
    }
5835
5836
    /**
5837
     * Returns the default fixture of fields used in most tests.
5838
     *
5839
     * @return \eZ\Publish\API\Repository\Values\Content\Field[]
5840
     */
5841
    private function createFieldsFixture()
5842
    {
5843
        return [
5844
            new Field(
5845
                [
5846
                    'id' => 0,
5847
                    'value' => 'Foo',
5848
                    'languageCode' => self::ENG_US,
5849
                    'fieldDefIdentifier' => 'description',
5850
                    'fieldTypeIdentifier' => 'ezrichtext',
5851
                ]
5852
            ),
5853
            new Field(
5854
                [
5855
                    'id' => 0,
5856
                    'value' => 'Bar',
5857
                    'languageCode' => self::ENG_GB,
5858
                    'fieldDefIdentifier' => 'description',
5859
                    'fieldTypeIdentifier' => 'ezrichtext',
5860
                ]
5861
            ),
5862
            new Field(
5863
                [
5864
                    'id' => 0,
5865
                    'value' => 'An awesome multi-lang forum²',
5866
                    'languageCode' => self::ENG_US,
5867
                    'fieldDefIdentifier' => 'name',
5868
                    'fieldTypeIdentifier' => 'ezstring',
5869
                ]
5870
            ),
5871
            new Field(
5872
                [
5873
                    'id' => 0,
5874
                    'value' => 'An awesome multi-lang forum²³',
5875
                    'languageCode' => self::ENG_GB,
5876
                    'fieldDefIdentifier' => 'name',
5877
                    'fieldTypeIdentifier' => 'ezstring',
5878
                ]
5879
            ),
5880
        ];
5881
    }
5882
5883
    /**
5884
     * Gets expected property values for the "Media" ContentInfo ValueObject.
5885
     *
5886
     * @return array
5887
     */
5888
    private function getExpectedMediaContentInfoProperties()
5889
    {
5890
        return [
5891
            'id' => self::MEDIA_CONTENT_ID,
5892
            'contentTypeId' => 1,
5893
            'name' => 'Media',
5894
            'sectionId' => 3,
5895
            'currentVersionNo' => 1,
5896
            'published' => true,
5897
            'ownerId' => 14,
5898
            'modificationDate' => $this->createDateTime(1060695457),
5899
            'publishedDate' => $this->createDateTime(1060695457),
5900
            'alwaysAvailable' => 1,
5901
            'remoteId' => self::MEDIA_REMOTE_ID,
5902
            'mainLanguageCode' => self::ENG_US,
5903
            'mainLocationId' => 43,
5904
            'status' => ContentInfo::STATUS_PUBLISHED,
5905
        ];
5906
    }
5907
5908
    /**
5909
     * @covers \eZ\Publish\API\Repository\ContentService::hideContent
5910
     *
5911
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
5912
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
5913
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException
5914
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
5915
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
5916
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
5917
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
5918
     */
5919
    public function testHideContent(): void
5920
    {
5921
        $contentTypeService = $this->getRepository()->getContentTypeService();
5922
5923
        $locationCreateStructs = array_map(
5924
            function (Location $parentLocation) {
5925
                return $this->locationService->newLocationCreateStruct($parentLocation->id);
5926
            },
5927
            $this->createParentLocationsForHideReveal(2)
5928
        );
5929
5930
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
5931
5932
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
5933
        $contentCreate->setField('name', 'Folder to hide');
5934
5935
        $content = $this->contentService->createContent(
5936
            $contentCreate,
5937
            $locationCreateStructs
5938
        );
5939
5940
        $publishedContent = $this->contentService->publishVersion($content->versionInfo);
5941
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
5942
5943
        // Sanity check
5944
        $this->assertCount(3, $locations);
5945
        $this->assertCount(0, $this->filterHiddenLocations($locations));
5946
5947
        $this->contentService->hideContent($publishedContent->contentInfo);
5948
5949
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
5950
        $this->assertCount(3, $locations);
5951
        $this->assertCount(3, $this->filterHiddenLocations($locations));
5952
    }
5953
5954
    /**
5955
     * @covers \eZ\Publish\API\Repository\ContentService::revealContent
5956
     *
5957
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
5958
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
5959
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
5960
     */
5961
    public function testRevealContent()
5962
    {
5963
        $contentTypeService = $this->getRepository()->getContentTypeService();
5964
5965
        $locationCreateStructs = array_map(
5966
            function (Location $parentLocation) {
5967
                return $this->locationService->newLocationCreateStruct($parentLocation->id);
5968
            },
5969
            $this->createParentLocationsForHideReveal(2)
5970
        );
5971
5972
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
5973
5974
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
5975
        $contentCreate->setField('name', 'Folder to hide');
5976
5977
        $locationCreateStructs[0]->hidden = true;
5978
5979
        $content = $this->contentService->createContent(
5980
            $contentCreate,
5981
            $locationCreateStructs
5982
        );
5983
5984
        $publishedContent = $this->contentService->publishVersion($content->versionInfo);
5985
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
5986
5987
        // Sanity check
5988
        $hiddenLocations = $this->filterHiddenLocations($locations);
5989
        $this->assertCount(3, $locations);
5990
        $this->assertCount(1, $hiddenLocations);
5991
5992
        $this->contentService->hideContent($publishedContent->contentInfo);
5993
        $this->assertCount(
5994
            3,
5995
            $this->filterHiddenLocations(
5996
                $this->locationService->loadLocations($publishedContent->contentInfo)
5997
            )
5998
        );
5999
6000
        $this->contentService->revealContent($publishedContent->contentInfo);
6001
6002
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
6003
        $hiddenLocationsAfterReveal = $this->filterHiddenLocations($locations);
6004
        $this->assertCount(3, $locations);
6005
        $this->assertCount(1, $hiddenLocationsAfterReveal);
6006
        $this->assertEquals($hiddenLocations, $hiddenLocationsAfterReveal);
6007
    }
6008
6009
    /**
6010
     * @depends testRevealContent
6011
     */
6012
    public function testRevealContentWithHiddenParent()
6013
    {
6014
        $contentTypeService = $this->getRepository()->getContentTypeService();
6015
6016
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
6017
6018
        $contentNames = [
6019
            'Parent Content',
6020
            'Child (Nesting 1)',
6021
            'Child (Nesting 2)',
6022
            'Child (Nesting 3)',
6023
            'Child (Nesting 4)',
6024
        ];
6025
6026
        $parentLocation = $this->locationService->newLocationCreateStruct(
6027
            $this->generateId('location', 2)
6028
        );
6029
6030
        /** @var Content[] $contents */
6031
        $contents = [];
6032
6033
        foreach ($contentNames as $contentName) {
6034
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6035
            $contentCreate->setField('name', $contentName);
6036
6037
            $content = $this->contentService->createContent($contentCreate, [$parentLocation]);
6038
            $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo);
6039
6040
            $parentLocation = $this->locationService->newLocationCreateStruct(
6041
                $this->generateId('location', $publishedContent->contentInfo->mainLocationId)
6042
            );
6043
        }
6044
6045
        $this->contentService->hideContent($contents[0]->contentInfo);
6046
        $this->contentService->hideContent($contents[2]->contentInfo);
6047
        $this->contentService->revealContent($contents[2]->contentInfo);
6048
6049
        $parentContent = $this->contentService->loadContent($contents[0]->id);
6050
        $parentLocation = $this->locationService->loadLocation($parentContent->contentInfo->mainLocationId);
6051
        $parentSublocations = $this->locationService->loadLocationList([
6052
            $contents[1]->contentInfo->mainLocationId,
6053
            $contents[2]->contentInfo->mainLocationId,
6054
            $contents[3]->contentInfo->mainLocationId,
6055
            $contents[4]->contentInfo->mainLocationId,
6056
        ]);
6057
6058
        // Parent remains invisible
6059
        self::assertTrue($parentLocation->invisible);
6060
6061
        // All parent sublocations remain invisible as well
6062
        foreach ($parentSublocations as $parentSublocation) {
6063
            self::assertTrue($parentSublocation->invisible);
6064
        }
6065
    }
6066
6067
    /**
6068
     * @depends testRevealContent
6069
     */
6070
    public function testRevealContentWithHiddenChildren()
6071
    {
6072
        $contentTypeService = $this->getRepository()->getContentTypeService();
6073
6074
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
6075
6076
        $contentNames = [
6077
            'Parent Content',
6078
            'Child (Nesting 1)',
6079
            'Child (Nesting 2)',
6080
            'Child (Nesting 3)',
6081
            'Child (Nesting 4)',
6082
        ];
6083
6084
        $parentLocation = $this->locationService->newLocationCreateStruct(
6085
            $this->generateId('location', 2)
6086
        );
6087
6088
        /** @var Content[] $contents */
6089
        $contents = [];
6090
6091
        foreach ($contentNames as $contentName) {
6092
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6093
            $contentCreate->setField('name', $contentName);
6094
6095
            $content = $this->contentService->createContent($contentCreate, [$parentLocation]);
6096
            $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo);
6097
6098
            $parentLocation = $this->locationService->newLocationCreateStruct(
6099
                $this->generateId('location', $publishedContent->contentInfo->mainLocationId)
6100
            );
6101
        }
6102
6103
        $this->contentService->hideContent($contents[0]->contentInfo);
6104
        $this->contentService->hideContent($contents[2]->contentInfo);
6105
        $this->contentService->revealContent($contents[0]->contentInfo);
6106
6107
        $directChildContent = $this->contentService->loadContent($contents[1]->id);
6108
        $directChildLocation = $this->locationService->loadLocation($directChildContent->contentInfo->mainLocationId);
6109
6110
        $childContent = $this->contentService->loadContent($contents[2]->id);
6111
        $childLocation = $this->locationService->loadLocation($childContent->contentInfo->mainLocationId);
6112
        $childSublocations = $this->locationService->loadLocationList([
6113
            $contents[3]->contentInfo->mainLocationId,
6114
            $contents[4]->contentInfo->mainLocationId,
6115
        ]);
6116
6117
        // Direct child content is not hidden
6118
        self::assertFalse($directChildContent->contentInfo->isHidden);
6119
6120
        // Direct child content location is still invisible
6121
        self::assertFalse($directChildLocation->invisible);
6122
6123
        // Child content is still hidden
6124
        self::assertTrue($childContent->contentInfo->isHidden);
6125
6126
        // Child content location is still invisible
6127
        self::assertTrue($childLocation->invisible);
6128
6129
        // All childs sublocations remain invisible as well
6130
        foreach ($childSublocations as $childSublocation) {
6131
            self::assertTrue($childSublocation->invisible);
6132
        }
6133
    }
6134
6135
    public function testHideContentWithParentLocation()
6136
    {
6137
        $contentTypeService = $this->getRepository()->getContentTypeService();
6138
6139
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
6140
6141
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6142
        $contentCreate->setField('name', 'Parent');
6143
6144
        $content = $this->contentService->createContent(
6145
            $contentCreate,
6146
            [
6147
                $this->locationService->newLocationCreateStruct(
6148
                    $this->generateId('location', 2)
6149
                ),
6150
            ]
6151
        );
6152
6153
        $publishedContent = $this->contentService->publishVersion($content->versionInfo);
6154
6155
        $this->contentService->hideContent($publishedContent->contentInfo);
6156
6157
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
6158
6159
        $childContentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6160
        $childContentCreate->setField('name', 'Child');
6161
6162
        $childContent = $this->contentService->createContent(
6163
            $childContentCreate,
6164
            [
6165
                $this->locationService->newLocationCreateStruct(
6166
                    $locations[0]->id
6167
                ),
6168
            ]
6169
        );
6170
6171
        $publishedChildContent = $this->contentService->publishVersion($childContent->versionInfo);
6172
6173
        $childLocations = $this->locationService->loadLocations($publishedChildContent->contentInfo);
6174
6175
        $this->assertTrue($locations[0]->hidden);
6176
        $this->assertTrue($locations[0]->invisible);
6177
6178
        $this->assertFalse($childLocations[0]->hidden);
6179
        $this->assertTrue($childLocations[0]->invisible);
6180
    }
6181
6182
    public function testChangeContentName()
6183
    {
6184
        $contentDraft = $this->createContentDraft(
6185
            'folder',
6186
            $this->generateId('location', 2),
6187
            [
6188
                'name' => 'Marco',
6189
            ]
6190
        );
6191
6192
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
6193
        $contentMetadataUpdateStruct = new ContentMetadataUpdateStruct([
6194
            'name' => 'Polo',
6195
        ]);
6196
        $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct);
6197
6198
        $updatedContent = $this->contentService->loadContent($publishedContent->id);
6199
6200
        $this->assertEquals('Marco', $publishedContent->contentInfo->name);
6201
        $this->assertEquals('Polo', $updatedContent->contentInfo->name);
6202
    }
6203
6204
    public function testCopyTranslationsFromPublishedToDraft()
6205
    {
6206
        $contentDraft = $this->createContentDraft(
6207
            'folder',
6208
            $this->generateId('location', 2),
6209
            [
6210
                'name' => 'Folder US',
6211
            ]
6212
        );
6213
6214
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
6215
6216
        $deDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6217
6218
        $contentUpdateStruct = new ContentUpdateStruct([
6219
            'initialLanguageCode' => self::GER_DE,
6220
            'fields' => $contentDraft->getFields(),
6221
        ]);
6222
6223
        $contentUpdateStruct->setField('name', 'Folder GER', self::GER_DE);
6224
6225
        $deContent = $this->contentService->updateContent($deDraft->versionInfo, $contentUpdateStruct);
6226
6227
        $updatedContent = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo);
6228
        $this->assertEquals(
6229
            [
6230
                self::ENG_US => 'Folder US',
6231
                self::GER_DE => 'Folder GER',
6232
            ],
6233
            $updatedContent->fields['name']
6234
        );
6235
6236
        $gbDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6237
6238
        $contentUpdateStruct = new ContentUpdateStruct([
6239
            'initialLanguageCode' => self::ENG_GB,
6240
            'fields' => $contentDraft->getFields(),
6241
        ]);
6242
6243
        $contentUpdateStruct->setField('name', 'Folder GB', self::ENG_GB);
6244
6245
        $gbContent = $this->contentService->updateContent($gbDraft->versionInfo, $contentUpdateStruct);
6246
        $this->contentService->publishVersion($gbDraft->versionInfo);
6247
        $updatedContent = $this->contentService->loadContent($gbContent->id, null, $gbContent->versionInfo->versionNo);
6248
        $this->assertEquals(
6249
            [
6250
                self::ENG_US => 'Folder US',
6251
                self::ENG_GB => 'Folder GB',
6252
            ],
6253
            $updatedContent->fields['name']
6254
        );
6255
6256
        $dePublished = $this->contentService->publishVersion($deDraft->versionInfo);
6257
        $this->assertEquals(
6258
            [
6259
                self::ENG_US => 'Folder US',
6260
                self::GER_DE => 'Folder GER',
6261
                self::ENG_GB => 'Folder GB',
6262
            ],
6263
            $dePublished->fields['name']
6264
        );
6265
    }
6266
6267
    /**
6268
     * Create structure of parent folders with Locations to be used for Content hide/reveal tests.
6269
     *
6270
     * @param int $parentLocationId
6271
     *
6272
     * @return \eZ\Publish\API\Repository\Values\Content\Location[] A list of Locations aimed to be parents
6273
     *
6274
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
6275
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
6276
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6277
     */
6278
    private function createParentLocationsForHideReveal(int $parentLocationId): array
6279
    {
6280
        $parentFoldersLocationsIds = [
6281
            $this->createFolder([self::ENG_US => 'P1'], $parentLocationId)->contentInfo->mainLocationId,
6282
            $this->createFolder([self::ENG_US => 'P2'], $parentLocationId)->contentInfo->mainLocationId,
6283
            $this->createFolder([self::ENG_US => 'P3'], $parentLocationId)->contentInfo->mainLocationId,
6284
        ];
6285
6286
        return array_values($this->locationService->loadLocationList($parentFoldersLocationsIds));
6287
    }
6288
6289
    /**
6290
     * Filter Locations list by hidden only.
6291
     *
6292
     * @param \eZ\Publish\API\Repository\Values\Content\Location[] $locations
6293
     *
6294
     * @return array
6295
     */
6296
    private function filterHiddenLocations(array $locations): array
6297
    {
6298
        return array_values(
6299
            array_filter(
6300
                $locations,
6301
                function (Location $location) {
6302
                    return $location->hidden;
6303
                }
6304
            )
6305
        );
6306
    }
6307
6308
    public function testPublishVersionWithSelectedLanguages()
6309
    {
6310
        $publishedContent = $this->createFolder(
6311
            [
6312
                self::ENG_US => 'Published US',
6313
                self::GER_DE => 'Published DE',
6314
            ],
6315
            $this->generateId('location', 2)
6316
        );
6317
6318
        $draft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6319
        $contentUpdateStruct = new ContentUpdateStruct([
6320
            'initialLanguageCode' => self::ENG_US,
6321
        ]);
6322
        $contentUpdateStruct->setField('name', 'Draft 1 US', self::ENG_US);
6323
        $contentUpdateStruct->setField('name', 'Draft 1 DE', self::GER_DE);
6324
6325
        $this->contentService->updateContent($draft->versionInfo, $contentUpdateStruct);
6326
6327
        $this->contentService->publishVersion($draft->versionInfo, [self::GER_DE]);
6328
        $content = $this->contentService->loadContent($draft->contentInfo->id);
6329
        $this->assertEquals(
6330
            [
6331
                self::ENG_US => 'Published US',
6332
                self::GER_DE => 'Draft 1 DE',
6333
            ],
6334
            $content->fields['name']
6335
        );
6336
    }
6337
6338
    public function testCreateContentWithRomanianSpecialCharsInTitle()
6339
    {
6340
        $baseName = 'ȘșțȚdfdf';
6341
        $expectedPath = '/SstTdfdf';
6342
6343
        $this->createFolder([self::ENG_US => $baseName], 2);
6344
6345
        $urlAliasService = $this->getRepository()->getURLAliasService();
6346
        $urlAlias = $urlAliasService->lookup($expectedPath);
6347
        $this->assertSame($expectedPath, $urlAlias->path);
6348
    }
6349
6350
    /**
6351
     * @param int $amountOfDrafts
6352
     *
6353
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6354
     */
6355
    private function createContentDrafts(int $amountOfDrafts): void
6356
    {
6357
        if (0 >= $amountOfDrafts) {
6358
            throw new InvalidArgumentException('$amountOfDrafts', 'Must be greater then 0');
6359
        }
6360
6361
        $publishedContent = $this->createContentVersion1();
6362
6363
        for ($i = 1; $i <= $amountOfDrafts; ++$i) {
6364
            $this->contentService->createContentDraft($publishedContent->contentInfo);
6365
        }
6366
    }
6367
6368
    /**
6369
     * @param array $limitationValues
6370
     *
6371
     * @return \eZ\Publish\API\Repository\Values\User\User
6372
     *
6373
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
6374
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
6375
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6376
     */
6377
    private function createUserWithVersionReadLimitations(array $limitationValues = []): User
6378
    {
6379
        $limitations = [
6380
            new LocationLimitation(['limitationValues' => $limitationValues]),
6381
        ];
6382
6383
        return $this->createUserWithPolicies(
6384
            'user',
6385
            [
6386
                ['module' => 'content', 'function' => 'versionread', 'limitations' => $limitations],
6387
                ['module' => 'content', 'function' => 'create'],
6388
                ['module' => 'content', 'function' => 'read'],
6389
                ['module' => 'content', 'function' => 'edit'],
6390
            ]
6391
        );
6392
    }
6393
6394
    /**
6395
     * @param \eZ\Publish\API\Repository\Values\Content\Content[] $drafts
6396
     *
6397
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
6398
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6399
     *
6400
     * @return object
6401
     */
6402
    private function createContentWithReverseRelations(array $drafts)
6403
    {
6404
        $contentWithReverseRelations = new class() {
6405
            /** @var \eZ\Publish\API\Repository\Values\Content\Content */
6406
            public $content;
6407
6408
            /** @var \eZ\Publish\API\Repository\Values\Content\Content[] */
6409
            public $reverseRelations;
6410
        };
6411
        $content = $this->createContentVersion1();
6412
        $versionInfo = $content->getVersionInfo();
6413
        $contentInfo = $versionInfo->getContentInfo();
6414
        $contentWithReverseRelations->content = $content;
6415
6416
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $draft */
6417
        foreach ($drafts as $draft) {
6418
            $this->contentService->addRelation(
6419
                $draft->getVersionInfo(),
6420
                $contentInfo
6421
            );
6422
6423
            $contentWithReverseRelations->reverseRelations[] = $this->contentService->publishVersion($draft->getVersionInfo());
6424
        }
6425
6426
        return $contentWithReverseRelations;
6427
    }
6428
6429
    public function testPublishVersionByUserWithLanguageLimitation()
6430
    {
6431
        $publishedContent = $this->createFolder(
6432
            [
6433
                self::ENG_US => 'Published US',
6434
                self::GER_DE => 'Published DE',
6435
            ],
6436
            $this->generateId('location', 2)
6437
        );
6438
6439
        $draft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6440
6441
        $contentUpdateStruct = new ContentUpdateStruct([
6442
            'initialLanguageCode' => self::GER_DE,
6443
        ]);
6444
        $contentUpdateStruct->setField('name', 'Draft 1 DE', self::GER_DE);
6445
6446
        $this->contentService->updateContent($draft->versionInfo, $contentUpdateStruct);
6447
6448
        $user = $this->createUserWithPolicies(
6449
            'user',
6450
            [
6451
                [
6452
                    'module' => 'content',
6453
                    'function' => 'publish',
6454
                    'limitations' => [new LanguageLimitation(['limitationValues' => [self::GER_DE]])],
6455
                ],
6456
            ]
6457
        );
6458
6459
        $admin = $this->permissionResolver->getCurrentUserReference();
6460
        $this->permissionResolver->setCurrentUserReference($user);
6461
6462
        $this->contentService->publishVersion($draft->versionInfo, [self::GER_DE]);
6463
6464
        $this->permissionResolver->setCurrentUserReference($admin);
6465
        $content = $this->contentService->loadContent($draft->contentInfo->id);
6466
        $this->assertEquals(
6467
            [
6468
                self::ENG_US => 'Published US',
6469
                self::GER_DE => 'Draft 1 DE',
6470
            ],
6471
            $content->fields['name']
6472
        );
6473
    }
6474
}
6475