Completed
Push — EZP-30427 ( 70aaad...6c786c )
by
unknown
20:55
created

ContentServiceTest::testLoadRelations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 54
rs 9.0036
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

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

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

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

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

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