From eaaaa4c3f0d6093c62692d13f9989df802f9c2e3 Mon Sep 17 00:00:00 2001 From: Spokje <39382-Spokje@users.noreply.drupalcode.org> Date: Thu, 11 May 2023 13:00:32 +0300 Subject: [PATCH] Issue #2581223 by Spokje, Dylan Donkersgoed, attisan, DieterHolvoet, jienckebd, Suresh Prabhu Parkala, KapilV, dwkitchen: Support for configuration entities --- inline_entity_form.module | 17 ++- src/Element/InlineEntityForm.php | 7 +- src/Form/ConfigEntityInlineForm.php | 163 ++++++++++++++++++++++++++++ src/Form/EntityInlineForm.php | 4 +- src/TranslationHelper.php | 87 ++++++++------- 5 files changed, 230 insertions(+), 48 deletions(-) create mode 100644 src/Form/ConfigEntityInlineForm.php diff --git a/inline_entity_form.module b/inline_entity_form.module index 1636ba24..8545189f 100644 --- a/inline_entity_form.module +++ b/inline_entity_form.module @@ -11,11 +11,12 @@ */ use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; +use Drupal\Core\Entity\ContentEntityTypeInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\inline_entity_form\ElementSubmit; use Drupal\inline_entity_form\WidgetSubmit; -use Drupal\inline_entity_form\Form\EntityInlineForm; use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex; use Drupal\inline_entity_form\MigrationHelper; use Drupal\migrate\Plugin\MigrateSourceInterface; @@ -33,7 +34,12 @@ function inline_entity_form_entity_type_build(array &$entity_types) { foreach ($entity_types as &$entity_type) { if (!$entity_type->hasHandlerClass('inline_form')) { - $entity_type->setHandlerClass('inline_form', '\Drupal\inline_entity_form\Form\EntityInlineForm'); + if ($entity_type instanceof ContentEntityTypeInterface) { + $entity_type->setHandlerClass('inline_form', '\Drupal\inline_entity_form\Form\EntityInlineForm'); + } + elseif ($entity_type instanceof ConfigEntityTypeInterface) { + $entity_type->setHandlerClass('inline_form', '\Drupal\inline_entity_form\Form\ConfigEntityInlineForm'); + } } } } @@ -244,7 +250,9 @@ function inline_entity_form_open_form(array $form, FormStateInterface $form_stat */ function inline_entity_form_cleanup_form_state(array $form, FormStateInterface $form_state) { $element = inline_entity_form_get_element($form, $form_state); - EntityInlineForm::submitCleanFormState($element['form']['inline_entity_form'], $form_state); + $entity_type = $element['form']['inline_entity_form']['#entity_type']; + $handler = \Drupal::entityTypeManager()->getHandler($entity_type, 'inline_form'); + get_class($handler)::submitCleanFormState($element['form']['inline_entity_form'], $form_state); } /** @@ -310,7 +318,8 @@ function inline_entity_form_cleanup_row_form_state(array $form, FormStateInterfa $element = inline_entity_form_get_element($form, $form_state); $delta = $form_state->getTriggeringElement()['#ief_row_delta']; $entity_form = $element['entities'][$delta]['form']['inline_entity_form']; - EntityInlineForm::submitCleanFormState($entity_form, $form_state); + $handler = \Drupal::entityTypeManager()->getHandler($entity_form['#entity_type'], 'inline_form'); + get_class($handler)::submitCleanFormState($entity_form, $form_state); } /** diff --git a/src/Element/InlineEntityForm.php b/src/Element/InlineEntityForm.php index 74a71424..4bce4d40 100644 --- a/src/Element/InlineEntityForm.php +++ b/src/Element/InlineEntityForm.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\RevisionLogInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\RenderElement; +use Drupal\Core\TypedData\TranslatableInterface; use Drupal\inline_entity_form\ElementSubmit; use Drupal\inline_entity_form\TranslationHelper; @@ -131,8 +132,10 @@ class InlineEntityForm extends RenderElement { } } // Prepare the entity form and the entity itself for translating. - $entity_form['#entity'] = TranslationHelper::prepareEntity($entity_form['#entity'], $form_state); - $entity_form['#translating'] = TranslationHelper::isTranslating($form_state) && $entity_form['#entity']->isTranslatable(); + if ($entity_form['#entity'] instanceof TranslatableInterface) { + $entity_form['#entity'] = TranslationHelper::prepareEntity($entity_form['#entity'], $form_state); + $entity_form['#translating'] = TranslationHelper::isTranslating($form_state) && $entity_form['#entity']->isTranslatable(); + } // Handle revisioning if the entity supports it. if ($entity_type->isRevisionable() && $entity_form['#revision']) { diff --git a/src/Form/ConfigEntityInlineForm.php b/src/Form/ConfigEntityInlineForm.php new file mode 100644 index 00000000..fef46993 --- /dev/null +++ b/src/Form/ConfigEntityInlineForm.php @@ -0,0 +1,163 @@ +<?php + +namespace Drupal\inline_entity_form\Form; + +use Drupal\Core\Config\Entity\ConfigEntityInterface; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityWithPluginCollectionInterface; +use Drupal\Core\Form\FormInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Form\SubformState; + +/** + * Generic entity inline form handler. + */ +class ConfigEntityInlineForm extends EntityInlineForm { + + /** + * {@inheritdoc} + */ + public function getTableFields($bundles) { + + $fields = []; + $fields['label'] = [ + 'type' => 'label', + 'label' => $this->getEntityType()->getLabel(), + 'weight' => 1, + ]; + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function entityForm(array $entity_form, FormStateInterface $form_state) { + /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */ + $entity = $entity_form['#entity']; + $form_object = $this->getFormObject($entity, $entity_form['#form_mode']); + $entity_form += $form_object->form($entity_form, $this->getSubformState($entity_form, $form_state)); + $entity_form['#ief_element_submit'][] = [ + get_class($this), + 'submitCleanFormState', + ]; + $this->expandCallbacks($form_object, $entity_form['#after_build']); + $this->expandCallbacks($form_object, $entity_form['#pre_render']); + // Allow other modules to alter the form. + $this->moduleHandler->alter('inline_entity_form_entity_form', $entity_form, $form_state); + + return $entity_form; + } + + /** + * {@inheritdoc} + */ + public function entityFormValidate(array &$entity_form, FormStateInterface $form_state) { + // Perform entity validation only if the inline form was submitted, + // skipping other requests such as file uploads. + $triggering_element = $form_state->getTriggeringElement(); + if (!empty($triggering_element['#ief_submit_trigger'])) { + /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */ + $entity = $entity_form['#entity']; + $this->buildEntity($entity_form, $entity, $form_state); + + $form_object = $this->getFormObject($entity, $entity_form['#form_mode']); + $form_object->validateForm($entity_form, $this->getSubformState($entity_form, $form_state)); + + foreach ($form_state->getErrors() as $name => $message) { + // $name may be unknown in $form_state and + // $form_state->setErrorByName($name, $message) may suppress the error + // message. + $form_state->setError($triggering_element, $message); + } + } + } + + /** + * {@inheritdoc} + */ + protected function buildEntity(array $entity_form, EntityInterface $entity, FormStateInterface $form_state) { + // Start section based on protected EntityForm::copyFormValuesToEntity() + // method. + $subform_state = $this->getSubformState($entity_form, $form_state); + $values = $subform_state->getValues(); + if ($entity instanceof EntityWithPluginCollectionInterface) { + // Do not manually update values represented by plugin collections. + $values = array_diff_key($values, $entity->getPluginCollections()); + } + // Invoke all specified builders for copying form values to entity fields. + if (isset($entity_form['#entity_builders'])) { + foreach ($entity_form['#entity_builders'] as $function) { + call_user_func_array($function, [ + $entity->getEntityTypeId(), + $entity, + &$entity_form, + &$form_state, + ]); + } + } + foreach ($values as $key => $value) { + $entity->set($key, $value); + } + // End section based on protected EntityForm::copyFormValuesToEntity() + // method. + } + + /** + * {@inheritdoc} + * + * Not applicable for config entities since they are not fieldable. + */ + public static function submitCleanFormState(&$entity_form, FormStateInterface $form_state) { + } + + /** + * Get the form object for the config entity and form mode. + * + * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity + * The config entity. + * @param string $form_mode + * The form mode. + * + * @return \Drupal\Core\Entity\EntityFormInterface + * The form object. + */ + protected function getFormObject(ConfigEntityInterface $entity, $form_mode) { + $form_object = $this->entityTypeManager->getFormObject($entity->getEntityTypeId(), $form_mode); + $form_object->setEntity($entity); + + return $form_object; + } + + /** + * Get the subform state for the entity form. + * + * @param array $entity_form + * The entity form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state of the main form. + * + * @return \Drupal\Core\Form\SubformState + * The subform state for the entity form. + */ + protected function getSubformState(array $entity_form, FormStateInterface $form_state) { + return SubformState::createForSubform($entity_form, $form_state->getCompleteForm(), $form_state); + } + + /** + * Expand callbacks in the form ::methodName() to className::methodName(). + * + * @param \Drupal\Core\Form\FormInterface $form_object + * The form object the callback belongs to. + * @param array &$callbacks + * An array of callbacks to expand. + */ + protected function expandCallbacks(FormInterface $form_object, array &$callbacks) { + foreach ($callbacks as &$callback) { + if (is_string($callback) && strpos($callback, '::') === 0) { + $callback = [$form_object, substr($callback, 2)]; + } + } + } + +} diff --git a/src/Form/EntityInlineForm.php b/src/Form/EntityInlineForm.php index b767601c..f6d4a578 100644 --- a/src/Form/EntityInlineForm.php +++ b/src/Form/EntityInlineForm.php @@ -309,12 +309,12 @@ class EntityInlineForm implements InlineFormInterface { * * @param array $entity_form * The entity form. - * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. */ - protected function buildEntity(array $entity_form, ContentEntityInterface $entity, FormStateInterface $form_state) { + protected function buildEntity(array $entity_form, EntityInterface $entity, FormStateInterface $form_state) { $form_display = $this->getFormDisplay($entity, $entity_form['#form_mode']); $form_display->extractFormValues($entity, $entity_form, $form_state); // Invoke all specified builders for copying form values to entity fields. diff --git a/src/TranslationHelper.php b/src/TranslationHelper.php index fd5d88c5..68e0200f 100644 --- a/src/TranslationHelper.php +++ b/src/TranslationHelper.php @@ -2,7 +2,8 @@ namespace Drupal\inline_entity_form; -use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\Core\Entity\ContentEntityTypeInterface; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; /** @@ -13,43 +14,46 @@ class TranslationHelper { /** * Prepares the inline entity for translation. * - * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * @param \Drupal\Core\Entity\EntityInterface $entity * The inline entity. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. * - * @return \Drupal\Core\Entity\ContentEntityInterface + * @return \Drupal\Core\Entity\EntityInterface * The prepared entity. * * @see \Drupal\Core\Entity\ContentEntityForm::initFormLangcodes() */ - public static function prepareEntity(ContentEntityInterface $entity, FormStateInterface $form_state) { - $form_langcode = $form_state->get('langcode'); - if (empty($form_langcode) || !$entity->isTranslatable()) { - return $entity; - } - - $entity_langcode = $entity->language()->getId(); - if (self::isTranslating($form_state) && !$entity->hasTranslation($form_langcode)) { - // Create a translation from the source language values. - $source = $form_state->get(['content_translation', 'source']); - $source_langcode = $source ? $source->getId() : $entity_langcode; - if (!$entity->hasTranslation($source_langcode)) { - $entity->addTranslation($source_langcode, $entity->toArray()); + public static function prepareEntity(EntityInterface $entity, FormStateInterface $form_state) { + if ($entity instanceof ContentEntityTypeInterface) { + $form_langcode = $form_state->get('langcode'); + if (empty($form_langcode) || !$entity->isTranslatable()) { + return $entity; } - $source_translation = $entity->getTranslation($source_langcode); - $entity->addTranslation($form_langcode, $source_translation->toArray()); - $translation = $entity->getTranslation($form_langcode); - $translation->set('content_translation_source', $source_langcode); - // Make sure we do not inherit the affected status from the source values. - if ($entity->getEntityType()->isRevisionable()) { - $translation->setRevisionTranslationAffected(NULL); + + $entity_langcode = $entity->language()->getId(); + if (self::isTranslating($form_state) && !$entity->hasTranslation($form_langcode)) { + // Create a translation from the source language values. + $source = $form_state->get(['content_translation', 'source']); + $source_langcode = $source ? $source->getId() : $entity_langcode; + if (!$entity->hasTranslation($source_langcode)) { + $entity->addTranslation($source_langcode, $entity->toArray()); + } + $source_translation = $entity->getTranslation($source_langcode); + $entity->addTranslation($form_langcode, $source_translation->toArray()); + $translation = $entity->getTranslation($form_langcode); + $translation->set('content_translation_source', $source_langcode); + // Make sure we do not inherit the affected status from the source + // values. + if ($entity->getEntityType()->isRevisionable()) { + $translation->setRevisionTranslationAffected(NULL); + } } - } - if ($entity_langcode != $form_langcode && $entity->hasTranslation($form_langcode)) { - // Switch to the needed translation. - $entity = $entity->getTranslation($form_langcode); + if ($entity_langcode != $form_langcode && $entity->hasTranslation($form_langcode)) { + // Switch to the needed translation. + $entity = $entity->getTranslation($form_langcode); + } } return $entity; @@ -61,7 +65,7 @@ class TranslationHelper { * Called on submit to allow the user to select a different language through * the langcode form element, which is then transferred to form state. * - * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. @@ -69,20 +73,23 @@ class TranslationHelper { * @return bool * TRUE if the entity langcode was updated, FALSE otherwise. */ - public static function updateEntityLangcode(ContentEntityInterface $entity, FormStateInterface $form_state) { + public static function updateEntityLangcode(EntityInterface $entity, FormStateInterface $form_state) { $changed = FALSE; - // This method is first called during form validation, at which point - // the 'langcode' form state flag hasn't been updated with the new value. - $form_langcode = $form_state->getValue(['langcode', 0, 'value'], $form_state->get('langcode')); - if (empty($form_langcode) || !$entity->isTranslatable()) { - return $changed; - } - $entity_langcode = $entity->language()->getId(); - if ($entity_langcode != $form_langcode && !$entity->hasTranslation($form_langcode)) { - $langcode_key = $entity->getEntityType()->getKey('langcode'); - $entity->set($langcode_key, $form_langcode); - $changed = TRUE; + if ($entity instanceof ContentEntityTypeInterface) { + // This method is first called during form validation, at which point + // the 'langcode' form state flag hasn't been updated with the new value. + $form_langcode = $form_state->getValue(['langcode', 0, 'value'], $form_state->get('langcode')); + if (empty($form_langcode) || !$entity->isTranslatable()) { + return $changed; + } + + $entity_langcode = $entity->language()->getId(); + if ($entity_langcode != $form_langcode && !$entity->hasTranslation($form_langcode)) { + $langcode_key = $entity->getEntityType()->getKey('langcode'); + $entity->set($langcode_key, $form_langcode); + $changed = TRUE; + } } return $changed; -- GitLab