Completed
Push — 7.5 ( 17c267...9e0292 )
by Łukasz
47:52 queued 28:25
created

testLoadContentDraftsReturnsEmptyArrayByDefault()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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