1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* File containing the BaseTest 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 Doctrine\DBAL\Connection; |
12
|
|
|
use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException; |
13
|
|
|
use eZ\Publish\API\Repository\Tests\PHPUnitConstraint\ValidationErrorOccurs as PHPUnitConstraintValidationErrorOccurs; |
14
|
|
|
use eZ\Publish\API\Repository\Tests\SetupFactory\Legacy; |
15
|
|
|
use eZ\Publish\API\Repository\Values\Content\Content; |
16
|
|
|
use eZ\Publish\API\Repository\Values\Content\Language; |
17
|
|
|
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; |
18
|
|
|
use PHPUnit\Framework\TestCase; |
19
|
|
|
use eZ\Publish\API\Repository\Repository; |
20
|
|
|
use eZ\Publish\API\Repository\Values\ValueObject; |
21
|
|
|
use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; |
22
|
|
|
use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; |
23
|
|
|
use eZ\Publish\API\Repository\Values\User\UserGroup; |
24
|
|
|
use eZ\Publish\Core\REST\Client\Sessionable; |
25
|
|
|
use DateTime; |
26
|
|
|
use ArrayObject; |
27
|
|
|
use Exception; |
28
|
|
|
use PDOException; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Base class for api specific tests. |
32
|
|
|
*/ |
33
|
|
|
abstract class BaseTest extends TestCase |
34
|
|
|
{ |
35
|
|
|
/** |
36
|
|
|
* Maximum integer number accepted by the different backends. |
37
|
|
|
*/ |
38
|
|
|
const DB_INT_MAX = 2147483647; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var \eZ\Publish\API\Repository\Tests\SetupFactory |
42
|
|
|
*/ |
43
|
|
|
private $setupFactory; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var \eZ\Publish\API\Repository\Repository |
47
|
|
|
*/ |
48
|
|
|
private $repository; |
49
|
|
|
|
50
|
|
|
protected function setUp() |
51
|
|
|
{ |
52
|
|
|
parent::setUp(); |
53
|
|
|
|
54
|
|
|
try { |
55
|
|
|
// Use setup factory instance here w/o clearing data in case test don't need to |
56
|
|
|
$repository = $this->getSetupFactory()->getRepository(false); |
57
|
|
|
|
58
|
|
|
// Set session if we are testing the REST backend to make it |
59
|
|
|
// possible to persist data in the memory backend during multiple |
60
|
|
|
// requests. |
61
|
|
|
if ($repository instanceof Sessionable) { |
62
|
|
|
$repository->setSession($id = md5(microtime())); |
63
|
|
|
} |
64
|
|
|
} catch (PDOException $e) { |
65
|
|
|
$this->fail( |
66
|
|
|
'The communication with the database cannot be established. ' . |
67
|
|
|
"This is required in order to perform the tests.\n\n" . |
68
|
|
|
'Exception: ' . $e |
69
|
|
|
); |
70
|
|
|
} catch (Exception $e) { |
71
|
|
|
$this->fail( |
72
|
|
|
'Cannot create a repository with predefined user. ' . |
73
|
|
|
'Check the UserService or RoleService implementation. ' . |
74
|
|
|
PHP_EOL . PHP_EOL . |
75
|
|
|
'Exception: ' . $e |
76
|
|
|
); |
77
|
|
|
} |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Resets the temporary used repository between each test run. |
82
|
|
|
*/ |
83
|
|
|
protected function tearDown() |
84
|
|
|
{ |
85
|
|
|
$this->repository = null; |
86
|
|
|
parent::tearDown(); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Returns the ID generator, fitting to the repository implementation. |
91
|
|
|
* |
92
|
|
|
* @return \eZ\Publish\API\Repository\Tests\IdManager |
93
|
|
|
*/ |
94
|
|
|
protected function getIdManager() |
95
|
|
|
{ |
96
|
|
|
return $this->getSetupFactory()->getIdManager(); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Generates a repository specific ID value. |
101
|
|
|
* |
102
|
|
|
* @param string $type |
103
|
|
|
* @param mixed $rawId |
104
|
|
|
* |
105
|
|
|
* @return mixed |
106
|
|
|
*/ |
107
|
|
|
protected function generateId($type, $rawId) |
108
|
|
|
{ |
109
|
|
|
return $this->getIdManager()->generateId($type, $rawId); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Parses a repository specific ID value. |
114
|
|
|
* |
115
|
|
|
* @param string $type |
116
|
|
|
* @param mixed $id |
117
|
|
|
* |
118
|
|
|
* @return mixed |
119
|
|
|
*/ |
120
|
|
|
protected function parseId($type, $id) |
121
|
|
|
{ |
122
|
|
|
return $this->getIdManager()->parseId($type, $id); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Returns a config setting provided by the setup factory. |
127
|
|
|
* |
128
|
|
|
* @param string $configKey |
129
|
|
|
* |
130
|
|
|
* @return mixed |
131
|
|
|
*/ |
132
|
|
|
protected function getConfigValue($configKey) |
133
|
|
|
{ |
134
|
|
|
return $this->getSetupFactory()->getConfigValue($configKey); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Tests if the currently tested api is based on a V4 implementation. |
139
|
|
|
* |
140
|
|
|
* @return bool |
141
|
|
|
*/ |
142
|
|
|
protected function isVersion4() |
143
|
|
|
{ |
144
|
|
|
return isset($_ENV['backendVersion']) && '4' === $_ENV['backendVersion']; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @param bool $initialInitializeFromScratch Only has an effect if set in first call within a test |
149
|
|
|
* |
150
|
|
|
* @return \eZ\Publish\API\Repository\Repository |
151
|
|
|
*/ |
152
|
|
|
protected function getRepository($initialInitializeFromScratch = true) |
153
|
|
|
{ |
154
|
|
|
if (null === $this->repository) { |
155
|
|
|
$this->repository = $this->getSetupFactory()->getRepository($initialInitializeFromScratch); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
return $this->repository; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* @return \eZ\Publish\API\Repository\Tests\SetupFactory |
163
|
|
|
*/ |
164
|
|
|
protected function getSetupFactory() |
165
|
|
|
{ |
166
|
|
|
if (null === $this->setupFactory) { |
167
|
|
|
if (false === ($setupClass = getenv('setupFactory'))) { |
168
|
|
|
$setupClass = Legacy::class; |
169
|
|
|
putenv("setupFactory=${setupClass}"); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
if (false === ($fixtureDir = getenv('fixtureDir'))) { |
173
|
|
|
putenv('fixtureDir=Legacy'); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
if (false === class_exists($setupClass)) { |
177
|
|
|
throw new \ErrorException( |
178
|
|
|
sprintf( |
179
|
|
|
'Environment variable "setupFactory" does not reference an existing class: %s. Did you forget to install an package dependency?', |
180
|
|
|
$setupClass |
181
|
|
|
) |
182
|
|
|
); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
$this->setupFactory = new $setupClass(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
return $this->setupFactory; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Asserts that properties given in $expectedValues are correctly set in |
193
|
|
|
* $actualObject. |
194
|
|
|
* |
195
|
|
|
* @param mixed[] $expectedValues |
196
|
|
|
* @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject |
197
|
|
|
*/ |
198
|
|
|
protected function assertPropertiesCorrect(array $expectedValues, ValueObject $actualObject) |
199
|
|
|
{ |
200
|
|
|
foreach ($expectedValues as $propertyName => $propertyValue) { |
201
|
|
|
if ($propertyValue instanceof ValueObject) { |
202
|
|
|
$this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); |
203
|
|
|
} elseif (is_array($propertyValue)) { |
204
|
|
|
foreach ($propertyValue as $key => $value) { |
205
|
|
|
if ($value instanceof ValueObject) { |
206
|
|
|
$this->assertStructPropertiesCorrect($value, $actualObject->$propertyName[$key]); |
207
|
|
|
} else { |
208
|
|
|
$this->assertPropertiesEqual("$propertyName\[$key\]", $value, $actualObject->$propertyName[$key]); |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
} else { |
212
|
|
|
$this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName); |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Asserts that properties given in $expectedValues are correctly set in |
219
|
|
|
* $actualObject. |
220
|
|
|
* |
221
|
|
|
* If the property type is array, it will be sorted before comparison. |
222
|
|
|
* |
223
|
|
|
* @TODO: introduced because of randomly failing tests, ref: https://jira.ez.no/browse/EZP-21734 |
224
|
|
|
* |
225
|
|
|
* @param mixed[] $expectedValues |
226
|
|
|
* @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject |
227
|
|
|
*/ |
228
|
|
|
protected function assertPropertiesCorrectUnsorted(array $expectedValues, ValueObject $actualObject) |
229
|
|
|
{ |
230
|
|
View Code Duplication |
foreach ($expectedValues as $propertyName => $propertyValue) { |
|
|
|
|
231
|
|
|
if ($propertyValue instanceof ValueObject) { |
232
|
|
|
$this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); |
233
|
|
|
} else { |
234
|
|
|
$this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName, true); |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Asserts all properties from $expectedValues are correctly set in |
241
|
|
|
* $actualObject. Additional (virtual) properties can be asserted using |
242
|
|
|
* $additionalProperties. |
243
|
|
|
* |
244
|
|
|
* @param \eZ\Publish\API\Repository\Values\ValueObject $expectedValues |
245
|
|
|
* @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject |
246
|
|
|
* @param array $propertyNames |
|
|
|
|
247
|
|
|
*/ |
248
|
|
|
protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = array()) |
249
|
|
|
{ |
250
|
|
View Code Duplication |
foreach ($expectedValues as $propertyName => $propertyValue) { |
|
|
|
|
251
|
|
|
if ($propertyValue instanceof ValueObject) { |
252
|
|
|
$this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); |
253
|
|
|
} else { |
254
|
|
|
$this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName); |
255
|
|
|
} |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
foreach ($additionalProperties as $propertyName) { |
259
|
|
|
$this->assertPropertiesEqual($propertyName, $expectedValues->$propertyName, $actualObject->$propertyName); |
260
|
|
|
} |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* @see \eZ\Publish\API\Repository\Tests\BaseTest::assertPropertiesCorrectUnsorted() |
265
|
|
|
* |
266
|
|
|
* @param array $items An array of scalar values |
267
|
|
|
*/ |
268
|
|
|
private function sortItems(array &$items) |
269
|
|
|
{ |
270
|
|
|
$sorter = function ($a, $b) { |
271
|
|
|
if (!is_scalar($a) || !is_scalar($b)) { |
272
|
|
|
$this->fail('Wrong usage: method ' . __METHOD__ . ' accepts only an array of scalar values'); |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
return strcmp($a, $b); |
276
|
|
|
}; |
277
|
|
|
usort($items, $sorter); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false) |
281
|
|
|
{ |
282
|
|
|
if ($expectedValue instanceof ArrayObject) { |
283
|
|
|
$expectedValue = $expectedValue->getArrayCopy(); |
284
|
|
|
} elseif ($expectedValue instanceof DateTime) { |
285
|
|
|
$expectedValue = $expectedValue->format(DateTime::RFC850); |
286
|
|
|
} |
287
|
|
|
if ($actualValue instanceof ArrayObject) { |
288
|
|
|
$actualValue = $actualValue->getArrayCopy(); |
289
|
|
|
} elseif ($actualValue instanceof DateTime) { |
290
|
|
|
$actualValue = $actualValue->format(DateTime::RFC850); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
if ($sortArray && is_array($actualValue) && is_array($expectedValue)) { |
294
|
|
|
$this->sortItems($actualValue); |
295
|
|
|
$this->sortItems($expectedValue); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
$this->assertEquals( |
299
|
|
|
$expectedValue, |
300
|
|
|
$actualValue, |
301
|
|
|
sprintf('Object property "%s" incorrect.', $propertyName) |
302
|
|
|
); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Create a user in editor user group. |
307
|
|
|
* |
308
|
|
|
* @param string $login |
309
|
|
|
* |
310
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
311
|
|
|
*/ |
312
|
|
|
protected function createUserVersion1($login = 'user') |
313
|
|
|
{ |
314
|
|
|
$repository = $this->getRepository(); |
315
|
|
|
|
316
|
|
|
/* BEGIN: Inline */ |
317
|
|
|
// ID of the "Editors" user group in an eZ Publish demo installation |
318
|
|
|
$editorsGroupId = 13; |
319
|
|
|
|
320
|
|
|
$userService = $repository->getUserService(); |
321
|
|
|
|
322
|
|
|
// Instantiate a create struct with mandatory properties |
323
|
|
|
$userCreate = $userService->newUserCreateStruct( |
324
|
|
|
$login, |
325
|
|
|
"{$login}@example.com", |
326
|
|
|
'secret', |
327
|
|
|
'eng-US' |
328
|
|
|
); |
329
|
|
|
$userCreate->enabled = true; |
330
|
|
|
|
331
|
|
|
// Set some fields required by the user ContentType |
332
|
|
|
$userCreate->setField('first_name', 'Example'); |
333
|
|
|
$userCreate->setField('last_name', 'User'); |
334
|
|
|
|
335
|
|
|
// Load parent group for the user |
336
|
|
|
$group = $userService->loadUserGroup($editorsGroupId); |
337
|
|
|
|
338
|
|
|
// Create a new user instance. |
339
|
|
|
$user = $userService->createUser($userCreate, array($group)); |
340
|
|
|
/* END: Inline */ |
341
|
|
|
|
342
|
|
|
return $user; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Create a user in new user group with editor rights limited to Media Library (/1/48/). |
347
|
|
|
* |
348
|
|
|
* @uses ::createCustomUserVersion1() |
349
|
|
|
* |
350
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
351
|
|
|
*/ |
352
|
|
|
protected function createMediaUserVersion1() |
353
|
|
|
{ |
354
|
|
|
return $this->createCustomUserVersion1( |
355
|
|
|
'Media Editor', |
356
|
|
|
'Editor', |
357
|
|
|
new SubtreeLimitation(array('limitationValues' => array('/1/43/'))) |
358
|
|
|
); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* Create a user with new user group and assign a existing role (optionally with RoleLimitation). |
363
|
|
|
* |
364
|
|
|
* @param string $userGroupName Name of the new user group to create |
365
|
|
|
* @param string $roleIdentifier Role identifier to assign to the new group |
366
|
|
|
* @param RoleLimitation|null $roleLimitation |
367
|
|
|
* |
368
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
369
|
|
|
*/ |
370
|
|
|
protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null) |
371
|
|
|
{ |
372
|
|
|
return $this->createCustomUserWithLogin( |
373
|
|
|
'user', |
374
|
|
|
'[email protected]', |
375
|
|
|
$userGroupName, |
376
|
|
|
$roleIdentifier, |
377
|
|
|
$roleLimitation |
378
|
|
|
); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
/** |
382
|
|
|
* Create a user with new user group and assign a existing role (optionally with RoleLimitation). |
383
|
|
|
* |
384
|
|
|
* @param string $login User login |
385
|
|
|
* @param string $email User e-mail |
386
|
|
|
* @param string $userGroupName Name of the new user group to create |
387
|
|
|
* @param string $roleIdentifier Role identifier to assign to the new group |
388
|
|
|
* @param RoleLimitation|null $roleLimitation |
389
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
390
|
|
|
*/ |
391
|
|
|
protected function createCustomUserWithLogin( |
392
|
|
|
$login, |
393
|
|
|
$email, |
394
|
|
|
$userGroupName, |
395
|
|
|
$roleIdentifier, |
396
|
|
|
RoleLimitation $roleLimitation = null |
397
|
|
|
) { |
398
|
|
|
$repository = $this->getRepository(); |
399
|
|
|
|
400
|
|
|
/* BEGIN: Inline */ |
401
|
|
|
// ID of the "Users" user group in an eZ Publish demo installation |
402
|
|
|
$rootUsersGroupId = $this->generateId('location', 4); |
403
|
|
|
|
404
|
|
|
$roleService = $repository->getRoleService(); |
405
|
|
|
$userService = $repository->getUserService(); |
406
|
|
|
|
407
|
|
|
// Get a group create struct |
408
|
|
|
$userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); |
409
|
|
|
$userGroupCreate->setField('name', $userGroupName); |
410
|
|
|
|
411
|
|
|
// Create new group with media editor rights |
412
|
|
|
$userGroup = $userService->createUserGroup( |
413
|
|
|
$userGroupCreate, |
414
|
|
|
$userService->loadUserGroup($rootUsersGroupId) |
415
|
|
|
); |
416
|
|
|
$roleService->assignRoleToUserGroup( |
417
|
|
|
$roleService->loadRoleByIdentifier($roleIdentifier), |
418
|
|
|
$userGroup, |
419
|
|
|
$roleLimitation |
420
|
|
|
); |
421
|
|
|
|
422
|
|
|
// Instantiate a create struct with mandatory properties |
423
|
|
|
$userCreate = $userService->newUserCreateStruct( |
424
|
|
|
$login, |
425
|
|
|
$email, |
426
|
|
|
'secret', |
427
|
|
|
'eng-US' |
428
|
|
|
); |
429
|
|
|
$userCreate->enabled = true; |
430
|
|
|
|
431
|
|
|
// Set some fields required by the user ContentType |
432
|
|
|
$userCreate->setField('first_name', 'Example'); |
433
|
|
|
$userCreate->setField('last_name', ucfirst($login)); |
434
|
|
|
|
435
|
|
|
// Create a new user instance. |
436
|
|
|
$user = $userService->createUser($userCreate, array($userGroup)); |
437
|
|
|
/* END: Inline */ |
438
|
|
|
|
439
|
|
|
return $user; |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
/** |
443
|
|
|
* Create a user using given data. |
444
|
|
|
* |
445
|
|
|
* @param string $login |
446
|
|
|
* @param string $firstName |
447
|
|
|
* @param string $lastName |
448
|
|
|
* @param \eZ\Publish\API\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default |
449
|
|
|
* |
450
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
451
|
|
|
*/ |
452
|
|
|
protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null) |
453
|
|
|
{ |
454
|
|
|
$repository = $this->getRepository(); |
455
|
|
|
|
456
|
|
|
$userService = $repository->getUserService(); |
457
|
|
|
if (null === $userGroup) { |
458
|
|
|
$userGroup = $userService->loadUserGroup(13); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
// Instantiate a create struct with mandatory properties |
462
|
|
|
$userCreate = $userService->newUserCreateStruct( |
463
|
|
|
$login, |
464
|
|
|
"{$login}@example.com", |
465
|
|
|
'secret', |
466
|
|
|
'eng-US' |
467
|
|
|
); |
468
|
|
|
$userCreate->enabled = true; |
469
|
|
|
|
470
|
|
|
// Set some fields required by the user ContentType |
471
|
|
|
$userCreate->setField('first_name', $firstName); |
472
|
|
|
$userCreate->setField('last_name', $lastName); |
473
|
|
|
|
474
|
|
|
// Create a new user instance. |
475
|
|
|
$user = $userService->createUser($userCreate, array($userGroup)); |
476
|
|
|
|
477
|
|
|
return $user; |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
/** |
481
|
|
|
* Only for internal use. |
482
|
|
|
* |
483
|
|
|
* Creates a \DateTime object for $timestamp in the current time zone |
484
|
|
|
* |
485
|
|
|
* @param int $timestamp |
486
|
|
|
* |
487
|
|
|
* @return \DateTime |
488
|
|
|
*/ |
489
|
|
|
public function createDateTime($timestamp = null) |
490
|
|
|
{ |
491
|
|
|
$dateTime = new \DateTime(); |
492
|
|
|
if ($timestamp !== null) { |
493
|
|
|
$dateTime->setTimestamp($timestamp); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
return $dateTime; |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
/** |
500
|
|
|
* Calls given Repository's aggregated SearchHandler::refresh(). |
501
|
|
|
* |
502
|
|
|
* Currently implemented only in Solr search engine. |
503
|
|
|
* |
504
|
|
|
* @param \eZ\Publish\API\Repository\Repository $repository |
505
|
|
|
*/ |
506
|
|
|
protected function refreshSearch(Repository $repository) |
507
|
|
|
{ |
508
|
|
|
$setupFactory = $this->getSetupFactory(); |
509
|
|
|
|
510
|
|
|
if (!$setupFactory instanceof LegacySolrSetupFactory) { |
|
|
|
|
511
|
|
|
return; |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
while (true) { |
515
|
|
|
$repositoryReflection = new \ReflectionObject($repository); |
516
|
|
|
// If the repository is decorated, we need to recurse in the "repository" property |
517
|
|
|
if (!$repositoryReflection->hasProperty('repository')) { |
518
|
|
|
break; |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
$repositoryProperty = $repositoryReflection->getProperty('repository'); |
522
|
|
|
$repositoryProperty->setAccessible(true); |
523
|
|
|
$repository = $repositoryProperty->getValue($repository); |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
$searchHandlerProperty = new \ReflectionProperty($repository, 'searchHandler'); |
527
|
|
|
$searchHandlerProperty->setAccessible(true); |
528
|
|
|
|
529
|
|
|
/** @var \EzSystems\EzPlatformSolrSearchEngine\Handler $searchHandler */ |
530
|
|
|
$searchHandler = $searchHandlerProperty->getValue($repository); |
531
|
|
|
|
532
|
|
|
$searchHandler->commit(); |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* Create role of a given name with the given policies described by an array. |
537
|
|
|
* |
538
|
|
|
* @param $roleName |
539
|
|
|
* @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []] |
540
|
|
|
* |
541
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\Role |
542
|
|
|
* |
543
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException |
544
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException |
545
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException |
546
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException |
547
|
|
|
*/ |
548
|
|
|
public function createRoleWithPolicies($roleName, array $policiesData) |
549
|
|
|
{ |
550
|
|
|
$repository = $this->getRepository(false); |
551
|
|
|
$roleService = $repository->getRoleService(); |
552
|
|
|
|
553
|
|
|
$roleCreateStruct = $roleService->newRoleCreateStruct($roleName); |
554
|
|
|
foreach ($policiesData as $policyData) { |
555
|
|
|
$policyCreateStruct = $roleService->newPolicyCreateStruct( |
556
|
|
|
$policyData['module'], |
557
|
|
|
$policyData['function'] |
558
|
|
|
); |
559
|
|
|
|
560
|
|
|
if (isset($policyData['limitations'])) { |
561
|
|
|
foreach ($policyData['limitations'] as $limitation) { |
562
|
|
|
$policyCreateStruct->addLimitation($limitation); |
563
|
|
|
} |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
$roleCreateStruct->addPolicy($policyCreateStruct); |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
$roleDraft = $roleService->createRole($roleCreateStruct); |
570
|
|
|
|
571
|
|
|
$roleService->publishRoleDraft($roleDraft); |
572
|
|
|
|
573
|
|
|
return $roleService->loadRole($roleDraft->id); |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
/** |
577
|
|
|
* Create user and assign new role with the given policies. |
578
|
|
|
* |
579
|
|
|
* @param string $login |
580
|
|
|
* @param array $policiesData list of policies in the form of <code>[ [ 'module' => 'name', 'function' => 'name'] ]</code> |
581
|
|
|
* |
582
|
|
|
* @return \eZ\Publish\API\Repository\Values\User\User |
583
|
|
|
* |
584
|
|
|
* @throws \Exception |
585
|
|
|
*/ |
586
|
|
|
public function createUserWithPolicies($login, array $policiesData) |
587
|
|
|
{ |
588
|
|
|
$repository = $this->getRepository(false); |
589
|
|
|
$roleService = $repository->getRoleService(); |
590
|
|
|
$userService = $repository->getUserService(); |
591
|
|
|
|
592
|
|
|
$repository->beginTransaction(); |
593
|
|
|
try { |
594
|
|
|
$userCreateStruct = $userService->newUserCreateStruct( |
595
|
|
|
$login, |
596
|
|
|
"{$login}@test.local", |
597
|
|
|
$login, |
598
|
|
|
'eng-GB' |
599
|
|
|
); |
600
|
|
|
$userCreateStruct->setField('first_name', $login); |
601
|
|
|
$userCreateStruct->setField('last_name', $login); |
602
|
|
|
$user = $userService->createUser($userCreateStruct, [$userService->loadUserGroup(4)]); |
603
|
|
|
|
604
|
|
|
$role = $this->createRoleWithPolicies(uniqid('role_for_' . $login . '_', true), $policiesData); |
605
|
|
|
$roleService->assignRoleToUser($role, $user); |
606
|
|
|
|
607
|
|
|
$repository->commit(); |
608
|
|
|
|
609
|
|
|
return $user; |
610
|
|
|
} catch (\Exception $ex) { |
611
|
|
|
$repository->rollback(); |
612
|
|
|
throw $ex; |
613
|
|
|
} |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
|
|
* @return \Doctrine\DBAL\Connection |
618
|
|
|
* |
619
|
|
|
* @throws \ErrorException |
620
|
|
|
*/ |
621
|
|
|
protected function getRawDatabaseConnection() |
622
|
|
|
{ |
623
|
|
|
$connection = $this |
624
|
|
|
->getSetupFactory() |
625
|
|
|
->getServiceContainer()->get('ezpublish.api.storage_engine.legacy.connection'); |
626
|
|
|
|
627
|
|
|
if (!$connection instanceof Connection) { |
628
|
|
|
throw new \RuntimeException( |
629
|
|
|
sprintf('Expected %s got %s', Connection::class, get_class($connection)) |
630
|
|
|
); |
631
|
|
|
} |
632
|
|
|
|
633
|
|
|
return $connection; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/** |
637
|
|
|
* Executes the given callback passing raw Database Connection (\Doctrine\DBAL\Connection). |
638
|
|
|
* Returns the result returned by the given callback. |
639
|
|
|
* |
640
|
|
|
* **Note**: The method clears the entire persistence cache pool. |
641
|
|
|
* |
642
|
|
|
* @throws \Exception |
643
|
|
|
* |
644
|
|
|
* @param callable $callback |
645
|
|
|
* |
646
|
|
|
* @return mixed the return result of the given callback |
647
|
|
|
*/ |
648
|
|
|
public function performRawDatabaseOperation(callable $callback) |
649
|
|
|
{ |
650
|
|
|
$repository = $this->getRepository(false); |
651
|
|
|
$repository->beginTransaction(); |
652
|
|
|
try { |
653
|
|
|
$callback( |
654
|
|
|
$this->getRawDatabaseConnection() |
655
|
|
|
); |
656
|
|
|
$repository->commit(); |
657
|
|
|
} catch (Exception $e) { |
658
|
|
|
$repository->rollback(); |
659
|
|
|
throw $e; |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
/** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cachePool */ |
663
|
|
|
$cachePool = $this |
664
|
|
|
->getSetupFactory() |
665
|
|
|
->getServiceContainer()->get('ezpublish.cache_pool'); |
666
|
|
|
|
667
|
|
|
$cachePool->clear(); |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
/** |
671
|
|
|
* Traverse all errors for all fields in all Translations to find expected one. |
672
|
|
|
* |
673
|
|
|
* @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception |
674
|
|
|
* @param string $expectedValidationErrorMessage |
675
|
|
|
*/ |
676
|
|
|
protected function assertValidationErrorOccurs( |
677
|
|
|
ContentFieldValidationException $exception, |
678
|
|
|
$expectedValidationErrorMessage |
679
|
|
|
) { |
680
|
|
|
$constraint = new PHPUnitConstraintValidationErrorOccurs($expectedValidationErrorMessage); |
681
|
|
|
|
682
|
|
|
self::assertThat($exception, $constraint); |
683
|
|
|
} |
684
|
|
|
|
685
|
|
|
/** |
686
|
|
|
* Create 'folder' Content. |
687
|
|
|
* |
688
|
|
|
* @param array $names Folder names in the form of <code>['<language_code>' => '<name>']</code> |
689
|
|
|
* @param int $parentLocationId |
690
|
|
|
* |
691
|
|
|
* @return \eZ\Publish\API\Repository\Values\Content\Content published Content |
692
|
|
|
* |
693
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException |
694
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException |
695
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException |
696
|
|
|
*/ |
697
|
|
|
protected function createFolder(array $names, $parentLocationId) |
698
|
|
|
{ |
699
|
|
|
$repository = $this->getRepository(false); |
700
|
|
|
$contentService = $repository->getContentService(); |
701
|
|
|
$contentTypeService = $repository->getContentTypeService(); |
702
|
|
|
$locationService = $repository->getLocationService(); |
703
|
|
|
|
704
|
|
|
if (empty($names)) { |
705
|
|
|
throw new \RuntimeException(sprintf('%s expects non-empty names list', __METHOD__)); |
706
|
|
|
} |
707
|
|
|
$mainLanguageCode = array_keys($names)[0]; |
708
|
|
|
|
709
|
|
|
$struct = $contentService->newContentCreateStruct( |
710
|
|
|
$contentTypeService->loadContentTypeByIdentifier('folder'), |
711
|
|
|
$mainLanguageCode |
712
|
|
|
); |
713
|
|
|
foreach ($names as $languageCode => $translatedName) { |
714
|
|
|
$struct->setField('name', $translatedName, $languageCode); |
715
|
|
|
} |
716
|
|
|
$contentDraft = $contentService->createContent( |
717
|
|
|
$struct, |
718
|
|
|
[$locationService->newLocationCreateStruct($parentLocationId)] |
719
|
|
|
); |
720
|
|
|
|
721
|
|
|
return $contentService->publishVersion($contentDraft->versionInfo); |
722
|
|
|
} |
723
|
|
|
|
724
|
|
|
/** |
725
|
|
|
* Update 'folder' Content. |
726
|
|
|
* |
727
|
|
|
* @param \eZ\Publish\API\Repository\Values\Content\Content $content |
728
|
|
|
* @param array $names Folder names in the form of <code>['<language_code>' => '<name>']</code> |
729
|
|
|
* |
730
|
|
|
* @return \eZ\Publish\API\Repository\Values\Content\Content |
731
|
|
|
* |
732
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\BadStateException |
733
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException |
734
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException |
735
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException |
736
|
|
|
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException |
737
|
|
|
*/ |
738
|
|
|
protected function updateFolder(Content $content, array $names) |
739
|
|
|
{ |
740
|
|
|
$repository = $this->getRepository(false); |
741
|
|
|
$contentService = $repository->getContentService(); |
742
|
|
|
|
743
|
|
|
$draft = $contentService->createContentDraft($content->contentInfo); |
744
|
|
|
$struct = $contentService->newContentUpdateStruct(); |
745
|
|
|
$struct->initialLanguageCode = array_keys($names)[0]; |
|
|
|
|
746
|
|
|
|
747
|
|
|
foreach ($names as $languageCode => $translatedName) { |
748
|
|
|
$struct->setField('name', $translatedName, $languageCode); |
749
|
|
|
} |
750
|
|
|
|
751
|
|
|
return $contentService->updateContent($draft->versionInfo, $struct); |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
/** |
755
|
|
|
* Add new Language to the Repository. |
756
|
|
|
* |
757
|
|
|
* @param string $languageCode |
758
|
|
|
* @param string $name |
759
|
|
|
* @param bool $enabled |
760
|
|
|
* |
761
|
|
|
* @return \eZ\Publish\API\Repository\Values\Content\Language |
762
|
|
|
*/ |
763
|
|
|
protected function createLanguage(string $languageCode, string $name, bool $enabled = true): Language |
764
|
|
|
{ |
765
|
|
|
$repository = $this->getRepository(false); |
766
|
|
|
|
767
|
|
|
$languageService = $repository->getContentLanguageService(); |
768
|
|
|
$languageStruct = $languageService->newLanguageCreateStruct(); |
769
|
|
|
$languageStruct->name = $name; |
770
|
|
|
$languageStruct->languageCode = $languageCode; |
771
|
|
|
$languageStruct->enabled = $enabled; |
772
|
|
|
|
773
|
|
|
return $languageService->createLanguage($languageStruct); |
774
|
|
|
} |
775
|
|
|
} |
776
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.