Pathologic was missing because of a .git folder inside.
[yaffs-website] / web / modules / contrib / inline_entity_form / inline_entity_form.module
1 <?php
2
3 /**
4  * @file
5  * Provides a widget for inline management (creation, modification, removal) of
6  * referenced entities. The primary use case is the parent -> children one
7  * (for example, order -> line items), where the child entities are never
8  * managed outside the parent form.
9  */
10
11 use Drupal\Component\Utility\NestedArray;
12 use Drupal\Core\Form\FormStateInterface;
13 use Drupal\Core\Render\Element;
14 use Drupal\inline_entity_form\ElementSubmit;
15 use Drupal\inline_entity_form\WidgetSubmit;
16 use Drupal\inline_entity_form\Form\EntityInlineForm;
17 use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex;
18
19 /**
20  * Implements hook_entity_type_build().
21  */
22 function inline_entity_form_entity_type_build(array &$entity_types) {
23   /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
24   if (isset($entity_types['node']) && !$entity_types['node']->getHandlerClass('inline_form')) {
25     $entity_types['node']->setHandlerClass('inline_form', '\Drupal\inline_entity_form\Form\NodeInlineForm');
26   }
27
28   foreach ($entity_types as &$entity_type) {
29     if (!$entity_type->hasHandlerClass('inline_form')) {
30       $entity_type->setHandlerClass('inline_form', '\Drupal\inline_entity_form\Form\EntityInlineForm');
31     }
32   }
33 }
34
35 /**
36  * Implements hook_form_alter().
37  */
38 function inline_entity_form_form_alter(&$form, FormStateInterface $form_state, $form_id) {
39   // Attach the IEF handlers only if the current form has an IEF widget.
40   $widget_state = $form_state->get('inline_entity_form');
41   if (!is_null($widget_state)) {
42     ElementSubmit::attach($form, $form_state);
43     WidgetSubmit::attach($form, $form_state);
44   }
45 }
46
47 /**
48  * Implements hook_theme().
49  */
50 function inline_entity_form_theme() {
51   return [
52     'inline_entity_form_entity_table' => [
53       'render element' => 'form',
54       'function' => 'theme_inline_entity_form_entity_table',
55     ],
56   ];
57 }
58
59 /**
60  * Provides the form for adding existing entities through an autocomplete field.
61  *
62  * @param array $reference_form
63  *   The form array that will receive the form.
64  * @param \Drupal\Core\Form\FormStateInterface $form_state
65  *   The form state of the parent form.
66  *
67  * @return array
68  *   The form array containing the embedded form.
69  */
70 function inline_entity_form_reference_form($reference_form, &$form_state) {
71   $labels = $reference_form['#ief_labels'];
72   $ief_id = $reference_form['#ief_id'];
73   /** @var \Drupal\field\Entity\FieldConfig $instance */
74   $instance = $form_state->get(['inline_entity_form', $ief_id, 'instance']);
75
76   $reference_form['#title'] = t('Add existing @type_singular', ['@type_singular' => $labels['singular']]);
77   $reference_form['entity_id'] = [
78     '#type' => 'entity_autocomplete',
79     '#title' => t('@label', ['@label' => ucwords($labels['singular'])]),
80     '#target_type' => $instance->getSetting('target_type'),
81     '#selection_handler' => $instance->getSetting('handler'),
82     '#selection_settings' => $instance->getSetting('handler_settings'),
83     '#required' => TRUE,
84     '#maxlength' => 255,
85   ];
86   // Add the actions
87   $reference_form['actions'] = [
88     '#type' => 'container',
89     '#weight' => 100,
90   ];
91   $reference_form['actions']['ief_reference_save'] = [
92     '#type' => 'submit',
93     '#value' => t('Add @type_singular', ['@type_singular' => $labels['singular']]),
94     '#name' => 'ief-reference-submit-' . $reference_form['#ief_id'],
95     '#limit_validation_errors' => [$reference_form['#parents']],
96     '#attributes' => ['class' => ['ief-entity-submit']],
97     '#ajax' => [
98       'callback' => 'inline_entity_form_get_element',
99       'wrapper' => 'inline-entity-form-' . $reference_form['#ief_id'],
100     ],
101   ];
102   InlineEntityFormComplex::addSubmitCallbacks($reference_form['actions']['ief_reference_save']);
103   $reference_form['actions']['ief_reference_cancel'] = [
104     '#type' => 'submit',
105     '#value' => t('Cancel'),
106     '#name' => 'ief-reference-cancel-' . $reference_form['#ief_id'],
107     '#limit_validation_errors' => [],
108     '#ajax' => [
109       'callback' => 'inline_entity_form_get_element',
110       'wrapper' => 'inline-entity-form-' . $reference_form['#ief_id'],
111     ],
112     '#submit' => [['\Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex', 'closeForm']],
113   ];
114
115   $reference_form['#element_validate'][] = 'inline_entity_form_reference_form_validate';
116   $reference_form['#ief_element_submit'][] = 'inline_entity_form_reference_form_submit';
117
118   // Allow other modules to alter the form.
119   \Drupal::moduleHandler()->alter('inline_entity_form_reference_form', $reference_form, $form_state);
120
121   return $reference_form;
122 }
123
124 /**
125  * Validates the form for adding existing entities.
126  *
127  * @param array $reference_form
128  *  The reference entity form.
129  * @param \Drupal\Core\Form\FormStateInterface $form_state
130  *   The form state of the parent form.
131  */
132 function inline_entity_form_reference_form_validate(&$reference_form, FormStateInterface $form_state) {
133   $form_values = NestedArray::getValue($form_state->getValues(), $reference_form['#parents']);
134   if (empty($form_values['entity_id'])) {
135     // The entity_id element is required, the value is empty only if
136     // the form was cancelled.
137     return;
138   }
139   $ief_id = $reference_form['#ief_id'];
140   $labels = $reference_form['#ief_labels'];
141   $storage = \Drupal::entityTypeManager()->getStorage($reference_form['#entity_type']);
142   $entity = $storage->load($form_values['entity_id']);
143
144   // Check if the entity is already referenced by the field.
145   if (!empty($entity)) {
146     foreach ($form_state->get(['inline_entity_form', $ief_id, 'entities']) as $key => $value) {
147       if ($value['entity'] && $value['entity']->id() == $entity->id()) {
148         $form_state->setError($reference_form['entity_id'], t('The selected @label has already been added.', ['@label' => $labels['singular']]));
149         break;
150       }
151     }
152   }
153 }
154
155 /**
156  * Submits the form for adding existing entities.
157  *
158  * Adds the specified entity to the IEF form state.
159  *
160  * @param array $reference_form
161  *  The reference entity form.
162  * @param \Drupal\Core\Form\FormStateInterface $form_state
163  *   The form state of the parent form.
164  */
165 function inline_entity_form_reference_form_submit($reference_form, FormStateInterface $form_state) {
166   $ief_id = $reference_form['#ief_id'];
167   $form_values = NestedArray::getValue($form_state->getValues(), $reference_form['#parents']);
168   $storage = \Drupal::entityTypeManager()->getStorage($reference_form['#entity_type']);
169   $entity = $storage->load($form_values['entity_id']);
170   $entities = &$form_state->get(['inline_entity_form', $ief_id, 'entities']);
171   // Determine the correct weight of the new element.
172   $weight = 0;
173   if ($entities) {
174     $weight = max(array_keys($entities)) + 1;
175   }
176
177   $entities[] = [
178     'entity' => $entity,
179     'weight' => $weight,
180     'form' => NULL,
181     'needs_save' => FALSE,
182   ];
183   $form_state->set(['inline_entity_form', $ief_id, 'entities'], $entities);
184 }
185
186 /**
187  * Button #submit callback: Opens a form in the IEF widget.
188  *
189  * The form is shown below the entity table, at the bottom of the widget.
190  *
191  * @param $form
192  *   The complete parent form.
193  * @param $form_state
194  *   The form state of the parent form.
195  */
196 function inline_entity_form_open_form($form, FormStateInterface $form_state) {
197   $element = inline_entity_form_get_element($form, $form_state);
198   $ief_id = $element['#ief_id'];
199   $form_state->setRebuild();
200
201   // Get the current form values.
202   $parents = array_merge($element['#field_parents'], [$element['#field_name']]);
203   $form_values = NestedArray::getValue($form_state->getUserInput(), $parents);
204
205   $triggering_element = $form_state->getTriggeringElement();
206   $form_state->set(['inline_entity_form', $ief_id, 'form'], $triggering_element['#ief_form']);
207   if (!empty($form_values['actions']['bundle'])) {
208     $form_state->set(['inline_entity_form', $ief_id, 'form settings'], [
209       'bundle' => $form_values['actions']['bundle'],
210     ]);
211   }
212 }
213
214 /**
215  * Button #submit callback: Cleans up form state for a closed entity form.
216  *
217  * @param $form
218  *   The complete parent form.
219  * @param $form_state
220  *   The form state of the parent form.
221  */
222 function inline_entity_form_cleanup_form_state($form, FormStateInterface $form_state) {
223   $element = inline_entity_form_get_element($form, $form_state);
224   EntityInlineForm::submitCleanFormState($element['form']['inline_entity_form'], $form_state);
225 }
226
227 /**
228  * Button #submit callback: Opens a row form in the IEF widget.
229  *
230  * The row is identified by #ief_row_delta stored on the triggering
231  * element.
232  *
233  * @param $form
234  *   The complete parent form.
235  * @param $form_state
236  *   The form state of the parent form.
237  */
238 function inline_entity_form_open_row_form($form, FormStateInterface $form_state) {
239   $element = inline_entity_form_get_element($form, $form_state);
240   $ief_id = $element['#ief_id'];
241   $delta = $form_state->getTriggeringElement()['#ief_row_delta'];
242
243   $form_state->setRebuild();
244   $form_state->set(['inline_entity_form', $ief_id, 'entities', $delta, 'form'], $form_state->getTriggeringElement()['#ief_row_form']);
245 }
246
247
248 /**
249  * Closes all open IEF forms.
250  *
251  * Recurses and closes open forms in nested IEF widgets as well.
252  *
253  * @param $elements
254  *   An array of form elements containing entity forms.
255  * @param $form_state
256  *   The form state of the parent form.
257  */
258 function inline_entity_form_close_all_forms($elements, FormStateInterface $form_state) {
259   // Recurse through all children.
260   foreach (Element::children($elements) as $key) {
261     if (!empty($elements[$key])) {
262       inline_entity_form_close_all_forms($elements[$key], $form_state);
263     }
264   }
265
266   if (!empty($elements['#ief_id'])) {
267     $ief_id = $elements['#ief_id'];
268     // Close the main form.
269     $form_state->set(['inline_entity_form', $ief_id, 'form'], NULL);
270     // Close the row forms.
271     $entities = $form_state->get(['inline_entity_form', $ief_id, 'entities']);
272     foreach ($entities as $key => $value) {
273       $entities[$key]['form'] = NULL;
274     }
275     $form_state->set(['inline_entity_form', $ief_id, 'entities'], $entities);
276   }
277 }
278
279 /**
280  * Button #submit callback: Cleans up form state for a closed entity row form.
281  *
282  * @param $form
283  *   The complete parent form.
284  * @param $form_state
285  *   The form state of the parent form.
286  */
287 function inline_entity_form_cleanup_row_form_state($form, FormStateInterface $form_state) {
288   $element = inline_entity_form_get_element($form, $form_state);
289   $delta = $form_state->getTriggeringElement()['#ief_row_delta'];
290   $entity_form = $element['entities'][$delta]['form']['inline_entity_form'];
291   EntityInlineForm::submitCleanFormState($entity_form, $form_state);
292 }
293
294 /**
295  * Returns an IEF widget nearest to the triggering element.
296  */
297 function inline_entity_form_get_element($form, FormStateInterface $form_state) {
298   $element = [];
299   $triggering_element = $form_state->getTriggeringElement();
300
301   // Remove the action and the actions container.
302   $array_parents = array_slice($triggering_element['#array_parents'], 0, -2);
303
304   while (!isset($element['#ief_root'])) {
305     $element = NestedArray::getValue($form, $array_parents);
306     array_pop($array_parents);
307   }
308
309   return $element;
310 }
311
312 /**
313  * Themes the table showing existing entity references in the widget.
314  *
315  * @param array $variables
316  *   Contains the form element data from $element['entities'].
317  */
318 function theme_inline_entity_form_entity_table($variables) {
319   $renderer = \Drupal::service('renderer');
320   $form = $variables['form'];
321   $entity_type = $form['#entity_type'];
322
323   $fields = $form['#table_fields'];
324   $has_tabledrag = \Drupal::entityTypeManager()->getHandler($entity_type, 'inline_form')->isTableDragEnabled($form);
325
326   // Sort the fields by weight.
327   uasort($fields, '\Drupal\Component\Utility\SortArray::sortByWeightElement');
328
329   $header = [];
330   if ($has_tabledrag) {
331     $header[] = ['data' => '', 'class' => ['ief-tabledrag-header']];
332     $header[] = ['data' => t('Sort order'), 'class' => ['ief-sort-order-header']];
333   }
334   // Add header columns for each field.
335   $first = TRUE;
336   foreach ($fields as $field_name => $field) {
337     $column = ['data' => $field['label']];
338     // The first column gets a special class.
339     if ($first) {
340       $column['class'] = ['ief-first-column-header'];
341       $first = FALSE;
342     }
343     $header[] = $column;
344   }
345   $header[] = t('Operations');
346
347   // Build an array of entity rows for the table.
348   $rows = [];
349   foreach (Element::children($form) as $key) {
350     /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
351     $entity = $form[$key]['#entity'];
352     $row_classes = ['ief-row-entity'];
353     $cells = [];
354     if ($has_tabledrag) {
355       $cells[] = ['data' => '', 'class' => ['ief-tabledrag-handle']];
356       $cells[] = $renderer->render($form[$key]['delta']);
357       $row_classes[] = 'draggable';
358     }
359     // Add a special class to rows that have a form underneath, to allow
360     // for additional styling.
361     if (!empty($form[$key]['form'])) {
362       $row_classes[] = 'ief-row-entity-form';
363     }
364
365     foreach ($fields as $field_name => $field) {
366       $data = '';
367       if ($field['type'] == 'label') {
368         $data = $variables['form'][$key]['#label'];
369       }
370       elseif ($field['type'] == 'field' && $entity->hasField($field_name)) {
371         $display_options = ['label' => 'hidden'];
372         if (isset($field['display_options'])) {
373           $display_options += $field['display_options'];
374         }
375         $data = $entity->get($field_name)->view($display_options);
376       }
377       elseif ($field['type'] == 'callback') {
378         $arguments = [
379           'entity' => $entity,
380           'variables' => $variables,
381         ];
382         if (isset($field['callback_arguments'])) {
383           $arguments = array_merge($arguments, $field['callback_arguments']);
384         }
385
386         $data = call_user_func_array($field['callback'], $arguments);
387       }
388
389       $cells[] = ['data' => $data, 'class' => ['inline-entity-form-' . $entity_type . '-' . $field_name]];
390     }
391
392     // Add the buttons belonging to the "Operations" column.
393     $cells[] = $renderer->render($form[$key]['actions']);
394     // Create the row.
395     $rows[] = ['data' => $cells, 'class' => $row_classes];
396     // If the current entity array specifies a form, output it in the next row.
397     if (!empty($form[$key]['form'])) {
398       $row = [
399         ['data' => $renderer->render($form[$key]['form']), 'colspan' => count($fields) + 1],
400       ];
401       $rows[] = ['data' => $row, 'class' => ['ief-row-form'], 'no_striping' => TRUE];
402     }
403   }
404
405   if (!empty($rows)) {
406     $tabledrag = [];
407     if ($has_tabledrag) {
408       $tabledrag = [
409         [
410           'action' => 'order',
411           'relationship' => 'sibling',
412           'group' => 'ief-entity-delta',
413         ],
414       ];
415     }
416
417     $table = [
418       '#type' => 'table',
419       '#header' => $header,
420       '#rows' => $rows,
421       '#attributes' => [
422         'id' => 'ief-entity-table-' . $form['#id'],
423         'class' => ['ief-entity-table'],
424       ],
425       '#tabledrag' => $tabledrag,
426     ];
427
428     return $renderer->render($table);
429   }
430 }