Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / inline_entity_form / src / Plugin / Field / FieldWidget / InlineEntityFormBase.php
1 <?php
2
3 namespace Drupal\inline_entity_form\Plugin\Field\FieldWidget;
4
5 use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
6 use Drupal\Core\Entity\EntityInterface;
7 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
8 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 use Drupal\Core\Field\FieldDefinitionInterface;
10 use Drupal\Core\Field\FieldItemListInterface;
11 use Drupal\Core\Field\WidgetBase;
12 use Drupal\Core\Form\FormStateInterface;
13 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
14 use Drupal\inline_entity_form\TranslationHelper;
15 use Symfony\Component\DependencyInjection\ContainerInterface;
16
17 /**
18  * Inline entity form widget base class.
19  */
20 abstract class InlineEntityFormBase extends WidgetBase implements ContainerFactoryPluginInterface {
21
22   /**
23    * The inline entity form id.
24    *
25    * @var string
26    */
27   protected $iefId;
28
29   /**
30    * The entity type bundle info.
31    *
32    * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
33    */
34   protected $entityTypeBundleInfo;
35
36   /**
37    * The entity type manager.
38    *
39    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
40    */
41   protected $entityTypeManager;
42
43   /**
44    * The inline entity from handler.
45    *
46    * @var \Drupal\inline_entity_form\InlineFormInterface
47    */
48   protected $inlineFormHandler;
49
50   /**
51    * The entity display repository.
52    *
53    * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
54    */
55   protected $entityDisplayRepository;
56
57   /**
58    * Constructs an InlineEntityFormBase object.
59    *
60    * @param array $plugin_id
61    *   The plugin_id for the widget.
62    * @param mixed $plugin_definition
63    *   The plugin implementation definition.
64    * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
65    *   The definition of the field to which the widget is associated.
66    * @param array $settings
67    *   The widget settings.
68    * @param array $third_party_settings
69    *   Any third party settings.
70    * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
71    *   The entity type bundle info.
72    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
73    *   The entity type manager.
74    * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
75    *   The entity display repository.
76    */
77   public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository) {
78     parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
79
80     $this->entityTypeBundleInfo = $entity_type_bundle_info;
81     $this->entityTypeManager = $entity_type_manager;
82     $this->entityDisplayRepository = $entity_display_repository;
83     $this->createInlineFormHandler();
84   }
85
86   /**
87    * {@inheritdoc}
88    */
89   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
90     return new static(
91       $plugin_id,
92       $plugin_definition,
93       $configuration['field_definition'],
94       $configuration['settings'],
95       $configuration['third_party_settings'],
96       $container->get('entity_type.bundle.info'),
97       $container->get('entity_type.manager'),
98       $container->get('entity_display.repository')
99     );
100   }
101
102   /**
103    * Creates an instance of the inline form handler for the current entity type.
104    */
105   protected function createInlineFormHandler() {
106     if (!isset($this->inlineFormHandler)) {
107       $target_type = $this->getFieldSetting('target_type');
108       $this->inlineFormHandler = $this->entityTypeManager->getHandler($target_type, 'inline_form');
109     }
110   }
111
112   /**
113    * {@inheritdoc}
114    */
115   public function __sleep() {
116     $keys = array_diff(parent::__sleep(), ['inlineFormHandler']);
117     return $keys;
118   }
119
120   /**
121    * {@inheritdoc}
122    */
123   public function __wakeup() {
124     parent::__wakeup();
125     $this->createInlineFormHandler();
126   }
127
128   /**
129    * Sets inline entity form ID.
130    *
131    * @param string $ief_id
132    *   The inline entity form ID.
133    */
134   protected function setIefId($ief_id) {
135     $this->iefId = $ief_id;
136   }
137
138   /**
139    * Gets inline entity form ID.
140    *
141    * @return string
142    *   Inline entity form ID.
143    */
144   protected function getIefId() {
145     return $this->iefId;
146   }
147
148   /**
149    * Gets the target bundles for the current field.
150    *
151    * @return string[]
152    *   A list of bundles.
153    */
154   protected function getTargetBundles() {
155     $settings = $this->getFieldSettings();
156     if (!empty($settings['handler_settings']['target_bundles'])) {
157       $target_bundles = array_values($settings['handler_settings']['target_bundles']);
158       // Filter out target bundles which no longer exist.
159       $existing_bundles = array_keys($this->entityTypeBundleInfo->getBundleInfo($settings['target_type']));
160       $target_bundles = array_intersect($target_bundles, $existing_bundles);
161     }
162     else {
163       // If no target bundles have been specified then all are available.
164       $target_bundles = array_keys($this->entityTypeBundleInfo->getBundleInfo($settings['target_type']));
165     }
166
167     return $target_bundles;
168   }
169
170   /**
171    * Gets the bundles for which the current user has create access.
172    *
173    * @return string[]
174    *   The list of bundles.
175    */
176   protected function getCreateBundles() {
177     $create_bundles = [];
178     foreach ($this->getTargetBundles() as $bundle) {
179       if ($this->getAccessHandler()->createAccess($bundle)) {
180         $create_bundles[] = $bundle;
181       }
182     }
183
184     return $create_bundles;
185   }
186
187   /**
188    * {@inheritdoc}
189    */
190   public static function defaultSettings() {
191     return [
192       'form_mode' => 'default',
193       'override_labels' => FALSE,
194       'label_singular' => '',
195       'label_plural' => '',
196       'collapsible' => FALSE,
197       'collapsed' => FALSE,
198     ];
199   }
200
201   /**
202    * {@inheritdoc}
203    */
204   public function settingsForm(array $form, FormStateInterface $form_state) {
205     $entity_type_id = $this->getFieldSetting('target_type');
206     $states_prefix = 'fields[' . $this->fieldDefinition->getName() . '][settings_edit_form][settings]';
207     $element = [];
208     $element['form_mode'] = [
209       '#type' => 'select',
210       '#title' => $this->t('Form mode'),
211       '#default_value' => $this->getSetting('form_mode'),
212       '#options' => $this->entityDisplayRepository->getFormModeOptions($entity_type_id),
213       '#required' => TRUE,
214     ];
215     $element['override_labels'] = [
216       '#type' => 'checkbox',
217       '#title' => $this->t('Override labels'),
218       '#default_value' => $this->getSetting('override_labels'),
219     ];
220     $element['label_singular'] = [
221       '#type' => 'textfield',
222       '#title' => $this->t('Singular label'),
223       '#default_value' => $this->getSetting('label_singular'),
224       '#states' => [
225         'visible' => [
226           ':input[name="' . $states_prefix . '[override_labels]"]' => ['checked' => TRUE],
227         ],
228       ],
229     ];
230     $element['label_plural'] = [
231       '#type' => 'textfield',
232       '#title' => $this->t('Plural label'),
233       '#default_value' => $this->getSetting('label_plural'),
234       '#states' => [
235         'visible' => [
236           ':input[name="' . $states_prefix . '[override_labels]"]' => ['checked' => TRUE],
237         ],
238       ],
239     ];
240     $element['collapsible'] = [
241       '#type' => 'checkbox',
242       '#title' => $this->t('Collapsible'),
243       '#default_value' => $this->getSetting('collapsible'),
244     ];
245     $element['collapsed'] = [
246       '#type' => 'checkbox',
247       '#title' => $this->t('Collapsed by default'),
248       '#default_value' => $this->getSetting('collapsed'),
249       '#states' => [
250         'visible' => [
251           ':input[name="' . $states_prefix . '[collapsible]"]' => ['checked' => TRUE],
252         ],
253       ],
254     ];
255
256     return $element;
257   }
258
259   /**
260    * {@inheritdoc}
261    */
262   public function settingsSummary() {
263     $summary = [];
264     if ($entity_form_mode = $this->getEntityFormMode()) {
265       $form_mode_label = $entity_form_mode->label();
266     }
267     else {
268       $form_mode_label = $this->t('Default');
269     }
270     $summary[] = t('Form mode: @mode', ['@mode' => $form_mode_label]);
271     if ($this->getSetting('override_labels')) {
272       $summary[] = $this->t(
273         'Overriden labels are used: %singular and %plural',
274         ['%singular' => $this->getSetting('label_singular'), '%plural' => $this->getSetting('label_plural')]
275       );
276     }
277     else {
278       $summary[] = $this->t('Default labels are used.');
279     }
280
281     if ($this->getSetting('collapsible')) {
282       $summary[] = $this->t($this->getSetting('collapsed') ? 'Collapsible, collapsed by default' : 'Collapsible');
283     }
284
285     return $summary;
286   }
287
288   /**
289    * Gets the entity type managed by this handler.
290    *
291    * @return \Drupal\Core\Entity\EntityTypeInterface
292    *   The entity type.
293    */
294   protected function getEntityTypeLabels() {
295     // The admin has specified the exact labels that should be used.
296     if ($this->getSetting('override_labels')) {
297       return [
298         'singular' => $this->getSetting('label_singular'),
299         'plural' => $this->getSetting('label_plural'),
300       ];
301     }
302     else {
303       $this->createInlineFormHandler();
304       return $this->inlineFormHandler->getEntityTypeLabels();
305     }
306   }
307
308   /**
309    * Checks whether we can build entity form at all.
310    *
311    * - Is IEF handler loaded?
312    * - Are we on a "real" entity form and not on default value widget?
313    *
314    * @param \Drupal\Core\Form\FormStateInterface $form_state
315    *   Form state.
316    *
317    * @return bool
318    *   TRUE if we are able to proceed with form build and FALSE if not.
319    */
320   protected function canBuildForm(FormStateInterface $form_state) {
321     if ($this->isDefaultValueWidget($form_state)) {
322       return FALSE;
323     }
324
325     if (!$this->inlineFormHandler) {
326       return FALSE;
327     }
328
329     return TRUE;
330   }
331
332   /**
333    * Prepares the form state for the current widget.
334    *
335    * @param \Drupal\Core\Form\FormStateInterface $form_state
336    *   The form state.
337    * @param \Drupal\Core\Field\FieldItemListInterface $items
338    *   The field values.
339    * @param bool $translating
340    *   Whether there's a translation in progress.
341    */
342   protected function prepareFormState(FormStateInterface $form_state, FieldItemListInterface $items, $translating = FALSE) {
343     $widget_state = $form_state->get(['inline_entity_form', $this->iefId]);
344     if (empty($widget_state)) {
345       $widget_state = [
346         'instance' => $this->fieldDefinition,
347         'form' => NULL,
348         'delete' => [],
349         'entities' => [],
350       ];
351       // Store the $items entities in the widget state, for further manipulation.
352       foreach ($items as $delta => $item) {
353         $entity = $item->entity;
354         // The $entity can be NULL if the reference is broken.
355         if ($entity) {
356           // Display the entity in the correct translation.
357           if ($translating) {
358             $entity = TranslationHelper::prepareEntity($entity, $form_state);
359           }
360           $widget_state['entities'][$delta] = [
361             'entity' => $entity,
362             'weight' => $delta,
363             'form' => NULL,
364             'needs_save' => $entity->isNew(),
365           ];
366         }
367       }
368       $form_state->set(['inline_entity_form', $this->iefId], $widget_state);
369     }
370   }
371
372   /**
373    * Gets inline entity form element.
374    *
375    * @param string $operation
376    *   The operation (i.e. 'add' or 'edit').
377    * @param string $bundle
378    *   Entity bundle.
379    * @param string $langcode
380    *   Entity langcode.
381    * @param array $parents
382    *   Array of parent element names.
383    * @param \Drupal\Core\Entity\EntityInterface $entity
384    *   Optional entity object.
385    *
386    * @return array
387    *   IEF form element structure.
388    */
389   protected function getInlineEntityForm($operation, $bundle, $langcode, $delta, array $parents, EntityInterface $entity = NULL) {
390     $element = [
391       '#type' => 'inline_entity_form',
392       '#entity_type' => $this->getFieldSetting('target_type'),
393       '#bundle' => $bundle,
394       '#langcode' => $langcode,
395       '#default_value' => $entity,
396       '#op' => $operation,
397       '#form_mode' => $this->getSetting('form_mode'),
398       '#save_entity' => FALSE,
399       '#ief_row_delta' => $delta,
400       // Used by Field API and controller methods to find the relevant
401       // values in $form_state.
402       '#parents' => $parents,
403       // Labels could be overridden in field widget settings. We won't have
404       // access to those in static callbacks (#process, ...) so let's add
405       // them here.
406       '#ief_labels' => $this->getEntityTypeLabels(),
407       // Identifies the IEF widget to which the form belongs.
408       '#ief_id' => $this->getIefId(),
409     ];
410
411     return $element;
412   }
413
414   /**
415    * Determines whether there's a translation in progress.
416    *
417    * Ensures that at least one target bundle has translations enabled.
418    * Otherwise the widget will skip translation even if it's happening
419    * on the parent form itself.
420    *
421    * @param \Drupal\Core\Form\FormStateInterface $form_state
422    *   The form state.
423    *
424    * @return bool
425    *   TRUE if translating is in progress, FALSE otherwise.
426    *
427    * @see \Drupal\inline_entity_form\TranslationHelper::initFormLangcodes()
428    */
429   protected function isTranslating(FormStateInterface $form_state) {
430     if (TranslationHelper::isTranslating($form_state)) {
431       $translation_manager = \Drupal::service('content_translation.manager');
432       $target_type = $this->getFieldSetting('target_type');
433       foreach ($this->getTargetBundles() as $bundle) {
434         if ($translation_manager->isEnabled($target_type, $bundle)) {
435           return TRUE;
436         }
437       }
438     }
439
440     return FALSE;
441   }
442
443   /**
444    * After-build callback for removing the translatability clue from the widget.
445    *
446    * IEF expects the entity reference field to not be translatable, to avoid
447    * different translations having different references.
448    * However, that causes ContentTranslationHandler::addTranslatabilityClue()
449    * to add an "(all languages)" suffix to the widget title. That suffix is
450    * incorrect, since IEF does ensure that specific entity translations are
451    * being edited.
452    */
453   public static function removeTranslatabilityClue(array $element, FormStateInterface $form_state) {
454     $element['#title'] = $element['#field_title'];
455     return $element;
456   }
457
458   /**
459    * Adds submit callbacks to the inline entity form.
460    *
461    * @param array $element
462    *   Form array structure.
463    */
464   public static function addIefSubmitCallbacks($element) {
465     $element['#ief_element_submit'][] = [get_called_class(), 'submitSaveEntity'];
466     return $element;
467   }
468
469   /**
470    * Marks created/edited entity with "needs save" flag.
471    *
472    * Note that at this point the entity is not yet saved, since the user might
473    * still decide to cancel the parent form.
474    *
475    * @param $entity_form
476    *   The form of the entity being managed inline.
477    * @param \Drupal\Core\Form\FormStateInterface $form_state
478    *   The form state of the parent form.
479    */
480   public static function submitSaveEntity($entity_form, FormStateInterface $form_state) {
481     $ief_id = $entity_form['#ief_id'];
482     /** @var \Drupal\Core\Entity\EntityInterface $entity */
483     $entity = $entity_form['#entity'];
484
485     if (in_array($entity_form['#op'], ['add', 'duplicate'])) {
486       // Determine the correct weight of the new element.
487       $weight = 0;
488       $entities = $form_state->get(['inline_entity_form', $ief_id, 'entities']);
489       if (!empty($entities)) {
490         $weight = max(array_keys($entities)) + 1;
491       }
492       // Add the entity to form state, mark it for saving, and close the form.
493       $entities[] = [
494         'entity' => $entity,
495         'weight' => $weight,
496         'form' => NULL,
497         'needs_save' => TRUE,
498       ];
499       $form_state->set(['inline_entity_form', $ief_id, 'entities'], $entities);
500     }
501     else {
502       $delta = $entity_form['#ief_row_delta'];
503       $entities = $form_state->get(['inline_entity_form', $ief_id, 'entities']);
504       $entities[$delta]['entity'] = $entity;
505       $entities[$delta]['needs_save'] = TRUE;
506       $form_state->set(['inline_entity_form', $ief_id, 'entities'], $entities);
507     }
508   }
509
510   /**
511    * {@inheritdoc}
512    */
513   public function calculateDependencies() {
514     $dependencies = parent::calculateDependencies();
515     if ($entity_form_mode = $this->getEntityFormMode()) {
516       $dependencies['config'][] = $entity_form_mode->getConfigDependencyName();
517     }
518     return $dependencies;
519   }
520
521   /**
522    * Gets the entity form mode instance for this widget.
523    *
524    * @return \Drupal\Core\Entity\EntityFormModeInterface|null
525    *   The form mode instance, or NULL if the default one is used.
526    */
527   protected function getEntityFormMode() {
528     $form_mode = $this->getSetting('form_mode');
529     if ($form_mode != 'default') {
530       $entity_type_id = $this->getFieldSetting('target_type');
531       return $this->entityTypeManager->getStorage('entity_form_mode')->load($entity_type_id . '.' . $form_mode);
532     }
533     return NULL;
534   }
535
536   /**
537    * Gets the access handler for the target entity type.
538    *
539    * @return \Drupal\Core\Entity\EntityAccessControlHandlerInterface
540    *   The access handler.
541    */
542   protected function getAccessHandler() {
543     $entity_type_id = $this->getFieldSetting('target_type');
544     return $this->entityTypeManager->getAccessControlHandler($entity_type_id);
545   }
546
547   /**
548    * {@inheritdoc}
549    */
550   public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
551     if ($this->canBuildForm($form_state)) {
552       return parent::form($items, $form, $form_state, $get_delta);
553     }
554     return [];
555   }
556
557   /**
558    * Determines if the current user can add any new entities.
559    *
560    * @return bool
561    */
562   protected function canAddNew() {
563     $create_bundles = $this->getCreateBundles();
564     return !empty($create_bundles);
565   }
566
567 }