3 namespace Drupal\Tests\Core\Config\Entity;
5 use Drupal\Component\Uuid\UuidInterface;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
8 use Drupal\Core\Cache\MemoryCache\MemoryCache;
9 use Drupal\Core\Config\Config;
10 use Drupal\Core\Config\ConfigDuplicateUUIDException;
11 use Drupal\Core\Config\ConfigFactoryInterface;
12 use Drupal\Core\Config\ConfigManagerInterface;
13 use Drupal\Core\Config\Entity\ConfigEntityBase;
14 use Drupal\Core\Config\Entity\ConfigEntityInterface;
15 use Drupal\Core\Config\Entity\ConfigEntityStorage;
16 use Drupal\Core\Config\Entity\ConfigEntityType;
17 use Drupal\Core\Config\ImmutableConfig;
18 use Drupal\Core\Config\TypedConfigManagerInterface;
19 use Drupal\Core\Entity\EntityInterface;
20 use Drupal\Core\Entity\EntityMalformedException;
21 use Drupal\Core\Entity\EntityStorageException;
22 use Drupal\Core\Entity\EntityTypeManagerInterface;
23 use Drupal\Core\Entity\Query\QueryFactoryInterface;
24 use Drupal\Core\Entity\Query\QueryInterface;
25 use Drupal\Core\Extension\ModuleHandlerInterface;
26 use Drupal\Core\Language\Language;
27 use Drupal\Core\Language\LanguageManagerInterface;
28 use Drupal\Tests\UnitTestCase;
29 use Prophecy\Argument;
30 use Symfony\Component\DependencyInjection\ContainerBuilder;
33 * @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityStorage
36 class ConfigEntityStorageTest extends UnitTestCase {
39 * The type ID of the entity under test.
43 protected $entityTypeId;
48 * @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ProphecyInterface
50 protected $moduleHandler;
55 * @var \Drupal\Component\Uuid\UuidInterface|\Prophecy\Prophecy\ProphecyInterface
57 protected $uuidService;
60 * The language manager.
62 * @var \Drupal\Core\Language\LanguageManagerInterface|\Prophecy\Prophecy\ProphecyInterface
64 protected $languageManager;
69 * @var \Drupal\Core\Config\Entity\ConfigEntityStorage
71 protected $entityStorage;
74 * The config factory service.
76 * @var \Drupal\Core\Config\ConfigFactoryInterface|\Prophecy\Prophecy\ProphecyInterface
78 protected $configFactory;
83 * @var \Drupal\Core\Entity\Query\QueryInterface|\Prophecy\Prophecy\ProphecyInterface
85 protected $entityQuery;
88 * The mocked cache backend.
90 * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\Prophecy\Prophecy\ProphecyInterface
92 protected $cacheTagsInvalidator;
95 * The configuration manager.
97 * @var \Drupal\Core\Config\ConfigManagerInterface|\Prophecy\Prophecy\ProphecyInterface
99 protected $configManager;
104 * @covers ::__construct
106 protected function setUp() {
109 $this->entityTypeId = 'test_entity_type';
111 $entity_type = new ConfigEntityType([
112 'id' => $this->entityTypeId,
113 'class' => get_class($this->getMockEntity()),
114 'provider' => 'the_provider',
115 'config_prefix' => 'the_config_prefix',
119 'langcode' => 'langcode',
121 'list_cache_tags' => [$this->entityTypeId . '_list'],
124 $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
126 $this->uuidService = $this->prophesize(UuidInterface::class);
128 $this->languageManager = $this->prophesize(LanguageManagerInterface::class);
129 $this->languageManager->getCurrentLanguage()->willReturn(new Language(['id' => 'hu']));
131 $this->configFactory = $this->prophesize(ConfigFactoryInterface::class);
133 $this->entityQuery = $this->prophesize(QueryInterface::class);
134 $entity_query_factory = $this->prophesize(QueryFactoryInterface::class);
135 $entity_query_factory->get($entity_type, 'AND')->willReturn($this->entityQuery->reveal());
137 $this->entityStorage = new ConfigEntityStorage($entity_type, $this->configFactory->reveal(), $this->uuidService->reveal(), $this->languageManager->reveal(), new MemoryCache());
138 $this->entityStorage->setModuleHandler($this->moduleHandler->reveal());
140 $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
141 $entity_type_manager->getDefinition('test_entity_type')->willReturn($entity_type);
143 $this->cacheTagsInvalidator = $this->prophesize(CacheTagsInvalidatorInterface::class);
145 $typed_config_manager = $this->prophesize(TypedConfigManagerInterface::class);
146 $typed_config_manager
147 ->getDefinition(Argument::containingString('the_provider.the_config_prefix.'))
148 ->willReturn(['mapping' => ['id' => '', 'uuid' => '', 'dependencies' => '']]);
150 $this->configManager = $this->prophesize(ConfigManagerInterface::class);
152 $container = new ContainerBuilder();
153 $container->set('entity_type.manager', $entity_type_manager->reveal());
154 $container->set('entity.query.config', $entity_query_factory->reveal());
155 $container->set('config.typed', $typed_config_manager->reveal());
156 $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator->reveal());
157 $container->set('config.manager', $this->configManager->reveal());
158 $container->set('language_manager', $this->languageManager->reveal());
159 \Drupal::setContainer($container);
167 public function testCreateWithPredefinedUuid() {
168 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
170 $entity = $this->getMockEntity();
171 $entity->set('id', 'foo');
172 $entity->set('langcode', 'hu');
173 $entity->set('uuid', 'baz');
174 $entity->setOriginalId('foo');
175 $entity->enforceIsNew();
177 $this->moduleHandler->invokeAll('test_entity_type_create', [$entity])
179 $this->moduleHandler->invokeAll('entity_create', [$entity, 'test_entity_type'])
182 $this->uuidService->generate()->shouldNotBeCalled();
184 $entity = $this->entityStorage->create(['id' => 'foo', 'uuid' => 'baz']);
185 $this->assertInstanceOf(EntityInterface::class, $entity);
186 $this->assertSame('foo', $entity->id());
187 $this->assertSame('baz', $entity->uuid());
194 * @return \Drupal\Core\Entity\EntityInterface
196 public function testCreate() {
197 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
199 $entity = $this->getMockEntity();
200 $entity->set('id', 'foo');
201 $entity->set('langcode', 'hu');
202 $entity->set('uuid', 'bar');
203 $entity->setOriginalId('foo');
204 $entity->enforceIsNew();
206 $this->moduleHandler->invokeAll('test_entity_type_create', [$entity])
208 $this->moduleHandler->invokeAll('entity_create', [$entity, 'test_entity_type'])
211 $this->uuidService->generate()->willReturn('bar');
213 $entity = $this->entityStorage->create(['id' => 'foo']);
214 $this->assertInstanceOf(EntityInterface::class, $entity);
215 $this->assertSame('foo', $entity->id());
216 $this->assertSame('bar', $entity->uuid());
224 public function testCreateWithCurrentLanguage() {
225 $this->languageManager->getLanguage('hu')->willReturn(new Language(['id' => 'hu']));
227 $entity = $this->entityStorage->create(['id' => 'foo']);
228 $this->assertSame('hu', $entity->language()->getId());
235 public function testCreateWithExplicitLanguage() {
236 $this->languageManager->getLanguage('en')->willReturn(new Language(['id' => 'en']));
238 $entity = $this->entityStorage->create(['id' => 'foo', 'langcode' => 'en']);
239 $this->assertSame('en', $entity->language()->getId());
246 * @param \Drupal\Core\Entity\EntityInterface $entity
248 * @return \Drupal\Core\Entity\EntityInterface
250 * @depends testCreate
252 public function testSaveInsert(EntityInterface $entity) {
253 $immutable_config_object = $this->prophesize(ImmutableConfig::class);
254 $immutable_config_object->isNew()->willReturn(TRUE);
256 $config_object = $this->prophesize(Config::class);
257 $config_object->setData(['id' => 'foo', 'uuid' => 'bar', 'dependencies' => []])
259 $config_object->save(FALSE)->shouldBeCalled();
260 $config_object->get()->willReturn([]);
262 $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
265 $this->configFactory->get('the_provider.the_config_prefix.foo')
266 ->willReturn($immutable_config_object->reveal());
267 $this->configFactory->getEditable('the_provider.the_config_prefix.foo')
268 ->willReturn($config_object->reveal());
270 $this->moduleHandler->invokeAll('test_entity_type_presave', [$entity])
272 $this->moduleHandler->invokeAll('entity_presave', [$entity, 'test_entity_type'])
274 $this->moduleHandler->invokeAll('test_entity_type_insert', [$entity])
276 $this->moduleHandler->invokeAll('entity_insert', [$entity, 'test_entity_type'])
279 $this->entityQuery->condition('uuid', 'bar')->willReturn($this->entityQuery);
280 $this->entityQuery->execute()->willReturn([]);
282 $return = $this->entityStorage->save($entity);
283 $this->assertSame(SAVED_NEW, $return);
291 * @param \Drupal\Core\Entity\EntityInterface $entity
293 * @return \Drupal\Core\Entity\EntityInterface
295 * @depends testSaveInsert
297 public function testSaveUpdate(EntityInterface $entity) {
298 $immutable_config_object = $this->prophesize(ImmutableConfig::class);
299 $immutable_config_object->isNew()->willReturn(FALSE);
301 $config_object = $this->prophesize(Config::class);
302 $config_object->setData(['id' => 'foo', 'uuid' => 'bar', 'dependencies' => []])
304 $config_object->save(FALSE)->shouldBeCalled();
305 $config_object->get()->willReturn([]);
307 $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
310 $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
312 ->shouldBeCalledTimes(2);
314 ->get('the_provider.the_config_prefix.foo')
315 ->willReturn($immutable_config_object->reveal())
316 ->shouldBeCalledTimes(1);
318 ->getEditable('the_provider.the_config_prefix.foo')
319 ->willReturn($config_object->reveal())
320 ->shouldBeCalledTimes(1);
322 $this->moduleHandler->invokeAll('test_entity_type_presave', [$entity])
324 $this->moduleHandler->invokeAll('entity_presave', [$entity, 'test_entity_type'])
326 $this->moduleHandler->invokeAll('test_entity_type_update', [$entity])
328 $this->moduleHandler->invokeAll('entity_update', [$entity, 'test_entity_type'])
331 $this->entityQuery->condition('uuid', 'bar')->willReturn($this->entityQuery);
332 $this->entityQuery->execute()->willReturn([$entity->id()]);
334 $return = $this->entityStorage->save($entity);
335 $this->assertSame(SAVED_UPDATED, $return);
343 * @depends testSaveInsert
345 public function testSaveRename(ConfigEntityInterface $entity) {
346 $immutable_config_object = $this->prophesize(ImmutableConfig::class);
347 $immutable_config_object->isNew()->willReturn(FALSE);
349 $config_object = $this->prophesize(Config::class);
350 $config_object->setData(['id' => 'bar', 'uuid' => 'bar', 'dependencies' => []])
352 $config_object->save(FALSE)
354 $config_object->get()->willReturn([]);
356 $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
359 $this->configFactory->get('the_provider.the_config_prefix.foo')
360 ->willReturn($immutable_config_object->reveal());
361 $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
363 $this->configFactory->rename('the_provider.the_config_prefix.foo', 'the_provider.the_config_prefix.bar')
365 $this->configFactory->getEditable('the_provider.the_config_prefix.bar')
366 ->willReturn($config_object->reveal());
368 // Performing a rename does not change the original ID until saving.
369 $this->assertSame('foo', $entity->getOriginalId());
370 $entity->set('id', 'bar');
371 $this->assertSame('foo', $entity->getOriginalId());
373 $this->entityQuery->condition('uuid', 'bar')->willReturn($this->entityQuery);
374 $this->entityQuery->execute()->willReturn([$entity->id()]);
376 $return = $this->entityStorage->save($entity);
377 $this->assertSame(SAVED_UPDATED, $return);
378 $this->assertSame('bar', $entity->getOriginalId());
384 public function testSaveInvalid() {
385 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
386 ->shouldNotBeCalled();
388 $entity = $this->getMockEntity();
389 $this->setExpectedException(EntityMalformedException::class, 'The entity does not have an ID.');
390 $this->entityStorage->save($entity);
397 public function testSaveDuplicate() {
398 $config_object = $this->prophesize(ImmutableConfig::class);
399 $config_object->isNew()->willReturn(FALSE);
401 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
402 ->shouldNotBeCalled();
404 $this->configFactory->get('the_provider.the_config_prefix.foo')
405 ->willReturn($config_object->reveal());
407 $entity = $this->getMockEntity(['id' => 'foo']);
408 $entity->enforceIsNew();
410 $this->setExpectedException(EntityStorageException::class);
411 $this->entityStorage->save($entity);
418 public function testSaveMismatch() {
419 $config_object = $this->prophesize(ImmutableConfig::class);
420 $config_object->isNew()->willReturn(TRUE);
422 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
423 ->shouldNotBeCalled();
425 $this->configFactory->get('the_provider.the_config_prefix.foo')
426 ->willReturn($config_object->reveal());
428 $this->entityQuery->condition('uuid', NULL)->willReturn($this->entityQuery);
429 $this->entityQuery->execute()->willReturn(['baz']);
431 $entity = $this->getMockEntity(['id' => 'foo']);
432 $this->setExpectedException(ConfigDuplicateUUIDException::class, 'when this UUID is already used for');
433 $this->entityStorage->save($entity);
440 public function testSaveNoMismatch() {
441 $immutable_config_object = $this->prophesize(ImmutableConfig::class);
442 $immutable_config_object->isNew()->willReturn(TRUE);
444 $config_object = $this->prophesize(Config::class);
445 $config_object->get()->willReturn([]);
446 $config_object->setData(['id' => 'foo', 'uuid' => NULL, 'dependencies' => []])
448 $config_object->save(FALSE)->shouldBeCalled();
450 $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
453 $this->configFactory->get('the_provider.the_config_prefix.baz')
454 ->willReturn($immutable_config_object->reveal())
456 $this->configFactory->rename('the_provider.the_config_prefix.baz', 'the_provider.the_config_prefix.foo')
458 $this->configFactory->getEditable('the_provider.the_config_prefix.foo')
459 ->willReturn($config_object->reveal())
462 $this->entityQuery->condition('uuid', NULL)->willReturn($this->entityQuery);
463 $this->entityQuery->execute()->willReturn(['baz']);
465 $entity = $this->getMockEntity(['id' => 'foo']);
466 $entity->setOriginalId('baz');
467 $entity->enforceIsNew();
468 $this->entityStorage->save($entity);
475 public function testSaveChangedUuid() {
476 $config_object = $this->prophesize(ImmutableConfig::class);
477 $config_object->get()->willReturn(['id' => 'foo']);
478 $config_object->get('id')->willReturn('foo');
479 $config_object->isNew()->willReturn(FALSE);
480 $config_object->getName()->willReturn('foo');
481 $config_object->getCacheContexts()->willReturn([]);
482 $config_object->getCacheTags()->willReturn(['config:foo']);
483 $config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
485 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
486 ->shouldNotBeCalled();
488 $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
489 ->willReturn([$config_object->reveal()]);
490 $this->configFactory->get('the_provider.the_config_prefix.foo')
491 ->willReturn($config_object->reveal());
492 $this->configFactory->rename(Argument::cetera())->shouldNotBeCalled();
494 $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
495 $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
497 $this->entityQuery->condition('uuid', 'baz')->willReturn($this->entityQuery);
498 $this->entityQuery->execute()->willReturn(['foo']);
500 $entity = $this->getMockEntity(['id' => 'foo']);
502 $entity->set('uuid', 'baz');
503 $this->setExpectedException(ConfigDuplicateUUIDException::class, 'when this entity already exists with UUID');
504 $this->entityStorage->save($entity);
510 * @covers ::mapFromStorageRecords
511 * @covers ::doLoadMultiple
513 public function testLoad() {
514 $config_object = $this->prophesize(ImmutableConfig::class);
515 $config_object->get()->willReturn(['id' => 'foo']);
516 $config_object->get('id')->willReturn('foo');
517 $config_object->getCacheContexts()->willReturn([]);
518 $config_object->getCacheTags()->willReturn(['config:foo']);
519 $config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
520 $config_object->getName()->willReturn('foo');
522 $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
523 ->willReturn([$config_object->reveal()]);
525 $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
526 $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
528 $entity = $this->entityStorage->load('foo');
529 $this->assertInstanceOf(EntityInterface::class, $entity);
530 $this->assertSame('foo', $entity->id());
534 * @covers ::loadMultiple
536 * @covers ::mapFromStorageRecords
537 * @covers ::doLoadMultiple
539 public function testLoadMultipleAll() {
540 $foo_config_object = $this->prophesize(ImmutableConfig::class);
541 $foo_config_object->get()->willReturn(['id' => 'foo']);
542 $foo_config_object->get('id')->willReturn('foo');
543 $foo_config_object->getCacheContexts()->willReturn([]);
544 $foo_config_object->getCacheTags()->willReturn(['config:foo']);
545 $foo_config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
546 $foo_config_object->getName()->willReturn('foo');
548 $bar_config_object = $this->prophesize(ImmutableConfig::class);
549 $bar_config_object->get()->willReturn(['id' => 'bar']);
550 $bar_config_object->get('id')->willReturn('bar');
551 $bar_config_object->getCacheContexts()->willReturn([]);
552 $bar_config_object->getCacheTags()->willReturn(['config:bar']);
553 $bar_config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
554 $bar_config_object->getName()->willReturn('foo');
556 $this->configFactory->listAll('the_provider.the_config_prefix.')
557 ->willReturn(['the_provider.the_config_prefix.foo', 'the_provider.the_config_prefix.bar']);
558 $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo', 'the_provider.the_config_prefix.bar'])
559 ->willReturn([$foo_config_object->reveal(), $bar_config_object->reveal()]);
561 $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
562 $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
564 $entities = $this->entityStorage->loadMultiple();
565 $expected['foo'] = 'foo';
566 $expected['bar'] = 'bar';
567 $this->assertContainsOnlyInstancesOf(EntityInterface::class, $entities);
568 foreach ($entities as $id => $entity) {
569 $this->assertSame($id, $entity->id());
570 $this->assertSame($expected[$id], $entity->id());
575 * @covers ::loadMultiple
577 * @covers ::mapFromStorageRecords
578 * @covers ::doLoadMultiple
580 public function testLoadMultipleIds() {
581 $config_object = $this->prophesize(ImmutableConfig::class);
582 $config_object->get()->willReturn(['id' => 'foo']);
583 $config_object->get('id')->willReturn('foo');
584 $config_object->getCacheContexts()->willReturn([]);
585 $config_object->getCacheTags()->willReturn(['config:foo']);
586 $config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
587 $config_object->getName()->willReturn('foo');
589 $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
590 ->willReturn([$config_object->reveal()]);
592 $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
593 $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
595 $entities = $this->entityStorage->loadMultiple(['foo']);
596 $this->assertContainsOnlyInstancesOf(EntityInterface::class, $entities);
597 foreach ($entities as $id => $entity) {
598 $this->assertSame($id, $entity->id());
603 * @covers ::loadRevision
605 public function testLoadRevision() {
606 $this->assertSame(NULL, $this->entityStorage->loadRevision(1));
610 * @covers ::deleteRevision
612 public function testDeleteRevision() {
613 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
614 ->shouldNotBeCalled();
616 $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
623 public function testDelete() {
624 // Dependencies are tested in
625 // \Drupal\Tests\config\Kernel\ConfigDependencyTest.
627 ->getConfigEntitiesToChangeOnDependencyRemoval('config', ['the_provider.the_config_prefix.foo'], FALSE)
628 ->willReturn(['update' => [], 'delete' => [], 'unchanged' => []]);
630 ->getConfigEntitiesToChangeOnDependencyRemoval('config', ['the_provider.the_config_prefix.bar'], FALSE)
631 ->willReturn(['update' => [], 'delete' => [], 'unchanged' => []]);
634 foreach (['foo', 'bar'] as $id) {
635 $entity = $this->getMockEntity(['id' => $id]);
636 $entities[] = $entity;
638 $config_object = $this->prophesize(Config::class);
639 $config_object->delete()->shouldBeCalled();
641 $this->configFactory->getEditable("the_provider.the_config_prefix.$id")
642 ->willReturn($config_object->reveal());
644 $this->moduleHandler->invokeAll('test_entity_type_predelete', [$entity])
646 $this->moduleHandler->invokeAll('entity_predelete', [$entity, 'test_entity_type'])
649 $this->moduleHandler->invokeAll('test_entity_type_delete', [$entity])
651 $this->moduleHandler->invokeAll('entity_delete', [$entity, 'test_entity_type'])
655 $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
658 $this->entityStorage->delete($entities);
665 public function testDeleteNothing() {
666 $this->moduleHandler->getImplementations(Argument::cetera())->shouldNotBeCalled();
667 $this->moduleHandler->invokeAll(Argument::cetera())->shouldNotBeCalled();
669 $this->configFactory->get(Argument::cetera())->shouldNotBeCalled();
670 $this->configFactory->getEditable(Argument::cetera())->shouldNotBeCalled();
672 $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
674 $this->entityStorage->delete([]);
678 * Creates an entity with specific methods mocked.
680 * @param array $values
681 * (optional) Values to pass to the constructor.
682 * @param array $methods
683 * (optional) The methods to mock.
685 * @return \Drupal\Core\Entity\EntityInterface|\PHPUnit_Framework_MockObject_MockObject
687 public function getMockEntity(array $values = [], $methods = []) {
688 return $this->getMockForAbstractClass(ConfigEntityBase::class, [$values, 'test_entity_type'], '', TRUE, TRUE, TRUE, $methods);
693 namespace Drupal\Core\Config\Entity;
695 if (!defined('SAVED_NEW')) {
696 define('SAVED_NEW', 1);
698 if (!defined('SAVED_UPDATED')) {
699 define('SAVED_UPDATED', 2);