Completed
Push — EZP-30969-fetch-reverse-relati... ( d71d24 )
by
unknown
121:16 queued 99:27
created

createContentWithReverseRelations()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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