Added Entity and Entity Reference Revisions which got dropped somewhere along the...
[yaffs-website] / web / modules / contrib / entity_reference_revisions / entity_reference_revisions.module
1 <?php
2
3 /**
4  * @file
5  * Provides a field that can reference other entities.
6  */
7
8 use Drupal\Component\Utility\NestedArray;
9 use Drupal\Core\Entity\ContentEntityInterface;
10 use Drupal\Core\Entity\TranslatableRevisionableStorageInterface;
11 use Drupal\Core\Form\FormStateInterface;
12 use Drupal\Core\Render\Element;
13 use Drupal\Core\Routing\RouteMatchInterface;
14 use Drupal\field\Entity\FieldStorageConfig;
15 use Drupal\field\Entity\FieldConfig;
16 use Drupal\field\FieldStorageConfigInterface;
17 use Drupal\Core\Url;
18
19 /**
20  * Implements hook_help().
21  */
22 function entity_reference_revisions_help($route_name, RouteMatchInterface $route_match) {
23   switch ($route_name) {
24     case 'help.page.entity_reference_revisions':
25       $output = '';
26       $output .= '<h3>' . t('About') . '</h3>';
27       $output .= '<p>' . t('The Entity Reference Revisions module allows you to create fields that contain links to other entities (such as content items, taxonomy terms, etc.) within the site. This allows you, for example, to include a link to a user within a content item. For more information, see <a href=":er_do">the online documentation for the Entity Reference Revisions module</a> and the <a href=":field_help">Field module help page</a>.', [':field_help' => Url::fromRoute('help.page', ['name' => 'field'])->toString(), ':er_do' => 'https://drupal.org/documentation/modules/entity_reference_revisions']) . '</p>';
28       $output .= '<h3>' . t('Uses') . '</h3>';
29       $output .= '<dl>';
30       $output .= '<dt>' . t('Managing and displaying entity reference fields') . '</dt>';
31       $output .= '<dd>' . t('The <em>settings</em> and the <em>display</em> of the entity reference field can be configured separately. See the <a href=":field_ui">Field UI help</a> for more information on how to manage fields and their display.', [':field_ui' => Url::fromRoute('help.page', ['name' => 'field_ui'])->toString()]) . '</dd>';
32       $output .= '<dt>' . t('Selecting reference type') . '</dt>';
33       $output .= '<dd>' . t('In the field settings you can select which entity type you want to create a reference to.') . '</dd>';
34       $output .= '<dt>' . t('Filtering and sorting reference fields') . '</dt>';
35       $output .= '<dd>' . t('Depending on the chosen entity type, additional filtering and sorting options are available for the list of entities that can be referred to, in the field settings. For example, the list of users can be filtered by role and sorted by name or ID.') . '</dd>';
36       $output .= '<dt>' . t('Displaying a reference') . '</dt>';
37       $output .= '<dd>' . t('An entity reference can be displayed as a simple label with or without a link to the entity. Alternatively, the referenced entity can be displayed as a teaser (or any other available view mode) inside the referencing entity.') . '</dd>';
38       $output .= '</dl>';
39       return $output;
40   }
41 }
42
43 /**
44  * Implements hook_field_widget_info_alter().
45  */
46 function entity_reference_revisions_field_widget_info_alter(&$info) {
47   if (isset($info['options_select'])) {
48     $info['options_select']['field_types'][] = 'entity_reference_revisions';
49   }
50   if (isset($info['options_buttons'])) {
51     $info['options_buttons']['field_types'][] = 'entity_reference_revisions';
52   }
53 }
54
55 /**
56  * Implements hook_ENTITY_TYPE_update() for 'field_storage_config'.
57  *
58  * Reset the instance handler settings, when the target type is changed.
59  */
60 function entity_reference_revisions_field_storage_config_update(FieldStorageConfigInterface $field_storage) {
61   if ($field_storage->getType() != 'entity_reference_revisions') {
62     // Only act on entity reference fields.
63     return;
64   }
65
66   if ($field_storage->isSyncing()) {
67     // Don't change anything during a configuration sync.
68     return;
69   }
70
71   if ($field_storage->getSetting('target_type') == $field_storage->original->getSetting('target_type')) {
72     // Target type didn't change.
73     return;
74   }
75
76   if (empty($field_storage->bundles)) {
77     // Field storage has no fields.
78     return;
79   }
80
81   $field_name = $field_storage->getName();
82
83   foreach ($field_storage->bundles() as $entity_type => $bundles) {
84     foreach ($bundles as $bundle) {
85       $field = FieldConfig::loadByName($entity_type, $bundle, $field_name);
86       $field->setSetting('handler_settings', []);
87       $field->save();
88     }
89   }
90 }
91
92 /**
93  * Render API callback: Processes the field settings form and allows access to
94  * the form state.
95  *
96  * @see entity_reference_revisions_field_field_settings_form()
97  */
98 function _entity_reference_revisions_field_field_settings_ajax_process($form, FormStateInterface $form_state) {
99   _entity_reference_revisions_field_field_settings_ajax_process_element($form, $form);
100   return $form;
101 }
102
103 /**
104  * Adds entity_reference specific properties to AJAX form elements from the
105  * field settings form.
106  *
107  * @see _entity_reference_revisions_field_field_settings_ajax_process()
108  */
109 function _entity_reference_revisions_field_field_settings_ajax_process_element(&$element, $main_form) {
110   if (!empty($element['#ajax'])) {
111     $element['#ajax'] = [
112       'callback' => 'entity_reference_revisions_settings_ajax',
113       'wrapper' => $main_form['#id'],
114       'element' => $main_form['#array_parents'],
115     ];
116   }
117
118   foreach (Element::children($element) as $key) {
119     _entity_reference_revisions_field_field_settings_ajax_process_element($element[$key], $main_form);
120   }
121 }
122
123 /**
124  * Render API callback: Moves entity_reference specific Form API elements
125  * (i.e. 'handler_settings') up a level for easier processing by the validation
126  * and submission handlers.
127  *
128  * @see _entity_reference_revisions_field_settings_process()
129  */
130 function _entity_reference_revisions_form_process_merge_parent($element) {
131   $parents = $element['#parents'];
132   array_pop($parents);
133   $element['#parents'] = $parents;
134   return $element;
135 }
136
137 /**
138  * Form element validation handler; Filters the #value property of an element.
139  */
140 function _entity_reference_revisions_element_validate_filter(&$element, FormStateInterface $form_state) {
141   $element['#value'] = array_filter($element['#value']);
142   $form_state->setValueForElement($element, $element['#value']);
143 }
144
145 /**
146  * Ajax callback for the handler settings form.
147  *
148  * @see entity_reference_revisions_field_field_settings_form()
149  */
150 function entity_reference_revisions_settings_ajax($form, FormStateInterface $form_state) {
151   return NestedArray::getValue($form, $form_state->getTriggeringElement()['#ajax']['element']);
152 }
153
154 /**
155  * Submit handler for the non-JS case.
156  *
157  * @see entity_reference_revisions_field_field_settings_form()
158  */
159 function entity_reference_revisions_settings_ajax_submit($form, FormStateInterface $form_state) {
160   $form_state->setRebuild();
161 }
162
163 /**
164  * Creates a field of an entity reference revisions field storage on the specified bundle.
165  *
166  * @param string $entity_type
167  *   The type of entity the field will be attached to.
168  * @param string $bundle
169  *   The bundle name of the entity the field will be attached to.
170  * @param string $field_name
171  *   The name of the field; if it already exists, a new instance of the existing
172  *   field will be created.
173  * @param string $field_label
174  *   The label of the field.
175  * @param string $target_entity_type
176  *   The type of the referenced entity.
177  * @param string $selection_handler
178  *   The selection handler used by this field.
179  * @param array $selection_handler_settings
180  *   An array of settings supported by the selection handler specified above.
181  *   (e.g. 'target_bundles', 'sort', 'auto_create', etc).
182  * @param int $cardinality
183  *   The cardinality of the field.
184  *
185  * @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase::buildConfigurationForm()
186  */
187 function entity_reference_revisions_create_field($entity_type, $bundle, $field_name, $field_label, $target_entity_type, $selection_handler = 'default', $selection_handler_settings = [], $cardinality = 1) {
188   // Look for or add the specified field to the requested entity bundle.
189   if (!FieldStorageConfig::loadByName($entity_type, $field_name)) {
190     \Drupal::entityTypeManager()->getStorage('field_storage_config')->create([
191       'field_name' => $field_name,
192       'type' => 'entity_reference_revisions',
193       'entity_type' => $entity_type,
194       'cardinality' => $cardinality,
195       'settings' => [
196         'target_type' => $target_entity_type,
197       ],
198     ])->save();
199   }
200   if (!FieldConfig::loadByName($entity_type, $bundle, $field_name)) {
201     \Drupal::entityTypeManager()->getStorage('field_config')->create([
202       'field_name' => $field_name,
203       'entity_type' => $entity_type,
204       'bundle' => $bundle,
205       'label' => $field_label,
206       'settings' => [
207         'handler' => $selection_handler,
208         'handler_settings' => $selection_handler_settings,
209       ],
210     ])->save();
211   }
212 }
213
214 /**
215  * Implements hook_form_FORM_ID_alter() for 'field_ui_field_storage_add_form'.
216  */
217 function entity_reference_revisions_form_field_ui_field_storage_add_form_alter(array &$form) {
218   // Move the "Entity reference revisions" option to the end of the list and rename it to
219   // "Other".
220   unset($form['add']['new_storage_type']['#options'][(string) t('Reference revisions')]['entity_reference_revisions']);
221   $form['add']['new_storage_type']['#options'][(string) t('Reference revisions')]['entity_reference_revisions'] = t('Other…');
222 }
223
224 /**
225  * Implements hook_entity_revision_create().
226  */
227 function entity_reference_revisions_entity_revision_create(ContentEntityInterface $new_revision, ContentEntityInterface $entity, $keep_untranslatable_fields) {
228   $entity_type_manager = \Drupal::entityTypeManager();
229   $storage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId());
230   foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) {
231     if ($field_definition->getType() == 'entity_reference_revisions' && !$field_definition->isTranslatable()) {
232       $target_entity_type_id = $field_definition->getSetting('target_type');
233       if ($entity_type_manager->getDefinition($target_entity_type_id)->get('entity_revision_parent_id_field')) {
234
235         // The default implementation copied the values from the current
236         // default revision into the field since it is not translatable.
237         // Take the originally referenced entity, create a new revision
238         // of it and set that instead on the new entity revision.
239         $active_langcode = $entity->language()->getId();
240         $target_storage = \Drupal::entityTypeManager()->getStorage($target_entity_type_id);
241         if ($target_storage instanceof TranslatableRevisionableStorageInterface) {
242
243           $items = $entity->get($field_name);
244           $translation_items = NULL;
245           if (!$new_revision->isDefaultTranslation() && $storage instanceof TranslatableRevisionableStorageInterface) {
246             $translation_items = $items;
247             $items = $storage->load($new_revision->id())->get($field_name);
248           }
249
250           $values = [];
251           foreach ($items as $delta => $item) {
252             // Use the item from the translation if it exists.
253             // If we have translation items, use that if one with the matching
254             // target id exists.
255             if ($translation_items) {
256               foreach ($translation_items as $translation_item) {
257                 if ($item->target_id == $translation_item->target_id) {
258                   $item = $translation_item;
259                   break;
260                 }
261               }
262             }
263
264             /** @var \Drupal\Core\Entity\ContentEntityInterface $target_entity */
265             $target_entity = $item->entity;
266             if (!$target_entity->hasTranslation($active_langcode)) {
267               $target_entity->addTranslation($active_langcode, $target_entity->toArray());
268             }
269             $target_entity = $item->entity->getTranslation($active_langcode);
270             $revised_entity = $target_storage->createRevision($target_entity, $new_revision->isDefaultRevision(), $keep_untranslatable_fields);
271
272             // Restore the revision ID.
273             $revision_key = $revised_entity->getEntityType()->getKey('revision');
274             $revised_entity->set($revision_key, $revised_entity->getLoadedRevisionId());
275             $values[$delta] = $revised_entity;
276           }
277           $new_revision->set($field_name, $values);
278         }
279       }
280     }
281   }
282 }