diff --git a/config/schema/entity_embed.schema.yml b/config/schema/entity_embed.schema.yml index 014e705..3f0b6f0 100644 --- a/config/schema/entity_embed.schema.yml +++ b/config/schema/entity_embed.schema.yml @@ -29,6 +29,9 @@ embed.embed_type_settings.entity: display_review: type: boolean label: 'Display review step' + inline_entity_edit_enabled: + type: boolean + label: 'Inline entity editing enabled' entity_embed.settings: type: config_object diff --git a/src/Form/EntityEmbedDialog.php b/src/Form/EntityEmbedDialog.php index 5ae3b11..83514bc 100644 --- a/src/Form/EntityEmbedDialog.php +++ b/src/Form/EntityEmbedDialog.php @@ -195,7 +195,7 @@ class EntityEmbedDialog extends FormBase { $form_state->set('entity', $entity ?: NULL); if (!$form_state->get('step')) { - // If an entity has been selected, then always skip to the embed options. + // If an entity has been selected, then skip to the embed or edit options. if ($form_state->get('entity')) { $form_state->set('step', 'embed'); } @@ -222,6 +222,9 @@ class EntityEmbedDialog extends FormBase { elseif ($form_state->get('step') == 'embed') { $form = $this->buildEmbedStep($form, $form_state); } + elseif ($form_state->get('step') == 'entity_edit') { + $form = $this->buildEntityEditStep($form, $form_state); + } return $form; } @@ -430,6 +433,22 @@ class EntityEmbedDialog extends FormBase { '#title' => $this->t('Selected entity'), '#markup' => $entity_label, ]; + + $inline_entity_edit_enabled = $embed_button->getTypeSetting('inline_entity_edit_enabled'); + if ($inline_entity_edit_enabled && $this->moduleHandler->moduleExists('inline_entity_form')) { + // Provide a link to switch to the entity edit step. + $form['entity_edit'] = [ + '#type' => 'button', + '#executes_submit_callback' => FALSE, + '#value' => $this->t('Edit'), + '#access' => $entity->access('update'), + '#ajax' => [ + 'callback' => '::submitAndShowEntityEdit', + 'event' => 'click', + ], + ]; + } + $form['attributes']['data-entity-type'] = [ '#type' => 'hidden', '#value' => $entity_element['data-entity-type'], @@ -557,6 +576,120 @@ class EntityEmbedDialog extends FormBase { return $form; } + /** + * Form constructor for the entity embedding step. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form structure. + */ + public function buildEntityEditStep(array $form, FormStateInterface $form_state) { + // Entity element is calculated on every AJAX request/submit. + // See ::buildForm(). + $entity_element = $form_state->get('entity_element'); + + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity = $form_state->get('entity'); + + $form['#title'] = $this->t('Edit @type', array('@type' => $entity->getEntityType()->getSingularLabel())); + + if ($this->moduleHandler->moduleExists('inline_entity_form')) { + // @TODO: Make form_mode configurable? + $form['inline_entity_form'] = [ + '#type' => 'inline_entity_form', + '#entity_type' => $entity->getEntityType()->id(), + '#bundle' => $entity->bundle(), + '#default_value' => $entity, + '#op' => 'edit', + '#form_mode' => 'default', + '#save_entity' => TRUE, + ]; + + $form['attributes']['data-entity-type'] = array( + '#type' => 'hidden', + '#value' => $entity_element['data-entity-type'], + ); + $form['attributes']['data-entity-uuid'] = array( + '#type' => 'hidden', + '#value' => $entity_element['data-entity-uuid'], + ); + + // TODO: Also set data-entity-embed-display-settings. + $form['attributes']['data-entity-embed-display'] = array( + '#type' => 'hidden', + '#default_value' => $entity_element['data-entity-embed-display'], + '#required' => TRUE, + ); + $form['attributes']['data-align'] = array( + '#type' => 'hidden', + '#default_value' => isset($entity_element['data-align']) ? $entity_element['data-align'] : '', + ); + $form['attributes']['data-caption'] = array( + '#type' => 'hidden', + '#default_value' => isset($entity_element['data-caption']) ? Html::decodeEntities($entity_element['data-caption']) : '', + '#element_validate' => array('::escapeValue'), + ); + + $form['actions'] = array( + '#type' => 'actions', + ); + $form['actions']['back'] = array( + '#type' => 'submit', + '#value' => $this->t('Back'), + // No regular submit-handler. This form only works via JavaScript. + '#submit' => array(), + '#ajax' => array( + 'callback' => '::submitAndShowEmbed', + 'event' => 'click', + ), + ); + $form['actions']['save_modal'] = array( + '#type' => 'submit', + '#value' => $this->t('Save'), + '#button_type' => 'primary', + // Pretend to be IEFs submit button. + '#submit' => array( + [ + 'Drupal\inline_entity_form\ElementSubmit', + 'trigger' + ] + ), + '#ief_submit_trigger' => TRUE, + '#ief_submit_trigger_all' => TRUE, + // No regular submit-handler. This form only works via JavaScript. + '#ajax' => array( + 'callback' => '::submitEntityEditStep', + 'event' => 'click', + ), + ); + } + else { + // Without the inline entity form module, the only option is to go back. + $form['message'] = array( + '#markup' => $this->t('The Inline Entity Form module is required.'), + ); + $form['actions'] = array( + '#type' => 'actions', + ); + $form['actions']['back'] = array( + '#type' => 'submit', + '#value' => $this->t('Back'), + // No regular submit-handler. This form only works via JavaScript. + '#submit' => array(), + '#ajax' => array( + 'callback' => '::submitAndShowEmbed', + 'event' => 'click', + ), + ); + } + + return $form; + } + /** * {@inheritdoc} */ @@ -777,6 +910,21 @@ class EntityEmbedDialog extends FormBase { return $this->submitStep($form, $form_state, 'embed'); } + /** + * Submit and show entity edit step after submit. + * + * @param array $form + * The form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + * + * @return \Drupal\Core\Ajax\AjaxResponse + * The ajax response. + */ + public function submitAndShowEntityEdit(array $form, FormStateInterface $form_state) { + return $this->submitStep($form, $form_state, 'entity_edit'); + } + /** * Form submission handler for the entity embedding step. * @@ -838,6 +986,46 @@ class EntityEmbedDialog extends FormBase { return $response; } + /** + * Form submission handler for the entity editing step. + * + * On success this will submit the command to save the embedded entity with + * the configured display settings to the WYSIWYG element, and then close the + * modal dialog. On form errors, this will rebuild the form and display the + * error messages. + * + * @param array $form + * An associative array containing the structure of the form. + * @param FormStateInterface $form_state + * An associative array containing the current state of the form. + * + * @return \Drupal\Core\Ajax\AjaxResponse + * The ajax response. + */ + public function submitEntityEditStep(array &$form, FormStateInterface $form_state) { + $response = new AjaxResponse(); + + // Display errors in form, if any. + if ($form_state->hasAnyErrors()) { + unset($form['#prefix'], $form['#suffix']); + $form['status_messages'] = array( + '#type' => 'status_messages', + '#weight' => -10, + ); + $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $form)); + } + else { + $form_state->set('step', 'embed'); + $form_state->setRebuild(TRUE); + $rebuild_form = $this->formBuilder->rebuildForm('entity_embed_dialog', $form_state, $form); + unset($rebuild_form['#prefix'], $rebuild_form['#suffix']); + $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $rebuild_form)); + $response->addCommand(new SetDialogTitleCommand('', $rebuild_form['#title'])); + } + + return $response; + } + /** * Form element validation handler; Escapes the value an element. * diff --git a/src/Plugin/EmbedType/Entity.php b/src/Plugin/EmbedType/Entity.php index 554ff3d..654001b 100644 --- a/src/Plugin/EmbedType/Entity.php +++ b/src/Plugin/EmbedType/Entity.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeRepositoryInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\PluginDependencyTrait; @@ -53,6 +54,13 @@ class Entity extends EmbedTypeBase implements ContainerFactoryPluginInterface { */ protected $displayPluginManager; + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + /** * {@inheritdoc} * @@ -70,13 +78,16 @@ class Entity extends EmbedTypeBase implements ContainerFactoryPluginInterface { * The entity type bundle info service. * @param \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager $display_plugin_manager * The plugin manager. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeRepositoryInterface $entity_type_repository, EntityTypeBundleInfoInterface $bundle_info, EntityEmbedDisplayManager $display_plugin_manager) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeRepositoryInterface $entity_type_repository, EntityTypeBundleInfoInterface $bundle_info, EntityEmbedDisplayManager $display_plugin_manager, ModuleHandlerInterface $module_handler) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->entityTypeManager = $entity_type_manager; $this->entityTypeRepository = $entity_type_repository; $this->entityTypeBundleInfo = $bundle_info; $this->displayPluginManager = $display_plugin_manager; + $this->moduleHandler = $module_handler; } /** @@ -90,7 +101,8 @@ class Entity extends EmbedTypeBase implements ContainerFactoryPluginInterface { $container->get('entity_type.manager'), $container->get('entity_type.repository'), $container->get('entity_type.bundle.info'), - $container->get('plugin.manager.entity_embed.display') + $container->get('plugin.manager.entity_embed.display'), + $container->get('module_handler') ); } @@ -106,6 +118,7 @@ class Entity extends EmbedTypeBase implements ContainerFactoryPluginInterface { 'entity_browser_settings' => [ 'display_review' => 0, ], + 'inline_entity_edit_enabled' => FALSE, ]; } @@ -196,6 +209,15 @@ class Entity extends EmbedTypeBase implements ContainerFactoryPluginInterface { } } + if ($this->moduleHandler->moduleExists('inline_entity_form')) { + $form['inline_entity_edit_enabled'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Enable inline editing'), + '#description' => $this->t('Allow embedded entities to be editable within the embed dialog.'), + '#default_value' => $this->getConfigurationValue('inline_entity_edit_enabled'), + ]; + } + return $form; } @@ -211,6 +233,9 @@ class Entity extends EmbedTypeBase implements ContainerFactoryPluginInterface { $entity_browser = $form_state->getValue('entity_browser') == '_none' ? '' : $form_state->getValue('entity_browser'); $form_state->setValue('entity_browser', $entity_browser); $form_state->setValue('entity_browser_settings', $form_state->getValue('entity_browser_settings', [])); + if ($form_state->hasValue('inline_entity_edit_enabled')) { + $form_state->setValue('inline_entity_edit_enabled', $form_state->getValue('inline_entity_edit_enabled')); + } parent::submitConfigurationForm($form, $form_state); }