8b4f2ba82726de9d9222e162220bb9db7f859b45
[yaffs-website] / web / core / modules / system / tests / modules / entity_test / entity_test.module
1 <?php
2
3 /**
4  * @file
5  * Test module for the entity API providing several entity types for testing.
6  */
7
8 use Drupal\Core\Access\AccessResult;
9 use Drupal\Core\Database\Query\AlterableInterface;
10 use Drupal\Core\Entity\ContentEntityInterface;
11 use Drupal\Core\Entity\EntityInterface;
12 use Drupal\Core\Entity\FieldableEntityInterface;
13 use Drupal\Core\Entity\EntityTypeInterface;
14 use Drupal\Core\Field\BaseFieldDefinition;
15 use Drupal\Core\Field\FieldDefinitionInterface;
16 use Drupal\Core\Field\FieldItemListInterface;
17 use Drupal\Core\Field\FieldStorageDefinitionInterface;
18 use Drupal\Core\Form\FormStateInterface;
19 use Drupal\Core\Session\AccountInterface;
20 use Drupal\Core\Entity\Entity\EntityFormDisplay;
21 use Drupal\Core\Url;
22
23 /**
24  * Filter that limits test entity list to revisable ones.
25  */
26 const ENTITY_TEST_TYPES_REVISABLE = 1;
27
28 /**
29  * Filter that limits test entity list to multilingual ones.
30  */
31 const ENTITY_TEST_TYPES_MULTILINGUAL = 2;
32
33 /**
34  * Filter that limits test entity list to routeable ones.
35  */
36 const ENTITY_TEST_TYPES_ROUTING = 3;
37
38 /**
39  * Returns a list of test entity types.
40  *
41  * The returned entity types are one for each available entity storage type:
42  * - The plain entity_test type supports neither revisions nor multilingual
43  *   properties.
44  * - The entity_test_mul type supports multilingual properties.
45  * - The entity_test_rev type supports revisions.
46  * - The entity_test_mulrev type supports both revisions and multilingual
47  *   properties.
48  *
49  * @param int $filter
50  *   Either ENTITY_TEST_TYPES_REVISABLE to only return revisable entity types or
51  *   ENTITY_TEST_TYPES_MULTILINGUAL to only return multilingual ones. Defaults
52  *   to NULL, which returns all.
53  *
54  * @return array
55  *   List with entity_types.
56  */
57 function entity_test_entity_types($filter = NULL) {
58   $types = [];
59   if ($filter === NULL || $filter === ENTITY_TEST_TYPES_ROUTING) {
60     $types[] = 'entity_test';
61   }
62   if ($filter != ENTITY_TEST_TYPES_REVISABLE) {
63     $types[] = 'entity_test_mul';
64     $types[] = 'entity_test_mul_langcode_key';
65     $types[] = 'entity_test_mul_changed';
66   }
67   if ($filter != ENTITY_TEST_TYPES_MULTILINGUAL) {
68     $types[] = 'entity_test_rev';
69   }
70   if ($filter === ENTITY_TEST_TYPES_ROUTING) {
71     $types[] = 'entity_test_base_field_display';
72     $types[] = 'entity_test_string_id';
73     $types[] = 'entity_test_no_id';
74   }
75   $types[] = 'entity_test_mulrev';
76   $types[] = 'entity_test_mulrev_changed';
77
78   return array_combine($types, $types);
79 }
80
81 /**
82  * Implements hook_entity_type_alter().
83  */
84 function entity_test_entity_type_alter(array &$entity_types) {
85   $state = \Drupal::state();
86
87   /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
88   foreach (entity_test_entity_types() as $entity_type) {
89     // Optionally specify a translation handler for testing translations.
90     if ($state->get('entity_test.translation')) {
91       $translation = $entity_types[$entity_type]->get('translation');
92       $translation[$entity_type] = TRUE;
93       $entity_types[$entity_type]->set('translation', $translation);
94     }
95   }
96
97   // Allow entity_test_rev tests to override the entity type definition.
98   $entity_types['entity_test_rev'] = $state->get('entity_test_rev.entity_type', $entity_types['entity_test_rev']);
99
100   // Enable the entity_test_new only when needed.
101   if (!$state->get('entity_test_new')) {
102     unset($entity_types['entity_test_new']);
103   }
104 }
105
106 /**
107  * Implements hook_entity_base_field_info().
108  */
109 function entity_test_entity_base_field_info(EntityTypeInterface $entity_type) {
110   $fields = [];
111
112   if ($entity_type->id() === 'entity_test' && \Drupal::state()->get('entity_test.internal_field')) {
113     $fields['internal_string_field'] = BaseFieldDefinition::create('string')
114       ->setLabel('Internal field')
115       ->setInternal(TRUE);
116   }
117   if ($entity_type->id() === 'entity_test_mul' && \Drupal::state()->get('entity_test.required_default_field')) {
118     $fields['required_default_field'] = BaseFieldDefinition::create('string')
119       ->setLabel('Required field with default value')
120       ->setRequired(TRUE)
121       ->setDefaultValue('this is a default value');
122   }
123   if ($entity_type->id() === 'entity_test_mul' && \Drupal::state()->get('entity_test.required_multi_default_field')) {
124     $fields['required_multi_default_field'] = BaseFieldDefinition::create('string')
125       ->setLabel('Required field with default value')
126       ->setRequired(TRUE)
127       ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
128       ->setDefaultValue([
129         ['value' => 'this is the first default field item'],
130         ['value' => 'this is the second default value'],
131         ['value' => 'you get the idea...'],
132       ]);
133   }
134   if ($entity_type->id() == 'entity_test_mulrev' && \Drupal::state()->get('entity_test.field_test_item')) {
135     $fields['field_test_item'] = BaseFieldDefinition::create('field_test')
136       ->setLabel(t('Field test'))
137       ->setDescription(t('A field test.'))
138       ->setRevisionable(TRUE)
139       ->setTranslatable(TRUE);
140   }
141   if ($entity_type->id() == 'entity_test_mulrev' && \Drupal::state()->get('entity_test.multi_column')) {
142     $fields['description'] = BaseFieldDefinition::create('shape')
143       ->setLabel(t('Some custom description'))
144       ->setTranslatable(TRUE);
145   }
146
147   return $fields;
148 }
149
150 /**
151  * Implements hook_entity_base_field_info_alter().
152  */
153 function entity_test_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
154   $state = \Drupal::state();
155   if ($entity_type->id() == 'entity_test_mulrev' && ($names = $state->get('entity_test.field_definitions.translatable'))) {
156     foreach ($names as $name => $value) {
157       $fields[$name]->setTranslatable($value);
158     }
159   }
160   if ($entity_type->id() == 'node' && $state->get('entity_test.node_remove_status_field')) {
161     unset($fields['status']);
162   }
163   if ($entity_type->id() == 'entity_test' && $state->get('entity_test.remove_name_field')) {
164     unset($fields['name']);
165   }
166   // In 8001 we are assuming that a new definition with multiple cardinality has
167   // been deployed.
168   // @todo Remove this if we end up using state definitions at runtime. See
169   //    https://www.drupal.org/node/2554235.
170   if ($entity_type->id() == 'entity_test' && $state->get('entity_test.db_updates.entity_definition_updates') == 8001) {
171     $fields['user_id']->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
172   }
173 }
174
175 /**
176  * Creates a new bundle for entity_test entities.
177  *
178  * @param string $bundle
179  *   The machine-readable name of the bundle.
180  * @param string $text
181  *   (optional) The human-readable name of the bundle. If none is provided, the
182  *   machine name will be used.
183  * @param string $entity_type
184  *   (optional) The entity type for which the bundle is created. Defaults to
185  *   'entity_test'.
186  */
187 function entity_test_create_bundle($bundle, $text = NULL, $entity_type = 'entity_test') {
188   $bundles = \Drupal::state()->get($entity_type . '.bundles') ?: [$entity_type => ['label' => 'Entity Test Bundle']];
189   $bundles += [$bundle => ['label' => $text ? $text : $bundle]];
190   \Drupal::state()->set($entity_type . '.bundles', $bundles);
191
192   \Drupal::entityManager()->onBundleCreate($bundle, $entity_type);
193 }
194
195 /**
196  * Deletes a bundle for entity_test entities.
197  *
198  * @param string $bundle
199  *   The machine-readable name of the bundle to delete.
200  * @param string $entity_type
201  *   (optional) The entity type for which the bundle is deleted. Defaults to
202  *   'entity_test'.
203  */
204 function entity_test_delete_bundle($bundle, $entity_type = 'entity_test') {
205   $bundles = \Drupal::state()->get($entity_type . '.bundles') ?: [$entity_type => ['label' => 'Entity Test Bundle']];
206   unset($bundles[$bundle]);
207   \Drupal::state()->set($entity_type . '.bundles', $bundles);
208
209   \Drupal::entityManager()->onBundleDelete($bundle, $entity_type);
210 }
211
212 /**
213  * Implements hook_entity_bundle_info().
214  */
215 function entity_test_entity_bundle_info() {
216   $bundles = [];
217   $entity_types = \Drupal::entityManager()->getDefinitions();
218   foreach ($entity_types as $entity_type_id => $entity_type) {
219     if ($entity_type->getProvider() == 'entity_test' && $entity_type_id != 'entity_test_with_bundle') {
220       $bundles[$entity_type_id] = \Drupal::state()->get($entity_type_id . '.bundles') ?: [$entity_type_id => ['label' => 'Entity Test Bundle']];
221     }
222   }
223   return $bundles;
224 }
225
226 /**
227  * Implements hook_entity_bundle_info_alter().
228  */
229 function entity_test_entity_bundle_info_alter(&$bundles) {
230   $entity_info = \Drupal::entityTypeManager()->getDefinitions();
231   $state = \Drupal::state();
232   foreach ($bundles as $entity_type_id => &$all_bundle_info) {
233     if ($entity_info[$entity_type_id]->getProvider() == 'entity_test') {
234       if ($state->get('entity_test.translation')) {
235         foreach ($all_bundle_info as $bundle_name => &$bundle_info) {
236           $bundle_info['translatable'] = TRUE;
237           if ($state->get('entity_test.untranslatable_fields.default_translation_affected')) {
238             $bundle_info['untranslatable_fields.default_translation_affected'] = TRUE;
239           }
240         }
241       }
242     }
243   }
244 }
245
246 /**
247  * Implements hook_entity_view_mode_info_alter().
248  */
249 function entity_test_entity_view_mode_info_alter(&$view_modes) {
250   $entity_info = \Drupal::entityManager()->getDefinitions();
251   foreach ($entity_info as $entity_type => $info) {
252     if ($entity_info[$entity_type]->getProvider() == 'entity_test' && !isset($view_modes[$entity_type])) {
253       $view_modes[$entity_type] = [
254         'full' => [
255           'label' => t('Full object'),
256           'status' => TRUE,
257           'cache' => TRUE,
258         ],
259         'teaser' => [
260           'label' => t('Teaser'),
261           'status' => TRUE,
262           'cache' => TRUE,
263         ],
264       ];
265     }
266   }
267 }
268
269 /**
270  * Implements hook_entity_form_mode_info_alter().
271  */
272 function entity_test_entity_form_mode_info_alter(&$form_modes) {
273   $entity_info = \Drupal::entityManager()->getDefinitions();
274   foreach ($entity_info as $entity_type => $info) {
275     if ($entity_info[$entity_type]->getProvider() == 'entity_test') {
276       $form_modes[$entity_type] = [
277         'compact' => [
278           'label' => t('Compact version'),
279           'status' => TRUE,
280         ],
281       ];
282     }
283   }
284 }
285
286 /**
287  * Implements hook_entity_extra_field_info().
288  */
289 function entity_test_entity_extra_field_info() {
290   $extra['entity_test']['bundle_with_extra_fields'] = [
291     'display' => [
292       // Note: those extra fields do not currently display anything, they are
293       // just used in \Drupal\Tests\field_ui\Kernel\EntityDisplayTest to test
294       // the behavior of entity display objects.
295       'display_extra_field' => [
296         'label' => t('Display extra field'),
297         'description' => t('An extra field on the display side.'),
298         'weight' => 5,
299         'visible' => TRUE,
300       ],
301       'display_extra_field_hidden' => [
302         'label' => t('Display extra field (hidden)'),
303         'description' => t('An extra field on the display side, hidden by default.'),
304         'visible' => FALSE,
305       ],
306     ]
307   ];
308
309   return $extra;
310 }
311
312 /**
313  * Implements hook_form_BASE_FORM_ID_alter().
314  */
315 function entity_test_form_entity_test_form_alter(&$form) {
316   switch (\Drupal::state()->get('entity_test.form.validate.test')) {
317     case 'form-level':
318       $form['#validate'][] = 'entity_test_form_entity_test_form_validate';
319       $form['#validate'][] = 'entity_test_form_entity_test_form_validate_check';
320       break;
321
322     case 'button-level':
323       $form['actions']['submit']['#validate'][] = 'entity_test_form_entity_test_form_validate';
324   }
325 }
326
327 /**
328  * Validation handler for the entity_test entity form.
329  */
330 function entity_test_form_entity_test_form_validate(array &$form, FormStateInterface $form_state) {
331   $form['#entity_test_form_validate'] = TRUE;
332 }
333
334 /**
335  * Validation handler for the entity_test entity form.
336  */
337 function entity_test_form_entity_test_form_validate_check(array &$form, FormStateInterface $form_state) {
338   if (!empty($form['#entity_test_form_validate'])) {
339     \Drupal::state()->set('entity_test.form.validate.result', TRUE);
340   }
341 }
342
343 /**
344  * Implements hook_form_BASE_FORM_ID_alter().
345  */
346 function entity_test_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
347   $langcode = $form_state->getFormObject()->getFormLangcode($form_state);
348   \Drupal::state()->set('entity_test.form_langcode', $langcode);
349 }
350
351 /**
352  * Loads a test entity.
353  *
354  * @param int $id
355  *   A test entity ID.
356  * @param bool $reset
357  *   A boolean indicating that the internal cache should be reset.
358  *
359  * @return \Drupal\entity_test\Entity\EntityTest
360  *   The loaded entity object, or NULL if the entity cannot be loaded.
361  */
362 function entity_test_load($id, $reset = FALSE) {
363   $storage = \Drupal::entityTypeManager()->getStorage('entity_test');
364   if ($reset) {
365     $storage->resetCache([$id]);
366   }
367   return $storage->load($id);
368 }
369
370 /**
371  * Loads a test entity.
372  *
373  * @param int $id
374  *   A test entity ID.
375  * @param bool $reset
376  *   A boolean indicating that the internal cache should be reset.
377  *
378  * @return \Drupal\entity_test\Entity\EntityTestRev
379  *   The loaded entity object, or NULL if the entity cannot be loaded.
380  */
381 function entity_test_rev_load($id, $reset = FALSE) {
382   $storage = \Drupal::entityTypeManager()->getStorage('entity_test_rev');
383   if ($reset) {
384     $storage->resetCache([$id]);
385   }
386   return $storage->load($id);
387 }
388
389 /**
390  * Loads a test entity.
391  *
392  * @param int $id
393  *   A test entity ID.
394  * @param bool $reset
395  *   A boolean indicating that the internal cache should be reset.
396  *
397  * @return \Drupal\entity_test\Entity\EntityTestMul
398  *   The loaded entity object, or FALSE if the entity cannot be loaded.
399  */
400 function entity_test_mul_load($id, $reset = FALSE) {
401   $storage = \Drupal::entityTypeManager()->getStorage('entity_test_mul');
402   if ($reset) {
403     $storage->resetCache([$id]);
404   }
405   return $storage->load($id);
406 }
407
408 /**
409  * Loads a test entity.
410  *
411  * @param int $id
412  *   A test entity ID.
413  * @param bool $reset
414  *   A boolean indicating that the internal cache should be reset.
415  *
416  * @return \Drupal\entity_test\Entity\EntityTestMulRev
417  *   The loaded entity object, or NULL if the entity cannot be loaded.
418  */
419 function entity_test_mulrev_load($id, $reset = FALSE) {
420   $storage = \Drupal::entityTypeManager()->getStorage('entity_test_mulrev');
421   if ($reset) {
422     $storage->resetCache([$id]);
423   }
424   return $storage->load($id);
425 }
426
427 /**
428  * Implements hook_ENTITY_TYPE_insert() for 'entity_test'.
429  */
430 function entity_test_entity_test_insert($entity) {
431   if ($entity->name->value == 'fail_insert') {
432     throw new Exception("Test exception rollback.");
433   }
434 }
435
436 /**
437  * Implements hook_entity_insert().
438  */
439 function entity_test_entity_insert(EntityInterface $entity) {
440   if ($entity->getEntityTypeId() == 'entity_test_mulrev' && $entity->label() == 'EntityLoadedRevisionTest') {
441     $entity->setNewRevision(FALSE);
442     $entity->save();
443   }
444 }
445
446 /**
447  * Implements hook_entity_update().
448  */
449 function entity_test_entity_update(EntityInterface $entity) {
450   if ($entity instanceof ContentEntityInterface) {
451     \Drupal::state()->set('entity_test.loadedRevisionId', $entity->getLoadedRevisionId());
452   }
453 }
454
455 /**
456  * Implements hook_entity_field_access().
457  *
458  * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess()
459  */
460 function entity_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
461   if ($field_definition->getName() == 'field_test_text') {
462     if ($items) {
463       if ($items->value == 'no access value') {
464         return AccessResult::forbidden()->addCacheableDependency($items->getEntity());
465       }
466       elseif ($items->value == 'custom cache tag value') {
467         return AccessResult::allowed()->addCacheableDependency($items->getEntity())->addCacheTags(['entity_test_access:field_test_text']);
468       }
469       elseif ($operation == 'edit' && $items->value == 'no edit access value') {
470         return AccessResult::forbidden()->addCacheableDependency($items->getEntity());
471       }
472     }
473   }
474   if ($field = \Drupal::state()->get('views_field_access_test-field')) {
475     if ($field_definition->getName() === $field) {
476       $result = AccessResult::allowedIfHasPermission($account, 'view test entity field');
477       // For test purposes we want to actively deny access.
478       if ($result->isNeutral()) {
479         $result = AccessResult::forbidden();
480       }
481       return $result;
482     }
483   }
484
485   // No opinion.
486   return AccessResult::neutral();
487 }
488
489 /**
490  * Implements hook_entity_field_access_alter().
491  *
492  * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess()
493  */
494 function entity_test_entity_field_access_alter(array &$grants, array $context) {
495   if ($context['field_definition']->getName() == 'field_test_text' && $context['items']->value == 'access alter value') {
496     $grants[':default'] = AccessResult::forbidden()->inheritCacheability($grants[':default'])->addCacheableDependency($context['items']->getEntity());
497   }
498 }
499
500 /**
501  * Implements hook_entity_form_display_alter().
502  */
503 function entity_test_entity_form_display_alter(EntityFormDisplay $form_display, $context) {
504   // Make the field_test_text field 42 characters for entity_test_mul.
505   if ($context['entity_type'] == 'entity_test') {
506     if ($component_options = $form_display->getComponent('field_test_text')) {
507       $component_options['settings']['size'] = 42;
508       $form_display->setComponent('field_test_text', $component_options);
509     }
510   }
511 }
512
513 /**
514  * Implements hook_entity_presave().
515  */
516 function entity_test_entity_presave(EntityInterface $entity) {
517   if (isset($GLOBALS['entity_test_throw_exception'])) {
518     throw new Exception('Entity presave exception', 1);
519   }
520
521   if ($entity->getEntityType()->id() == 'entity_view_display') {
522     $entity->setThirdPartySetting('entity_test', 'foo', 'bar');
523   }
524 }
525
526 /**
527  * Implements hook_entity_predelete().
528  */
529 function entity_test_entity_predelete(EntityInterface $entity) {
530   if (isset($GLOBALS['entity_test_throw_exception'])) {
531     throw new Exception('Entity predelete exception', 2);
532   }
533 }
534
535 /**
536  * Implements hook_entity_operation_alter().
537  */
538 function entity_test_entity_operation_alter(array &$operations, EntityInterface $entity) {
539   $valid_entity_type_ids = [
540     'user_role',
541     'block',
542   ];
543   if (in_array($entity->getEntityTypeId(), $valid_entity_type_ids)) {
544     if (\Drupal::service('router.route_provider')->getRouteByName("entity.{$entity->getEntityTypeId()}.test_operation")) {
545       $operations['test_operation'] = [
546         'title' => format_string('Test Operation: @label', ['@label' => $entity->label()]),
547         'url' => Url::fromRoute("entity.{$entity->getEntityTypeId()}.test_operation", [$entity->getEntityTypeId() => $entity->id()]),
548         'weight' => 50,
549       ];
550     }
551   }
552 }
553
554 /**
555  * Implements hook_entity_translation_create().
556  */
557 function entity_test_entity_translation_create(EntityInterface $translation) {
558   _entity_test_record_hooks('entity_translation_create', $translation->language()->getId());
559 }
560
561 /**
562  * Implements hook_entity_translation_insert().
563  */
564 function entity_test_entity_translation_insert(EntityInterface $translation) {
565   _entity_test_record_hooks('entity_translation_insert', $translation->language()->getId());
566 }
567
568 /**
569  * Implements hook_entity_translation_delete().
570  */
571 function entity_test_entity_translation_delete(EntityInterface $translation) {
572   _entity_test_record_hooks('entity_translation_delete', $translation->language()->getId());
573 }
574
575 /**
576  * Implements hook_ENTITY_TYPE_translation_create() for 'entity_test_mul'.
577  */
578 function entity_test_entity_test_mul_translation_create(EntityInterface $translation) {
579   _entity_test_record_hooks('entity_test_mul_translation_create', $translation->language()->getId());
580 }
581
582 /**
583  * Implements hook_ENTITY_TYPE_translation_insert() for 'entity_test_mul'.
584  */
585 function entity_test_entity_test_mul_translation_insert(EntityInterface $translation) {
586   _entity_test_record_hooks('entity_test_mul_translation_insert', $translation->language()->getId());
587 }
588
589 /**
590  * Implements hook_ENTITY_TYPE_translation_delete() for 'entity_test_mul'.
591  */
592 function entity_test_entity_test_mul_translation_delete(EntityInterface $translation) {
593   _entity_test_record_hooks('entity_test_mul_translation_delete', $translation->language()->getId());
594 }
595
596 /**
597  * Implements hook_ENTITY_TYPE_translation_create() for 'entity_test_mul_changed'.
598  */
599 function entity_test_entity_test_mul_changed_translation_create(EntityInterface $translation) {
600   _entity_test_record_hooks('entity_test_mul_changed_translation_create', $translation->language()->getId());
601 }
602
603 /**
604  * Implements hook_ENTITY_TYPE_translation_insert() for 'entity_test_mul_changed'.
605  */
606 function entity_test_entity_test_mul_changed_translation_insert(EntityInterface $translation) {
607   _entity_test_record_hooks('entity_test_mul_changed_translation_insert', $translation->language()->getId());
608 }
609
610 /**
611  * Implements hook_ENTITY_TYPE_translation_delete().
612  */
613 function entity_test_entity_test_mul_changed_translation_delete(EntityInterface $translation) {
614   _entity_test_record_hooks('entity_test_mul_changed_translation_delete', $translation->language()->getId());
615 }
616
617 /**
618  * Implements hook_ENTITY_TYPE_translation_create().
619  */
620 function entity_test_entity_test_mulrev_translation_create(EntityInterface $translation) {
621   _entity_test_record_hooks('entity_test_mulrev_translation_create', $translation->language()->getId());
622 }
623
624 /**
625  * Implements hook_ENTITY_TYPE_translation_insert().
626  */
627 function entity_test_entity_test_mulrev_translation_insert(EntityInterface $translation) {
628   _entity_test_record_hooks('entity_test_mulrev_translation_insert', $translation->language()->getId());
629 }
630
631 /**
632  * Implements hook_ENTITY_TYPE_translation_delete() for 'entity_test_mulrev'.
633  */
634 function entity_test_entity_test_mulrev_translation_delete(EntityInterface $translation) {
635   _entity_test_record_hooks('entity_test_mulrev_translation_delete', $translation->language()->getId());
636 }
637
638 /**
639  * Implements hook_ENTITY_TYPE_translation_create() for 'entity_test_mulrev_changed'.
640  */
641 function entity_test_entity_test_mulrev_changed_translation_create(EntityInterface $translation) {
642   _entity_test_record_hooks('entity_test_mulrev_changed_translation_create', $translation->language()->getId());
643 }
644
645 /**
646  * Implements hook_ENTITY_TYPE_translation_insert() for 'entity_test_mulrev'.
647  */
648 function entity_test_entity_test_mulrev_changed_translation_insert(EntityInterface $translation) {
649   _entity_test_record_hooks('entity_test_mulrev_changed_translation_insert', $translation->language()->getId());
650 }
651
652 /**
653  * Implements hook_ENTITY_TYPE_translation_delete().
654  */
655 function entity_test_entity_test_mulrev_changed_translation_delete(EntityInterface $translation) {
656   _entity_test_record_hooks('entity_test_mulrev_changed_translation_delete', $translation->language()->getId());
657 }
658
659 /**
660  * Implements hook_ENTITY_TYPE_translation_create() for 'entity_test_mul_langcode_key'.
661  */
662 function entity_test_entity_test_mul_langcode_key_translation_create(EntityInterface $translation) {
663   _entity_test_record_hooks('entity_test_mul_langcode_key_translation_create', $translation->language()->getId());
664 }
665
666 /**
667  * Implements hook_ENTITY_TYPE_translation_insert() for 'entity_test_mul_langcode_key'.
668  */
669 function entity_test_entity_test_mul_langcode_key_translation_insert(EntityInterface $translation) {
670   _entity_test_record_hooks('entity_test_mul_langcode_key_translation_insert', $translation->language()->getId());
671 }
672
673 /**
674  * Implements hook_ENTITY_TYPE_translation_delete() for 'entity_test_mul_langcode_key'.
675  */
676 function entity_test_entity_test_mul_langcode_key_translation_delete(EntityInterface $translation) {
677   _entity_test_record_hooks('entity_test_mul_langcode_key_translation_delete', $translation->language()->getId());
678 }
679
680 /**
681  * Field default value callback.
682  *
683  * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
684  *   The entity being created.
685  * @param \Drupal\Core\Field\FieldDefinitionInterface $definition
686  *   The field definition.
687  *
688  * @return array
689  *   An array of default values, in the same format as the $default_value
690  *   property.
691  *
692  * @see \Drupal\field\Entity\FieldConfig::$default_value
693  */
694 function entity_test_field_default_value(FieldableEntityInterface $entity, FieldDefinitionInterface $definition) {
695   // Include the field name and entity language in the generated values to check
696   // that they are correctly passed.
697   $string = $definition->getName() . '_' . $entity->language()->getId();
698   // Return a "default value" with multiple items.
699   return [
700     [
701       'shape' => "shape:0:$string",
702       'color' => "color:0:$string",
703     ],
704     [
705       'shape' => "shape:1:$string",
706       'color' => "color:1:$string",
707     ],
708   ];
709 }
710
711 /**
712  * Helper function to be used to record hook invocations.
713  *
714  * @param string $hook
715  *   The hook name.
716  * @param mixed $data
717  *   Arbitrary data associated with the hook invocation.
718  */
719 function _entity_test_record_hooks($hook, $data) {
720   $state = \Drupal::state();
721   $key = 'entity_test.hooks';
722   $hooks = $state->get($key);
723   $hooks[$hook] = $data;
724   $state->set($key, $hooks);
725 }
726
727 /**
728  * Implements hook_entity_prepare_view().
729  */
730 function entity_test_entity_prepare_view($entity_type, array $entities, array $displays) {
731   if ($entity_type == 'entity_test') {
732     foreach ($entities as $entity) {
733       /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
734
735       // Add a dummy field item attribute on field_test_text if it exists.
736       if ($entity->hasField('field_test_text') && $displays[$entity->bundle()]->getComponent('field_test_text')) {
737         foreach ($entity->get('field_test_text') as $item) {
738           $item->_attributes += ['data-field-item-attr' => 'foobar'];
739         }
740       }
741
742       // Add a dummy field item attribute on daterange fields if they exist.
743       $fields = $entity->getFieldDefinitions();
744       foreach ($fields as $field) {
745         if ($field->getType() === 'daterange') {
746           $item = $entity->get($field->getName());
747           $item->_attributes += ['data-field-item-attr' => 'foobar'];
748         }
749       }
750     }
751   }
752 }
753
754 /**
755  * Implements hook_entity_display_build_alter().
756  */
757 function entity_test_entity_display_build_alter(&$build, $context) {
758   /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
759   $entity = $context['entity'];
760   if ($entity->getEntityTypeId() == 'entity_test' && $entity->bundle() == 'display_build_alter_bundle') {
761     $build['entity_display_build_alter']['#markup'] = 'Content added in hook_entity_display_build_alter for entity id ' . $entity->id();
762   }
763 }
764
765 /**
766  * Implements hook_entity_access().
767  */
768 function entity_test_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
769   // Only apply to the 'entity_test' entities.
770   if ($entity->getEntityType()->getProvider() != 'entity_test') {
771     return AccessResult::neutral();
772   }
773   \Drupal::state()->set('entity_test_entity_access', TRUE);
774
775   // Attempt to allow access to entities with the title forbid_access,
776   // this will be overridden by
777   // \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess().
778   if ($entity->label() == 'forbid_access') {
779     return AccessResult::allowed();
780   }
781
782   // Create specific labels to allow or deny access based on certain test
783   // conditions.
784   // @see \Drupal\KernelTests\Core\Entity\EntityAccessControlHandlerTest
785   if ($entity->label() == 'Accessible') {
786     return AccessResult::allowed();
787   }
788   if ($entity->label() == 'Inaccessible') {
789     return AccessResult::forbidden();
790   }
791
792   // Uncacheable because the access result depends on a State key-value pair and
793   // might therefore change at any time.
794   $condition = \Drupal::state()->get("entity_test_entity_access.{$operation}." . $entity->id(), FALSE);
795   return AccessResult::allowedIf($condition)->setCacheMaxAge(0);
796 }
797
798 /**
799  * Implements hook_ENTITY_TYPE_access() for 'entity_test'.
800  */
801 function entity_test_entity_test_access(EntityInterface $entity, $operation, AccountInterface $account) {
802   \Drupal::state()->set('entity_test_entity_test_access', TRUE);
803
804   // No opinion.
805   return AccessResult::neutral();
806 }
807
808 /**
809  * Implements hook_entity_create_access().
810  */
811 function entity_test_entity_create_access(AccountInterface $account, $context, $entity_bundle) {
812   \Drupal::state()->set('entity_test_entity_create_access', TRUE);
813   \Drupal::state()->set('entity_test_entity_create_access_context', $context);
814
815   // No opinion.
816   return AccessResult::neutral();
817 }
818
819 /**
820  * Implements hook_ENTITY_TYPE_create_access() for 'entity_test'.
821  */
822 function entity_test_entity_test_create_access(AccountInterface $account, $context, $entity_bundle) {
823   \Drupal::state()->set('entity_test_entity_test_create_access', TRUE);
824
825   // No opinion.
826   return AccessResult::neutral();
827 }
828
829 /**
830  * Implements hook_query_entity_test_access_alter().
831  */
832 function entity_test_query_entity_test_access_alter(AlterableInterface $query) {
833   if (!\Drupal::state()->get('entity_test_query_access')) {
834     return;
835   }
836
837   /** @var \Drupal\Core\Database\Query\Select|\Drupal\Core\Database\Query\AlterableInterface $query */
838   if (!\Drupal::currentUser()->hasPermission('view all entity_test_query_access entities')) {
839     $query->condition('entity_test_query_access.name', 'published entity');
840   }
841 }