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