Completed
Push — ezp-30806-tmp-7.5 ( 069bb0...315c25 )
by
unknown
36:09 queued 14:41
created

ContentServiceTest::testCountContentDrafts()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 7
rs 10
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\SectionLimitation;
26
use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation;
27
use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation;
28
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
29
use DOMDocument;
30
use Exception;
31
use eZ\Publish\API\Repository\Values\User\User;
32
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException as CoreUnauthorizedException;
33
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
34
use InvalidArgumentException;
35
36
/**
37
 * Test case for operations in the ContentService using in memory storage.
38
 *
39
 * @see \eZ\Publish\API\Repository\ContentService
40
 * @group content
41
 */
42
class ContentServiceTest extends BaseContentServiceTest
43
{
44
    private const ADMINISTRATORS_USER_GROUP_NAME = 'Administrators';
45
    private const ADMINISTRATORS_USER_GROUP_ID = 12;
46
    private const ADMINISTRATORS_USER_GROUP_LOCATION_ID = 13;
47
48
    private const WRITERS_USER_GROUP_NAME = 'Writers';
49
50
    private const MEMBERS_USER_GROUP_ID = 11;
51
52
    private const MEDIA_CONTENT_ID = 41;
53
54
    private const MEDIA_REMOTE_ID = 'a6e35cbcb7cd6ae4b691f3eee30cd262';
55
    private const DEMO_DESIGN_REMOTE_ID = '8b8b22fe3c6061ed500fbd2b377b885f';
56
57
    private const FORUM_IDENTIFIER = 'forum';
58
59
    private const ENG_US = 'eng-US';
60
    private const GER_DE = 'ger-DE';
61
    private const ENG_GB = 'eng-GB';
62
63
    /** @var \eZ\Publish\API\Repository\PermissionResolver */
64
    private $permissionResolver;
65
66
    /** @var \eZ\Publish\API\Repository\ContentService */
67
    private $contentService;
68
69
    /** @var \eZ\Publish\API\Repository\LocationService */
70
    private $locationService;
71
72
    public function setUp(): void
73
    {
74
        parent::setUp();
75
76
        $repository = $this->getRepository();
77
        $this->permissionResolver = $repository->getPermissionResolver();
78
        $this->contentService = $repository->getContentService();
79
        $this->locationService = $repository->getLocationService();
80
    }
81
82
    /**
83
     * Test for the newContentCreateStruct() method.
84
     *
85
     * @see \eZ\Publish\API\Repository\ContentService::newContentCreateStruct()
86
     * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier
87
     * @group user
88
     * @group field-type
89
     */
90
    public function testNewContentCreateStruct()
91
    {
92
        $contentTypeService = $this->getRepository()->getContentTypeService();
93
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
94
95
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
96
97
        $this->assertInstanceOf(ContentCreateStruct::class, $contentCreate);
98
    }
99
100
    /**
101
     * Test for the createContent() method.
102
     *
103
     * @return \eZ\Publish\API\Repository\Values\Content\Content
104
     *
105
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
106
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentCreateStruct
107
     * @group user
108
     * @group field-type
109
     */
110
    public function testCreateContent()
111
    {
112
        if ($this->isVersion4()) {
113
            $this->markTestSkipped('This test requires eZ Publish 5');
114
        }
115
116
        $contentTypeService = $this->getRepository()->getContentTypeService();
117
118
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
119
120
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
121
        $contentCreate->setField('name', 'My awesome forum');
122
123
        $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
124
        $contentCreate->alwaysAvailable = true;
125
126
        $content = $this->contentService->createContent($contentCreate);
127
128
        $this->assertInstanceOf(Content::class, $content);
129
130
        return $content;
131
    }
132
133
    /**
134
     * Test for the createContent() method.
135
     *
136
     * Tests made for issue #EZP-20955 where Anonymous user is granted access to create content
137
     * and should have access to do that.
138
     *
139
     * @return \eZ\Publish\API\Repository\Values\Content\Content
140
     *
141
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
142
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentCreateStruct
143
     * @group user
144
     * @group field-type
145
     */
146
    public function testCreateContentAndPublishWithPrivilegedAnonymousUser()
147
    {
148
        if ($this->isVersion4()) {
149
            $this->markTestSkipped('This test requires eZ Publish 5');
150
        }
151
152
        $anonymousUserId = $this->generateId('user', 10);
153
154
        $repository = $this->getRepository();
155
        $contentTypeService = $this->getRepository()->getContentTypeService();
156
        $roleService = $repository->getRoleService();
157
158
        // Give Anonymous user role additional rights
159
        $role = $roleService->loadRoleByIdentifier('Anonymous');
160
        $roleDraft = $roleService->createRoleDraft($role);
161
        $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'create');
162
        $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]]));
163
        $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]]));
164
        $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]]));
165
        $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct);
166
167
        $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'publish');
168
        $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]]));
169
        $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]]));
170
        $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]]));
171
        $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct);
172
        $roleService->publishRoleDraft($roleDraft);
173
174
        // Set Anonymous user as current
175
        $repository->getPermissionResolver()->setCurrentUserReference($repository->getUserService()->loadUser($anonymousUserId));
176
177
        // Create a new content object:
178
        $contentCreate = $this->contentService->newContentCreateStruct(
179
            $contentTypeService->loadContentTypeByIdentifier('folder'),
180
            self::ENG_GB
181
        );
182
183
        $contentCreate->setField('name', 'Folder 1');
184
185
        $content = $this->contentService->createContent(
186
            $contentCreate,
187
            [$this->locationService->newLocationCreateStruct(2)]
188
        );
189
190
        $this->contentService->publishVersion(
191
            $content->getVersionInfo()
192
        );
193
    }
194
195
    /**
196
     * Test for the createContent() method.
197
     *
198
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
199
     *
200
     * @return \eZ\Publish\API\Repository\Values\Content\Content
201
     *
202
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
203
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
204
     */
205
    public function testCreateContentSetsContentInfo($content)
206
    {
207
        $this->assertInstanceOf(ContentInfo::class, $content->contentInfo);
208
209
        return $content;
210
    }
211
212
    /**
213
     * Test for the createContent() method.
214
     *
215
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
216
     *
217
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
218
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentSetsContentInfo
219
     */
220
    public function testCreateContentSetsExpectedContentInfo($content)
221
    {
222
        $this->assertEquals(
223
            [
224
                $content->id,
225
                28, // id of content type "forum"
226
                true,
227
                1,
228
                'abcdef0123456789abcdef0123456789',
229
                self::ENG_US,
230
                $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...
231
                false,
232
                null,
233
                // Main Location id for unpublished Content should be null
234
                null,
235
            ],
236
            [
237
                $content->contentInfo->id,
238
                $content->contentInfo->contentTypeId,
239
                $content->contentInfo->alwaysAvailable,
240
                $content->contentInfo->currentVersionNo,
241
                $content->contentInfo->remoteId,
242
                $content->contentInfo->mainLanguageCode,
243
                $content->contentInfo->ownerId,
244
                $content->contentInfo->published,
245
                $content->contentInfo->publishedDate,
246
                $content->contentInfo->mainLocationId,
247
            ]
248
        );
249
    }
250
251
    /**
252
     * Test for the createContent() method.
253
     *
254
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
255
     *
256
     * @return \eZ\Publish\API\Repository\Values\Content\Content
257
     *
258
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
259
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
260
     */
261
    public function testCreateContentSetsVersionInfo($content)
262
    {
263
        $this->assertInstanceOf(VersionInfo::class, $content->getVersionInfo());
264
265
        return $content;
266
    }
267
268
    /**
269
     * Test for the createContent() method.
270
     *
271
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
272
     *
273
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
274
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentSetsVersionInfo
275
     */
276
    public function testCreateContentSetsExpectedVersionInfo($content)
277
    {
278
        $this->assertEquals(
279
            [
280
                'status' => VersionInfo::STATUS_DRAFT,
281
                'versionNo' => 1,
282
                '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...
283
                'initialLanguageCode' => self::ENG_US,
284
            ],
285
            [
286
                'status' => $content->getVersionInfo()->status,
287
                'versionNo' => $content->getVersionInfo()->versionNo,
288
                'creatorId' => $content->getVersionInfo()->creatorId,
289
                'initialLanguageCode' => $content->getVersionInfo()->initialLanguageCode,
290
            ]
291
        );
292
        $this->assertTrue($content->getVersionInfo()->isDraft());
293
        $this->assertFalse($content->getVersionInfo()->isPublished());
294
        $this->assertFalse($content->getVersionInfo()->isArchived());
295
    }
296
297
    /**
298
     * Test for the createContent() method.
299
     *
300
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
301
     *
302
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
303
     * @depends testCreateContent
304
     */
305
    public function testCreateContentSetsExpectedContentType($content)
306
    {
307
        $contentType = $content->getContentType();
308
309
        $this->assertEquals(
310
            [
311
                $contentType->id,
312
                // Won't match as it's set to true in createContentDraftVersion1()
313
                //$contentType->defaultAlwaysAvailable,
314
                //$contentType->defaultSortField,
315
                //$contentType->defaultSortOrder,
316
            ],
317
            [
318
                $content->contentInfo->contentTypeId,
319
                //$content->contentInfo->alwaysAvailable,
320
                //$location->sortField,
321
                //$location->sortOrder,
322
            ]
323
        );
324
    }
325
326
    /**
327
     * Test for the createContent() method.
328
     *
329
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
330
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
331
     */
332
    public function testCreateContentThrowsInvalidArgumentException()
333
    {
334
        if ($this->isVersion4()) {
335
            $this->markTestSkipped('This test requires eZ Publish 5');
336
        }
337
338
        $contentTypeService = $this->getRepository()->getContentTypeService();
339
340
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
341
342
        $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
343
        $contentCreate1->setField('name', 'An awesome Sidelfingen forum');
344
345
        $contentCreate1->remoteId = 'abcdef0123456789abcdef0123456789';
346
        $contentCreate1->alwaysAvailable = true;
347
348
        $draft = $this->contentService->createContent($contentCreate1);
349
        $this->contentService->publishVersion($draft->versionInfo);
350
351
        $contentCreate2 = $this->contentService->newContentCreateStruct($contentType, self::ENG_GB);
352
        $contentCreate2->setField('name', 'An awesome Bielefeld forum');
353
354
        $contentCreate2->remoteId = 'abcdef0123456789abcdef0123456789';
355
        $contentCreate2->alwaysAvailable = false;
356
357
        $this->expectException(APIInvalidArgumentException::class);
358
        $this->contentService->createContent($contentCreate2);
359
    }
360
361
    /**
362
     * Test for the createContent() method.
363
     *
364
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
365
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
366
     */
367
    public function testCreateContentThrowsInvalidArgumentExceptionOnFieldTypeNotAccept()
368
    {
369
        $contentTypeService = $this->getRepository()->getContentTypeService();
370
371
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
372
373
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
374
        // The name field does only accept strings and null as its values
375
        $contentCreate->setField('name', new \stdClass());
376
377
        $this->expectException(APIInvalidArgumentException::class);
378
        $this->contentService->createContent($contentCreate);
379
    }
380
381
    /**
382
     * Test for the createContent() method.
383
     *
384
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
385
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
386
     */
387
    public function testCreateContentThrowsContentFieldValidationException()
388
    {
389
        $contentTypeService = $this->getRepository()->getContentTypeService();
390
391
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
392
393
        $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
394
        $contentCreate1->setField('name', 'An awesome Sidelfingen folder');
395
        // Violates string length constraint
396
        $contentCreate1->setField('short_name', str_repeat('a', 200));
397
398
        $this->expectException(ContentFieldValidationException::class);
399
400
        // Throws ContentFieldValidationException, since short_name does not pass validation of the string length validator
401
        $this->contentService->createContent($contentCreate1);
402
    }
403
404
    /**
405
     * Test for the createContent() method.
406
     *
407
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
408
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
409
     */
410
    public function testCreateContentRequiredFieldMissing()
411
    {
412
        $contentTypeService = $this->getRepository()->getContentTypeService();
413
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
414
415
        $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
416
        // Required field "name" is not set
417
418
        $this->expectException(ContentFieldValidationException::class);
419
420
        // Throws a ContentFieldValidationException, since a required field is missing
421
        $this->contentService->createContent($contentCreate1);
422
    }
423
424
    /**
425
     * Test for the createContent() method.
426
     *
427
     * NOTE: We have bidirectional dependencies between the ContentService and
428
     * the LocationService, so that we cannot use PHPUnit's test dependencies
429
     * here.
430
     *
431
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
432
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation
433
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationByRemoteId
434
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
435
     * @group user
436
     */
437
    public function testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately()
438
    {
439
        $this->createContentDraftVersion1();
440
441
        $this->expectException(NotFoundException::class);
442
443
        // The location will not have been created, yet, so this throws an exception
444
        $this->locationService->loadLocationByRemoteId('0123456789abcdef0123456789abcdef');
445
    }
446
447
    /**
448
     * Test for the createContent() method.
449
     *
450
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
451
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
452
     */
453
    public function testCreateContentThrowsInvalidArgumentExceptionWithLocationCreateParameter()
454
    {
455
        $parentLocationId = $this->generateId('location', 56);
456
        // $parentLocationId is a valid location ID
457
458
        $contentTypeService = $this->getRepository()->getContentTypeService();
459
460
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
461
462
        // Configure new locations
463
        $locationCreate1 = $this->locationService->newLocationCreateStruct($parentLocationId);
464
465
        $locationCreate1->priority = 23;
466
        $locationCreate1->hidden = true;
467
        $locationCreate1->remoteId = '0123456789abcdef0123456789aaaaaa';
468
        $locationCreate1->sortField = Location::SORT_FIELD_NODE_ID;
469
        $locationCreate1->sortOrder = Location::SORT_ORDER_DESC;
470
471
        $locationCreate2 = $this->locationService->newLocationCreateStruct($parentLocationId);
472
473
        $locationCreate2->priority = 42;
474
        $locationCreate2->hidden = true;
475
        $locationCreate2->remoteId = '0123456789abcdef0123456789bbbbbb';
476
        $locationCreate2->sortField = Location::SORT_FIELD_NODE_ID;
477
        $locationCreate2->sortOrder = Location::SORT_ORDER_DESC;
478
479
        // Configure new content object
480
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
481
482
        $contentCreate->setField('name', 'A awesome Sindelfingen forum');
483
        $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
484
        $contentCreate->alwaysAvailable = true;
485
486
        // Create new content object under the specified location
487
        $draft = $this->contentService->createContent(
488
            $contentCreate,
489
            [$locationCreate1]
490
        );
491
        $this->contentService->publishVersion($draft->versionInfo);
492
493
        $this->expectException(APIInvalidArgumentException::class);
494
        // Content remoteId already exists,
495
        $this->contentService->createContent(
496
            $contentCreate,
497
            [$locationCreate2]
498
        );
499
    }
500
501
    /**
502
     * Test for the loadContentInfo() method.
503
     *
504
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfo()
505
     * @group user
506
     */
507
    public function testLoadContentInfo()
508
    {
509
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
510
511
        // Load the ContentInfo for "Media" folder
512
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
513
514
        $this->assertInstanceOf(ContentInfo::class, $contentInfo);
515
516
        return $contentInfo;
517
    }
518
519
    /**
520
     * Test for the returned value of the loadContentInfo() method.
521
     *
522
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
523
     * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo
524
     *
525
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
526
     */
527
    public function testLoadContentInfoSetsExpectedContentInfo(ContentInfo $contentInfo)
528
    {
529
        $this->assertPropertiesCorrectUnsorted(
530
            $this->getExpectedMediaContentInfoProperties(),
531
            $contentInfo
532
        );
533
    }
534
535
    /**
536
     * Test for the loadContentInfo() method.
537
     *
538
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfo()
539
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
540
     */
541
    public function testLoadContentInfoThrowsNotFoundException()
542
    {
543
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
544
545
        $this->expectException(NotFoundException::class);
546
547
        $this->contentService->loadContentInfo($nonExistentContentId);
548
    }
549
550
    /**
551
     * Test for the loadContentInfoList() method.
552
     *
553
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoList()
554
     */
555
    public function testLoadContentInfoList()
556
    {
557
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
558
        $list = $this->contentService->loadContentInfoList([$mediaFolderId]);
559
560
        $this->assertCount(1, $list);
561
        $this->assertEquals([$mediaFolderId], array_keys($list), 'Array key was not content id');
562
        $this->assertInstanceOf(
563
            ContentInfo::class,
564
            $list[$mediaFolderId]
565
        );
566
    }
567
568
    /**
569
     * Test for the loadContentInfoList() method.
570
     *
571
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoList()
572
     * @depends testLoadContentInfoList
573
     */
574
    public function testLoadContentInfoListSkipsNotFoundItems()
575
    {
576
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
577
        $list = $this->contentService->loadContentInfoList([$nonExistentContentId]);
578
579
        $this->assertCount(0, $list);
580
    }
581
582
    /**
583
     * Test for the loadContentInfoByRemoteId() method.
584
     *
585
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId()
586
     */
587
    public function testLoadContentInfoByRemoteId()
588
    {
589
        // Load the ContentInfo for "Media" folder
590
        $contentInfo = $this->contentService->loadContentInfoByRemoteId('faaeb9be3bd98ed09f606fc16d144eca');
591
592
        $this->assertInstanceOf(ContentInfo::class, $contentInfo);
593
594
        return $contentInfo;
595
    }
596
597
    /**
598
     * Test for the returned value of the loadContentInfoByRemoteId() method.
599
     *
600
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoByRemoteId
601
     * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId
602
     *
603
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
604
     */
605
    public function testLoadContentInfoByRemoteIdSetsExpectedContentInfo(ContentInfo $contentInfo)
606
    {
607
        $this->assertPropertiesCorrectUnsorted(
608
            [
609
                'id' => 10,
610
                'contentTypeId' => 4,
611
                'name' => 'Anonymous User',
612
                'sectionId' => 2,
613
                'currentVersionNo' => 2,
614
                'published' => true,
615
                'ownerId' => 14,
616
                'modificationDate' => $this->createDateTime(1072180405),
617
                'publishedDate' => $this->createDateTime(1033920665),
618
                'alwaysAvailable' => 1,
619
                'remoteId' => 'faaeb9be3bd98ed09f606fc16d144eca',
620
                'mainLanguageCode' => self::ENG_US,
621
                'mainLocationId' => 45,
622
            ],
623
            $contentInfo
624
        );
625
    }
626
627
    /**
628
     * Test for the loadContentInfoByRemoteId() method.
629
     *
630
     * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId()
631
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoByRemoteId
632
     */
633
    public function testLoadContentInfoByRemoteIdThrowsNotFoundException()
634
    {
635
        $this->expectException(NotFoundException::class);
636
637
        $this->contentService->loadContentInfoByRemoteId('abcdefghijklmnopqrstuvwxyz0123456789');
638
    }
639
640
    /**
641
     * Test for the loadVersionInfo() method.
642
     *
643
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo()
644
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
645
     * @group user
646
     */
647
    public function testLoadVersionInfo()
648
    {
649
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
650
        // $mediaFolderId contains the ID of the "Media" folder
651
652
        // Load the ContentInfo for "Media" folder
653
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
654
655
        // Now load the current version info of the "Media" folder
656
        $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
657
658
        $this->assertInstanceOf(
659
            VersionInfo::class,
660
            $versionInfo
661
        );
662
    }
663
664
    /**
665
     * Test for the loadVersionInfoById() method.
666
     *
667
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById()
668
     */
669
    public function testLoadVersionInfoById()
670
    {
671
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
672
        // $mediaFolderId contains the ID of the "Media" folder
673
674
        // Load the VersionInfo for "Media" folder
675
        $versionInfo = $this->contentService->loadVersionInfoById($mediaFolderId);
676
677
        $this->assertInstanceOf(
678
            VersionInfo::class,
679
            $versionInfo
680
        );
681
682
        return $versionInfo;
683
    }
684
685
    /**
686
     * Test for the returned value of the loadVersionInfoById() method.
687
     *
688
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById
689
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
690
     *
691
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo
692
     */
693
    public function testLoadVersionInfoByIdSetsExpectedVersionInfo(VersionInfo $versionInfo)
694
    {
695
        $this->assertPropertiesCorrect(
696
            [
697
                'names' => [
698
                    self::ENG_US => 'Media',
699
                ],
700
                'contentInfo' => new ContentInfo($this->getExpectedMediaContentInfoProperties()),
701
                'id' => 472,
702
                'versionNo' => 1,
703
                'modificationDate' => $this->createDateTime(1060695457),
704
                'creatorId' => 14,
705
                'creationDate' => $this->createDateTime(1060695450),
706
                'status' => VersionInfo::STATUS_PUBLISHED,
707
                'initialLanguageCode' => self::ENG_US,
708
                'languageCodes' => [
709
                    self::ENG_US,
710
                ],
711
            ],
712
            $versionInfo
713
        );
714
        $this->assertTrue($versionInfo->isPublished());
715
        $this->assertFalse($versionInfo->isDraft());
716
        $this->assertFalse($versionInfo->isArchived());
717
    }
718
719
    /**
720
     * Test for the loadVersionInfoById() method.
721
     *
722
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById()
723
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById
724
     */
725
    public function testLoadVersionInfoByIdThrowsNotFoundException()
726
    {
727
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
728
729
        $this->expectException(NotFoundException::class);
730
731
        $this->contentService->loadVersionInfoById($nonExistentContentId);
732
    }
733
734
    /**
735
     * Test for the loadContentByContentInfo() method.
736
     *
737
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo()
738
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
739
     */
740
    public function testLoadContentByContentInfo()
741
    {
742
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
743
        // $mediaFolderId contains the ID of the "Media" folder
744
745
        // Load the ContentInfo for "Media" folder
746
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
747
748
        // Now load the current content version for the info instance
749
        $content = $this->contentService->loadContentByContentInfo($contentInfo);
750
751
        $this->assertInstanceOf(
752
            Content::class,
753
            $content
754
        );
755
    }
756
757
    /**
758
     * Test for the loadContentByVersionInfo() method.
759
     *
760
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo()
761
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
762
     */
763
    public function testLoadContentByVersionInfo()
764
    {
765
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
766
        // $mediaFolderId contains the ID of the "Media" folder
767
768
        // Load the ContentInfo for "Media" folder
769
        $contentInfo = $this->contentService->loadContentInfo($mediaFolderId);
770
771
        // Load the current VersionInfo
772
        $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
773
774
        // Now load the current content version for the info instance
775
        $content = $this->contentService->loadContentByVersionInfo($versionInfo);
776
777
        $this->assertInstanceOf(
778
            Content::class,
779
            $content
780
        );
781
    }
782
783
    /**
784
     * Test for the loadContent() method.
785
     *
786
     * @see \eZ\Publish\API\Repository\ContentService::loadContent()
787
     * @group user
788
     * @group field-type
789
     */
790
    public function testLoadContent()
791
    {
792
        $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID);
793
        // $mediaFolderId contains the ID of the "Media" folder
794
795
        // Load the Content for "Media" folder, any language and current version
796
        $content = $this->contentService->loadContent($mediaFolderId);
797
798
        $this->assertInstanceOf(
799
            Content::class,
800
            $content
801
        );
802
    }
803
804
    /**
805
     * Test for the loadContent() method.
806
     *
807
     * @see \eZ\Publish\API\Repository\ContentService::loadContent()
808
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
809
     */
810
    public function testLoadContentThrowsNotFoundException()
811
    {
812
        $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX);
813
814
        $this->expectException(NotFoundException::class);
815
816
        $this->contentService->loadContent($nonExistentContentId);
817
    }
818
819
    /**
820
     * Data provider for testLoadContentByRemoteId().
821
     *
822
     * @return array
823
     */
824
    public function contentRemoteIdVersionLanguageProvider()
825
    {
826
        return [
827
            ['f5c88a2209584891056f987fd965b0ba', null, null],
828
            ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], null],
829
            ['f5c88a2209584891056f987fd965b0ba', null, 1],
830
            ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], 1],
831
            [self::MEDIA_REMOTE_ID, null, null],
832
            [self::MEDIA_REMOTE_ID, [self::ENG_US], null],
833
            [self::MEDIA_REMOTE_ID, null, 1],
834
            [self::MEDIA_REMOTE_ID, [self::ENG_US], 1],
835
        ];
836
    }
837
838
    /**
839
     * Test for the loadContentByRemoteId() method.
840
     *
841
     * @covers \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId
842
     * @dataProvider contentRemoteIdVersionLanguageProvider
843
     *
844
     * @param string $remoteId
845
     * @param array|null $languages
846
     * @param int $versionNo
847
     */
848
    public function testLoadContentByRemoteId($remoteId, $languages, $versionNo)
849
    {
850
        $content = $this->contentService->loadContentByRemoteId($remoteId, $languages, $versionNo);
851
852
        $this->assertInstanceOf(
853
            Content::class,
854
            $content
855
        );
856
857
        $this->assertEquals($remoteId, $content->contentInfo->remoteId);
858
        if ($languages !== null) {
859
            $this->assertEquals($languages, $content->getVersionInfo()->languageCodes);
860
        }
861
        $this->assertEquals($versionNo ?: 1, $content->getVersionInfo()->versionNo);
862
    }
863
864
    /**
865
     * Test for the loadContentByRemoteId() method.
866
     *
867
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId()
868
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteId
869
     */
870
    public function testLoadContentByRemoteIdThrowsNotFoundException()
871
    {
872
        $this->expectException(NotFoundException::class);
873
874
        // This call will fail with a "NotFoundException", because no content object exists for the given remoteId
875
        $this->contentService->loadContentByRemoteId('a1b1c1d1e1f1a2b2c2d2e2f2a3b3c3d3');
876
    }
877
878
    /**
879
     * Test for the publishVersion() method.
880
     *
881
     * @return \eZ\Publish\API\Repository\Values\Content\Content
882
     *
883
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
884
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
885
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
886
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
887
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
888
     * @group user
889
     * @group field-type
890
     */
891
    public function testPublishVersion()
892
    {
893
        $time = time();
894
        $content = $this->createContentVersion1();
895
896
        $this->assertInstanceOf(Content::class, $content);
897
        $this->assertTrue($content->contentInfo->published);
898
        $this->assertEquals(VersionInfo::STATUS_PUBLISHED, $content->versionInfo->status);
899
        $this->assertGreaterThanOrEqual($time, $content->contentInfo->publishedDate->getTimestamp());
900
        $this->assertGreaterThanOrEqual($time, $content->contentInfo->modificationDate->getTimestamp());
901
        $this->assertTrue($content->versionInfo->isPublished());
902
        $this->assertFalse($content->versionInfo->isDraft());
903
        $this->assertFalse($content->versionInfo->isArchived());
904
905
        return $content;
906
    }
907
908
    /**
909
     * Test for the publishVersion() method.
910
     *
911
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
912
     *
913
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
914
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
915
     */
916
    public function testPublishVersionSetsExpectedContentInfo($content)
917
    {
918
        $this->assertEquals(
919
            [
920
                $content->id,
921
                true,
922
                1,
923
                'abcdef0123456789abcdef0123456789',
924
                self::ENG_US,
925
                $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...
926
                true,
927
            ],
928
            [
929
                $content->contentInfo->id,
930
                $content->contentInfo->alwaysAvailable,
931
                $content->contentInfo->currentVersionNo,
932
                $content->contentInfo->remoteId,
933
                $content->contentInfo->mainLanguageCode,
934
                $content->contentInfo->ownerId,
935
                $content->contentInfo->published,
936
            ]
937
        );
938
939
        $this->assertNotNull($content->contentInfo->mainLocationId);
940
        $date = new \DateTime('1984/01/01');
941
        $this->assertGreaterThan(
942
            $date->getTimestamp(),
943
            $content->contentInfo->publishedDate->getTimestamp()
944
        );
945
    }
946
947
    /**
948
     * Test for the publishVersion() method.
949
     *
950
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
951
     *
952
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
953
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
954
     */
955
    public function testPublishVersionSetsExpectedVersionInfo($content)
956
    {
957
        $this->assertEquals(
958
            [
959
                $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...
960
                self::ENG_US,
961
                VersionInfo::STATUS_PUBLISHED,
962
                1,
963
            ],
964
            [
965
                $content->getVersionInfo()->creatorId,
966
                $content->getVersionInfo()->initialLanguageCode,
967
                $content->getVersionInfo()->status,
968
                $content->getVersionInfo()->versionNo,
969
            ]
970
        );
971
972
        $date = new \DateTime('1984/01/01');
973
        $this->assertGreaterThan(
974
            $date->getTimestamp(),
975
            $content->getVersionInfo()->modificationDate->getTimestamp()
976
        );
977
978
        $this->assertNotNull($content->getVersionInfo()->modificationDate);
979
        $this->assertTrue($content->getVersionInfo()->isPublished());
980
        $this->assertFalse($content->getVersionInfo()->isDraft());
981
        $this->assertFalse($content->getVersionInfo()->isArchived());
982
    }
983
984
    /**
985
     * Test for the publishVersion() method.
986
     *
987
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
988
     *
989
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
990
     * @depends testPublishVersion
991
     */
992
    public function testPublishVersionSetsExpectedContentType($content)
993
    {
994
        $contentType = $content->getContentType();
995
996
        $this->assertEquals(
997
            [
998
                $contentType->id,
999
                // won't be a match as it's set to true in createContentDraftVersion1()
1000
                //$contentType->defaultAlwaysAvailable,
1001
                //$contentType->defaultSortField,
1002
                //$contentType->defaultSortOrder,
1003
            ],
1004
            [
1005
                $content->contentInfo->contentTypeId,
1006
                //$content->contentInfo->alwaysAvailable,
1007
                //$location->sortField,
1008
                //$location->sortOrder,
1009
            ]
1010
        );
1011
    }
1012
1013
    /**
1014
     * Test for the publishVersion() method.
1015
     *
1016
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1017
     *
1018
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1019
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
1020
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1021
     */
1022
    public function testPublishVersionCreatesLocationsDefinedOnCreate()
1023
    {
1024
        $content = $this->createContentVersion1();
1025
1026
        $location = $this->locationService->loadLocationByRemoteId(
1027
            '0123456789abcdef0123456789abcdef'
1028
        );
1029
1030
        $this->assertEquals(
1031
            $location->getContentInfo(),
1032
            $content->getVersionInfo()->getContentInfo()
1033
        );
1034
1035
        return [$content, $location];
1036
    }
1037
1038
    /**
1039
     * Test for the publishVersion() method.
1040
     *
1041
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1042
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionCreatesLocationsDefinedOnCreate
1043
     */
1044
    public function testCreateContentWithLocationCreateParameterCreatesExpectedLocation(array $testData)
1045
    {
1046
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */
1047
        /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */
1048
        list($content, $location) = $testData;
1049
1050
        $parentLocationId = $this->generateId('location', 56);
1051
        $parentLocation = $this->getRepository()->getLocationService()->loadLocation($parentLocationId);
1052
        $mainLocationId = $content->getVersionInfo()->getContentInfo()->mainLocationId;
1053
1054
        $this->assertPropertiesCorrect(
1055
            [
1056
                'id' => $mainLocationId,
1057
                'priority' => 23,
1058
                'hidden' => true,
1059
                'invisible' => true,
1060
                'remoteId' => '0123456789abcdef0123456789abcdef',
1061
                'parentLocationId' => $parentLocationId,
1062
                'pathString' => $parentLocation->pathString . $mainLocationId . '/',
1063
                'depth' => $parentLocation->depth + 1,
1064
                'sortField' => Location::SORT_FIELD_NODE_ID,
1065
                'sortOrder' => Location::SORT_ORDER_DESC,
1066
            ],
1067
            $location
1068
        );
1069
    }
1070
1071
    /**
1072
     * Test for the publishVersion() method.
1073
     *
1074
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1075
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1076
     */
1077
    public function testPublishVersionThrowsBadStateException()
1078
    {
1079
        $draft = $this->createContentDraftVersion1();
1080
1081
        // Publish the content draft
1082
        $this->contentService->publishVersion($draft->getVersionInfo());
1083
1084
        $this->expectException(BadStateException::class);
1085
1086
        // This call will fail with a "BadStateException", because the version is already published.
1087
        $this->contentService->publishVersion($draft->getVersionInfo());
1088
    }
1089
1090
    /**
1091
     * Test that publishVersion() does not affect publishedDate (assuming previous version exists).
1092
     *
1093
     * @covers \eZ\Publish\API\Repository\ContentService::publishVersion
1094
     */
1095
    public function testPublishVersionDoesNotChangePublishedDate()
1096
    {
1097
        $publishedContent = $this->createContentVersion1();
1098
1099
        // force timestamps to differ
1100
        sleep(1);
1101
1102
        $contentDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
1103
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1104
        $contentUpdateStruct->setField('name', 'New name');
1105
        $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct);
1106
        $republishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
1107
1108
        $this->assertEquals(
1109
            $publishedContent->contentInfo->publishedDate->getTimestamp(),
1110
            $republishedContent->contentInfo->publishedDate->getTimestamp()
1111
        );
1112
        $this->assertGreaterThan(
1113
            $publishedContent->contentInfo->modificationDate->getTimestamp(),
1114
            $republishedContent->contentInfo->modificationDate->getTimestamp()
1115
        );
1116
    }
1117
1118
    /**
1119
     * Test for the createContentDraft() method.
1120
     *
1121
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1122
     *
1123
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1124
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1125
     * @group user
1126
     */
1127
    public function testCreateContentDraft()
1128
    {
1129
        $content = $this->createContentVersion1();
1130
1131
        // Now we create a new draft from the published content
1132
        $draftedContent = $this->contentService->createContentDraft($content->contentInfo);
1133
1134
        $this->assertInstanceOf(
1135
            Content::class,
1136
            $draftedContent
1137
        );
1138
1139
        return $draftedContent;
1140
    }
1141
1142
    /**
1143
     * Test for the createContentDraft() method with given language for new draft.
1144
     *
1145
     * @covers \eZ\Publish\API\Repository\ContentService::createContentDraft
1146
     */
1147
    public function testCreateContentDraftInOtherLanguage()
1148
    {
1149
        $content = $this->createContentVersion1();
1150
1151
        $language = $this->getRepository()->getContentLanguageService()->loadLanguage('eng-GB');
1152
1153
        // Now we create a new draft from the published content
1154
        $draftedContent = $this->contentService->createContentDraft(
1155
            $content->contentInfo,
1156
            null,
1157
            null,
1158
            $language
1159
        );
1160
1161
        $this->assertEquals('eng-US', $content->versionInfo->initialLanguageCode);
1162
        $this->assertEquals('eng-GB', $draftedContent->versionInfo->initialLanguageCode);
1163
    }
1164
1165
    /**
1166
     * Test for the createContentDraft() method.
1167
     *
1168
     * Test that editor has access to edit own draft.
1169
     * Note: Editors have access to version_read, which is needed to load content drafts.
1170
     *
1171
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1172
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1173
     * @group user
1174
     */
1175
    public function testCreateContentDraftAndLoadAccess()
1176
    {
1177
        $user = $this->createUserVersion1();
1178
1179
        // Set new editor as user
1180
        $this->permissionResolver->setCurrentUserReference($user);
1181
1182
        // Create draft
1183
        $draft = $this->createContentDraftVersion1(2, 'folder');
1184
1185
        // Try to load the draft
1186
        $loadedDraft = $this->contentService->loadContent($draft->id);
1187
1188
        $this->assertEquals($draft->id, $loadedDraft->id);
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 testCreateContentDraftSetsExpectedProperties($draft)
1200
    {
1201
        $this->assertEquals(
1202
            [
1203
                'fieldCount' => 2,
1204
                'relationCount' => 0,
1205
            ],
1206
            [
1207
                'fieldCount' => count($draft->getFields()),
1208
                'relationCount' => count($this->getRepository()->getContentService()->loadRelations($draft->getVersionInfo())),
1209
            ]
1210
        );
1211
    }
1212
1213
    /**
1214
     * Test for the createContentDraft() method.
1215
     *
1216
     * @param \eZ\Publish\API\Repository\Values\Content\Content $draft
1217
     *
1218
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1219
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1220
     */
1221
    public function testCreateContentDraftSetsContentInfo($draft)
1222
    {
1223
        $contentInfo = $draft->contentInfo;
1224
1225
        $this->assertEquals(
1226
            [
1227
                $draft->id,
1228
                true,
1229
                1,
1230
                self::ENG_US,
1231
                $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...
1232
                'abcdef0123456789abcdef0123456789',
1233
                1,
1234
            ],
1235
            [
1236
                $contentInfo->id,
1237
                $contentInfo->alwaysAvailable,
1238
                $contentInfo->currentVersionNo,
1239
                $contentInfo->mainLanguageCode,
1240
                $contentInfo->ownerId,
1241
                $contentInfo->remoteId,
1242
                $contentInfo->sectionId,
1243
            ]
1244
        );
1245
    }
1246
1247
    /**
1248
     * Test for the createContentDraft() method.
1249
     *
1250
     * @param \eZ\Publish\API\Repository\Values\Content\Content $draft
1251
     *
1252
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1253
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1254
     */
1255
    public function testCreateContentDraftSetsVersionInfo($draft)
1256
    {
1257
        $versionInfo = $draft->getVersionInfo();
1258
1259
        $this->assertEquals(
1260
            [
1261
                '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...
1262
                'initialLanguageCode' => self::ENG_US,
1263
                'languageCodes' => [0 => self::ENG_US],
1264
                'status' => VersionInfo::STATUS_DRAFT,
1265
                'versionNo' => 2,
1266
            ],
1267
            [
1268
                'creatorId' => $versionInfo->creatorId,
1269
                'initialLanguageCode' => $versionInfo->initialLanguageCode,
1270
                'languageCodes' => $versionInfo->languageCodes,
1271
                'status' => $versionInfo->status,
1272
                'versionNo' => $versionInfo->versionNo,
1273
            ]
1274
        );
1275
        $this->assertTrue($versionInfo->isDraft());
1276
        $this->assertFalse($versionInfo->isPublished());
1277
        $this->assertFalse($versionInfo->isArchived());
1278
    }
1279
1280
    /**
1281
     * Test for the createContentDraft() method.
1282
     *
1283
     * @param \eZ\Publish\API\Repository\Values\Content\Content $draft
1284
     *
1285
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1286
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1287
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
1288
     */
1289
    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...
1290
    {
1291
        $content = $this->createContentVersion1();
1292
1293
        // Now we create a new draft from the published content
1294
        $this->contentService->createContentDraft($content->contentInfo);
1295
1296
        // This call will still load the published version
1297
        $versionInfoPublished = $this->contentService->loadVersionInfo($content->contentInfo);
1298
1299
        $this->assertEquals(1, $versionInfoPublished->versionNo);
1300
    }
1301
1302
    /**
1303
     * Test for the createContentDraft() method.
1304
     *
1305
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1306
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
1307
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1308
     */
1309
    public function testCreateContentDraftLoadContentStillLoadsPublishedVersion()
1310
    {
1311
        $content = $this->createContentVersion1();
1312
1313
        // Now we create a new draft from the published content
1314
        $this->contentService->createContentDraft($content->contentInfo);
1315
1316
        // This call will still load the published content version
1317
        $contentPublished = $this->contentService->loadContent($content->id);
1318
1319
        $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo);
1320
    }
1321
1322
    /**
1323
     * Test for the createContentDraft() method.
1324
     *
1325
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1326
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteId
1327
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1328
     */
1329
    public function testCreateContentDraftLoadContentByRemoteIdStillLoadsPublishedVersion()
1330
    {
1331
        $content = $this->createContentVersion1();
1332
1333
        // Now we create a new draft from the published content
1334
        $this->contentService->createContentDraft($content->contentInfo);
1335
1336
        // This call will still load the published content version
1337
        $contentPublished = $this->contentService->loadContentByRemoteId('abcdef0123456789abcdef0123456789');
1338
1339
        $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo);
1340
    }
1341
1342
    /**
1343
     * Test for the createContentDraft() method.
1344
     *
1345
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
1346
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo
1347
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1348
     */
1349
    public function testCreateContentDraftLoadContentByContentInfoStillLoadsPublishedVersion()
1350
    {
1351
        $content = $this->createContentVersion1();
1352
1353
        // Now we create a new draft from the published content
1354
        $this->contentService->createContentDraft($content->contentInfo);
1355
1356
        // This call will still load the published content version
1357
        $contentPublished = $this->contentService->loadContentByContentInfo($content->contentInfo);
1358
1359
        $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo);
1360
    }
1361
1362
    /**
1363
     * Test for the newContentUpdateStruct() method.
1364
     *
1365
     * @covers \eZ\Publish\API\Repository\ContentService::newContentUpdateStruct
1366
     * @group user
1367
     */
1368
    public function testNewContentUpdateStruct()
1369
    {
1370
        $updateStruct = $this->contentService->newContentUpdateStruct();
1371
1372
        $this->assertInstanceOf(
1373
            ContentUpdateStruct::class,
1374
            $updateStruct
1375
        );
1376
1377
        $this->assertPropertiesCorrect(
1378
            [
1379
                'initialLanguageCode' => null,
1380
                'fields' => [],
1381
            ],
1382
            $updateStruct
1383
        );
1384
    }
1385
1386
    /**
1387
     * Test for the updateContent() method.
1388
     *
1389
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1390
     *
1391
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1392
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentUpdateStruct
1393
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1394
     * @group user
1395
     * @group field-type
1396
     */
1397
    public function testUpdateContent()
1398
    {
1399
        $draftVersion2 = $this->createUpdatedDraftVersion2();
1400
1401
        $this->assertInstanceOf(
1402
            Content::class,
1403
            $draftVersion2
1404
        );
1405
1406
        $this->assertEquals(
1407
            $this->generateId('user', 10),
1408
            $draftVersion2->versionInfo->creatorId,
1409
            'creatorId is not properly set on new Version'
1410
        );
1411
1412
        return $draftVersion2;
1413
    }
1414
1415
    /**
1416
     * Test for the updateContent_WithDifferentUser() method.
1417
     *
1418
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1419
     *
1420
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1421
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentUpdateStruct
1422
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
1423
     * @group user
1424
     * @group field-type
1425
     */
1426
    public function testUpdateContentWithDifferentUser()
1427
    {
1428
        $arrayWithDraftVersion2 = $this->createUpdatedDraftVersion2NotAdmin();
1429
1430
        $this->assertInstanceOf(
1431
            Content::class,
1432
            $arrayWithDraftVersion2[0]
1433
        );
1434
1435
        $this->assertEquals(
1436
            $this->generateId('user', $arrayWithDraftVersion2[1]),
1437
            $arrayWithDraftVersion2[0]->versionInfo->creatorId,
1438
            'creatorId is not properly set on new Version'
1439
        );
1440
1441
        return $arrayWithDraftVersion2[0];
1442
    }
1443
1444
    /**
1445
     * Test for the updateContent() method.
1446
     *
1447
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1448
     *
1449
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1450
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1451
     */
1452
    public function testUpdateContentSetsExpectedFields($content)
1453
    {
1454
        $actual = $this->normalizeFields($content->getFields());
1455
1456
        $expected = [
1457
            new Field(
1458
                [
1459
                    'id' => 0,
1460
                    'value' => true,
1461
                    'languageCode' => self::ENG_GB,
1462
                    'fieldDefIdentifier' => 'description',
1463
                    'fieldTypeIdentifier' => 'ezrichtext',
1464
                ]
1465
            ),
1466
            new Field(
1467
                [
1468
                    'id' => 0,
1469
                    'value' => true,
1470
                    'languageCode' => self::ENG_US,
1471
                    'fieldDefIdentifier' => 'description',
1472
                    'fieldTypeIdentifier' => 'ezrichtext',
1473
                ]
1474
            ),
1475
            new Field(
1476
                [
1477
                    'id' => 0,
1478
                    'value' => true,
1479
                    'languageCode' => self::ENG_GB,
1480
                    'fieldDefIdentifier' => 'name',
1481
                    'fieldTypeIdentifier' => 'ezstring',
1482
                ]
1483
            ),
1484
            new Field(
1485
                [
1486
                    'id' => 0,
1487
                    'value' => true,
1488
                    'languageCode' => self::ENG_US,
1489
                    'fieldDefIdentifier' => 'name',
1490
                    'fieldTypeIdentifier' => 'ezstring',
1491
                ]
1492
            ),
1493
        ];
1494
1495
        $this->assertEquals($expected, $actual);
1496
    }
1497
1498
    /**
1499
     * Test for the updateContent() method.
1500
     *
1501
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1502
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1503
     */
1504
    public function testUpdateContentThrowsBadStateException()
1505
    {
1506
        $content = $this->createContentVersion1();
1507
1508
        // Now create an update struct and modify some fields
1509
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1510
        $contentUpdateStruct->setField('title', 'An awesome² story about ezp.');
1511
        $contentUpdateStruct->setField('title', 'An awesome²³ story about ezp.', self::ENG_GB);
1512
1513
        $contentUpdateStruct->initialLanguageCode = self::ENG_US;
1514
1515
        $this->expectException(BadStateException::class);
1516
1517
        // This call will fail with a "BadStateException", because $publishedContent is not a draft.
1518
        $this->contentService->updateContent(
1519
            $content->getVersionInfo(),
1520
            $contentUpdateStruct
1521
        );
1522
    }
1523
1524
    /**
1525
     * Test for the updateContent() method.
1526
     *
1527
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1528
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1529
     */
1530
    public function testUpdateContentThrowsInvalidArgumentExceptionWhenFieldTypeDoesNotAccept()
1531
    {
1532
        $draft = $this->createContentDraftVersion1();
1533
1534
        // Now create an update struct and modify some fields
1535
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1536
        // The name field does not accept a stdClass object as its input
1537
        $contentUpdateStruct->setField('name', new \stdClass(), self::ENG_US);
1538
1539
        $this->expectException(APIInvalidArgumentException::class);
1540
        // is not accepted
1541
        $this->contentService->updateContent(
1542
            $draft->getVersionInfo(),
1543
            $contentUpdateStruct
1544
        );
1545
    }
1546
1547
    /**
1548
     * Test for the updateContent() method.
1549
     *
1550
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1551
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1552
     */
1553
    public function testUpdateContentWhenMandatoryFieldIsEmpty()
1554
    {
1555
        $draft = $this->createContentDraftVersion1();
1556
1557
        // Now create an update struct and set a mandatory field to null
1558
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1559
        $contentUpdateStruct->setField('name', null);
1560
1561
        // Don't set this, then the above call without languageCode will fail
1562
        $contentUpdateStruct->initialLanguageCode = self::ENG_US;
1563
1564
        $this->expectException(ContentFieldValidationException::class);
1565
1566
        // This call will fail with a "ContentFieldValidationException", because the mandatory "name" field is empty.
1567
        $this->contentService->updateContent(
1568
            $draft->getVersionInfo(),
1569
            $contentUpdateStruct
1570
        );
1571
    }
1572
1573
    /**
1574
     * Test for the updateContent() method.
1575
     *
1576
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1577
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1578
     */
1579
    public function testUpdateContentThrowsContentFieldValidationException()
1580
    {
1581
        $contentTypeService = $this->getRepository()->getContentTypeService();
1582
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
1583
1584
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
1585
        $contentCreate->setField('name', 'An awesome Sidelfingen folder');
1586
1587
        $draft = $this->contentService->createContent($contentCreate);
1588
1589
        $contentUpdate = $this->contentService->newContentUpdateStruct();
1590
        // Violates string length constraint
1591
        $contentUpdate->setField('short_name', str_repeat('a', 200), self::ENG_US);
1592
1593
        $this->expectException(ContentFieldValidationException::class);
1594
1595
        // Throws ContentFieldValidationException because the string length validation of the field "short_name" fails
1596
        $this->contentService->updateContent($draft->getVersionInfo(), $contentUpdate);
1597
    }
1598
1599
    /**
1600
     * Test for the updateContent() method.
1601
     *
1602
     * @covers \eZ\Publish\API\Repository\ContentService::updateContent()
1603
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1604
     */
1605
    public function testUpdateContentValidatorIgnoresRequiredFieldsOfNotUpdatedLanguages()
1606
    {
1607
        $contentTypeService = $this->getRepository()->getContentTypeService();
1608
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
1609
1610
        // Create multilangual content
1611
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
1612
        $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_US);
1613
        $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_GB);
1614
1615
        $contentDraft = $this->contentService->createContent($contentCreate);
1616
1617
        // 2. Update content type definition
1618
        $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType);
1619
1620
        $fieldDefinition = $contentType->getFieldDefinition('description');
1621
        $fieldDefinitionUpdate = $contentTypeService->newFieldDefinitionUpdateStruct();
1622
        $fieldDefinitionUpdate->identifier = 'description';
1623
        $fieldDefinitionUpdate->isRequired = true;
1624
1625
        $contentTypeService->updateFieldDefinition(
1626
            $contentTypeDraft,
1627
            $fieldDefinition,
1628
            $fieldDefinitionUpdate
1629
        );
1630
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1631
1632
        // 3. Update only eng-US translation
1633
        $description = new DOMDocument();
1634
        $description->loadXML(<<<XML
1635
<?xml version="1.0" encoding="UTF-8"?>
1636
<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">
1637
    <para>Lorem ipsum dolor</para>
1638
</section>
1639
XML
1640
        );
1641
1642
        $contentUpdate = $this->contentService->newContentUpdateStruct();
1643
        $contentUpdate->setField('name', 'An awesome Sidelfingen folder (updated)', self::ENG_US);
1644
        $contentUpdate->setField('description', $description);
1645
1646
        $this->contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdate);
1647
    }
1648
1649
    /**
1650
     * Test for the updateContent() method.
1651
     *
1652
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
1653
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1654
     */
1655
    public function testUpdateContentWithNotUpdatingMandatoryField()
1656
    {
1657
        $draft = $this->createContentDraftVersion1();
1658
1659
        // Now create an update struct which does not overwrite mandatory
1660
        // fields
1661
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
1662
        $contentUpdateStruct->setField(
1663
            'description',
1664
            '<?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"/>'
1665
        );
1666
1667
        // Don't set this, then the above call without languageCode will fail
1668
        $contentUpdateStruct->initialLanguageCode = self::ENG_US;
1669
1670
        // This will only update the "description" field in the "eng-US" language
1671
        $updatedDraft = $this->contentService->updateContent(
1672
            $draft->getVersionInfo(),
1673
            $contentUpdateStruct
1674
        );
1675
1676
        foreach ($updatedDraft->getFields() as $field) {
1677
            if ($field->languageCode === self::ENG_US && $field->fieldDefIdentifier === 'name' && $field->value !== null) {
1678
                // Found field
1679
                return;
1680
            }
1681
        }
1682
        $this->fail(
1683
            'Field with identifier "name" in language "eng-US" could not be found or has empty value.'
1684
        );
1685
    }
1686
1687
    /**
1688
     * Test for the createContentDraft() method.
1689
     *
1690
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft($contentInfo, $versionInfo)
1691
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1692
     */
1693
    public function testCreateContentDraftWithSecondParameter()
1694
    {
1695
        $contentVersion2 = $this->createContentVersion2();
1696
1697
        // Now we create a new draft from the initial version
1698
        $draftedContentReloaded = $this->contentService->createContentDraft(
1699
            $contentVersion2->contentInfo,
1700
            $contentVersion2->getVersionInfo()
1701
        );
1702
1703
        $this->assertEquals(3, $draftedContentReloaded->getVersionInfo()->versionNo);
1704
    }
1705
1706
    /**
1707
     * Test for the createContentDraft() method with third parameter.
1708
     *
1709
     * @covers \eZ\Publish\Core\Repository\ContentService::createContentDraft
1710
     */
1711
    public function testCreateContentDraftWithThirdParameter()
1712
    {
1713
        $content = $this->contentService->loadContent(4);
1714
        $user = $this->createUserVersion1();
1715
1716
        $draftContent = $this->contentService->createContentDraft(
1717
            $content->contentInfo,
1718
            $content->getVersionInfo(),
1719
            $user
1720
        );
1721
1722
        $this->assertInstanceOf(
1723
            Content::class,
1724
            $draftContent
1725
        );
1726
    }
1727
1728
    /**
1729
     * Test for the publishVersion() method.
1730
     *
1731
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1732
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1733
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
1734
     */
1735
    public function testPublishVersionFromContentDraft()
1736
    {
1737
        $contentVersion2 = $this->createContentVersion2();
1738
1739
        $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo);
1740
1741
        $this->assertEquals(
1742
            [
1743
                'status' => VersionInfo::STATUS_PUBLISHED,
1744
                'versionNo' => 2,
1745
            ],
1746
            [
1747
                'status' => $versionInfo->status,
1748
                'versionNo' => $versionInfo->versionNo,
1749
            ]
1750
        );
1751
        $this->assertTrue($versionInfo->isPublished());
1752
        $this->assertFalse($versionInfo->isDraft());
1753
        $this->assertFalse($versionInfo->isArchived());
1754
    }
1755
1756
    /**
1757
     * Test for the publishVersion() method.
1758
     *
1759
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1760
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1761
     */
1762
    public function testPublishVersionFromContentDraftArchivesOldVersion()
1763
    {
1764
        $contentVersion2 = $this->createContentVersion2();
1765
1766
        $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1);
1767
1768
        $this->assertEquals(
1769
            [
1770
                'status' => VersionInfo::STATUS_ARCHIVED,
1771
                'versionNo' => 1,
1772
            ],
1773
            [
1774
                'status' => $versionInfo->status,
1775
                'versionNo' => $versionInfo->versionNo,
1776
            ]
1777
        );
1778
        $this->assertTrue($versionInfo->isArchived());
1779
        $this->assertFalse($versionInfo->isDraft());
1780
        $this->assertFalse($versionInfo->isPublished());
1781
    }
1782
1783
    /**
1784
     * Test for the publishVersion() method.
1785
     *
1786
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1787
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1788
     */
1789
    public function testPublishVersionFromContentDraftUpdatesContentInfoCurrentVersion()
1790
    {
1791
        $contentVersion2 = $this->createContentVersion2();
1792
1793
        $this->assertEquals(2, $contentVersion2->contentInfo->currentVersionNo);
1794
    }
1795
1796
    /**
1797
     * Test for the publishVersion() method.
1798
     *
1799
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1800
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1801
     */
1802
    public function testPublishVersionFromOldContentDraftArchivesNewerVersionNo()
1803
    {
1804
        $content = $this->createContentVersion1();
1805
1806
        // Create a new draft with versionNo = 2
1807
        $draftedContentVersion2 = $this->contentService->createContentDraft($content->contentInfo);
1808
1809
        // Create another new draft with versionNo = 3
1810
        $draftedContentVersion3 = $this->contentService->createContentDraft($content->contentInfo);
1811
1812
        // Publish draft with versionNo = 3
1813
        $this->contentService->publishVersion($draftedContentVersion3->getVersionInfo());
1814
1815
        // Publish the first draft with versionNo = 2
1816
        // currentVersionNo is now 2, versionNo 3 will be archived
1817
        $publishedDraft = $this->contentService->publishVersion($draftedContentVersion2->getVersionInfo());
1818
1819
        $this->assertEquals(2, $publishedDraft->contentInfo->currentVersionNo);
1820
    }
1821
1822
    /**
1823
     * Test for the publishVersion() method, and that it creates limited archives.
1824
     *
1825
     * @todo Adapt this when per content type archive limited is added on repository Content Type model.
1826
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
1827
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
1828
     */
1829
    public function testPublishVersionNotCreatingUnlimitedArchives()
1830
    {
1831
        $content = $this->createContentVersion1();
1832
1833
        // load first to make sure list gets updated also (cache)
1834
        $versionInfoList = $this->contentService->loadVersions($content->contentInfo);
1835
        $this->assertEquals(1, count($versionInfoList));
1836
        $this->assertEquals(1, $versionInfoList[0]->versionNo);
1837
1838
        // Create a new draft with versionNo = 2
1839
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1840
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1841
1842
        // Create a new draft with versionNo = 3
1843
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1844
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1845
1846
        // Create a new draft with versionNo = 4
1847
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1848
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1849
1850
        // Create a new draft with versionNo = 5
1851
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1852
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1853
1854
        // Create a new draft with versionNo = 6
1855
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1856
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1857
1858
        // Create a new draft with versionNo = 7
1859
        $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo);
1860
        $this->contentService->publishVersion($draftedContentVersion->getVersionInfo());
1861
1862
        $versionInfoList = $this->contentService->loadVersions($content->contentInfo);
1863
1864
        $this->assertEquals(6, count($versionInfoList));
1865
        $this->assertEquals(2, $versionInfoList[0]->versionNo);
1866
        $this->assertEquals(7, $versionInfoList[5]->versionNo);
1867
1868
        $this->assertEquals(
1869
            [
1870
                VersionInfo::STATUS_ARCHIVED,
1871
                VersionInfo::STATUS_ARCHIVED,
1872
                VersionInfo::STATUS_ARCHIVED,
1873
                VersionInfo::STATUS_ARCHIVED,
1874
                VersionInfo::STATUS_ARCHIVED,
1875
                VersionInfo::STATUS_PUBLISHED,
1876
            ],
1877
            [
1878
                $versionInfoList[0]->status,
1879
                $versionInfoList[1]->status,
1880
                $versionInfoList[2]->status,
1881
                $versionInfoList[3]->status,
1882
                $versionInfoList[4]->status,
1883
                $versionInfoList[5]->status,
1884
            ]
1885
        );
1886
    }
1887
1888
    /**
1889
     * Test for the newContentMetadataUpdateStruct() method.
1890
     *
1891
     * @covers \eZ\Publish\API\Repository\ContentService::newContentMetadataUpdateStruct
1892
     * @group user
1893
     */
1894
    public function testNewContentMetadataUpdateStruct()
1895
    {
1896
        // Creates a new metadata update struct
1897
        $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
1898
1899
        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...
1900
            $this->assertNull($propertyValue, "Property '{$propertyName}' initial value should be null'");
1901
        }
1902
1903
        $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222';
1904
        $metadataUpdate->mainLanguageCode = self::ENG_GB;
1905
        $metadataUpdate->alwaysAvailable = false;
1906
1907
        $this->assertInstanceOf(
1908
            ContentMetadataUpdateStruct::class,
1909
            $metadataUpdate
1910
        );
1911
    }
1912
1913
    /**
1914
     * Test for the updateContentMetadata() method.
1915
     *
1916
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1917
     *
1918
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
1919
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
1920
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentMetadataUpdateStruct
1921
     * @group user
1922
     */
1923
    public function testUpdateContentMetadata()
1924
    {
1925
        $content = $this->createContentVersion1();
1926
1927
        // Creates a metadata update struct
1928
        $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
1929
1930
        $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222';
1931
        $metadataUpdate->mainLanguageCode = self::ENG_GB;
1932
        $metadataUpdate->alwaysAvailable = false;
1933
        $metadataUpdate->publishedDate = $this->createDateTime(441759600); // 1984/01/01
1934
        $metadataUpdate->modificationDate = $this->createDateTime(441759600); // 1984/01/01
1935
1936
        // Update the metadata of the published content object
1937
        $content = $this->contentService->updateContentMetadata(
1938
            $content->contentInfo,
1939
            $metadataUpdate
1940
        );
1941
1942
        $this->assertInstanceOf(
1943
            Content::class,
1944
            $content
1945
        );
1946
1947
        return $content;
1948
    }
1949
1950
    /**
1951
     * Test for the updateContentMetadata() method.
1952
     *
1953
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1954
     *
1955
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
1956
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
1957
     */
1958
    public function testUpdateContentMetadataSetsExpectedProperties($content)
1959
    {
1960
        $contentInfo = $content->contentInfo;
1961
1962
        $this->assertEquals(
1963
            [
1964
                'remoteId' => 'aaaabbbbccccddddeeeeffff11112222',
1965
                'sectionId' => $this->generateId('section', 1),
1966
                'alwaysAvailable' => false,
1967
                'currentVersionNo' => 1,
1968
                'mainLanguageCode' => self::ENG_GB,
1969
                'modificationDate' => $this->createDateTime(441759600),
1970
                '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...
1971
                'published' => true,
1972
                'publishedDate' => $this->createDateTime(441759600),
1973
            ],
1974
            [
1975
                'remoteId' => $contentInfo->remoteId,
1976
                'sectionId' => $contentInfo->sectionId,
1977
                'alwaysAvailable' => $contentInfo->alwaysAvailable,
1978
                'currentVersionNo' => $contentInfo->currentVersionNo,
1979
                'mainLanguageCode' => $contentInfo->mainLanguageCode,
1980
                'modificationDate' => $contentInfo->modificationDate,
1981
                'ownerId' => $contentInfo->ownerId,
1982
                'published' => $contentInfo->published,
1983
                'publishedDate' => $contentInfo->publishedDate,
1984
            ]
1985
        );
1986
    }
1987
1988
    /**
1989
     * Test for the updateContentMetadata() method.
1990
     *
1991
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
1992
     *
1993
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
1994
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
1995
     */
1996
    public function testUpdateContentMetadataNotUpdatesContentVersion($content)
1997
    {
1998
        $this->assertEquals(1, $content->getVersionInfo()->versionNo);
1999
    }
2000
2001
    /**
2002
     * Test for the updateContentMetadata() method.
2003
     *
2004
     * @covers \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
2005
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
2006
     */
2007
    public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnDuplicateRemoteId()
2008
    {
2009
        $content = $this->createContentVersion1();
2010
2011
        // Creates a metadata update struct
2012
        $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
2013
        $metadataUpdate->remoteId = self::MEDIA_REMOTE_ID;
2014
2015
        $this->expectException(APIInvalidArgumentException::class);
2016
        // specified remoteId is already used by the "Media" page.
2017
        $this->contentService->updateContentMetadata(
2018
            $content->contentInfo,
2019
            $metadataUpdate
2020
        );
2021
    }
2022
2023
    /**
2024
     * Test for the updateContentMetadata() method.
2025
     *
2026
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContentMetadata
2027
     */
2028
    public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnNoMetadataPropertiesSet()
2029
    {
2030
        $contentInfo = $this->contentService->loadContentInfo(4);
2031
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
2032
2033
        $this->expectException(APIInvalidArgumentException::class);
2034
        $this->contentService->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct);
2035
    }
2036
2037
    /**
2038
     * Test for the deleteContent() method.
2039
     *
2040
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
2041
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2042
     */
2043
    public function testDeleteContent()
2044
    {
2045
        $contentVersion2 = $this->createContentVersion2();
2046
2047
        // Load the locations for this content object
2048
        $locations = $this->locationService->loadLocations($contentVersion2->contentInfo);
2049
2050
        // This will delete the content, all versions and the associated locations
2051
        $this->contentService->deleteContent($contentVersion2->contentInfo);
2052
2053
        $this->expectException(NotFoundException::class);
2054
2055
        foreach ($locations as $location) {
2056
            $this->locationService->loadLocation($location->id);
2057
        }
2058
    }
2059
2060
    /**
2061
     * Test for the deleteContent() method.
2062
     *
2063
     * Test for issue EZP-21057:
2064
     * "contentService: Unable to delete a content with an empty file attribute"
2065
     *
2066
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
2067
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2068
     */
2069
    public function testDeleteContentWithEmptyBinaryField()
2070
    {
2071
        $contentVersion = $this->createContentVersion1EmptyBinaryField();
2072
2073
        // Load the locations for this content object
2074
        $locations = $this->locationService->loadLocations($contentVersion->contentInfo);
2075
2076
        // This will delete the content, all versions and the associated locations
2077
        $this->contentService->deleteContent($contentVersion->contentInfo);
2078
2079
        $this->expectException(NotFoundException::class);
2080
2081
        foreach ($locations as $location) {
2082
            $this->locationService->loadLocation($location->id);
2083
        }
2084
    }
2085
2086
    public function testCountContentDraftsReturnsZeroByDefault(): void
2087
    {
2088
        $this->assertSame(0, $this->contentService->countContentDrafts());
2089
    }
2090
2091
    public function testCountContentDrafts(): void
2092
    {
2093
        // Create 5 drafts
2094
        $this->createContentDrafts(5);
2095
2096
        $this->assertSame(5, $this->contentService->countContentDrafts());
2097
    }
2098
2099
    public function testCountContentDraftsForUsers(): void
2100
    {
2101
        $newUser = $this->createUserWithPolicies(
2102
            'new_user',
2103
            [
2104
                ['module' => 'content', 'function' => 'create'],
2105
                ['module' => 'content', 'function' => 'read'],
2106
                ['module' => 'content', 'function' => 'publish'],
2107
                ['module' => 'content', 'function' => 'edit'],
2108
            ]
2109
        );
2110
2111
        $previousUser = $this->permissionResolver->getCurrentUserReference();
2112
2113
        // Set new editor as user
2114
        $this->permissionResolver->setCurrentUserReference($newUser);
2115
2116
        // Create a content draft as newUser
2117
        $publishedContent = $this->createContentVersion1();
2118
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2119
2120
        // Reset to previous current user
2121
        $this->permissionResolver->setCurrentUserReference($previousUser);
2122
2123
        // Now $contentDrafts for the previous current user and the new user
2124
        $newUserDrafts = $this->contentService->countContentDrafts($newUser);
2125
        $previousUserDrafts = $this->contentService->countContentDrafts();
2126
2127
        $this->assertSame(1, $newUserDrafts);
2128
        $this->assertSame(0, $previousUserDrafts);
2129
    }
2130
2131
    /**
2132
     * Test for the loadContentDrafts() method.
2133
     *
2134
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2135
     */
2136
    public function testLoadContentDraftsReturnsEmptyArrayByDefault()
2137
    {
2138
        $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...
2139
2140
        $this->assertSame([], $contentDrafts);
2141
    }
2142
2143
    /**
2144
     * Test for the loadContentDrafts() method.
2145
     *
2146
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2147
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
2148
     */
2149
    public function testLoadContentDrafts()
2150
    {
2151
        // "Media" content object
2152
        $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
2153
2154
        // "eZ Publish Demo Design ..." content object
2155
        $demoDesignContentInfo = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
2156
2157
        // Create some drafts
2158
        $this->contentService->createContentDraft($mediaContentInfo);
2159
        $this->contentService->createContentDraft($demoDesignContentInfo);
2160
2161
        // Now $contentDrafts should contain two drafted versions
2162
        $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...
2163
2164
        $actual = [
2165
            $draftedVersions[0]->status,
2166
            $draftedVersions[0]->getContentInfo()->remoteId,
2167
            $draftedVersions[1]->status,
2168
            $draftedVersions[1]->getContentInfo()->remoteId,
2169
        ];
2170
        sort($actual, SORT_STRING);
2171
2172
        $this->assertEquals(
2173
            [
2174
                VersionInfo::STATUS_DRAFT,
2175
                VersionInfo::STATUS_DRAFT,
2176
                self::DEMO_DESIGN_REMOTE_ID,
2177
                self::MEDIA_REMOTE_ID,
2178
            ],
2179
            $actual
2180
        );
2181
    }
2182
2183
    /**
2184
     * Test for the loadContentDrafts() method.
2185
     *
2186
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts($user)
2187
     */
2188
    public function testLoadContentDraftsWithFirstParameter()
2189
    {
2190
        $user = $this->createUserVersion1();
2191
2192
        // Get current user
2193
        $oldCurrentUser = $this->permissionResolver->getCurrentUserReference();
2194
2195
        // Set new editor as user
2196
        $this->permissionResolver->setCurrentUserReference($user);
2197
2198
        // "Media" content object
2199
        $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
2200
2201
        // Create a content draft
2202
        $this->contentService->createContentDraft($mediaContentInfo);
2203
2204
        // Reset to previous current user
2205
        $this->permissionResolver->setCurrentUserReference($oldCurrentUser);
2206
2207
        // Now $contentDrafts for the previous current user and the new user
2208
        $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...
2209
        $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...
2210
2211
        $this->assertSame([], $oldCurrentUserDrafts);
2212
2213
        $this->assertEquals(
2214
            [
2215
                VersionInfo::STATUS_DRAFT,
2216
                self::MEDIA_REMOTE_ID,
2217
            ],
2218
            [
2219
                $newCurrentUserDrafts[0]->status,
2220
                $newCurrentUserDrafts[0]->getContentInfo()->remoteId,
2221
            ]
2222
        );
2223
        $this->assertTrue($newCurrentUserDrafts[0]->isDraft());
2224
        $this->assertFalse($newCurrentUserDrafts[0]->isArchived());
2225
        $this->assertFalse($newCurrentUserDrafts[0]->isPublished());
2226
    }
2227
2228
    /**
2229
     * Test for the loadContentDraftList() method.
2230
     *
2231
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2232
     */
2233
    public function testLoadContentDraftListWithPaginationParameters()
2234
    {
2235
        // Create some drafts
2236
        $publishedContent = $this->createContentVersion1();
2237
        $draftContentA = $this->contentService->createContentDraft($publishedContent->contentInfo);
2238
        $draftContentB = $this->contentService->createContentDraft($draftContentA->contentInfo);
2239
        $draftContentC = $this->contentService->createContentDraft($draftContentB->contentInfo);
2240
        $draftContentD = $this->contentService->createContentDraft($draftContentC->contentInfo);
2241
        $draftContentE = $this->contentService->createContentDraft($draftContentD->contentInfo);
2242
2243
        $draftsOnPage1 = $this->contentService->loadContentDraftList(null, 0, 2);
2244
        $draftsOnPage2 = $this->contentService->loadContentDraftList(null, 2, 2);
2245
2246
        $this->assertSame(5, $draftsOnPage1->totalCount);
2247
        $this->assertSame(5, $draftsOnPage2->totalCount);
2248
        $this->assertEquals($draftContentE->getVersionInfo(), $draftsOnPage1->items[0]->getVersionInfo());
2249
        $this->assertEquals($draftContentD->getVersionInfo(), $draftsOnPage1->items[1]->getVersionInfo());
2250
        $this->assertEquals($draftContentC->getVersionInfo(), $draftsOnPage2->items[0]->getVersionInfo());
2251
        $this->assertEquals($draftContentB->getVersionInfo(), $draftsOnPage2->items[1]->getVersionInfo());
2252
    }
2253
2254
    /**
2255
     * Test for the loadContentDraftList() method.
2256
     *
2257
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts($user)
2258
     */
2259
    public function testLoadContentDraftListWithForUserWithLimitation()
2260
    {
2261
        $oldUser = $this->permissionResolver->getCurrentUserReference();
2262
2263
        $parentContent = $this->createFolder(['eng-US' => 'parentFolder'], 2);
2264
        $content = $this->createFolder(['eng-US' => 'parentFolder'], $parentContent->contentInfo->mainLocationId);
2265
2266
        // User has limitation to read versions only for `$content`, not for `$parentContent`
2267
        $newUser = $this->createUserWithVersionReadLimitations([$content->contentInfo->mainLocationId]);
2268
2269
        $this->permissionResolver->setCurrentUserReference($newUser);
2270
2271
        $contentDraftUnauthorized = $this->contentService->createContentDraft($parentContent->contentInfo);
2272
        $contentDraftA = $this->contentService->createContentDraft($content->contentInfo);
2273
        $contentDraftB = $this->contentService->createContentDraft($content->contentInfo);
2274
2275
        $newUserDraftList = $this->contentService->loadContentDraftList($newUser, 0);
2276
        $this->assertSame(3, $newUserDraftList->totalCount);
2277
        $this->assertEquals($contentDraftB->getVersionInfo(), $newUserDraftList->items[0]->getVersionInfo());
2278
        $this->assertEquals($contentDraftA->getVersionInfo(), $newUserDraftList->items[1]->getVersionInfo());
2279
        $this->assertEquals(
2280
            new UnauthorizedContentDraftListItem('content', 'versionread', ['contentId' => $contentDraftUnauthorized->id]),
2281
            $newUserDraftList->items[2]
2282
        );
2283
2284
        // Reset to previous user
2285
        $this->permissionResolver->setCurrentUserReference($oldUser);
2286
2287
        $oldUserDraftList = $this->contentService->loadContentDraftList();
2288
2289
        $this->assertSame(0, $oldUserDraftList->totalCount);
2290
        $this->assertSame([], $oldUserDraftList->items);
2291
    }
2292
2293
    /**
2294
     * Test for the loadContentDraftList() method.
2295
     *
2296
     * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts()
2297
     */
2298
    public function testLoadAllContentDrafts()
2299
    {
2300
        // Create more drafts then default pagination limit
2301
        $this->createContentDrafts(12);
2302
2303
        $this->assertCount(12, $this->contentService->loadContentDraftList());
2304
    }
2305
2306
    /**
2307
     * Test for the loadVersionInfo() method.
2308
     *
2309
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo)
2310
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2311
     */
2312
    public function testLoadVersionInfoWithSecondParameter()
2313
    {
2314
        $publishedContent = $this->createContentVersion1();
2315
2316
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2317
2318
        // Will return the VersionInfo of the $draftContent
2319
        $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2);
2320
2321
        $this->assertEquals(2, $versionInfo->versionNo);
2322
2323
        // Check that ContentInfo contained in VersionInfo has correct main Location id set
2324
        $this->assertEquals(
2325
            $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId,
2326
            $versionInfo->getContentInfo()->mainLocationId
2327
        );
2328
    }
2329
2330
    /**
2331
     * Test for the loadVersionInfo() method.
2332
     *
2333
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo)
2334
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoWithSecondParameter
2335
     */
2336
    public function testLoadVersionInfoThrowsNotFoundExceptionWithSecondParameter()
2337
    {
2338
        $draft = $this->createContentDraftVersion1();
2339
2340
        $this->expectException(NotFoundException::class);
2341
2342
        // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object.
2343
        $this->contentService->loadVersionInfo($draft->contentInfo, 2);
2344
    }
2345
2346
    /**
2347
     * Test for the loadVersionInfoById() method.
2348
     *
2349
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo)
2350
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoWithSecondParameter
2351
     */
2352
    public function testLoadVersionInfoByIdWithSecondParameter()
2353
    {
2354
        $publishedContent = $this->createContentVersion1();
2355
2356
        $draftContent = $this->contentService->createContentDraft($publishedContent->contentInfo);
2357
2358
        // Will return the VersionInfo of the $draftContent
2359
        $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2);
2360
2361
        $this->assertEquals(2, $versionInfo->versionNo);
2362
2363
        // Check that ContentInfo contained in VersionInfo has correct main Location id set
2364
        $this->assertEquals(
2365
            $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId,
2366
            $versionInfo->getContentInfo()->mainLocationId
2367
        );
2368
2369
        return [
2370
            'versionInfo' => $versionInfo,
2371
            'draftContent' => $draftContent,
2372
        ];
2373
    }
2374
2375
    /**
2376
     * Test for the returned value of the loadVersionInfoById() method.
2377
     *
2378
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoByIdWithSecondParameter
2379
     * @covers \eZ\Publish\API\Repository\ContentService::loadVersionInfoById
2380
     *
2381
     * @param array $data
2382
     */
2383
    public function testLoadVersionInfoByIdWithSecondParameterSetsExpectedVersionInfo(array $data)
2384
    {
2385
        /** @var VersionInfo $versionInfo */
2386
        $versionInfo = $data['versionInfo'];
2387
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $draftContent */
2388
        $draftContent = $data['draftContent'];
2389
2390
        $this->assertPropertiesCorrect(
2391
            [
2392
                'names' => [
2393
                    self::ENG_US => 'An awesome forum',
2394
                ],
2395
                'contentInfo' => new ContentInfo([
2396
                    'id' => $draftContent->contentInfo->id,
2397
                    'contentTypeId' => 28,
2398
                    'name' => 'An awesome forum',
2399
                    'sectionId' => 1,
2400
                    'currentVersionNo' => 1,
2401
                    'published' => true,
2402
                    'ownerId' => 14,
2403
                    // this Content Object is created at the test runtime
2404
                    'modificationDate' => $versionInfo->contentInfo->modificationDate,
2405
                    'publishedDate' => $versionInfo->contentInfo->publishedDate,
2406
                    'alwaysAvailable' => 1,
2407
                    'remoteId' => 'abcdef0123456789abcdef0123456789',
2408
                    'mainLanguageCode' => self::ENG_US,
2409
                    'mainLocationId' => $draftContent->contentInfo->mainLocationId,
2410
                    'status' => ContentInfo::STATUS_PUBLISHED,
2411
                ]),
2412
                'id' => $draftContent->versionInfo->id,
2413
                'versionNo' => 2,
2414
                'creatorId' => 14,
2415
                'status' => 0,
2416
                'initialLanguageCode' => self::ENG_US,
2417
                'languageCodes' => [
2418
                    self::ENG_US,
2419
                ],
2420
            ],
2421
            $versionInfo
2422
        );
2423
    }
2424
2425
    /**
2426
     * Test for the loadVersionInfoById() method.
2427
     *
2428
     * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo)
2429
     */
2430
    public function testLoadVersionInfoByIdThrowsNotFoundExceptionWithSecondParameter()
2431
    {
2432
        $content = $this->createContentVersion1();
2433
2434
        $this->expectException(NotFoundException::class);
2435
2436
        // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object.
2437
        $this->contentService->loadVersionInfoById($content->id, 2);
2438
    }
2439
2440
    /**
2441
     * Test for the loadContentByVersionInfo() method.
2442
     *
2443
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo($versionInfo, $languages)
2444
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2445
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByVersionInfo
2446
     */
2447
    public function testLoadContentByVersionInfoWithSecondParameter()
2448
    {
2449
        $sectionId = $this->generateId('section', 1);
2450
        $contentTypeService = $this->getRepository()->getContentTypeService();
2451
2452
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
2453
2454
        $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
2455
2456
        $contentCreateStruct->setField('name', 'Sindelfingen forum²');
2457
2458
        $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB);
2459
2460
        $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789';
2461
        // $sectionId contains the ID of section 1
2462
        $contentCreateStruct->sectionId = $sectionId;
2463
        $contentCreateStruct->alwaysAvailable = true;
2464
2465
        // Create a new content draft
2466
        $content = $this->contentService->createContent($contentCreateStruct);
2467
2468
        // Now publish this draft
2469
        $publishedContent = $this->contentService->publishVersion($content->getVersionInfo());
2470
2471
        // Will return a content instance with fields in "eng-US"
2472
        $reloadedContent = $this->contentService->loadContentByVersionInfo(
2473
            $publishedContent->getVersionInfo(),
2474
            [
2475
                self::ENG_GB,
2476
            ],
2477
            false
2478
        );
2479
2480
        $actual = [];
2481
        foreach ($reloadedContent->getFields() as $field) {
2482
            $actual[] = new Field(
2483
                [
2484
                    'id' => 0,
2485
                    'value' => $field->value !== null, // Actual value tested by FieldType integration tests
2486
                    'languageCode' => $field->languageCode,
2487
                    'fieldDefIdentifier' => $field->fieldDefIdentifier,
2488
                ]
2489
            );
2490
        }
2491
        usort(
2492
            $actual,
2493
            function ($field1, $field2) {
2494
                if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) {
2495
                    return strcasecmp($field1->languageCode, $field2->languageCode);
2496
                }
2497
2498
                return $return;
2499
            }
2500
        );
2501
2502
        $expected = [
2503
            new Field(
2504
                [
2505
                    'id' => 0,
2506
                    'value' => true,
2507
                    'languageCode' => self::ENG_GB,
2508
                    'fieldDefIdentifier' => 'description',
2509
                ]
2510
            ),
2511
            new Field(
2512
                [
2513
                    'id' => 0,
2514
                    'value' => true,
2515
                    'languageCode' => self::ENG_GB,
2516
                    'fieldDefIdentifier' => 'name',
2517
                ]
2518
            ),
2519
        ];
2520
2521
        $this->assertEquals($expected, $actual);
2522
    }
2523
2524
    /**
2525
     * Test for the loadContentByContentInfo() method.
2526
     *
2527
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages)
2528
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo
2529
     */
2530
    public function testLoadContentByContentInfoWithLanguageParameters()
2531
    {
2532
        $sectionId = $this->generateId('section', 1);
2533
        $contentTypeService = $this->getRepository()->getContentTypeService();
2534
2535
        $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
2536
2537
        $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
2538
2539
        $contentCreateStruct->setField('name', 'Sindelfingen forum²');
2540
2541
        $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB);
2542
2543
        $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789';
2544
        // $sectionId contains the ID of section 1
2545
        $contentCreateStruct->sectionId = $sectionId;
2546
        $contentCreateStruct->alwaysAvailable = true;
2547
2548
        // Create a new content draft
2549
        $content = $this->contentService->createContent($contentCreateStruct);
2550
2551
        // Now publish this draft
2552
        $publishedContent = $this->contentService->publishVersion($content->getVersionInfo());
2553
2554
        // Will return a content instance with fields in "eng-US"
2555
        $reloadedContent = $this->contentService->loadContentByContentInfo(
2556
            $publishedContent->contentInfo,
2557
            [
2558
                self::ENG_US,
2559
            ],
2560
            null,
2561
            false
2562
        );
2563
2564
        $actual = $this->normalizeFields($reloadedContent->getFields());
2565
2566
        $expected = [
2567
            new Field(
2568
                [
2569
                    'id' => 0,
2570
                    'value' => true,
2571
                    'languageCode' => self::ENG_US,
2572
                    'fieldDefIdentifier' => 'description',
2573
                    'fieldTypeIdentifier' => 'ezrichtext',
2574
                ]
2575
            ),
2576
            new Field(
2577
                [
2578
                    'id' => 0,
2579
                    'value' => true,
2580
                    'languageCode' => self::ENG_US,
2581
                    'fieldDefIdentifier' => 'name',
2582
                    'fieldTypeIdentifier' => 'ezstring',
2583
                ]
2584
            ),
2585
        ];
2586
2587
        $this->assertEquals($expected, $actual);
2588
2589
        // Will return a content instance with fields in "eng-GB" (versions prior to 6.0.0-beta9 returned "eng-US" also)
2590
        $reloadedContent = $this->contentService->loadContentByContentInfo(
2591
            $publishedContent->contentInfo,
2592
            [
2593
                self::ENG_GB,
2594
            ],
2595
            null,
2596
            true
2597
        );
2598
2599
        $actual = $this->normalizeFields($reloadedContent->getFields());
2600
2601
        $expected = [
2602
            new Field(
2603
                [
2604
                    'id' => 0,
2605
                    'value' => true,
2606
                    'languageCode' => self::ENG_GB,
2607
                    'fieldDefIdentifier' => 'description',
2608
                    'fieldTypeIdentifier' => 'ezrichtext',
2609
                ]
2610
            ),
2611
            new Field(
2612
                [
2613
                    'id' => 0,
2614
                    'value' => true,
2615
                    'languageCode' => self::ENG_GB,
2616
                    'fieldDefIdentifier' => 'name',
2617
                    'fieldTypeIdentifier' => 'ezstring',
2618
                ]
2619
            ),
2620
        ];
2621
2622
        $this->assertEquals($expected, $actual);
2623
2624
        // Will return a content instance with fields in main language "eng-US", as "fre-FR" does not exists
2625
        $reloadedContent = $this->contentService->loadContentByContentInfo(
2626
            $publishedContent->contentInfo,
2627
            [
2628
                'fre-FR',
2629
            ],
2630
            null,
2631
            true
2632
        );
2633
2634
        $actual = $this->normalizeFields($reloadedContent->getFields());
2635
2636
        $expected = [
2637
            new Field(
2638
                [
2639
                    'id' => 0,
2640
                    'value' => true,
2641
                    'languageCode' => self::ENG_US,
2642
                    'fieldDefIdentifier' => 'description',
2643
                    'fieldTypeIdentifier' => 'ezrichtext',
2644
                ]
2645
            ),
2646
            new Field(
2647
                [
2648
                    'id' => 0,
2649
                    'value' => true,
2650
                    'languageCode' => self::ENG_US,
2651
                    'fieldDefIdentifier' => 'name',
2652
                    'fieldTypeIdentifier' => 'ezstring',
2653
                ]
2654
            ),
2655
        ];
2656
2657
        $this->assertEquals($expected, $actual);
2658
    }
2659
2660
    /**
2661
     * Test for the loadContentByContentInfo() method.
2662
     *
2663
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo)
2664
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo
2665
     */
2666
    public function testLoadContentByContentInfoWithVersionNumberParameter()
2667
    {
2668
        $publishedContent = $this->createContentVersion1();
2669
2670
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2671
2672
        // This content instance is identical to $draftContent
2673
        $draftContentReloaded = $this->contentService->loadContentByContentInfo(
2674
            $publishedContent->contentInfo,
2675
            null,
2676
            2
2677
        );
2678
2679
        $this->assertEquals(
2680
            2,
2681
            $draftContentReloaded->getVersionInfo()->versionNo
2682
        );
2683
2684
        // Check that ContentInfo contained in reloaded draft Content has correct main Location id set
2685
        $this->assertEquals(
2686
            $publishedContent->versionInfo->contentInfo->mainLocationId,
2687
            $draftContentReloaded->versionInfo->contentInfo->mainLocationId
2688
        );
2689
    }
2690
2691
    /**
2692
     * Test for the loadContentByContentInfo() method.
2693
     *
2694
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo)
2695
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfoWithVersionNumberParameter
2696
     */
2697
    public function testLoadContentByContentInfoThrowsNotFoundExceptionWithVersionNumberParameter()
2698
    {
2699
        $content = $this->createContentVersion1();
2700
2701
        $this->expectException(NotFoundException::class);
2702
2703
        // This call will fail with a "NotFoundException", because no content with versionNo = 2 exists.
2704
        $this->contentService->loadContentByContentInfo($content->contentInfo, null, 2);
2705
    }
2706
2707
    /**
2708
     * Test for the loadContent() method.
2709
     *
2710
     * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages)
2711
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2712
     */
2713
    public function testLoadContentWithPrioritizedLanguages()
2714
    {
2715
        $draft = $this->createMultipleLanguageDraftVersion1();
2716
2717
        // This draft contains those fields localized with "eng-GB"
2718
        $draftLocalized = $this->contentService->loadContent($draft->id, [self::ENG_GB], null, false);
2719
2720
        $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB);
2721
2722
        return $draftLocalized;
2723
    }
2724
2725
    /**
2726
     * Test for the loadContent() method using undefined translation.
2727
     *
2728
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithPrioritizedLanguages
2729
     *
2730
     * @param \eZ\Publish\API\Repository\Values\Content\Content $contentDraft
2731
     */
2732
    public function testLoadContentWithPrioritizedLanguagesThrowsNotFoundException(Content $contentDraft)
2733
    {
2734
        $this->expectException(NotFoundException::class);
2735
2736
        $this->contentService->loadContent($contentDraft->id, [self::GER_DE], null, false);
2737
    }
2738
2739
    /**
2740
     * Test for the loadContent() method.
2741
     *
2742
     * @see \eZ\Publish\API\Repository\ContentService::loadContent
2743
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithPrioritizedLanguages
2744
     */
2745
    public function testLoadContentPassTroughPrioritizedLanguagesToContentType(Content $content): void
2746
    {
2747
        $contentTypeService = $this->getRepository()->getContentTypeService();
2748
2749
        $contentType = $contentTypeService->loadContentType(
2750
            $content->contentInfo->contentTypeId,
2751
            [self::ENG_GB]
2752
        );
2753
2754
        $this->assertEquals($contentType, $content->getContentType());
2755
    }
2756
2757
    /**
2758
     * Test for the loadContent() method.
2759
     *
2760
     * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages, $versionNo)
2761
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2762
     */
2763
    public function testLoadContentWithThirdParameter()
2764
    {
2765
        $publishedContent = $this->createContentVersion1();
2766
2767
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2768
2769
        // This content instance is identical to $draftContent
2770
        $draftContentReloaded = $this->contentService->loadContent($publishedContent->id, null, 2);
2771
2772
        $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo);
2773
2774
        // Check that ContentInfo contained in reloaded draft Content has correct main Location id set
2775
        $this->assertEquals(
2776
            $publishedContent->versionInfo->contentInfo->mainLocationId,
2777
            $draftContentReloaded->versionInfo->contentInfo->mainLocationId
2778
        );
2779
    }
2780
2781
    /**
2782
     * Test for the loadContent() method.
2783
     *
2784
     * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages, $versionNo)
2785
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithThirdParameter
2786
     */
2787
    public function testLoadContentThrowsNotFoundExceptionWithThirdParameter()
2788
    {
2789
        $content = $this->createContentVersion1();
2790
2791
        $this->expectException(NotFoundException::class);
2792
2793
        // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists.
2794
        $this->contentService->loadContent($content->id, null, 2);
2795
    }
2796
2797
    /**
2798
     * Test for the loadContentByRemoteId() method.
2799
     *
2800
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages)
2801
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2802
     */
2803
    public function testLoadContentByRemoteIdWithSecondParameter()
2804
    {
2805
        $draft = $this->createMultipleLanguageDraftVersion1();
2806
2807
        $this->contentService->publishVersion($draft->versionInfo);
2808
2809
        // This draft contains those fields localized with "eng-GB"
2810
        $draftLocalized = $this->contentService->loadContentByRemoteId(
2811
            $draft->contentInfo->remoteId,
2812
            [self::ENG_GB],
2813
            null,
2814
            false
2815
        );
2816
2817
        $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB);
2818
    }
2819
2820
    /**
2821
     * Test for the loadContentByRemoteId() method.
2822
     *
2823
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo)
2824
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
2825
     */
2826
    public function testLoadContentByRemoteIdWithThirdParameter()
2827
    {
2828
        $publishedContent = $this->createContentVersion1();
2829
2830
        $this->contentService->createContentDraft($publishedContent->contentInfo);
2831
2832
        // This content instance is identical to $draftContent
2833
        $draftContentReloaded = $this->contentService->loadContentByRemoteId(
2834
            $publishedContent->contentInfo->remoteId,
2835
            null,
2836
            2
2837
        );
2838
2839
        $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo);
2840
2841
        // Check that ContentInfo contained in reloaded draft Content has correct main Location id set
2842
        $this->assertEquals(
2843
            $publishedContent->versionInfo->contentInfo->mainLocationId,
2844
            $draftContentReloaded->versionInfo->contentInfo->mainLocationId
2845
        );
2846
    }
2847
2848
    /**
2849
     * Test for the loadContentByRemoteId() method.
2850
     *
2851
     * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo)
2852
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteIdWithThirdParameter
2853
     */
2854
    public function testLoadContentByRemoteIdThrowsNotFoundExceptionWithThirdParameter()
2855
    {
2856
        $content = $this->createContentVersion1();
2857
2858
        $this->expectException(NotFoundException::class);
2859
2860
        // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists.
2861
        $this->contentService->loadContentByRemoteId(
2862
            $content->contentInfo->remoteId,
2863
            null,
2864
            2
2865
        );
2866
    }
2867
2868
    /**
2869
     * Test that retrieval of translated name field respects prioritized language list.
2870
     *
2871
     * @dataProvider getPrioritizedLanguageList
2872
     * @param string[]|null $languageCodes
2873
     */
2874
    public function testLoadContentWithPrioritizedLanguagesList($languageCodes)
2875
    {
2876
        $content = $this->createContentVersion2();
2877
2878
        $content = $this->contentService->loadContent($content->id, $languageCodes);
2879
2880
        $expectedName = $content->getVersionInfo()->getName(
2881
            isset($languageCodes[0]) ? $languageCodes[0] : null
2882
        );
2883
        $nameValue = $content->getFieldValue('name');
2884
        /** @var \eZ\Publish\Core\FieldType\TextLine\Value $nameValue */
2885
        self::assertEquals($expectedName, $nameValue->text);
2886
        self::assertEquals($expectedName, $content->getVersionInfo()->getName());
2887
        // Also check value on shortcut method on content
2888
        self::assertEquals($expectedName, $content->getName());
2889
    }
2890
2891
    /**
2892
     * @return array
2893
     */
2894
    public function getPrioritizedLanguageList()
2895
    {
2896
        return [
2897
            [[self::ENG_US]],
2898
            [[self::ENG_GB]],
2899
            [[self::ENG_GB, self::ENG_US]],
2900
            [[self::ENG_US, self::ENG_GB]],
2901
        ];
2902
    }
2903
2904
    /**
2905
     * Test for the deleteVersion() method.
2906
     *
2907
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
2908
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
2909
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2910
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2911
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
2912
     */
2913
    public function testDeleteVersion()
2914
    {
2915
        $content = $this->createContentVersion1();
2916
2917
        // Create new draft, because published or last version of the Content can't be deleted
2918
        $draft = $this->contentService->createContentDraft(
2919
            $content->getVersionInfo()->getContentInfo()
2920
        );
2921
2922
        // Delete the previously created draft
2923
        $this->contentService->deleteVersion($draft->getVersionInfo());
2924
2925
        $versions = $this->contentService->loadVersions($content->getVersionInfo()->getContentInfo());
2926
2927
        $this->assertCount(1, $versions);
2928
        $this->assertEquals(
2929
            $content->getVersionInfo()->id,
2930
            $versions[0]->id
2931
        );
2932
    }
2933
2934
    /**
2935
     * Test for the deleteVersion() method.
2936
     *
2937
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
2938
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
2939
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2940
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2941
     */
2942
    public function testDeleteVersionThrowsBadStateExceptionOnPublishedVersion()
2943
    {
2944
        $content = $this->createContentVersion1();
2945
2946
        $this->expectException(BadStateException::class);
2947
2948
        // This call will fail with a "BadStateException", because the content version is currently published.
2949
        $this->contentService->deleteVersion($content->getVersionInfo());
2950
    }
2951
2952
    /**
2953
     * Test for the deleteVersion() method.
2954
     *
2955
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
2956
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
2957
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
2958
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2959
     */
2960
    public function testDeleteVersionWorksIfOnlyVersionIsDraft()
2961
    {
2962
        $draft = $this->createContentDraftVersion1();
2963
2964
        $this->contentService->deleteVersion($draft->getVersionInfo());
2965
2966
        $this->expectException(NotFoundException::class);
2967
2968
        // This call will fail with a "NotFound", because we allow to delete content if remaining version is draft.
2969
        // Can normally only happen if there where always only a draft to begin with, simplifies UI edit API usage.
2970
        $this->contentService->loadContent($draft->id);
2971
    }
2972
2973
    /**
2974
     * Test for the loadVersions() method.
2975
     *
2976
     * @see \eZ\Publish\API\Repository\ContentService::loadVersions()
2977
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
2978
     *
2979
     * @return VersionInfo[]
2980
     */
2981
    public function testLoadVersions()
2982
    {
2983
        $contentVersion2 = $this->createContentVersion2();
2984
2985
        // Load versions of this ContentInfo instance
2986
        $versions = $this->contentService->loadVersions($contentVersion2->contentInfo);
2987
2988
        $expectedVersionsOrder = [
2989
            $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1),
2990
            $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 2),
2991
        ];
2992
2993
        $this->assertEquals($expectedVersionsOrder, $versions);
2994
2995
        return $versions;
2996
    }
2997
2998
    /**
2999
     * Test for the loadVersions() method.
3000
     *
3001
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersions
3002
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersions
3003
     *
3004
     * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo[] $versions
3005
     */
3006
    public function testLoadVersionsSetsExpectedVersionInfo(array $versions)
3007
    {
3008
        $this->assertCount(2, $versions);
3009
3010
        $expectedVersions = [
3011
            [
3012
                'versionNo' => 1,
3013
                'creatorId' => 14,
3014
                'status' => VersionInfo::STATUS_ARCHIVED,
3015
                'initialLanguageCode' => self::ENG_US,
3016
                'languageCodes' => [self::ENG_US],
3017
            ],
3018
            [
3019
                'versionNo' => 2,
3020
                'creatorId' => 10,
3021
                'status' => VersionInfo::STATUS_PUBLISHED,
3022
                'initialLanguageCode' => self::ENG_US,
3023
                'languageCodes' => [self::ENG_US, self::ENG_GB],
3024
            ],
3025
        ];
3026
3027
        $this->assertPropertiesCorrect($expectedVersions[0], $versions[0]);
3028
        $this->assertPropertiesCorrect($expectedVersions[1], $versions[1]);
3029
        $this->assertEquals(
3030
            $versions[0]->creationDate->getTimestamp(),
3031
            $versions[1]->creationDate->getTimestamp(),
3032
            'Creation time did not match within delta of 2 seconds',
3033
            2
3034
        );
3035
        $this->assertEquals(
3036
            $versions[0]->modificationDate->getTimestamp(),
3037
            $versions[1]->modificationDate->getTimestamp(),
3038
            'Creation time did not match within delta of 2 seconds',
3039
            2
3040
        );
3041
        $this->assertTrue($versions[0]->isArchived());
3042
        $this->assertFalse($versions[0]->isDraft());
3043
        $this->assertFalse($versions[0]->isPublished());
3044
3045
        $this->assertTrue($versions[1]->isPublished());
3046
        $this->assertFalse($versions[1]->isDraft());
3047
        $this->assertFalse($versions[1]->isArchived());
3048
    }
3049
3050
    /**
3051
     * Test for the copyContent() method.
3052
     *
3053
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
3054
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
3055
     * @group field-type
3056
     */
3057
    public function testCopyContent()
3058
    {
3059
        $parentLocationId = $this->generateId('location', 56);
3060
3061
        $contentVersion2 = $this->createMultipleLanguageContentVersion2();
3062
3063
        // Configure new target location
3064
        $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId);
3065
3066
        $targetLocationCreate->priority = 42;
3067
        $targetLocationCreate->hidden = true;
3068
        $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789';
3069
        $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID;
3070
        $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC;
3071
3072
        // Copy content with all versions and drafts
3073
        $contentCopied = $this->contentService->copyContent(
3074
            $contentVersion2->contentInfo,
3075
            $targetLocationCreate
3076
        );
3077
3078
        $this->assertInstanceOf(
3079
            Content::class,
3080
            $contentCopied
3081
        );
3082
3083
        $this->assertNotEquals(
3084
            $contentVersion2->contentInfo->remoteId,
3085
            $contentCopied->contentInfo->remoteId
3086
        );
3087
3088
        $this->assertNotEquals(
3089
            $contentVersion2->id,
3090
            $contentCopied->id
3091
        );
3092
3093
        $this->assertEquals(
3094
            2,
3095
            count($this->contentService->loadVersions($contentCopied->contentInfo))
3096
        );
3097
3098
        $this->assertEquals(2, $contentCopied->getVersionInfo()->versionNo);
3099
3100
        $this->assertAllFieldsEquals($contentCopied->getFields());
3101
3102
        $this->assertDefaultContentStates($contentCopied->contentInfo);
3103
3104
        $this->assertNotNull(
3105
            $contentCopied->contentInfo->mainLocationId,
3106
            'Expected main location to be set given we provided a LocationCreateStruct'
3107
        );
3108
    }
3109
3110
    /**
3111
     * Test for the copyContent() method with ezsettings.default.content.retain_owner_on_copy set to false
3112
     * See settings/test/integration_legacy.yml for service override.
3113
     *
3114
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
3115
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
3116
     * @group field-type
3117
     */
3118
    public function testCopyContentWithNewOwner()
3119
    {
3120
        $parentLocationId = $this->generateId('location', 56);
3121
3122
        $userService = $this->getRepository()->getUserService();
3123
3124
        $newOwner = $this->createUser('new_owner', 'foo', 'bar');
3125
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $contentVersion2 */
3126
        $contentVersion2 = $this->createContentDraftVersion1(
3127
            $parentLocationId,
3128
            self::FORUM_IDENTIFIER,
3129
            'name',
3130
            $newOwner
3131
        );
3132
3133
        // Configure new target location
3134
        $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId);
3135
3136
        $targetLocationCreate->priority = 42;
3137
        $targetLocationCreate->hidden = true;
3138
        $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789';
3139
        $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID;
3140
        $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC;
3141
3142
        // Copy content with all versions and drafts
3143
        $contentCopied = $this->contentService->copyContent(
3144
            $contentVersion2->contentInfo,
3145
            $targetLocationCreate
3146
        );
3147
3148
        $this->assertEquals(
3149
            $newOwner->id,
3150
            $contentVersion2->contentInfo->ownerId
3151
        );
3152
        $this->assertEquals(
3153
            $userService->loadUserByLogin('admin')->getUserId(),
3154
            $contentCopied->contentInfo->ownerId
3155
        );
3156
    }
3157
3158
    /**
3159
     * Test for the copyContent() method.
3160
     *
3161
     * @see \eZ\Publish\API\Repository\ContentService::copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo)
3162
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent
3163
     */
3164
    public function testCopyContentWithGivenVersion()
3165
    {
3166
        $parentLocationId = $this->generateId('location', 56);
3167
3168
        $contentVersion2 = $this->createContentVersion2();
3169
3170
        // Configure new target location
3171
        $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId);
3172
3173
        $targetLocationCreate->priority = 42;
3174
        $targetLocationCreate->hidden = true;
3175
        $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789';
3176
        $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID;
3177
        $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC;
3178
3179
        // Copy only the initial version
3180
        $contentCopied = $this->contentService->copyContent(
3181
            $contentVersion2->contentInfo,
3182
            $targetLocationCreate,
3183
            $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1)
3184
        );
3185
3186
        $this->assertInstanceOf(
3187
            Content::class,
3188
            $contentCopied
3189
        );
3190
3191
        $this->assertNotEquals(
3192
            $contentVersion2->contentInfo->remoteId,
3193
            $contentCopied->contentInfo->remoteId
3194
        );
3195
3196
        $this->assertNotEquals(
3197
            $contentVersion2->id,
3198
            $contentCopied->id
3199
        );
3200
3201
        $this->assertEquals(
3202
            1,
3203
            count($this->contentService->loadVersions($contentCopied->contentInfo))
3204
        );
3205
3206
        $this->assertEquals(1, $contentCopied->getVersionInfo()->versionNo);
3207
3208
        $this->assertNotNull(
3209
            $contentCopied->contentInfo->mainLocationId,
3210
            'Expected main location to be set given we provided a LocationCreateStruct'
3211
        );
3212
    }
3213
3214
    /**
3215
     * Test for the addRelation() method.
3216
     *
3217
     * @return \eZ\Publish\API\Repository\Values\Content\Content
3218
     *
3219
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3220
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft
3221
     */
3222
    public function testAddRelation()
3223
    {
3224
        $draft = $this->createContentDraftVersion1();
3225
3226
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3227
3228
        // Create relation between new content object and "Media" page
3229
        $relation = $this->contentService->addRelation(
3230
            $draft->getVersionInfo(),
3231
            $media
3232
        );
3233
3234
        $this->assertInstanceOf(
3235
            '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Relation',
3236
            $relation
3237
        );
3238
3239
        return $this->contentService->loadRelations($draft->getVersionInfo());
3240
    }
3241
3242
    /**
3243
     * Test for the addRelation() method.
3244
     *
3245
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3246
     *
3247
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3248
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3249
     */
3250
    public function testAddRelationAddsRelationToContent($relations)
3251
    {
3252
        $this->assertEquals(
3253
            1,
3254
            count($relations)
3255
        );
3256
    }
3257
3258
    /**
3259
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3260
     */
3261
    protected function assertExpectedRelations($relations)
3262
    {
3263
        $this->assertEquals(
3264
            [
3265
                'type' => Relation::COMMON,
3266
                'sourceFieldDefinitionIdentifier' => null,
3267
                'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3268
                'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3269
            ],
3270
            [
3271
                'type' => $relations[0]->type,
3272
                'sourceFieldDefinitionIdentifier' => $relations[0]->sourceFieldDefinitionIdentifier,
3273
                'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3274
                'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3275
            ]
3276
        );
3277
    }
3278
3279
    /**
3280
     * Test for the addRelation() method.
3281
     *
3282
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3283
     *
3284
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3285
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3286
     */
3287
    public function testAddRelationSetsExpectedRelations($relations)
3288
    {
3289
        $this->assertExpectedRelations($relations);
3290
    }
3291
3292
    /**
3293
     * Test for the createContentDraft() method.
3294
     *
3295
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
3296
     *
3297
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
3298
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelationSetsExpectedRelations
3299
     */
3300
    public function testCreateContentDraftWithRelations()
3301
    {
3302
        $draft = $this->createContentDraftVersion1();
3303
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3304
3305
        // Create relation between new content object and "Media" page
3306
        $this->contentService->addRelation(
3307
            $draft->getVersionInfo(),
3308
            $media
3309
        );
3310
3311
        $content = $this->contentService->publishVersion($draft->versionInfo);
3312
        $newDraft = $this->contentService->createContentDraft($content->contentInfo);
3313
3314
        return $this->contentService->loadRelations($newDraft->getVersionInfo());
3315
    }
3316
3317
    /**
3318
     * Test for the createContentDraft() method.
3319
     *
3320
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3321
     *
3322
     * @return \eZ\Publish\API\Repository\Values\Content\Relation[]
3323
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraftWithRelations
3324
     */
3325
    public function testCreateContentDraftWithRelationsCreatesRelations($relations)
3326
    {
3327
        $this->assertEquals(
3328
            1,
3329
            count($relations)
3330
        );
3331
3332
        return $relations;
3333
    }
3334
3335
    /**
3336
     * Test for the createContentDraft() method.
3337
     *
3338
     * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations
3339
     *
3340
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraftWithRelationsCreatesRelations
3341
     */
3342
    public function testCreateContentDraftWithRelationsCreatesExpectedRelations($relations)
3343
    {
3344
        $this->assertExpectedRelations($relations);
3345
    }
3346
3347
    /**
3348
     * Test for the addRelation() method.
3349
     *
3350
     * @see \eZ\Publish\API\Repository\ContentService::addRelation()
3351
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3352
     */
3353
    public function testAddRelationThrowsBadStateException()
3354
    {
3355
        $content = $this->createContentVersion1();
3356
3357
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3358
3359
        $this->expectException(BadStateException::class);
3360
3361
        // This call will fail with a "BadStateException", because content is published and not a draft.
3362
        $this->contentService->addRelation(
3363
            $content->getVersionInfo(),
3364
            $media
3365
        );
3366
    }
3367
3368
    /**
3369
     * Test for the loadRelations() method.
3370
     *
3371
     * @see \eZ\Publish\API\Repository\ContentService::loadRelations()
3372
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3373
     */
3374
    public function testLoadRelations()
3375
    {
3376
        $draft = $this->createContentDraftVersion1();
3377
3378
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3379
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3380
3381
        // Create relation between new content object and "Media" page
3382
        $this->contentService->addRelation(
3383
            $draft->getVersionInfo(),
3384
            $media
3385
        );
3386
3387
        // Create another relation with the "Demo Design" page
3388
        $this->contentService->addRelation(
3389
            $draft->getVersionInfo(),
3390
            $demoDesign
3391
        );
3392
3393
        $relations = $this->contentService->loadRelations($draft->getVersionInfo());
3394
3395
        usort(
3396
            $relations,
3397
            function ($rel1, $rel2) {
3398
                return strcasecmp(
3399
                    $rel2->getDestinationContentInfo()->remoteId,
3400
                    $rel1->getDestinationContentInfo()->remoteId
3401
                );
3402
            }
3403
        );
3404
3405
        $this->assertEquals(
3406
            [
3407
                [
3408
                    'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3409
                    'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3410
                ],
3411
                [
3412
                    'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3413
                    'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3414
                ],
3415
            ],
3416
            [
3417
                [
3418
                    'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3419
                    'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3420
                ],
3421
                [
3422
                    'sourceContentInfo' => $relations[1]->sourceContentInfo->remoteId,
3423
                    'destinationContentInfo' => $relations[1]->destinationContentInfo->remoteId,
3424
                ],
3425
            ]
3426
        );
3427
    }
3428
3429
    /**
3430
     * Test for the loadRelations() method.
3431
     *
3432
     * @see \eZ\Publish\API\Repository\ContentService::loadRelations()
3433
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3434
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations
3435
     */
3436
    public function testLoadRelationsSkipsArchivedContent()
3437
    {
3438
        $trashService = $this->getRepository()->getTrashService();
3439
3440
        $draft = $this->createContentDraftVersion1();
3441
3442
        // Load other content objects
3443
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3444
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3445
3446
        // Create relation between new content object and "Media" page
3447
        $this->contentService->addRelation(
3448
            $draft->getVersionInfo(),
3449
            $media
3450
        );
3451
3452
        // Create another relation with the "Demo Design" page
3453
        $this->contentService->addRelation(
3454
            $draft->getVersionInfo(),
3455
            $demoDesign
3456
        );
3457
3458
        $demoDesignLocation = $this->locationService->loadLocation($demoDesign->mainLocationId);
3459
3460
        // Trashing Content's last Location will change its status to archived,
3461
        // in this case relation towards it will not be loaded.
3462
        $trashService->trash($demoDesignLocation);
3463
3464
        // Load all relations
3465
        $relations = $this->contentService->loadRelations($draft->getVersionInfo());
3466
3467
        $this->assertCount(1, $relations);
3468
        $this->assertEquals(
3469
            [
3470
                [
3471
                    'sourceContentInfo' => 'abcdef0123456789abcdef0123456789',
3472
                    'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3473
                ],
3474
            ],
3475
            [
3476
                [
3477
                    'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3478
                    'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3479
                ],
3480
            ]
3481
        );
3482
    }
3483
3484
    /**
3485
     * Test for the loadRelations() method.
3486
     *
3487
     * @see \eZ\Publish\API\Repository\ContentService::loadRelations()
3488
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3489
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations
3490
     */
3491
    public function testLoadRelationsSkipsDraftContent()
3492
    {
3493
        $draft = $this->createContentDraftVersion1();
3494
3495
        // Load other content objects
3496
        $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID);
3497
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3498
3499
        // Create draft of "Media" page
3500
        $mediaDraft = $this->contentService->createContentDraft($media->contentInfo);
3501
3502
        // Create relation between "Media" page and new content object draft.
3503
        // This relation will not be loaded before the draft is published.
3504
        $this->contentService->addRelation(
3505
            $mediaDraft->getVersionInfo(),
3506
            $draft->getVersionInfo()->getContentInfo()
3507
        );
3508
3509
        // Create another relation with the "Demo Design" page
3510
        $this->contentService->addRelation(
3511
            $mediaDraft->getVersionInfo(),
3512
            $demoDesign
3513
        );
3514
3515
        $relations = $this->contentService->loadRelations($mediaDraft->getVersionInfo());
3516
3517
        $this->assertCount(1, $relations);
3518
        $this->assertEquals(
3519
            [
3520
                [
3521
                    'sourceContentInfo' => self::MEDIA_REMOTE_ID,
3522
                    'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3523
                ],
3524
            ],
3525
            [
3526
                [
3527
                    'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId,
3528
                    'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId,
3529
                ],
3530
            ]
3531
        );
3532
    }
3533
3534
    /**
3535
     * Test for the countReverseRelations() method.
3536
     *
3537
     * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations
3538
     */
3539
    public function testCountReverseRelations(): void
3540
    {
3541
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3542
            $this->contentService->createContentDraft(
3543
                $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3544
            ),
3545
            $this->contentService->createContentDraft(
3546
                $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3547
            ),
3548
        ]);
3549
3550
        $contentInfo = $contentWithReverseRelations->content->getVersionInfo()->getContentInfo();
3551
3552
        $this->assertEquals(2, $this->contentService->countReverseRelations($contentInfo));
3553
    }
3554
3555
    /**
3556
     * Test for the countReverseRelations() method.
3557
     *
3558
     * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations
3559
     */
3560
    public function testCountReverseRelationsReturnsZeroByDefault(): void
3561
    {
3562
        $draft = $this->createContentDraftVersion1();
3563
3564
        $this->assertSame(0, $this->contentService->countReverseRelations($draft->getVersionInfo()->getContentInfo()));
3565
    }
3566
3567
    /**
3568
     * Test for the countReverseRelations() method.
3569
     *
3570
     * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations
3571
     */
3572
    public function testCountReverseRelationsForUnauthorizedUser(): void
3573
    {
3574
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3575
            $this->contentService->createContentDraft(
3576
                $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3577
            ),
3578
        ]);
3579
        $mediaUser = $this->createMediaUserVersion1();
3580
        $this->permissionResolver->setCurrentUserReference($mediaUser);
3581
3582
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3583
3584
        $this->assertSame(0, $this->contentService->countReverseRelations($contentInfo));
3585
    }
3586
3587
    /**
3588
     * Test for the loadReverseRelations() method.
3589
     *
3590
     * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations()
3591
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3592
     */
3593
    public function testLoadReverseRelations()
3594
    {
3595
        $versionInfo = $this->createContentVersion1()->getVersionInfo();
3596
        $contentInfo = $versionInfo->getContentInfo();
3597
3598
        // Create some drafts
3599
        $mediaDraft = $this->contentService->createContentDraft(
3600
            $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID)
3601
        );
3602
        $demoDesignDraft = $this->contentService->createContentDraft(
3603
            $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID)
3604
        );
3605
3606
        // Create relation between new content object and "Media" page
3607
        $relation1 = $this->contentService->addRelation(
3608
            $mediaDraft->getVersionInfo(),
3609
            $contentInfo
3610
        );
3611
3612
        // Create another relation with the "Demo Design" page
3613
        $relation2 = $this->contentService->addRelation(
3614
            $demoDesignDraft->getVersionInfo(),
3615
            $contentInfo
3616
        );
3617
3618
        // Publish drafts, so relations become active
3619
        $this->contentService->publishVersion($mediaDraft->getVersionInfo());
3620
        $this->contentService->publishVersion($demoDesignDraft->getVersionInfo());
3621
3622
        $relations = $this->contentService->loadRelations($versionInfo);
3623
        $reverseRelations = $this->contentService->loadReverseRelations($contentInfo);
3624
3625
        $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id);
3626
        $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id);
3627
3628
        $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id);
3629
        $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id);
3630
3631
        $this->assertEquals(0, count($relations));
3632
        $this->assertEquals(2, count($reverseRelations));
3633
3634
        usort(
3635
            $reverseRelations,
3636
            function ($rel1, $rel2) {
3637
                return strcasecmp(
3638
                    $rel2->getSourceContentInfo()->remoteId,
3639
                    $rel1->getSourceContentInfo()->remoteId
3640
                );
3641
            }
3642
        );
3643
3644
        $this->assertEquals(
3645
            [
3646
                [
3647
                    'sourceContentInfo' => self::MEDIA_REMOTE_ID,
3648
                    'destinationContentInfo' => 'abcdef0123456789abcdef0123456789',
3649
                ],
3650
                [
3651
                    'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3652
                    'destinationContentInfo' => 'abcdef0123456789abcdef0123456789',
3653
                ],
3654
            ],
3655
            [
3656
                [
3657
                    'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId,
3658
                    'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId,
3659
                ],
3660
                [
3661
                    'sourceContentInfo' => $reverseRelations[1]->sourceContentInfo->remoteId,
3662
                    'destinationContentInfo' => $reverseRelations[1]->destinationContentInfo->remoteId,
3663
                ],
3664
            ]
3665
        );
3666
    }
3667
3668
    /**
3669
     * Test for the loadReverseRelations() method.
3670
     *
3671
     * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations()
3672
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3673
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadReverseRelations
3674
     */
3675
    public function testLoadReverseRelationsSkipsArchivedContent()
3676
    {
3677
        $trashService = $this->getRepository()->getTrashService();
3678
3679
        $versionInfo = $this->createContentVersion1()->getVersionInfo();
3680
        $contentInfo = $versionInfo->getContentInfo();
3681
3682
        // Create some drafts
3683
        $mediaDraft = $this->contentService->createContentDraft(
3684
            $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID)
3685
        );
3686
        $demoDesignDraft = $this->contentService->createContentDraft(
3687
            $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID)
3688
        );
3689
3690
        // Create relation between new content object and "Media" page
3691
        $relation1 = $this->contentService->addRelation(
3692
            $mediaDraft->getVersionInfo(),
3693
            $contentInfo
3694
        );
3695
3696
        // Create another relation with the "Demo Design" page
3697
        $relation2 = $this->contentService->addRelation(
3698
            $demoDesignDraft->getVersionInfo(),
3699
            $contentInfo
3700
        );
3701
3702
        // Publish drafts, so relations become active
3703
        $this->contentService->publishVersion($mediaDraft->getVersionInfo());
3704
        $this->contentService->publishVersion($demoDesignDraft->getVersionInfo());
3705
3706
        $demoDesignLocation = $this->locationService->loadLocation($demoDesignDraft->contentInfo->mainLocationId);
3707
3708
        // Trashing Content's last Location will change its status to archived,
3709
        // in this case relation from it will not be loaded.
3710
        $trashService->trash($demoDesignLocation);
3711
3712
        // Load all relations
3713
        $relations = $this->contentService->loadRelations($versionInfo);
3714
        $reverseRelations = $this->contentService->loadReverseRelations($contentInfo);
3715
3716
        $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id);
3717
        $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id);
3718
3719
        $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id);
3720
        $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id);
3721
3722
        $this->assertEquals(0, count($relations));
3723
        $this->assertEquals(1, count($reverseRelations));
3724
3725
        $this->assertEquals(
3726
            [
3727
                [
3728
                    'sourceContentInfo' => self::MEDIA_REMOTE_ID,
3729
                    'destinationContentInfo' => 'abcdef0123456789abcdef0123456789',
3730
                ],
3731
            ],
3732
            [
3733
                [
3734
                    'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId,
3735
                    'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId,
3736
                ],
3737
            ]
3738
        );
3739
    }
3740
3741
    /**
3742
     * Test for the loadReverseRelations() method.
3743
     *
3744
     * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations()
3745
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation
3746
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadReverseRelations
3747
     */
3748
    public function testLoadReverseRelationsSkipsDraftContent()
3749
    {
3750
        // Load "Media" page Content
3751
        $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID);
3752
3753
        // Create some drafts
3754
        $newDraftVersionInfo = $this->createContentDraftVersion1()->getVersionInfo();
3755
        $demoDesignDraft = $this->contentService->createContentDraft(
3756
            $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID)
3757
        );
3758
3759
        // Create relation between "Media" page and new content object
3760
        $relation1 = $this->contentService->addRelation(
3761
            $newDraftVersionInfo,
3762
            $media->contentInfo
3763
        );
3764
3765
        // Create another relation with the "Demo Design" page
3766
        $relation2 = $this->contentService->addRelation(
3767
            $demoDesignDraft->getVersionInfo(),
3768
            $media->contentInfo
3769
        );
3770
3771
        // Publish drafts, so relations become active
3772
        $this->contentService->publishVersion($demoDesignDraft->getVersionInfo());
3773
        // We will not publish new Content draft, therefore relation from it
3774
        // will not be loaded as reverse relation for "Media" page
3775
3776
        $relations = $this->contentService->loadRelations($media->versionInfo);
3777
        $reverseRelations = $this->contentService->loadReverseRelations($media->contentInfo);
3778
3779
        $this->assertEquals($media->contentInfo->id, $relation1->getDestinationContentInfo()->id);
3780
        $this->assertEquals($newDraftVersionInfo->contentInfo->id, $relation1->getSourceContentInfo()->id);
3781
3782
        $this->assertEquals($media->contentInfo->id, $relation2->getDestinationContentInfo()->id);
3783
        $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id);
3784
3785
        $this->assertEquals(0, count($relations));
3786
        $this->assertEquals(1, count($reverseRelations));
3787
3788
        $this->assertEquals(
3789
            [
3790
                [
3791
                    'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID,
3792
                    'destinationContentInfo' => self::MEDIA_REMOTE_ID,
3793
                ],
3794
            ],
3795
            [
3796
                [
3797
                    'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId,
3798
                    'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId,
3799
                ],
3800
            ]
3801
        );
3802
    }
3803
3804
    /**
3805
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3806
     */
3807
    public function testLoadReverseRelationList(): void
3808
    {
3809
        $draft1 = $this->contentService->createContentDraft(
3810
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3811
        );
3812
        $draft2 = $this->contentService->createContentDraft(
3813
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3814
        );
3815
        $draft3 = $this->contentService->createContentDraft(
3816
            $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo
3817
        );
3818
3819
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3820
            $draft1,
3821
            $draft2,
3822
            $draft3,
3823
        ]);
3824
3825
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3826
3827
        $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo);
3828
3829
        $this->assertSame(3, $reverseRelationList->totalCount);
3830
        $this->assertEquals(
3831
            $contentWithReverseRelations->reverseRelations[2]->contentInfo,
3832
            $reverseRelationList->items[0]->getRelation()->sourceContentInfo
3833
        );
3834
        $this->assertEquals(
3835
            $contentWithReverseRelations->reverseRelations[1]->contentInfo,
3836
            $reverseRelationList->items[1]->getRelation()->sourceContentInfo
3837
        );
3838
        $this->assertEquals(
3839
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3840
            $reverseRelationList->items[2]->getRelation()->sourceContentInfo
3841
        );
3842
    }
3843
3844
    /**
3845
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3846
     */
3847
    public function testLoadReverseRelationListWithPagination(): void
3848
    {
3849
        $draft1 = $this->contentService->createContentDraft(
3850
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3851
        );
3852
        $draft2 = $this->contentService->createContentDraft(
3853
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3854
        );
3855
        $draft3 = $this->contentService->createContentDraft(
3856
            $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo
3857
        );
3858
3859
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3860
            $draft1,
3861
            $draft2,
3862
            $draft3,
3863
        ]);
3864
3865
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3866
3867
        $reverseRelationPage1 = $this->contentService->loadReverseRelationList($contentInfo, 0, 2);
3868
        $reverseRelationPage2 = $this->contentService->loadReverseRelationList($contentInfo, 2, 2);
3869
        $this->assertSame(3, $reverseRelationPage1->totalCount);
3870
        $this->assertSame(3, $reverseRelationPage2->totalCount);
3871
        $this->assertEquals(
3872
            $contentWithReverseRelations->reverseRelations[2]->contentInfo,
3873
            $reverseRelationPage1->items[0]->getRelation()->sourceContentInfo
3874
        );
3875
        $this->assertEquals(
3876
            $contentWithReverseRelations->reverseRelations[1]->contentInfo,
3877
            $reverseRelationPage1->items[1]->getRelation()->sourceContentInfo
3878
        );
3879
        $this->assertEquals(
3880
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3881
            $reverseRelationPage2->items[0]->getRelation()->sourceContentInfo
3882
        );
3883
    }
3884
3885
    /**
3886
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3887
     */
3888
    public function testLoadReverseRelationListSkipsArchivedContent(): void
3889
    {
3890
        $trashService = $this->getRepository()->getTrashService();
3891
3892
        $draft1 = $this->contentService->createContentDraft(
3893
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3894
        );
3895
        $draft2 = $this->contentService->createContentDraft(
3896
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3897
        );
3898
        $draft3 = $this->contentService->createContentDraft(
3899
            $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo
3900
        );
3901
3902
        $contentWithReverseRelations = $this->createContentWithReverseRelations([
3903
            $draft1,
3904
            $draft2,
3905
            $draft3,
3906
        ]);
3907
3908
        $locationToTrash = $this->locationService->loadLocation($draft3->contentInfo->mainLocationId);
3909
3910
        // Trashing Content's last Location will change its status to archived, in this case relation from it will not be loaded.
3911
        $trashService->trash($locationToTrash);
3912
3913
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3914
        $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo);
3915
3916
        $this->assertSame(2, $reverseRelationList->totalCount);
3917
        $this->assertEquals(
3918
            $contentWithReverseRelations->reverseRelations[1]->contentInfo,
3919
            $reverseRelationList->items[0]->getRelation()->sourceContentInfo
3920
        );
3921
        $this->assertEquals(
3922
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3923
            $reverseRelationList->items[1]->getRelation()->sourceContentInfo
3924
        );
3925
    }
3926
3927
    /**
3928
     * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList
3929
     */
3930
    public function testLoadReverseRelationListSkipsDraftContent()
3931
    {
3932
        $draft1 = $this->contentService->createContentDraft(
3933
            $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo
3934
        );
3935
3936
        $contentWithReverseRelations = $this->createContentWithReverseRelations([$draft1]);
3937
3938
        $contentInfo = $contentWithReverseRelations->content->contentInfo;
3939
3940
        // create a relation, but without publishing it
3941
        $draft2 = $this->contentService->createContentDraft(
3942
            $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo
3943
        );
3944
        $this->contentService->addRelation(
3945
            $draft2->getVersionInfo(),
3946
            $contentInfo
3947
        );
3948
3949
        $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo);
3950
3951
        $this->assertSame(1, $reverseRelationList->totalCount);
3952
        $this->assertEquals(
3953
            $contentWithReverseRelations->reverseRelations[0]->contentInfo,
3954
            $reverseRelationList->items[0]->getRelation()->sourceContentInfo
3955
        );
3956
    }
3957
3958
    /**
3959
     * Test for the deleteRelation() method.
3960
     *
3961
     * @see \eZ\Publish\API\Repository\ContentService::deleteRelation()
3962
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations
3963
     */
3964
    public function testDeleteRelation()
3965
    {
3966
        $draft = $this->createContentDraftVersion1();
3967
3968
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3969
        $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID);
3970
3971
        // Establish some relations
3972
        $this->contentService->addRelation($draft->getVersionInfo(), $media);
3973
        $this->contentService->addRelation($draft->getVersionInfo(), $demoDesign);
3974
3975
        // Delete one of the currently created relations
3976
        $this->contentService->deleteRelation($draft->getVersionInfo(), $media);
3977
3978
        // The relations array now contains only one element
3979
        $relations = $this->contentService->loadRelations($draft->getVersionInfo());
3980
3981
        $this->assertEquals(1, count($relations));
3982
    }
3983
3984
    /**
3985
     * Test for the deleteRelation() method.
3986
     *
3987
     * @see \eZ\Publish\API\Repository\ContentService::deleteRelation()
3988
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteRelation
3989
     */
3990
    public function testDeleteRelationThrowsBadStateException()
3991
    {
3992
        $content = $this->createContentVersion1();
3993
3994
        // Load the destination object
3995
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
3996
3997
        // Create a new draft
3998
        $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo);
3999
4000
        // Add a relation
4001
        $this->contentService->addRelation($draftVersion2->getVersionInfo(), $media);
4002
4003
        // Publish new version
4004
        $contentVersion2 = $this->contentService->publishVersion(
4005
            $draftVersion2->getVersionInfo()
4006
        );
4007
4008
        $this->expectException(BadStateException::class);
4009
4010
        // This call will fail with a "BadStateException", because content is published and not a draft.
4011
        $this->contentService->deleteRelation(
4012
            $contentVersion2->getVersionInfo(),
4013
            $media
4014
        );
4015
    }
4016
4017
    /**
4018
     * Test for the deleteRelation() method.
4019
     *
4020
     * @see \eZ\Publish\API\Repository\ContentService::deleteRelation()
4021
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteRelation
4022
     */
4023
    public function testDeleteRelationThrowsInvalidArgumentException()
4024
    {
4025
        $draft = $this->createContentDraftVersion1();
4026
4027
        // Load the destination object
4028
        $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID);
4029
4030
        // This call will fail with a "InvalidArgumentException", because no relation exists between $draft and $media.
4031
        $this->expectException(APIInvalidArgumentException::class);
4032
        $this->contentService->deleteRelation(
4033
            $draft->getVersionInfo(),
4034
            $media
4035
        );
4036
    }
4037
4038
    /**
4039
     * Test for the createContent() method.
4040
     *
4041
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
4042
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4043
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4044
     */
4045
    public function testCreateContentInTransactionWithRollback()
4046
    {
4047
        if ($this->isVersion4()) {
4048
            $this->markTestSkipped('This test requires eZ Publish 5');
4049
        }
4050
4051
        $repository = $this->getRepository();
4052
4053
        $contentTypeService = $this->getRepository()->getContentTypeService();
4054
4055
        // Start a transaction
4056
        $repository->beginTransaction();
4057
4058
        try {
4059
            $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
4060
4061
            // Get a content create struct and set mandatory properties
4062
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
4063
            $contentCreate->setField('name', 'Sindelfingen forum');
4064
4065
            $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
4066
            $contentCreate->alwaysAvailable = true;
4067
4068
            // Create a new content object
4069
            $contentId = $this->contentService->createContent($contentCreate)->id;
4070
        } catch (Exception $e) {
4071
            // Cleanup hanging transaction on error
4072
            $repository->rollback();
4073
            throw $e;
4074
        }
4075
4076
        // Rollback all changes
4077
        $repository->rollback();
4078
4079
        try {
4080
            // This call will fail with a "NotFoundException"
4081
            $this->contentService->loadContent($contentId);
4082
        } catch (NotFoundException $e) {
4083
            // This is expected
4084
            return;
4085
        }
4086
4087
        $this->fail('Content object still exists after rollback.');
4088
    }
4089
4090
    /**
4091
     * Test for the createContent() method.
4092
     *
4093
     * @see \eZ\Publish\API\Repository\ContentService::createContent()
4094
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4095
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4096
     */
4097
    public function testCreateContentInTransactionWithCommit()
4098
    {
4099
        if ($this->isVersion4()) {
4100
            $this->markTestSkipped('This test requires eZ Publish 5');
4101
        }
4102
4103
        $repository = $this->getRepository();
4104
4105
        $contentTypeService = $repository->getContentTypeService();
4106
4107
        // Start a transaction
4108
        $repository->beginTransaction();
4109
4110
        try {
4111
            $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER);
4112
4113
            // Get a content create struct and set mandatory properties
4114
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
4115
            $contentCreate->setField('name', 'Sindelfingen forum');
4116
4117
            $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789';
4118
            $contentCreate->alwaysAvailable = true;
4119
4120
            // Create a new content object
4121
            $contentId = $this->contentService->createContent($contentCreate)->id;
4122
4123
            // Commit changes
4124
            $repository->commit();
4125
        } catch (Exception $e) {
4126
            // Cleanup hanging transaction on error
4127
            $repository->rollback();
4128
            throw $e;
4129
        }
4130
4131
        // Load the new content object
4132
        $content = $this->contentService->loadContent($contentId);
4133
4134
        $this->assertEquals($contentId, $content->id);
4135
    }
4136
4137
    /**
4138
     * Test for the createContent() method.
4139
     *
4140
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
4141
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
4142
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentThrowsNotFoundException
4143
     */
4144
    public function testCreateContentWithLocationCreateParameterInTransactionWithRollback()
4145
    {
4146
        $repository = $this->getRepository();
4147
4148
        // Start a transaction
4149
        $repository->beginTransaction();
4150
4151
        try {
4152
            $draft = $this->createContentDraftVersion1();
4153
        } catch (Exception $e) {
4154
            // Cleanup hanging transaction on error
4155
            $repository->rollback();
4156
            throw $e;
4157
        }
4158
4159
        $contentId = $draft->id;
4160
4161
        // Roleback the transaction
4162
        $repository->rollback();
4163
4164
        try {
4165
            // This call will fail with a "NotFoundException"
4166
            $this->contentService->loadContent($contentId);
4167
        } catch (NotFoundException $e) {
4168
            return;
4169
        }
4170
4171
        $this->fail('Can still load content object after rollback.');
4172
    }
4173
4174
    /**
4175
     * Test for the createContent() method.
4176
     *
4177
     * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs)
4178
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately
4179
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentThrowsNotFoundException
4180
     */
4181
    public function testCreateContentWithLocationCreateParameterInTransactionWithCommit()
4182
    {
4183
        $repository = $this->getRepository();
4184
4185
        // Start a transaction
4186
        $repository->beginTransaction();
4187
4188
        try {
4189
            $draft = $this->createContentDraftVersion1();
4190
4191
            $contentId = $draft->id;
4192
4193
            // Roleback the transaction
4194
            $repository->commit();
4195
        } catch (Exception $e) {
4196
            // Cleanup hanging transaction on error
4197
            $repository->rollback();
4198
            throw $e;
4199
        }
4200
4201
        // Load the new content object
4202
        $content = $this->contentService->loadContent($contentId);
4203
4204
        $this->assertEquals($contentId, $content->id);
4205
    }
4206
4207
    /**
4208
     * Test for the createContentDraft() method.
4209
     *
4210
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
4211
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
4212
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4213
     */
4214
    public function testCreateContentDraftInTransactionWithRollback()
4215
    {
4216
        $repository = $this->getRepository();
4217
4218
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4219
4220
        // Load the user group content object
4221
        $content = $this->contentService->loadContent($contentId);
4222
4223
        // Start a new transaction
4224
        $repository->beginTransaction();
4225
4226
        try {
4227
            // Create a new draft
4228
            $drafted = $this->contentService->createContentDraft($content->contentInfo);
4229
4230
            // Store version number for later reuse
4231
            $versionNo = $drafted->versionInfo->versionNo;
4232
        } catch (Exception $e) {
4233
            // Cleanup hanging transaction on error
4234
            $repository->rollback();
4235
            throw $e;
4236
        }
4237
4238
        // Rollback
4239
        $repository->rollback();
4240
4241
        try {
4242
            // This call will fail with a "NotFoundException"
4243
            $this->contentService->loadContent($contentId, null, $versionNo);
4244
        } catch (NotFoundException $e) {
4245
            return;
4246
        }
4247
4248
        $this->fail('Can still load content draft after rollback');
4249
    }
4250
4251
    /**
4252
     * Test for the createContentDraft() method.
4253
     *
4254
     * @see \eZ\Publish\API\Repository\ContentService::createContentDraft()
4255
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft
4256
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4257
     */
4258
    public function testCreateContentDraftInTransactionWithCommit()
4259
    {
4260
        $repository = $this->getRepository();
4261
4262
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4263
4264
        // Load the user group content object
4265
        $content = $this->contentService->loadContent($contentId);
4266
4267
        // Start a new transaction
4268
        $repository->beginTransaction();
4269
4270
        try {
4271
            // Create a new draft
4272
            $drafted = $this->contentService->createContentDraft($content->contentInfo);
4273
4274
            // Store version number for later reuse
4275
            $versionNo = $drafted->versionInfo->versionNo;
4276
4277
            // Commit all changes
4278
            $repository->commit();
4279
        } catch (Exception $e) {
4280
            // Cleanup hanging transaction on error
4281
            $repository->rollback();
4282
            throw $e;
4283
        }
4284
4285
        $content = $this->contentService->loadContent($contentId, null, $versionNo);
4286
4287
        $this->assertEquals(
4288
            $versionNo,
4289
            $content->getVersionInfo()->versionNo
4290
        );
4291
    }
4292
4293
    /**
4294
     * Test for the publishVersion() method.
4295
     *
4296
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
4297
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
4298
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4299
     */
4300
    public function testPublishVersionInTransactionWithRollback()
4301
    {
4302
        $repository = $this->getRepository();
4303
4304
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4305
4306
        // Load the user group content object
4307
        $content = $this->contentService->loadContent($contentId);
4308
4309
        // Start a new transaction
4310
        $repository->beginTransaction();
4311
4312
        try {
4313
            $draftVersion = $this->contentService->createContentDraft($content->contentInfo)->getVersionInfo();
4314
4315
            // Publish a new version
4316
            $content = $this->contentService->publishVersion($draftVersion);
4317
4318
            // Store version number for later reuse
4319
            $versionNo = $content->versionInfo->versionNo;
4320
        } catch (Exception $e) {
4321
            // Cleanup hanging transaction on error
4322
            $repository->rollback();
4323
            throw $e;
4324
        }
4325
4326
        // Rollback
4327
        $repository->rollback();
4328
4329
        try {
4330
            // This call will fail with a "NotFoundException"
4331
            $this->contentService->loadContent($contentId, null, $versionNo);
4332
        } catch (NotFoundException $e) {
4333
            return;
4334
        }
4335
4336
        $this->fail('Can still load content draft after rollback');
4337
    }
4338
4339
    /**
4340
     * Test for the publishVersion() method.
4341
     *
4342
     * @see \eZ\Publish\API\Repository\ContentService::publishVersion()
4343
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
4344
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo
4345
     */
4346
    public function testPublishVersionInTransactionWithCommit()
4347
    {
4348
        $repository = $this->getRepository();
4349
4350
        // Load the user group content object
4351
        $template = $this->contentService->loadContent(self::ADMINISTRATORS_USER_GROUP_ID);
4352
4353
        // Start a new transaction
4354
        $repository->beginTransaction();
4355
4356
        try {
4357
            // Publish a new version
4358
            $content = $this->contentService->publishVersion(
4359
                $this->contentService->createContentDraft($template->contentInfo)->getVersionInfo()
4360
            );
4361
4362
            // Store version number for later reuse
4363
            $versionNo = $content->versionInfo->versionNo;
4364
4365
            // Commit all changes
4366
            $repository->commit();
4367
        } catch (Exception $e) {
4368
            // Cleanup hanging transaction on error
4369
            $repository->rollback();
4370
            throw $e;
4371
        }
4372
4373
        // Load current version info
4374
        $versionInfo = $this->contentService->loadVersionInfo($content->contentInfo);
4375
4376
        $this->assertEquals($versionNo, $versionInfo->versionNo);
4377
    }
4378
4379
    /**
4380
     * Test for the updateContent() method.
4381
     *
4382
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
4383
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
4384
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4385
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4386
     */
4387
    public function testUpdateContentInTransactionWithRollback()
4388
    {
4389
        $repository = $this->getRepository();
4390
4391
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4392
4393
        // Create a new user group draft
4394
        $draft = $this->contentService->createContentDraft(
4395
            $this->contentService->loadContentInfo($contentId)
4396
        );
4397
4398
        // Get an update struct and change the group name
4399
        $contentUpdate = $this->contentService->newContentUpdateStruct();
4400
        $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US);
4401
4402
        // Start a transaction
4403
        $repository->beginTransaction();
4404
4405
        try {
4406
            // Update the group name
4407
            $draft = $this->contentService->updateContent(
4408
                $draft->getVersionInfo(),
4409
                $contentUpdate
4410
            );
4411
4412
            // Publish updated version
4413
            $this->contentService->publishVersion($draft->getVersionInfo());
4414
        } catch (Exception $e) {
4415
            // Cleanup hanging transaction on error
4416
            $repository->rollback();
4417
            throw $e;
4418
        }
4419
4420
        // Rollback all changes.
4421
        $repository->rollback();
4422
4423
        // Name will still be "Administrator users"
4424
        $name = $this->contentService->loadContent($contentId)->getFieldValue('name');
4425
4426
        $this->assertEquals('Administrator users', $name);
4427
    }
4428
4429
    /**
4430
     * Test for the updateContent() method.
4431
     *
4432
     * @see \eZ\Publish\API\Repository\ContentService::updateContent()
4433
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
4434
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
4435
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4436
     */
4437
    public function testUpdateContentInTransactionWithCommit()
4438
    {
4439
        $repository = $this->getRepository();
4440
4441
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4442
4443
        // Create a new user group draft
4444
        $draft = $this->contentService->createContentDraft(
4445
            $this->contentService->loadContentInfo($contentId)
4446
        );
4447
4448
        // Get an update struct and change the group name
4449
        $contentUpdate = $this->contentService->newContentUpdateStruct();
4450
        $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US);
4451
4452
        // Start a transaction
4453
        $repository->beginTransaction();
4454
4455
        try {
4456
            // Update the group name
4457
            $draft = $this->contentService->updateContent(
4458
                $draft->getVersionInfo(),
4459
                $contentUpdate
4460
            );
4461
4462
            // Publish updated version
4463
            $this->contentService->publishVersion($draft->getVersionInfo());
4464
4465
            // Commit all changes.
4466
            $repository->commit();
4467
        } catch (Exception $e) {
4468
            // Cleanup hanging transaction on error
4469
            $repository->rollback();
4470
            throw $e;
4471
        }
4472
4473
        // Name is now "Administrators"
4474
        $name = $this->contentService->loadContent($contentId)->getFieldValue('name', self::ENG_US);
4475
4476
        $this->assertEquals(self::ADMINISTRATORS_USER_GROUP_NAME, $name);
4477
    }
4478
4479
    /**
4480
     * Test for the updateContentMetadata() method.
4481
     *
4482
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
4483
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
4484
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4485
     */
4486
    public function testUpdateContentMetadataInTransactionWithRollback()
4487
    {
4488
        $repository = $this->getRepository();
4489
4490
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4491
4492
        // Load a ContentInfo object
4493
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4494
4495
        // Store remoteId for later testing
4496
        $remoteId = $contentInfo->remoteId;
4497
4498
        // Start a transaction
4499
        $repository->beginTransaction();
4500
4501
        try {
4502
            // Get metadata update struct and change remoteId
4503
            $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
4504
            $metadataUpdate->remoteId = md5(microtime(true));
4505
4506
            // Update the metadata of the published content object
4507
            $this->contentService->updateContentMetadata(
4508
                $contentInfo,
4509
                $metadataUpdate
4510
            );
4511
        } catch (Exception $e) {
4512
            // Cleanup hanging transaction on error
4513
            $repository->rollback();
4514
            throw $e;
4515
        }
4516
4517
        // Rollback all changes.
4518
        $repository->rollback();
4519
4520
        // Load current remoteId
4521
        $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId;
4522
4523
        $this->assertEquals($remoteId, $remoteIdReloaded);
4524
    }
4525
4526
    /**
4527
     * Test for the updateContentMetadata() method.
4528
     *
4529
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
4530
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata
4531
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4532
     */
4533
    public function testUpdateContentMetadataInTransactionWithCommit()
4534
    {
4535
        $repository = $this->getRepository();
4536
4537
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4538
4539
        // Load a ContentInfo object
4540
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4541
4542
        // Store remoteId for later testing
4543
        $remoteId = $contentInfo->remoteId;
4544
4545
        // Start a transaction
4546
        $repository->beginTransaction();
4547
4548
        try {
4549
            // Get metadata update struct and change remoteId
4550
            $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct();
4551
            $metadataUpdate->remoteId = md5(microtime(true));
4552
4553
            // Update the metadata of the published content object
4554
            $this->contentService->updateContentMetadata(
4555
                $contentInfo,
4556
                $metadataUpdate
4557
            );
4558
4559
            // Commit all changes.
4560
            $repository->commit();
4561
        } catch (Exception $e) {
4562
            // Cleanup hanging transaction on error
4563
            $repository->rollback();
4564
            throw $e;
4565
        }
4566
4567
        // Load current remoteId
4568
        $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId;
4569
4570
        $this->assertNotEquals($remoteId, $remoteIdReloaded);
4571
    }
4572
4573
    /**
4574
     * Test for the updateContentMetadata() method, and how cache + transactions play together.
4575
     *
4576
     * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata()
4577
     * @depends testUpdateContentMetadata
4578
     * @depends testLoadContentInfo
4579
     */
4580
    public function testUpdateContentMetadataCheckWithinTransaction()
4581
    {
4582
        $repository = $this->getRepository();
4583
        $contentService = $repository->getContentService();
4584
        $contentId = $this->generateId('object', 12);
4585
4586
        // Load a ContentInfo object, and warmup cache
4587
        $contentInfo = $contentService->loadContentInfo($contentId);
4588
4589
        // Store remoteId for later testing
4590
        $remoteId = $contentInfo->remoteId;
4591
4592
        // Start a transaction
4593
        $repository->beginTransaction();
4594
4595
        try {
4596
            // Get metadata update struct and change remoteId
4597
            $metadataUpdate = $contentService->newContentMetadataUpdateStruct();
4598
            $metadataUpdate->remoteId = md5(microtime(true));
4599
4600
            // Update the metadata of the published content object
4601
            $contentService->updateContentMetadata(
4602
                $contentInfo,
4603
                $metadataUpdate
4604
            );
4605
4606
            // Check that it's been updated
4607
            $remoteIdReloaded = $contentService->loadContentInfo($contentId)->remoteId;
4608
            $this->assertNotEquals($remoteId, $remoteIdReloaded);
4609
4610
            // Commit all changes.
4611
            $repository->commit();
4612
        } catch (Exception $e) {
4613
            // Cleanup hanging transaction on error
4614
            $repository->rollback();
4615
            throw $e;
4616
        }
4617
    }
4618
4619
    /**
4620
     * Test for the deleteVersion() method.
4621
     *
4622
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
4623
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4624
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4625
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts
4626
     */
4627
    public function testDeleteVersionInTransactionWithRollback()
4628
    {
4629
        $repository = $this->getRepository();
4630
4631
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4632
4633
        // Start a new transaction
4634
        $repository->beginTransaction();
4635
4636
        try {
4637
            // Create a new draft
4638
            $draft = $this->contentService->createContentDraft(
4639
                $this->contentService->loadContentInfo($contentId)
4640
            );
4641
4642
            $this->contentService->deleteVersion($draft->getVersionInfo());
4643
        } catch (Exception $e) {
4644
            // Cleanup hanging transaction on error
4645
            $repository->rollback();
4646
            throw $e;
4647
        }
4648
4649
        // Rollback all changes.
4650
        $repository->rollback();
4651
4652
        // This array will be empty
4653
        $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...
4654
4655
        $this->assertSame([], $drafts);
4656
    }
4657
4658
    /**
4659
     * Test for the deleteVersion() method.
4660
     *
4661
     * @see \eZ\Publish\API\Repository\ContentService::deleteVersion()
4662
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent
4663
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4664
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts
4665
     */
4666
    public function testDeleteVersionInTransactionWithCommit()
4667
    {
4668
        $repository = $this->getRepository();
4669
4670
        $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID);
4671
4672
        // Start a new transaction
4673
        $repository->beginTransaction();
4674
4675
        try {
4676
            // Create a new draft
4677
            $draft = $this->contentService->createContentDraft(
4678
                $this->contentService->loadContentInfo($contentId)
4679
            );
4680
4681
            $this->contentService->deleteVersion($draft->getVersionInfo());
4682
4683
            // Commit all changes.
4684
            $repository->commit();
4685
        } catch (Exception $e) {
4686
            // Cleanup hanging transaction on error
4687
            $repository->rollback();
4688
            throw $e;
4689
        }
4690
4691
        // This array will contain no element
4692
        $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...
4693
4694
        $this->assertSame([], $drafts);
4695
    }
4696
4697
    /**
4698
     * Test for the deleteContent() method.
4699
     *
4700
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
4701
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent
4702
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4703
     */
4704
    public function testDeleteContentInTransactionWithRollback()
4705
    {
4706
        $repository = $this->getRepository();
4707
4708
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4709
4710
        // Load a ContentInfo instance
4711
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4712
4713
        // Start a new transaction
4714
        $repository->beginTransaction();
4715
4716
        try {
4717
            // Delete content object
4718
            $this->contentService->deleteContent($contentInfo);
4719
        } catch (Exception $e) {
4720
            // Cleanup hanging transaction on error
4721
            $repository->rollback();
4722
            throw $e;
4723
        }
4724
4725
        // Rollback all changes
4726
        $repository->rollback();
4727
4728
        // This call will return the original content object
4729
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4730
4731
        $this->assertEquals($contentId, $contentInfo->id);
4732
    }
4733
4734
    /**
4735
     * Test for the deleteContent() method.
4736
     *
4737
     * @see \eZ\Publish\API\Repository\ContentService::deleteContent()
4738
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent
4739
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo
4740
     */
4741
    public function testDeleteContentInTransactionWithCommit()
4742
    {
4743
        $repository = $this->getRepository();
4744
4745
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4746
4747
        // Load a ContentInfo instance
4748
        $contentInfo = $this->contentService->loadContentInfo($contentId);
4749
4750
        // Start a new transaction
4751
        $repository->beginTransaction();
4752
4753
        try {
4754
            // Delete content object
4755
            $this->contentService->deleteContent($contentInfo);
4756
4757
            // Commit all changes
4758
            $repository->commit();
4759
        } catch (Exception $e) {
4760
            // Cleanup hanging transaction on error
4761
            $repository->rollback();
4762
            throw $e;
4763
        }
4764
4765
        // Deleted content info is not found anymore
4766
        try {
4767
            $this->contentService->loadContentInfo($contentId);
4768
        } catch (NotFoundException $e) {
4769
            return;
4770
        }
4771
4772
        $this->fail('Can still load ContentInfo after commit.');
4773
    }
4774
4775
    /**
4776
     * Test for the copyContent() method.
4777
     *
4778
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
4779
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent
4780
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct
4781
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren
4782
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation
4783
     */
4784
    public function testCopyContentInTransactionWithRollback()
4785
    {
4786
        $repository = $this->getRepository();
4787
4788
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4789
        $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID);
4790
4791
        // Load content object to copy
4792
        $content = $this->contentService->loadContent($contentId);
4793
4794
        // Create new target location
4795
        $locationCreate = $this->locationService->newLocationCreateStruct($locationId);
4796
4797
        // Start a new transaction
4798
        $repository->beginTransaction();
4799
4800
        try {
4801
            // Copy content with all versions and drafts
4802
            $this->contentService->copyContent(
4803
                $content->contentInfo,
4804
                $locationCreate
4805
            );
4806
        } catch (Exception $e) {
4807
            // Cleanup hanging transaction on error
4808
            $repository->rollback();
4809
            throw $e;
4810
        }
4811
4812
        // Rollback all changes
4813
        $repository->rollback();
4814
4815
        $this->refreshSearch($repository);
4816
4817
        // This array will only contain a single admin user object
4818
        $locations = $this->locationService->loadLocationChildren(
4819
            $this->locationService->loadLocation($locationId)
4820
        )->locations;
4821
4822
        $this->assertEquals(1, count($locations));
4823
    }
4824
4825
    /**
4826
     * Test for the copyContent() method.
4827
     *
4828
     * @see \eZ\Publish\API\Repository\ContentService::copyContent()
4829
     * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent
4830
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct
4831
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren
4832
     * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation
4833
     */
4834
    public function testCopyContentInTransactionWithCommit()
4835
    {
4836
        $repository = $this->getRepository();
4837
4838
        $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID);
4839
        $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID);
4840
4841
        // Load content object to copy
4842
        $content = $this->contentService->loadContent($contentId);
4843
4844
        // Create new target location
4845
        $locationCreate = $this->locationService->newLocationCreateStruct($locationId);
4846
4847
        // Start a new transaction
4848
        $repository->beginTransaction();
4849
4850
        try {
4851
            // Copy content with all versions and drafts
4852
            $this->contentService->copyContent(
4853
                $content->contentInfo,
4854
                $locationCreate
4855
            );
4856
4857
            // Commit all changes
4858
            $repository->commit();
4859
        } catch (Exception $e) {
4860
            // Cleanup hanging transaction on error
4861
            $repository->rollback();
4862
            throw $e;
4863
        }
4864
4865
        $this->refreshSearch($repository);
4866
4867
        // This will contain the admin user and the new child location
4868
        $locations = $this->locationService->loadLocationChildren(
4869
            $this->locationService->loadLocation($locationId)
4870
        )->locations;
4871
4872
        $this->assertEquals(2, count($locations));
4873
    }
4874
4875
    public function testURLAliasesCreatedForNewContent()
4876
    {
4877
        $urlAliasService = $this->getRepository()->getURLAliasService();
4878
4879
        $draft = $this->createContentDraftVersion1();
4880
4881
        // Automatically creates a new URLAlias for the content
4882
        $liveContent = $this->contentService->publishVersion($draft->getVersionInfo());
4883
4884
        $location = $this->locationService->loadLocation(
4885
            $liveContent->getVersionInfo()->getContentInfo()->mainLocationId
4886
        );
4887
4888
        $aliases = $urlAliasService->listLocationAliases($location, false);
4889
4890
        $this->assertAliasesCorrect(
4891
            [
4892
                '/Design/Plain-site/An-awesome-forum' => [
4893
                    'type' => URLAlias::LOCATION,
4894
                    'destination' => $location->id,
4895
                    'path' => '/Design/Plain-site/An-awesome-forum',
4896
                    'languageCodes' => [self::ENG_US],
4897
                    'isHistory' => false,
4898
                    'isCustom' => false,
4899
                    'forward' => false,
4900
                ],
4901
            ],
4902
            $aliases
4903
        );
4904
    }
4905
4906
    public function testURLAliasesCreatedForUpdatedContent()
4907
    {
4908
        $urlAliasService = $this->getRepository()->getURLAliasService();
4909
4910
        $draft = $this->createUpdatedDraftVersion2();
4911
4912
        $location = $this->locationService->loadLocation(
4913
            $draft->getVersionInfo()->getContentInfo()->mainLocationId
4914
        );
4915
4916
        // Load and assert URL aliases before publishing updated Content, so that
4917
        // SPI cache is warmed up and cache invalidation is also tested.
4918
        $aliases = $urlAliasService->listLocationAliases($location, false);
4919
4920
        $this->assertAliasesCorrect(
4921
            [
4922
                '/Design/Plain-site/An-awesome-forum' => [
4923
                    'type' => URLAlias::LOCATION,
4924
                    'destination' => $location->id,
4925
                    'path' => '/Design/Plain-site/An-awesome-forum',
4926
                    'languageCodes' => [self::ENG_US],
4927
                    'alwaysAvailable' => true,
4928
                    'isHistory' => false,
4929
                    'isCustom' => false,
4930
                    'forward' => false,
4931
                ],
4932
            ],
4933
            $aliases
4934
        );
4935
4936
        // Automatically marks old aliases for the content as history
4937
        // and creates new aliases, based on the changes
4938
        $liveContent = $this->contentService->publishVersion($draft->getVersionInfo());
4939
4940
        $location = $this->locationService->loadLocation(
4941
            $liveContent->getVersionInfo()->getContentInfo()->mainLocationId
4942
        );
4943
4944
        $aliases = $urlAliasService->listLocationAliases($location, false);
4945
4946
        $this->assertAliasesCorrect(
4947
            [
4948
                '/Design/Plain-site/An-awesome-forum2' => [
4949
                    'type' => URLAlias::LOCATION,
4950
                    'destination' => $location->id,
4951
                    'path' => '/Design/Plain-site/An-awesome-forum2',
4952
                    'languageCodes' => [self::ENG_US],
4953
                    'alwaysAvailable' => true,
4954
                    'isHistory' => false,
4955
                    'isCustom' => false,
4956
                    'forward' => false,
4957
                ],
4958
                '/Design/Plain-site/An-awesome-forum23' => [
4959
                    'type' => URLAlias::LOCATION,
4960
                    'destination' => $location->id,
4961
                    'path' => '/Design/Plain-site/An-awesome-forum23',
4962
                    'languageCodes' => [self::ENG_GB],
4963
                    'alwaysAvailable' => true,
4964
                    'isHistory' => false,
4965
                    'isCustom' => false,
4966
                    'forward' => false,
4967
                ],
4968
            ],
4969
            $aliases
4970
        );
4971
    }
4972
4973
    public function testCustomURLAliasesNotHistorizedOnUpdatedContent()
4974
    {
4975
        $urlAliasService = $this->getRepository()->getURLAliasService();
4976
4977
        $content = $this->createContentVersion1();
4978
4979
        // Create a custom URL alias
4980
        $urlAliasService->createUrlAlias(
4981
            $this->locationService->loadLocation(
4982
                $content->getVersionInfo()->getContentInfo()->mainLocationId
4983
            ),
4984
            '/my/fancy/story-about-ez-publish',
4985
            self::ENG_US
4986
        );
4987
4988
        $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo);
4989
4990
        $contentUpdate = $this->contentService->newContentUpdateStruct();
4991
        $contentUpdate->initialLanguageCode = self::ENG_US;
4992
        $contentUpdate->setField('name', 'Amazing Bielefeld forum');
4993
4994
        $draftVersion2 = $this->contentService->updateContent(
4995
            $draftVersion2->getVersionInfo(),
4996
            $contentUpdate
4997
        );
4998
4999
        // Only marks auto-generated aliases as history
5000
        // the custom one is left untouched
5001
        $liveContent = $this->contentService->publishVersion($draftVersion2->getVersionInfo());
5002
5003
        $location = $this->locationService->loadLocation(
5004
            $liveContent->getVersionInfo()->getContentInfo()->mainLocationId
5005
        );
5006
5007
        $aliases = $urlAliasService->listLocationAliases($location);
5008
5009
        $this->assertAliasesCorrect(
5010
            [
5011
                '/my/fancy/story-about-ez-publish' => [
5012
                    'type' => URLAlias::LOCATION,
5013
                    'destination' => $location->id,
5014
                    'path' => '/my/fancy/story-about-ez-publish',
5015
                    'languageCodes' => [self::ENG_US],
5016
                    'isHistory' => false,
5017
                    'isCustom' => true,
5018
                    'forward' => false,
5019
                    'alwaysAvailable' => false,
5020
                ],
5021
            ],
5022
            $aliases
5023
        );
5024
    }
5025
5026
    /**
5027
     * Test to ensure that old versions are not affected by updates to newer
5028
     * drafts.
5029
     */
5030
    public function testUpdatingDraftDoesNotUpdateOldVersions()
5031
    {
5032
        $contentVersion2 = $this->createContentVersion2();
5033
5034
        $loadedContent1 = $this->contentService->loadContent($contentVersion2->id, null, 1);
5035
        $loadedContent2 = $this->contentService->loadContent($contentVersion2->id, null, 2);
5036
5037
        $this->assertNotEquals(
5038
            $loadedContent1->getFieldValue('name', self::ENG_US),
5039
            $loadedContent2->getFieldValue('name', self::ENG_US)
5040
        );
5041
    }
5042
5043
    /**
5044
     * Test scenario with writer and publisher users.
5045
     * Writer can only create content. Publisher can publish this content.
5046
     */
5047
    public function testPublishWorkflow()
5048
    {
5049
        $this->createRoleWithPolicies('Publisher', [
5050
            ['module' => 'content', 'function' => 'read'],
5051
            ['module' => 'content', 'function' => 'create'],
5052
            ['module' => 'content', 'function' => 'publish'],
5053
        ]);
5054
5055
        $this->createRoleWithPolicies('Writer', [
5056
            ['module' => 'content', 'function' => 'read'],
5057
            ['module' => 'content', 'function' => 'create'],
5058
        ]);
5059
5060
        $writerUser = $this->createCustomUserWithLogin(
5061
            'writer',
5062
            '[email protected]',
5063
            self::WRITERS_USER_GROUP_NAME,
5064
            'Writer'
5065
        );
5066
5067
        $publisherUser = $this->createCustomUserWithLogin(
5068
            'publisher',
5069
            '[email protected]',
5070
            'Publishers',
5071
            'Publisher'
5072
        );
5073
5074
        $this->permissionResolver->setCurrentUserReference($writerUser);
5075
        $draft = $this->createContentDraftVersion1();
5076
5077
        $this->permissionResolver->setCurrentUserReference($publisherUser);
5078
        $content = $this->contentService->publishVersion($draft->versionInfo);
5079
5080
        $this->contentService->loadContent($content->id);
5081
    }
5082
5083
    /**
5084
     * Test publish / content policy is required to be able to publish content.
5085
     */
5086
    public function testPublishContentWithoutPublishPolicyThrowsException()
5087
    {
5088
        $this->createRoleWithPolicies('Writer', [
5089
            ['module' => 'content', 'function' => 'read'],
5090
            ['module' => 'content', 'function' => 'create'],
5091
            ['module' => 'content', 'function' => 'edit'],
5092
        ]);
5093
        $writerUser = $this->createCustomUserWithLogin(
5094
            'writer',
5095
            '[email protected]',
5096
            self::WRITERS_USER_GROUP_NAME,
5097
            'Writer'
5098
        );
5099
        $this->permissionResolver->setCurrentUserReference($writerUser);
5100
5101
        $this->expectException(CoreUnauthorizedException::class);
5102
        $this->expectExceptionMessageRegExp('/User does not have access to \'publish\' \'content\'/');
5103
5104
        $this->createContentVersion1();
5105
    }
5106
5107
    /**
5108
     * Test removal of the specific translation from all the Versions of a Content Object.
5109
     *
5110
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5111
     */
5112
    public function testDeleteTranslation()
5113
    {
5114
        $content = $this->createContentVersion2();
5115
5116
        // create multiple versions to exceed archive limit
5117
        for ($i = 0; $i < 5; ++$i) {
5118
            $contentDraft = $this->contentService->createContentDraft($content->contentInfo);
5119
            $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
5120
            $contentDraft = $this->contentService->updateContent(
5121
                $contentDraft->versionInfo,
5122
                $contentUpdateStruct
5123
            );
5124
            $this->contentService->publishVersion($contentDraft->versionInfo);
5125
        }
5126
5127
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB);
5128
5129
        $this->assertTranslationDoesNotExist(self::ENG_GB, $content->id);
5130
    }
5131
5132
    /**
5133
     * Test deleting a Translation which is initial for some Version, updates initialLanguageCode
5134
     * with mainLanguageCode (assuming they are different).
5135
     */
5136
    public function testDeleteTranslationUpdatesInitialLanguageCodeVersion()
5137
    {
5138
        $content = $this->createContentVersion2();
5139
        // create another, copied, version
5140
        $contentDraft = $this->contentService->updateContent(
5141
            $this->contentService->createContentDraft($content->contentInfo)->versionInfo,
5142
            $this->contentService->newContentUpdateStruct()
5143
        );
5144
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
5145
5146
        // remove first version with only one translation as it is not the subject of this test
5147
        $this->contentService->deleteVersion(
5148
            $this->contentService->loadVersionInfo($publishedContent->contentInfo, 1)
5149
        );
5150
5151
        // sanity check
5152
        self::assertEquals(self::ENG_US, $content->contentInfo->mainLanguageCode);
5153
        self::assertEquals(self::ENG_US, $content->versionInfo->initialLanguageCode);
5154
5155
        // update mainLanguageCode so it is different than initialLanguageCode for Version
5156
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
5157
        $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB;
5158
        $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct);
5159
5160
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US);
5161
5162
        $this->assertTranslationDoesNotExist(self::ENG_US, $content->id);
5163
    }
5164
5165
    /**
5166
     * Test removal of the specific translation properly updates languages of the URL alias.
5167
     *
5168
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5169
     */
5170
    public function testDeleteTranslationUpdatesUrlAlias()
5171
    {
5172
        $urlAliasService = $this->getRepository()->getURLAliasService();
5173
5174
        $content = $this->createContentVersion2();
5175
        $mainLocation = $this->locationService->loadLocation($content->contentInfo->mainLocationId);
5176
5177
        // create custom URL alias for Content main Location
5178
        $urlAliasService->createUrlAlias($mainLocation, '/my-custom-url', self::ENG_GB);
5179
5180
        // create secondary Location for Content
5181
        $secondaryLocation = $this->locationService->createLocation(
5182
            $content->contentInfo,
5183
            $this->locationService->newLocationCreateStruct(2)
5184
        );
5185
5186
        // create custom URL alias for Content secondary Location
5187
        $urlAliasService->createUrlAlias($secondaryLocation, '/my-secondary-url', self::ENG_GB);
5188
5189
        // delete Translation
5190
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB);
5191
5192
        foreach ([$mainLocation, $secondaryLocation] as $location) {
5193
            // check auto-generated URL aliases
5194
            foreach ($urlAliasService->listLocationAliases($location, false) as $alias) {
5195
                self::assertNotContains(self::ENG_GB, $alias->languageCodes);
5196
            }
5197
5198
            // check custom URL aliases
5199
            foreach ($urlAliasService->listLocationAliases($location) as $alias) {
5200
                self::assertNotContains(self::ENG_GB, $alias->languageCodes);
5201
            }
5202
        }
5203
    }
5204
5205
    /**
5206
     * Test removal of a main translation throws BadStateException.
5207
     *
5208
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5209
     */
5210
    public function testDeleteTranslationMainLanguageThrowsBadStateException()
5211
    {
5212
        $content = $this->createContentVersion2();
5213
5214
        // delete first version which has only one translation
5215
        $this->contentService->deleteVersion($this->contentService->loadVersionInfo($content->contentInfo, 1));
5216
5217
        // try to delete main translation
5218
        $this->expectException(BadStateException::class);
5219
        $this->expectExceptionMessage('Specified translation is the main translation of the Content Object');
5220
5221
        $this->contentService->deleteTranslation($content->contentInfo, $content->contentInfo->mainLanguageCode);
5222
    }
5223
5224
    /**
5225
     * Test removal of a Translation is possible when some archived Versions have only this Translation.
5226
     *
5227
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5228
     */
5229
    public function testDeleteTranslationDeletesSingleTranslationVersions()
5230
    {
5231
        // content created by the createContentVersion1 method has eng-US translation only.
5232
        $content = $this->createContentVersion1();
5233
5234
        // create new version and add eng-GB translation
5235
        $contentDraft = $this->contentService->createContentDraft($content->contentInfo);
5236
        $contentUpdateStruct = $this->contentService->newContentUpdateStruct();
5237
        $contentUpdateStruct->setField('name', 'Awesome Board', self::ENG_GB);
5238
        $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct);
5239
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
5240
5241
        // update mainLanguageCode to avoid exception related to that
5242
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
5243
        $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB;
5244
5245
        $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct);
5246
5247
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US);
5248
5249
        $this->assertTranslationDoesNotExist(self::ENG_US, $content->id);
5250
    }
5251
5252
    /**
5253
     * Test removal of the translation by the user who is not allowed to delete a content
5254
     * throws UnauthorizedException.
5255
     *
5256
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5257
     */
5258
    public function testDeleteTranslationThrowsUnauthorizedException()
5259
    {
5260
        $content = $this->createContentVersion2();
5261
5262
        // create user that can read/create/edit but cannot delete content
5263
        $this->createRoleWithPolicies('Writer', [
5264
            ['module' => 'content', 'function' => 'read'],
5265
            ['module' => 'content', 'function' => 'versionread'],
5266
            ['module' => 'content', 'function' => 'create'],
5267
            ['module' => 'content', 'function' => 'edit'],
5268
        ]);
5269
        $writerUser = $this->createCustomUserWithLogin(
5270
            'writer',
5271
            '[email protected]',
5272
            self::WRITERS_USER_GROUP_NAME,
5273
            'Writer'
5274
        );
5275
        $this->permissionResolver->setCurrentUserReference($writerUser);
5276
5277
        $this->expectException(UnauthorizedException::class);
5278
        $this->expectExceptionMessage('User does not have access to \'remove\' \'content\'');
5279
5280
        $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB);
5281
    }
5282
5283
    /**
5284
     * Test removal of a non-existent translation throws InvalidArgumentException.
5285
     *
5286
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation
5287
     */
5288
    public function testDeleteTranslationThrowsInvalidArgumentException()
5289
    {
5290
        // content created by the createContentVersion1 method has eng-US translation only.
5291
        $content = $this->createContentVersion1();
5292
5293
        $this->expectException(APIInvalidArgumentException::class);
5294
        $this->expectExceptionMessage('Argument \'$languageCode\' is invalid: ger-DE does not exist in the Content item');
5295
5296
        $this->contentService->deleteTranslation($content->contentInfo, self::GER_DE);
5297
    }
5298
5299
    /**
5300
     * Test deleting a Translation from Draft.
5301
     *
5302
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5303
     */
5304
    public function testDeleteTranslationFromDraft()
5305
    {
5306
        $languageCode = self::ENG_GB;
5307
        $content = $this->createMultipleLanguageContentVersion2();
5308
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5309
        $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5310
        $content = $this->contentService->publishVersion($draft->versionInfo);
5311
5312
        $loadedContent = $this->contentService->loadContent($content->id);
5313
        self::assertNotContains($languageCode, $loadedContent->versionInfo->languageCodes);
5314
        self::assertEmpty($loadedContent->getFieldsByLanguage($languageCode));
5315
    }
5316
5317
    /**
5318
     * Get values for multilingual field.
5319
     *
5320
     * @return array
5321
     */
5322
    public function providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing()
5323
    {
5324
        return [
5325
            [
5326
                [self::ENG_US => 'US Name', self::ENG_GB => 'GB Name'],
5327
            ],
5328
            [
5329
                [self::ENG_US => 'Same Name', self::ENG_GB => 'Same Name'],
5330
            ],
5331
        ];
5332
    }
5333
5334
    /**
5335
     * Test deleting a Translation from Draft removes previously stored URL aliases for published Content.
5336
     *
5337
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5338
     *
5339
     * @dataProvider providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing
5340
     *
5341
     * @param string[] $fieldValues translated field values
5342
     *
5343
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
5344
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
5345
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
5346
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
5347
     */
5348
    public function testDeleteTranslationFromDraftRemovesUrlAliasOnPublishing(array $fieldValues)
5349
    {
5350
        $urlAliasService = $this->getRepository()->getURLAliasService();
5351
5352
        // set language code to be removed
5353
        $languageCode = self::ENG_GB;
5354
        $draft = $this->createMultilingualContentDraft(
5355
            'folder',
5356
            2,
5357
            self::ENG_US,
5358
            [
5359
                'name' => [
5360
                    self::ENG_GB => $fieldValues[self::ENG_GB],
5361
                    self::ENG_US => $fieldValues[self::ENG_US],
5362
                ],
5363
            ]
5364
        );
5365
        $content = $this->contentService->publishVersion($draft->versionInfo);
5366
5367
        // create secondary location
5368
        $this->locationService->createLocation(
5369
            $content->contentInfo,
5370
            $this->locationService->newLocationCreateStruct(5)
5371
        );
5372
5373
        // sanity check
5374
        $locations = $this->locationService->loadLocations($content->contentInfo);
5375
        self::assertCount(2, $locations, 'Sanity check: Expected to find 2 Locations');
5376
        foreach ($locations as $location) {
5377
            $urlAliasService->createUrlAlias($location, '/us-custom_' . $location->id, self::ENG_US);
5378
            $urlAliasService->createUrlAlias($location, '/gb-custom_' . $location->id, self::ENG_GB);
5379
5380
            // check default URL aliases
5381
            $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode);
5382
            self::assertNotEmpty($aliases, 'Sanity check: URL alias for the translation does not exist');
5383
5384
            // check custom URL aliases
5385
            $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode);
5386
            self::assertNotEmpty($aliases, 'Sanity check: Custom URL alias for the translation does not exist');
5387
        }
5388
5389
        // delete translation and publish new version
5390
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5391
        $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5392
        $this->contentService->publishVersion($draft->versionInfo);
5393
5394
        // check that aliases does not exist
5395
        foreach ($locations as $location) {
5396
            // check default URL aliases
5397
            $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode);
5398
            self::assertEmpty($aliases, 'URL alias for the deleted translation still exists');
5399
5400
            // check custom URL aliases
5401
            $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode);
5402
            self::assertEmpty($aliases, 'Custom URL alias for the deleted translation still exists');
5403
        }
5404
    }
5405
5406
    /**
5407
     * Test that URL aliases for deleted Translations are properly archived.
5408
     */
5409
    public function testDeleteTranslationFromDraftArchivesUrlAliasOnPublishing()
5410
    {
5411
        $urlAliasService = $this->getRepository()->getURLAliasService();
5412
5413
        $content = $this->contentService->publishVersion(
5414
            $this->createMultilingualContentDraft(
5415
                'folder',
5416
                2,
5417
                self::ENG_US,
5418
                [
5419
                    'name' => [
5420
                        self::ENG_GB => 'BritishEnglishContent',
5421
                        self::ENG_US => 'AmericanEnglishContent',
5422
                    ],
5423
                ]
5424
            )->versionInfo
5425
        );
5426
5427
        $unrelatedContent = $this->contentService->publishVersion(
5428
            $this->createMultilingualContentDraft(
5429
                'folder',
5430
                2,
5431
                self::ENG_US,
5432
                [
5433
                    'name' => [
5434
                        self::ENG_GB => 'AnotherBritishContent',
5435
                        self::ENG_US => 'AnotherAmericanContent',
5436
                    ],
5437
                ]
5438
            )->versionInfo
5439
        );
5440
5441
        $urlAlias = $urlAliasService->lookup('/BritishEnglishContent');
5442
        self::assertFalse($urlAlias->isHistory);
5443
        self::assertEquals($urlAlias->path, '/BritishEnglishContent');
5444
        self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId);
5445
5446
        $draft = $this->contentService->deleteTranslationFromDraft(
5447
            $this->contentService->createContentDraft($content->contentInfo)->versionInfo,
5448
            self::ENG_GB
5449
        );
5450
        $content = $this->contentService->publishVersion($draft->versionInfo);
5451
5452
        $urlAlias = $urlAliasService->lookup('/BritishEnglishContent');
5453
        self::assertTrue($urlAlias->isHistory);
5454
        self::assertEquals($urlAlias->path, '/BritishEnglishContent');
5455
        self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId);
5456
5457
        $unrelatedUrlAlias = $urlAliasService->lookup('/AnotherBritishContent');
5458
        self::assertFalse($unrelatedUrlAlias->isHistory);
5459
        self::assertEquals($unrelatedUrlAlias->path, '/AnotherBritishContent');
5460
        self::assertEquals($unrelatedUrlAlias->destination, $unrelatedContent->contentInfo->mainLocationId);
5461
    }
5462
5463
    /**
5464
     * Test deleting a Translation from Draft which has single Translation throws BadStateException.
5465
     *
5466
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5467
     */
5468
    public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnSingleTranslation()
5469
    {
5470
        // create Content with single Translation
5471
        $publishedContent = $this->contentService->publishVersion(
5472
            $this->createContentDraft(
5473
                self::FORUM_IDENTIFIER,
5474
                2,
5475
                ['name' => 'Eng-US Version name']
5476
            )->versionInfo
5477
        );
5478
5479
        // update mainLanguageCode to avoid exception related to trying to delete main Translation
5480
        $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct();
5481
        $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB;
5482
        $publishedContent = $this->contentService->updateContentMetadata(
5483
            $publishedContent->contentInfo,
5484
            $contentMetadataUpdateStruct
5485
        );
5486
5487
        // create single Translation Version from the first one
5488
        $draft = $this->contentService->createContentDraft(
5489
            $publishedContent->contentInfo,
5490
            $publishedContent->versionInfo
5491
        );
5492
5493
        $this->expectException(BadStateException::class);
5494
        $this->expectExceptionMessage('Specified Translation is the only one Content Object Version has');
5495
5496
        // attempt to delete Translation
5497
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, self::ENG_US);
5498
    }
5499
5500
    /**
5501
     * Test deleting the Main Translation from Draft throws BadStateException.
5502
     *
5503
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5504
     */
5505
    public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnMainTranslation()
5506
    {
5507
        $mainLanguageCode = self::ENG_US;
5508
        $draft = $this->createMultilingualContentDraft(
5509
            self::FORUM_IDENTIFIER,
5510
            2,
5511
            $mainLanguageCode,
5512
            [
5513
                'name' => [
5514
                    self::ENG_US => 'An awesome eng-US forum',
5515
                    self::ENG_GB => 'An awesome eng-GB forum',
5516
                ],
5517
            ]
5518
        );
5519
5520
        $this->expectException(BadStateException::class);
5521
        $this->expectExceptionMessage('Specified Translation is the main Translation of the Content Object');
5522
5523
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $mainLanguageCode);
5524
    }
5525
5526
    /**
5527
     * Test deleting the Translation from Published Version throws BadStateException.
5528
     *
5529
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5530
     */
5531
    public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnPublishedVersion()
5532
    {
5533
        $languageCode = self::ENG_US;
5534
        $content = $this->createMultipleLanguageContentVersion2();
5535
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5536
        $publishedContent = $this->contentService->publishVersion($draft->versionInfo);
5537
5538
        $this->expectException(BadStateException::class);
5539
        $this->expectExceptionMessage('Version is not a draft');
5540
5541
        $this->contentService->deleteTranslationFromDraft($publishedContent->versionInfo, $languageCode);
5542
    }
5543
5544
    /**
5545
     * Test deleting a Translation from Draft throws UnauthorizedException if user cannot edit Content.
5546
     *
5547
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5548
     */
5549
    public function testDeleteTranslationFromDraftThrowsUnauthorizedException()
5550
    {
5551
        $languageCode = self::ENG_GB;
5552
        $content = $this->createMultipleLanguageContentVersion2();
5553
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5554
5555
        // create user that can read/create/delete but cannot edit or content
5556
        $this->createRoleWithPolicies('Writer', [
5557
            ['module' => 'content', 'function' => 'read'],
5558
            ['module' => 'content', 'function' => 'versionread'],
5559
            ['module' => 'content', 'function' => 'create'],
5560
            ['module' => 'content', 'function' => 'delete'],
5561
        ]);
5562
        $writerUser = $this->createCustomUserWithLogin(
5563
            'user',
5564
            '[email protected]',
5565
            self::WRITERS_USER_GROUP_NAME,
5566
            'Writer'
5567
        );
5568
        $this->permissionResolver->setCurrentUserReference($writerUser);
5569
5570
        $this->expectException(UnauthorizedException::class);
5571
        $this->expectExceptionMessage('User does not have access to \'edit\' \'content\'');
5572
5573
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5574
    }
5575
5576
    /**
5577
     * Test deleting a non-existent Translation from Draft throws InvalidArgumentException.
5578
     *
5579
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft
5580
     */
5581
    public function testDeleteTranslationFromDraftThrowsInvalidArgumentException()
5582
    {
5583
        $languageCode = self::GER_DE;
5584
        $content = $this->createMultipleLanguageContentVersion2();
5585
        $draft = $this->contentService->createContentDraft($content->contentInfo);
5586
        $this->expectException(APIInvalidArgumentException::class);
5587
        $this->expectExceptionMessageRegExp('/The Version \(ContentId=\d+, VersionNo=\d+\) is not translated into ger-DE/');
5588
        $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode);
5589
    }
5590
5591
    /**
5592
     * Test loading list of Content items.
5593
     */
5594
    public function testLoadContentListByContentInfo()
5595
    {
5596
        $allLocationsCount = $this->locationService->getAllLocationsCount();
5597
        $contentInfoList = array_map(
5598
            function (Location $location) {
5599
                return $location->contentInfo;
5600
            },
5601
            $this->locationService->loadAllLocations(0, $allLocationsCount)
5602
        );
5603
5604
        $contentList = $this->contentService->loadContentListByContentInfo($contentInfoList);
5605
        self::assertCount(count($contentInfoList), $contentList);
5606
        foreach ($contentList as $content) {
5607
            try {
5608
                $loadedContent = $this->contentService->loadContent($content->id);
5609
                self::assertEquals($loadedContent, $content, "Failed to properly bulk-load Content {$content->id}");
5610
            } catch (NotFoundException $e) {
5611
                self::fail("Failed to load Content {$content->id}: {$e->getMessage()}");
5612
            } catch (UnauthorizedException $e) {
5613
                self::fail("Failed to load Content {$content->id}: {$e->getMessage()}");
5614
            }
5615
        }
5616
    }
5617
5618
    /**
5619
     * Test loading content versions after removing exactly two drafts.
5620
     *
5621
     * @see https://jira.ez.no/browse/EZP-30271
5622
     *
5623
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteVersion
5624
     */
5625
    public function testLoadVersionsAfterDeletingTwoDrafts()
5626
    {
5627
        $content = $this->createFolder([self::ENG_GB => 'Foo'], 2);
5628
5629
        // First update and publish
5630
        $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo1']);
5631
        $content = $this->contentService->publishVersion($modifiedContent->versionInfo);
5632
5633
        // Second update and publish
5634
        $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo2']);
5635
        $content = $this->contentService->publishVersion($modifiedContent->versionInfo);
5636
5637
        // Create drafts
5638
        $this->updateFolder($content, [self::ENG_GB => 'Foo3']);
5639
        $this->updateFolder($content, [self::ENG_GB => 'Foo4']);
5640
5641
        $versions = $this->contentService->loadVersions($content->contentInfo);
5642
5643
        foreach ($versions as $key => $version) {
5644
            if ($version->isDraft()) {
5645
                $this->contentService->deleteVersion($version);
5646
                unset($versions[$key]);
5647
            }
5648
        }
5649
5650
        $this->assertEquals($versions, $this->contentService->loadVersions($content->contentInfo));
5651
    }
5652
5653
    /**
5654
     * Tests loading list of content versions of status draft.
5655
     */
5656
    public function testLoadVersionsOfStatusDraft()
5657
    {
5658
        $content = $this->createContentVersion1();
5659
5660
        $this->contentService->createContentDraft($content->contentInfo);
5661
        $this->contentService->createContentDraft($content->contentInfo);
5662
        $this->contentService->createContentDraft($content->contentInfo);
5663
5664
        $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_DRAFT);
5665
5666
        $this->assertSame(\count($versions), 3);
5667
    }
5668
5669
    /**
5670
     * Tests loading list of content versions of status archived.
5671
     */
5672
    public function testLoadVersionsOfStatusArchived()
5673
    {
5674
        $content = $this->createContentVersion1();
5675
5676
        $draft1 = $this->contentService->createContentDraft($content->contentInfo);
5677
        $this->contentService->publishVersion($draft1->versionInfo);
5678
5679
        $draft2 = $this->contentService->createContentDraft($content->contentInfo);
5680
        $this->contentService->publishVersion($draft2->versionInfo);
5681
5682
        $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_ARCHIVED);
5683
5684
        $this->assertSame(\count($versions), 2);
5685
    }
5686
5687
    /**
5688
     * Asserts that all aliases defined in $expectedAliasProperties with the
5689
     * given properties are available in $actualAliases and not more.
5690
     *
5691
     * @param array $expectedAliasProperties
5692
     * @param array $actualAliases
5693
     */
5694
    private function assertAliasesCorrect(array $expectedAliasProperties, array $actualAliases)
5695
    {
5696
        foreach ($actualAliases as $actualAlias) {
5697
            if (!isset($expectedAliasProperties[$actualAlias->path])) {
5698
                $this->fail(
5699
                    sprintf(
5700
                        'Alias with path "%s" in languages "%s" not expected.',
5701
                        $actualAlias->path,
5702
                        implode(', ', $actualAlias->languageCodes)
5703
                    )
5704
                );
5705
            }
5706
5707
            foreach ($expectedAliasProperties[$actualAlias->path] as $propertyName => $propertyValue) {
5708
                $this->assertEquals(
5709
                    $propertyValue,
5710
                    $actualAlias->$propertyName,
5711
                    sprintf(
5712
                        'Property $%s incorrect on alias with path "%s" in languages "%s".',
5713
                        $propertyName,
5714
                        $actualAlias->path,
5715
                        implode(', ', $actualAlias->languageCodes)
5716
                    )
5717
                );
5718
            }
5719
5720
            unset($expectedAliasProperties[$actualAlias->path]);
5721
        }
5722
5723
        if (!empty($expectedAliasProperties)) {
5724
            $this->fail(
5725
                sprintf(
5726
                    'Missing expected aliases with paths "%s".',
5727
                    implode('", "', array_keys($expectedAliasProperties))
5728
                )
5729
            );
5730
        }
5731
    }
5732
5733
    /**
5734
     * Asserts that the given fields are equal to the default fields fixture.
5735
     *
5736
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields
5737
     */
5738
    private function assertAllFieldsEquals(array $fields)
5739
    {
5740
        $actual = $this->normalizeFields($fields);
5741
        $expected = $this->normalizeFields($this->createFieldsFixture());
5742
5743
        $this->assertEquals($expected, $actual);
5744
    }
5745
5746
    /**
5747
     * Asserts that the given fields are equal to a language filtered set of the
5748
     * default fields fixture.
5749
     *
5750
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields
5751
     * @param string $languageCode
5752
     */
5753
    private function assertLocaleFieldsEquals(array $fields, $languageCode)
5754
    {
5755
        $actual = $this->normalizeFields($fields);
5756
5757
        $expected = [];
5758
        foreach ($this->normalizeFields($this->createFieldsFixture()) as $field) {
5759
            if ($field->languageCode !== $languageCode) {
5760
                continue;
5761
            }
5762
            $expected[] = $field;
5763
        }
5764
5765
        $this->assertEquals($expected, $actual);
5766
    }
5767
5768
    /**
5769
     * This method normalizes a set of fields and returns a normalized set.
5770
     *
5771
     * Normalization means it resets the storage specific field id to zero and
5772
     * it sorts the field by their identifier and their language code. In
5773
     * addition, the field value is removed, since this one depends on the
5774
     * specific FieldType, which is tested in a dedicated integration test.
5775
     *
5776
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields
5777
     *
5778
     * @return \eZ\Publish\API\Repository\Values\Content\Field[]
5779
     */
5780
    private function normalizeFields(array $fields)
5781
    {
5782
        $normalized = [];
5783
        foreach ($fields as $field) {
5784
            $normalized[] = new Field(
5785
                [
5786
                    'id' => 0,
5787
                    'value' => $field->value !== null,
5788
                    'languageCode' => $field->languageCode,
5789
                    'fieldDefIdentifier' => $field->fieldDefIdentifier,
5790
                    'fieldTypeIdentifier' => $field->fieldTypeIdentifier,
5791
                ]
5792
            );
5793
        }
5794
        usort(
5795
            $normalized,
5796
            function ($field1, $field2) {
5797
                if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) {
5798
                    return strcasecmp($field1->languageCode, $field2->languageCode);
5799
                }
5800
5801
                return $return;
5802
            }
5803
        );
5804
5805
        return $normalized;
5806
    }
5807
5808
    /**
5809
     * Asserts that given Content has default ContentStates.
5810
     *
5811
     * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo
5812
     */
5813
    private function assertDefaultContentStates(ContentInfo $contentInfo)
5814
    {
5815
        $objectStateService = $this->getRepository()->getObjectStateService();
5816
5817
        $objectStateGroups = $objectStateService->loadObjectStateGroups();
5818
5819
        foreach ($objectStateGroups as $objectStateGroup) {
5820
            $contentState = $objectStateService->getContentState($contentInfo, $objectStateGroup);
5821
            foreach ($objectStateService->loadObjectStates($objectStateGroup) as $objectState) {
5822
                // Only check the first object state which is the default one.
5823
                $this->assertEquals(
5824
                    $objectState,
5825
                    $contentState
5826
                );
5827
                break;
5828
            }
5829
        }
5830
    }
5831
5832
    /**
5833
     * Assert that given Content has no references to a translation specified by the $languageCode.
5834
     *
5835
     * @param string $languageCode
5836
     * @param int $contentId
5837
     */
5838
    private function assertTranslationDoesNotExist($languageCode, $contentId)
5839
    {
5840
        $content = $this->contentService->loadContent($contentId);
5841
5842
        foreach ($content->fields as $field) {
5843
            /** @var array $field */
5844
            self::assertArrayNotHasKey($languageCode, $field);
5845
            self::assertNotEquals($languageCode, $content->contentInfo->mainLanguageCode);
5846
            self::assertArrayNotHasKey($languageCode, $content->versionInfo->getNames());
5847
            self::assertNotEquals($languageCode, $content->versionInfo->initialLanguageCode);
5848
            self::assertNotContains($languageCode, $content->versionInfo->languageCodes);
5849
        }
5850
        foreach ($this->contentService->loadVersions($content->contentInfo) as $versionInfo) {
5851
            self::assertArrayNotHasKey($languageCode, $versionInfo->getNames());
5852
            self::assertNotEquals($languageCode, $versionInfo->contentInfo->mainLanguageCode);
5853
            self::assertNotEquals($languageCode, $versionInfo->initialLanguageCode);
5854
            self::assertNotContains($languageCode, $versionInfo->languageCodes);
5855
        }
5856
    }
5857
5858
    /**
5859
     * Returns the default fixture of fields used in most tests.
5860
     *
5861
     * @return \eZ\Publish\API\Repository\Values\Content\Field[]
5862
     */
5863
    private function createFieldsFixture()
5864
    {
5865
        return [
5866
            new Field(
5867
                [
5868
                    'id' => 0,
5869
                    'value' => 'Foo',
5870
                    'languageCode' => self::ENG_US,
5871
                    'fieldDefIdentifier' => 'description',
5872
                    'fieldTypeIdentifier' => 'ezrichtext',
5873
                ]
5874
            ),
5875
            new Field(
5876
                [
5877
                    'id' => 0,
5878
                    'value' => 'Bar',
5879
                    'languageCode' => self::ENG_GB,
5880
                    'fieldDefIdentifier' => 'description',
5881
                    'fieldTypeIdentifier' => 'ezrichtext',
5882
                ]
5883
            ),
5884
            new Field(
5885
                [
5886
                    'id' => 0,
5887
                    'value' => 'An awesome multi-lang forum²',
5888
                    'languageCode' => self::ENG_US,
5889
                    'fieldDefIdentifier' => 'name',
5890
                    'fieldTypeIdentifier' => 'ezstring',
5891
                ]
5892
            ),
5893
            new Field(
5894
                [
5895
                    'id' => 0,
5896
                    'value' => 'An awesome multi-lang forum²³',
5897
                    'languageCode' => self::ENG_GB,
5898
                    'fieldDefIdentifier' => 'name',
5899
                    'fieldTypeIdentifier' => 'ezstring',
5900
                ]
5901
            ),
5902
        ];
5903
    }
5904
5905
    /**
5906
     * Gets expected property values for the "Media" ContentInfo ValueObject.
5907
     *
5908
     * @return array
5909
     */
5910
    private function getExpectedMediaContentInfoProperties()
5911
    {
5912
        return [
5913
            'id' => self::MEDIA_CONTENT_ID,
5914
            'contentTypeId' => 1,
5915
            'name' => 'Media',
5916
            'sectionId' => 3,
5917
            'currentVersionNo' => 1,
5918
            'published' => true,
5919
            'ownerId' => 14,
5920
            'modificationDate' => $this->createDateTime(1060695457),
5921
            'publishedDate' => $this->createDateTime(1060695457),
5922
            'alwaysAvailable' => 1,
5923
            'remoteId' => self::MEDIA_REMOTE_ID,
5924
            'mainLanguageCode' => self::ENG_US,
5925
            'mainLocationId' => 43,
5926
            'status' => ContentInfo::STATUS_PUBLISHED,
5927
        ];
5928
    }
5929
5930
    /**
5931
     * @covers \eZ\Publish\API\Repository\ContentService::hideContent
5932
     *
5933
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
5934
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
5935
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException
5936
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
5937
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
5938
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
5939
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
5940
     */
5941
    public function testHideContent(): void
5942
    {
5943
        $contentTypeService = $this->getRepository()->getContentTypeService();
5944
5945
        $locationCreateStructs = array_map(
5946
            function (Location $parentLocation) {
5947
                return $this->locationService->newLocationCreateStruct($parentLocation->id);
5948
            },
5949
            $this->createParentLocationsForHideReveal(2)
5950
        );
5951
5952
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
5953
5954
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
5955
        $contentCreate->setField('name', 'Folder to hide');
5956
5957
        $content = $this->contentService->createContent(
5958
            $contentCreate,
5959
            $locationCreateStructs
5960
        );
5961
5962
        $publishedContent = $this->contentService->publishVersion($content->versionInfo);
5963
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
5964
5965
        // Sanity check
5966
        $this->assertCount(3, $locations);
5967
        $this->assertCount(0, $this->filterHiddenLocations($locations));
5968
5969
        $this->contentService->hideContent($publishedContent->contentInfo);
5970
5971
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
5972
        $this->assertCount(3, $locations);
5973
        $this->assertCount(3, $this->filterHiddenLocations($locations));
5974
    }
5975
5976
    /**
5977
     * @covers \eZ\Publish\API\Repository\ContentService::revealContent
5978
     *
5979
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
5980
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
5981
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
5982
     */
5983
    public function testRevealContent()
5984
    {
5985
        $contentTypeService = $this->getRepository()->getContentTypeService();
5986
5987
        $locationCreateStructs = array_map(
5988
            function (Location $parentLocation) {
5989
                return $this->locationService->newLocationCreateStruct($parentLocation->id);
5990
            },
5991
            $this->createParentLocationsForHideReveal(2)
5992
        );
5993
5994
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
5995
5996
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
5997
        $contentCreate->setField('name', 'Folder to hide');
5998
5999
        $locationCreateStructs[0]->hidden = true;
6000
6001
        $content = $this->contentService->createContent(
6002
            $contentCreate,
6003
            $locationCreateStructs
6004
        );
6005
6006
        $publishedContent = $this->contentService->publishVersion($content->versionInfo);
6007
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
6008
6009
        // Sanity check
6010
        $hiddenLocations = $this->filterHiddenLocations($locations);
6011
        $this->assertCount(3, $locations);
6012
        $this->assertCount(1, $hiddenLocations);
6013
6014
        $this->contentService->hideContent($publishedContent->contentInfo);
6015
        $this->assertCount(
6016
            3,
6017
            $this->filterHiddenLocations(
6018
                $this->locationService->loadLocations($publishedContent->contentInfo)
6019
            )
6020
        );
6021
6022
        $this->contentService->revealContent($publishedContent->contentInfo);
6023
6024
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
6025
        $hiddenLocationsAfterReveal = $this->filterHiddenLocations($locations);
6026
        $this->assertCount(3, $locations);
6027
        $this->assertCount(1, $hiddenLocationsAfterReveal);
6028
        $this->assertEquals($hiddenLocations, $hiddenLocationsAfterReveal);
6029
    }
6030
6031
    /**
6032
     * @depends testRevealContent
6033
     */
6034
    public function testRevealContentWithHiddenParent()
6035
    {
6036
        $contentTypeService = $this->getRepository()->getContentTypeService();
6037
6038
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
6039
6040
        $contentNames = [
6041
            'Parent Content',
6042
            'Child (Nesting 1)',
6043
            'Child (Nesting 2)',
6044
            'Child (Nesting 3)',
6045
            'Child (Nesting 4)',
6046
        ];
6047
6048
        $parentLocation = $this->locationService->newLocationCreateStruct(
6049
            $this->generateId('location', 2)
6050
        );
6051
6052
        /** @var Content[] $contents */
6053
        $contents = [];
6054
6055
        foreach ($contentNames as $contentName) {
6056
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6057
            $contentCreate->setField('name', $contentName);
6058
6059
            $content = $this->contentService->createContent($contentCreate, [$parentLocation]);
6060
            $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo);
6061
6062
            $parentLocation = $this->locationService->newLocationCreateStruct(
6063
                $this->generateId('location', $publishedContent->contentInfo->mainLocationId)
6064
            );
6065
        }
6066
6067
        $this->contentService->hideContent($contents[0]->contentInfo);
6068
        $this->contentService->hideContent($contents[2]->contentInfo);
6069
        $this->contentService->revealContent($contents[2]->contentInfo);
6070
6071
        $parentContent = $this->contentService->loadContent($contents[0]->id);
6072
        $parentLocation = $this->locationService->loadLocation($parentContent->contentInfo->mainLocationId);
6073
        $parentSublocations = $this->locationService->loadLocationList([
6074
            $contents[1]->contentInfo->mainLocationId,
6075
            $contents[2]->contentInfo->mainLocationId,
6076
            $contents[3]->contentInfo->mainLocationId,
6077
            $contents[4]->contentInfo->mainLocationId,
6078
        ]);
6079
6080
        // Parent remains invisible
6081
        self::assertTrue($parentLocation->invisible);
6082
6083
        // All parent sublocations remain invisible as well
6084
        foreach ($parentSublocations as $parentSublocation) {
6085
            self::assertTrue($parentSublocation->invisible);
6086
        }
6087
    }
6088
6089
    /**
6090
     * @depends testRevealContent
6091
     */
6092
    public function testRevealContentWithHiddenChildren()
6093
    {
6094
        $contentTypeService = $this->getRepository()->getContentTypeService();
6095
6096
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
6097
6098
        $contentNames = [
6099
            'Parent Content',
6100
            'Child (Nesting 1)',
6101
            'Child (Nesting 2)',
6102
            'Child (Nesting 3)',
6103
            'Child (Nesting 4)',
6104
        ];
6105
6106
        $parentLocation = $this->locationService->newLocationCreateStruct(
6107
            $this->generateId('location', 2)
6108
        );
6109
6110
        /** @var Content[] $contents */
6111
        $contents = [];
6112
6113
        foreach ($contentNames as $contentName) {
6114
            $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6115
            $contentCreate->setField('name', $contentName);
6116
6117
            $content = $this->contentService->createContent($contentCreate, [$parentLocation]);
6118
            $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo);
6119
6120
            $parentLocation = $this->locationService->newLocationCreateStruct(
6121
                $this->generateId('location', $publishedContent->contentInfo->mainLocationId)
6122
            );
6123
        }
6124
6125
        $this->contentService->hideContent($contents[0]->contentInfo);
6126
        $this->contentService->hideContent($contents[2]->contentInfo);
6127
        $this->contentService->revealContent($contents[0]->contentInfo);
6128
6129
        $directChildContent = $this->contentService->loadContent($contents[1]->id);
6130
        $directChildLocation = $this->locationService->loadLocation($directChildContent->contentInfo->mainLocationId);
6131
6132
        $childContent = $this->contentService->loadContent($contents[2]->id);
6133
        $childLocation = $this->locationService->loadLocation($childContent->contentInfo->mainLocationId);
6134
        $childSublocations = $this->locationService->loadLocationList([
6135
            $contents[3]->contentInfo->mainLocationId,
6136
            $contents[4]->contentInfo->mainLocationId,
6137
        ]);
6138
6139
        // Direct child content is not hidden
6140
        self::assertFalse($directChildContent->contentInfo->isHidden);
6141
6142
        // Direct child content location is still invisible
6143
        self::assertFalse($directChildLocation->invisible);
6144
6145
        // Child content is still hidden
6146
        self::assertTrue($childContent->contentInfo->isHidden);
6147
6148
        // Child content location is still invisible
6149
        self::assertTrue($childLocation->invisible);
6150
6151
        // All childs sublocations remain invisible as well
6152
        foreach ($childSublocations as $childSublocation) {
6153
            self::assertTrue($childSublocation->invisible);
6154
        }
6155
    }
6156
6157
    public function testHideContentWithParentLocation()
6158
    {
6159
        $contentTypeService = $this->getRepository()->getContentTypeService();
6160
6161
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
6162
6163
        $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6164
        $contentCreate->setField('name', 'Parent');
6165
6166
        $content = $this->contentService->createContent(
6167
            $contentCreate,
6168
            [
6169
                $this->locationService->newLocationCreateStruct(
6170
                    $this->generateId('location', 2)
6171
                ),
6172
            ]
6173
        );
6174
6175
        $publishedContent = $this->contentService->publishVersion($content->versionInfo);
6176
6177
        $this->contentService->hideContent($publishedContent->contentInfo);
6178
6179
        $locations = $this->locationService->loadLocations($publishedContent->contentInfo);
6180
6181
        $childContentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US);
6182
        $childContentCreate->setField('name', 'Child');
6183
6184
        $childContent = $this->contentService->createContent(
6185
            $childContentCreate,
6186
            [
6187
                $this->locationService->newLocationCreateStruct(
6188
                    $locations[0]->id
6189
                ),
6190
            ]
6191
        );
6192
6193
        $publishedChildContent = $this->contentService->publishVersion($childContent->versionInfo);
6194
6195
        $childLocations = $this->locationService->loadLocations($publishedChildContent->contentInfo);
6196
6197
        $this->assertTrue($locations[0]->hidden);
6198
        $this->assertTrue($locations[0]->invisible);
6199
6200
        $this->assertFalse($childLocations[0]->hidden);
6201
        $this->assertTrue($childLocations[0]->invisible);
6202
    }
6203
6204
    public function testChangeContentName()
6205
    {
6206
        $contentDraft = $this->createContentDraft(
6207
            'folder',
6208
            $this->generateId('location', 2),
6209
            [
6210
                'name' => 'Marco',
6211
            ]
6212
        );
6213
6214
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
6215
        $contentMetadataUpdateStruct = new ContentMetadataUpdateStruct([
6216
            'name' => 'Polo',
6217
        ]);
6218
        $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct);
6219
6220
        $updatedContent = $this->contentService->loadContent($publishedContent->id);
6221
6222
        $this->assertEquals('Marco', $publishedContent->contentInfo->name);
6223
        $this->assertEquals('Polo', $updatedContent->contentInfo->name);
6224
    }
6225
6226
    public function testCopyTranslationsFromPublishedToDraft()
6227
    {
6228
        $contentDraft = $this->createContentDraft(
6229
            'folder',
6230
            $this->generateId('location', 2),
6231
            [
6232
                'name' => 'Folder US',
6233
            ]
6234
        );
6235
6236
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
6237
6238
        $deDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6239
6240
        $contentUpdateStruct = new ContentUpdateStruct([
6241
            'initialLanguageCode' => self::GER_DE,
6242
            'fields' => $contentDraft->getFields(),
6243
        ]);
6244
6245
        $contentUpdateStruct->setField('name', 'Folder GER', self::GER_DE);
6246
6247
        $deContent = $this->contentService->updateContent($deDraft->versionInfo, $contentUpdateStruct);
6248
6249
        $updatedContent = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo);
6250
        $this->assertEquals(
6251
            [
6252
                self::ENG_US => 'Folder US',
6253
                self::GER_DE => 'Folder GER',
6254
            ],
6255
            $updatedContent->fields['name']
6256
        );
6257
6258
        $gbDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6259
6260
        $contentUpdateStruct = new ContentUpdateStruct([
6261
            'initialLanguageCode' => self::ENG_GB,
6262
            'fields' => $contentDraft->getFields(),
6263
        ]);
6264
6265
        $contentUpdateStruct->setField('name', 'Folder GB', self::ENG_GB);
6266
6267
        $gbContent = $this->contentService->updateContent($gbDraft->versionInfo, $contentUpdateStruct);
6268
        $this->contentService->publishVersion($gbDraft->versionInfo);
6269
        $updatedContent = $this->contentService->loadContent($gbContent->id, null, $gbContent->versionInfo->versionNo);
6270
        $this->assertEquals(
6271
            [
6272
                self::ENG_US => 'Folder US',
6273
                self::ENG_GB => 'Folder GB',
6274
            ],
6275
            $updatedContent->fields['name']
6276
        );
6277
6278
        $dePublished = $this->contentService->publishVersion($deDraft->versionInfo);
6279
        $this->assertEquals(
6280
            [
6281
                self::ENG_US => 'Folder US',
6282
                self::GER_DE => 'Folder GER',
6283
                self::ENG_GB => 'Folder GB',
6284
            ],
6285
            $dePublished->fields['name']
6286
        );
6287
    }
6288
6289
    public function testCopyTranslationsFromInvalidPublishedContentToDraft()
6290
    {
6291
        $contentTypeService = $this->getRepository()->getContentTypeService();
6292
6293
        // Create content type for testing
6294
        $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct('test_copy_translation');
6295
        $contentTypeCreateStruct->mainLanguageCode = 'eng-US';
6296
        $contentTypeCreateStruct->names = ['eng-US' => 'Test Content Type for Copy Translations'];
6297
        $fieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring');
6298
        $fieldDefinition->position = 1;
6299
        $contentTypeCreateStruct->addFieldDefinition($fieldDefinition);
6300
        $contentTypeService->publishContentTypeDraft(
6301
            $contentTypeService->createContentType(
6302
                $contentTypeCreateStruct,
6303
                [$contentTypeService->loadContentTypeGroupByIdentifier('Content')]
6304
            )
6305
        );
6306
        $contentType = $contentTypeService->loadContentTypeByIdentifier('test_copy_translation');
6307
6308
        // Create entry content
6309
        $contentDraft = $this->createContentDraft(
6310
            'test_copy_translation',
6311
            $this->generateId('location', 2),
6312
            [
6313
                'name' => 'Folder US',
6314
            ]
6315
        );
6316
        $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo);
6317
6318
        // Create translation draft that would act as an OLD version
6319
        $deDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6320
        $contentUpdateStruct = new ContentUpdateStruct([
6321
            'initialLanguageCode' => self::GER_DE,
6322
            'fields' => $contentDraft->getFields(),
6323
        ]);
6324
6325
        $contentUpdateStruct->setField('name', 'Folder GER', self::GER_DE);
6326
        $deContent = $this->contentService->updateContent($deDraft->versionInfo, $contentUpdateStruct);
6327
6328
        // Update published version, as copying is only done when there is a diff between published and draft
6329
        $gbDraft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6330
        $contentUpdateStruct = new ContentUpdateStruct([
6331
            'initialLanguageCode' => self::ENG_US,
6332
        ]);
6333
        $contentUpdateStruct->setField('name', 'Folder US 2', self::ENG_US);
6334
6335
        $gbContent = $this->contentService->updateContent($gbDraft->versionInfo, $contentUpdateStruct);
6336
        $this->contentService->publishVersion($gbContent->versionInfo);
6337
6338
        // Update content type with new required field
6339
        $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType);
6340
        $fieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct('req_field', 'ezstring');
6341
        $fieldDefinition->position = 2;
6342
        $fieldDefinition->isRequired = true;
6343
        $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinition);
6344
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
6345
6346
        // Reload previous german draft, it is now in invalid state for both ENG_US and GER_DE
6347
        $invalidContentDraft = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo);
6348
        $contentUpdateStruct = new ContentUpdateStruct([
6349
            'initialLanguageCode' => self::GER_DE,
6350
        ]);
6351
        $contentUpdateStruct->setField('req_field', 'Required field DE', self::GER_DE);
6352
6353
        $this->contentService->updateContent($invalidContentDraft->versionInfo, $contentUpdateStruct);
6354
        $this->contentService->publishVersion($invalidContentDraft->versionInfo, [self::GER_DE]);
6355
6356
        $publishedContent = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo);
6357
6358
        $this->assertEquals(
6359
            [
6360
                self::GER_DE => 'Folder GER',
6361
                self::ENG_US => 'Folder US 2',
6362
            ],
6363
            $publishedContent->fields['name']
6364
        );
6365
        // Missing values were copied from last updated draft
6366
        $this->assertEquals(
6367
            [
6368
                self::GER_DE => 'Required field DE',
6369
                self::ENG_US => 'Required field DE',
6370
            ],
6371
            $publishedContent->fields['req_field']
6372
        );
6373
    }
6374
6375
    /**
6376
     * Create structure of parent folders with Locations to be used for Content hide/reveal tests.
6377
     *
6378
     * @param int $parentLocationId
6379
     *
6380
     * @return \eZ\Publish\API\Repository\Values\Content\Location[] A list of Locations aimed to be parents
6381
     *
6382
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
6383
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
6384
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6385
     */
6386
    private function createParentLocationsForHideReveal(int $parentLocationId): array
6387
    {
6388
        $parentFoldersLocationsIds = [
6389
            $this->createFolder([self::ENG_US => 'P1'], $parentLocationId)->contentInfo->mainLocationId,
6390
            $this->createFolder([self::ENG_US => 'P2'], $parentLocationId)->contentInfo->mainLocationId,
6391
            $this->createFolder([self::ENG_US => 'P3'], $parentLocationId)->contentInfo->mainLocationId,
6392
        ];
6393
6394
        return array_values($this->locationService->loadLocationList($parentFoldersLocationsIds));
6395
    }
6396
6397
    /**
6398
     * Filter Locations list by hidden only.
6399
     *
6400
     * @param \eZ\Publish\API\Repository\Values\Content\Location[] $locations
6401
     *
6402
     * @return array
6403
     */
6404
    private function filterHiddenLocations(array $locations): array
6405
    {
6406
        return array_values(
6407
            array_filter(
6408
                $locations,
6409
                function (Location $location) {
6410
                    return $location->hidden;
6411
                }
6412
            )
6413
        );
6414
    }
6415
6416
    public function testPublishVersionWithSelectedLanguages()
6417
    {
6418
        $publishedContent = $this->createFolder(
6419
            [
6420
                self::ENG_US => 'Published US',
6421
                self::GER_DE => 'Published DE',
6422
            ],
6423
            $this->generateId('location', 2)
6424
        );
6425
6426
        $draft = $this->contentService->createContentDraft($publishedContent->contentInfo);
6427
        $contentUpdateStruct = new ContentUpdateStruct([
6428
            'initialLanguageCode' => self::ENG_US,
6429
        ]);
6430
        $contentUpdateStruct->setField('name', 'Draft 1 US', self::ENG_US);
6431
        $contentUpdateStruct->setField('name', 'Draft 1 DE', self::GER_DE);
6432
6433
        $this->contentService->updateContent($draft->versionInfo, $contentUpdateStruct);
6434
6435
        $this->contentService->publishVersion($draft->versionInfo, [self::GER_DE]);
6436
        $content = $this->contentService->loadContent($draft->contentInfo->id);
6437
        $this->assertEquals(
6438
            [
6439
                self::ENG_US => 'Published US',
6440
                self::GER_DE => 'Draft 1 DE',
6441
            ],
6442
            $content->fields['name']
6443
        );
6444
    }
6445
6446
    public function testCreateContentWithRomanianSpecialCharsInTitle()
6447
    {
6448
        $baseName = 'ȘșțȚdfdf';
6449
        $expectedPath = '/SstTdfdf';
6450
6451
        $this->createFolder([self::ENG_US => $baseName], 2);
6452
6453
        $urlAliasService = $this->getRepository()->getURLAliasService();
6454
        $urlAlias = $urlAliasService->lookup($expectedPath);
6455
        $this->assertSame($expectedPath, $urlAlias->path);
6456
    }
6457
6458
    /**
6459
     * @param int $amountOfDrafts
6460
     *
6461
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6462
     */
6463
    private function createContentDrafts(int $amountOfDrafts): void
6464
    {
6465
        if (0 >= $amountOfDrafts) {
6466
            throw new InvalidArgumentException('$amountOfDrafts', 'Must be greater then 0');
6467
        }
6468
6469
        $publishedContent = $this->createContentVersion1();
6470
6471
        for ($i = 1; $i <= $amountOfDrafts; ++$i) {
6472
            $this->contentService->createContentDraft($publishedContent->contentInfo);
6473
        }
6474
    }
6475
6476
    /**
6477
     * @param array $limitationValues
6478
     *
6479
     * @return \eZ\Publish\API\Repository\Values\User\User
6480
     *
6481
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
6482
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
6483
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6484
     */
6485
    private function createUserWithVersionReadLimitations(array $limitationValues = []): User
6486
    {
6487
        $limitations = [
6488
            new LocationLimitation(['limitationValues' => $limitationValues]),
6489
        ];
6490
6491
        return $this->createUserWithPolicies(
6492
            'user',
6493
            [
6494
                ['module' => 'content', 'function' => 'versionread', 'limitations' => $limitations],
6495
                ['module' => 'content', 'function' => 'create'],
6496
                ['module' => 'content', 'function' => 'read'],
6497
                ['module' => 'content', 'function' => 'edit'],
6498
            ]
6499
        );
6500
    }
6501
6502
    /**
6503
     * @param \eZ\Publish\API\Repository\Values\Content\Content[] $drafts
6504
     *
6505
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException
6506
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
6507
     *
6508
     * @return object
6509
     */
6510
    private function createContentWithReverseRelations(array $drafts)
6511
    {
6512
        $contentWithReverseRelations = new class() {
6513
            /** @var \eZ\Publish\API\Repository\Values\Content\Content */
6514
            public $content;
6515
6516
            /** @var \eZ\Publish\API\Repository\Values\Content\Content[] */
6517
            public $reverseRelations;
6518
        };
6519
        $content = $this->createContentVersion1();
6520
        $versionInfo = $content->getVersionInfo();
6521
        $contentInfo = $versionInfo->getContentInfo();
6522
        $contentWithReverseRelations->content = $content;
6523
6524
        /** @var \eZ\Publish\API\Repository\Values\Content\Content $draft */
6525
        foreach ($drafts as $draft) {
6526
            $this->contentService->addRelation(
6527
                $draft->getVersionInfo(),
6528
                $contentInfo
6529
            );
6530
6531
            $contentWithReverseRelations->reverseRelations[] = $this->contentService->publishVersion($draft->getVersionInfo());
6532
        }
6533
6534
        return $contentWithReverseRelations;
6535
    }
6536
}
6537