From fbaab63b35b36f743bd250450d8e9e331d1229b2 Mon Sep 17 00:00:00 2001 From: slashrsm Date: Fri, 19 Feb 2016 16:07:59 +0530 Subject: [PATCH] Issue #2512702 by slashrsm, Lukas von Blarer, CTaPByK, EclipseGc: Implement configuration UI --- README.md | 14 +- config/schema/entity_browser.schema.yml | 3 + entity_browser.info.yml | 2 +- entity_browser.links.action.yml | 12 + entity_browser.links.menu.yml | 5 + entity_browser.routing.yml | 34 +++ entity_browser.services.yml | 5 + src/Controllers/CtoolsFallback.php | 29 +++ src/Controllers/EntityBrowserListBuilder.php | 45 ++++ src/DisplayBase.php | 7 +- src/DisplayInterface.php | 3 +- src/Entity/EntityBrowser.php | 78 +++++- src/EntityBrowserInterface.php | 46 +++- src/Form/DisplayConfig.php | 31 +++ src/Form/EntityBrowserDeleteForm.php | 52 ++++ src/Form/GeneralInfoConfig.php | 150 ++++++++++++ src/Form/PluginConfigFormBase.php | 55 +++++ src/Form/SelectionDisplayConfig.php | 31 +++ src/Form/WidgetSelectorConfig.php | 31 +++ src/Form/WidgetsConfig.php | 222 ++++++++++++++++++ src/Plugin/EntityBrowser/Display/IFrame.php | 47 ++++ src/Plugin/EntityBrowser/Display/Modal.php | 27 ++- src/Plugin/EntityBrowser/Widget/Upload.php | 13 + src/PluginConfigurationFormTrait.php | 51 ++++ src/Routing/CtoolsFallbackRouteEnhancer.php | 50 ++++ src/SelectionDisplayBase.php | 7 +- src/SelectionDisplayInterface.php | 3 +- src/Tests/ConfigUITest.php | 174 ++++++++++++++ src/WidgetBase.php | 7 +- src/WidgetInterface.php | 3 +- src/WidgetSelectorBase.php | 33 ++- src/WidgetSelectorInterface.php | 5 +- src/Wizard/EntityBrowserWizard.php | 78 ++++++ src/Wizard/EntityBrowserWizardAdd.php | 21 ++ .../Kernel/Extension/EntityBrowserTest.php | 2 - 35 files changed, 1348 insertions(+), 28 deletions(-) create mode 100644 entity_browser.links.action.yml create mode 100644 entity_browser.links.menu.yml create mode 100644 src/Controllers/CtoolsFallback.php create mode 100644 src/Controllers/EntityBrowserListBuilder.php create mode 100644 src/Form/DisplayConfig.php create mode 100644 src/Form/EntityBrowserDeleteForm.php create mode 100644 src/Form/GeneralInfoConfig.php create mode 100644 src/Form/PluginConfigFormBase.php create mode 100644 src/Form/SelectionDisplayConfig.php create mode 100644 src/Form/WidgetSelectorConfig.php create mode 100644 src/Form/WidgetsConfig.php create mode 100644 src/PluginConfigurationFormTrait.php create mode 100644 src/Routing/CtoolsFallbackRouteEnhancer.php create mode 100644 src/Tests/ConfigUITest.php create mode 100644 src/Wizard/EntityBrowserWizard.php create mode 100644 src/Wizard/EntityBrowserWizardAdd.php diff --git a/README.md b/README.md index 34b117b..0489948 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,15 @@ Provides standardized interface to list, create and select entities. ## Requirements * Latest dev release of Drupal 8.x. +* [Chaos tool set](https://drupal.org/project/ctools) (soft dependency for configuration UI) ## Configuration -There is no UI to configure entity browsers ATM. In order to test this module -you need to import yml files using drush or configuration management admin pages -(admin/config/development/configuration/single/import). +Module ships with a simple configuration UI which allows you to create, edit +and delete entity browsers. It depends on +[Chaos tool set](https://drupal.org/project/ctools). Enable it and navigate to +/admin/config/content/entity_browser. -We also provided a module that will create: - - content type with two entity reference fields - - two entity browsers (listing files and nodes) - - a view that is used on nodes entity browser - - form display configuration for entity reference fields to use entity browsers - In order to use this configuration for testing or to help you contribute just enable "Entity Browser example" module (entity_browser_example). diff --git a/config/schema/entity_browser.schema.yml b/config/schema/entity_browser.schema.yml index 6fb3d2e..750f536 100644 --- a/config/schema/entity_browser.schema.yml +++ b/config/schema/entity_browser.schema.yml @@ -63,6 +63,9 @@ entity_browser.browser.display.iframe: link_text: type: string label: 'Link text' + auto_open: + type: boolean + label: 'Auto open' entity_browser.browser.display.modal: type: mapping diff --git a/entity_browser.info.yml b/entity_browser.info.yml index 2369eb4..0f80902 100644 --- a/entity_browser.info.yml +++ b/entity_browser.info.yml @@ -4,4 +4,4 @@ type: module package: Media core: 8.x test_dependencies: - - ctools \ No newline at end of file + - ctools diff --git a/entity_browser.links.action.yml b/entity_browser.links.action.yml new file mode 100644 index 0000000..ec70619 --- /dev/null +++ b/entity_browser.links.action.yml @@ -0,0 +1,12 @@ +media.bundle_add: + route_name: media.bundle_add + title: 'Add media bundle' + appears_on: + - entity.media_bundle.collection + +entity_browser.add: + route_name: entity.entity_browser.add_form + title: 'Add Entity browser' + weight: 0 + appears_on: + - entity.entity_browser.collection diff --git a/entity_browser.links.menu.yml b/entity_browser.links.menu.yml new file mode 100644 index 0000000..4fa4b8a --- /dev/null +++ b/entity_browser.links.menu.yml @@ -0,0 +1,5 @@ +entity.entity_browser.collection: + title: 'Entity browsers' + parent: system.admin_config_content + description: 'Manage entity browsers.' + route_name: entity.entity_browser.collection diff --git a/entity_browser.routing.yml b/entity_browser.routing.yml index f7ddc09..412ed6e 100644 --- a/entity_browser.routing.yml +++ b/entity_browser.routing.yml @@ -11,3 +11,37 @@ entity_browser.edit_form: parameters: entity: type: entity:{entity_type} + +entity.entity_browser.add_form: + path: '/admin/config/content/entity_browser/add' + defaults: + _entity_wizard: 'entity_browser.add' + _title: 'Add Entity browser' + tempstore_id: 'entity_browser.config' + requirements: + _permission: 'administer entity browsers' + +entity.entity_browser.edit_form: + path: '/admin/config/content/entity_browser/{machine_name}/{step}' + defaults: + _entity_wizard: 'entity_browser.edit' + _title: 'Edit Entity browser' + tempstore_id: 'entity_browser.config' + requirements: + _permission: 'administer entity browsers' + +entity.entity_browser.collection: + path: '/admin/config/content/entity_browser' + defaults: + _entity_list: 'entity_browser' + _title: 'Entity Browsers' + requirements: + _permission: 'administer entity browsers' + +entity.entity_browser.delete_form: + path: '/admin/config/content/entity_browser/{entity_browser}/delete' + defaults: + _entity_form: 'entity_browser.delete' + _title: 'Delete Entity browser' + requirements: + _permission: 'administer entity browsers' diff --git a/entity_browser.services.yml b/entity_browser.services.yml index 120f350..88fedfd 100644 --- a/entity_browser.services.yml +++ b/entity_browser.services.yml @@ -17,3 +17,8 @@ services: entity_browser.route_subscriber: class: Drupal\entity_browser\RouteSubscriber arguments: ['@entity.manager', '@plugin.manager.entity_browser.display', '@entity.query'] + entity_browser.ctools_fallback_route_enhancer: + class: Drupal\entity_browser\Routing\CtoolsFallbackRouteEnhancer + arguments: ['@module_handler'] + tags: + - { name: route_enhancer } diff --git a/src/Controllers/CtoolsFallback.php b/src/Controllers/CtoolsFallback.php new file mode 100644 index 0000000..b85cd37 --- /dev/null +++ b/src/Controllers/CtoolsFallback.php @@ -0,0 +1,29 @@ + $this->t( + 'This form depends on Chaos tool suite module. Enable it and reload this page.', + [':url' => Url::fromUri('https://drupal.org/project/ctools')->toString()] + ), + ]; + } + +} diff --git a/src/Controllers/EntityBrowserListBuilder.php b/src/Controllers/EntityBrowserListBuilder.php new file mode 100644 index 0000000..53cf970 --- /dev/null +++ b/src/Controllers/EntityBrowserListBuilder.php @@ -0,0 +1,45 @@ +t('ID'); + $header['name'] = $this->t('Name'); + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + /* @var $entity \Drupal\entity_browser\Entity\EntityBrowser */ + $row['id'] = $entity->id(); + $row['name'] = $entity->label(); + return $row + parent::buildRow($entity); + } + +} diff --git a/src/DisplayBase.php b/src/DisplayBase.php index e378b5b..cbbd83b 100644 --- a/src/DisplayBase.php +++ b/src/DisplayBase.php @@ -16,6 +16,8 @@ */ abstract class DisplayBase extends PluginBase implements DisplayInterface, ContainerFactoryPluginInterface { + use PluginConfigurationFormTrait; + /** * Plugin label. * @@ -78,7 +80,10 @@ public function defaultConfiguration() { * {@inheritdoc} */ public function getConfiguration() { - return $this->configuration; + return array_diff_key( + $this->configuration, + ['entity_browser_id' => 0] + ); } /** diff --git a/src/DisplayInterface.php b/src/DisplayInterface.php index 6e6dfb8..61e9521 100644 --- a/src/DisplayInterface.php +++ b/src/DisplayInterface.php @@ -9,11 +9,12 @@ use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\Component\Plugin\PluginInspectionInterface; +use Drupal\Core\Plugin\PluginFormInterface; /** * Defines the interface for entity browser displays. */ -interface DisplayInterface extends PluginInspectionInterface, ConfigurablePluginInterface { +interface DisplayInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface { /** * Returns the display label. diff --git a/src/Entity/EntityBrowser.php b/src/Entity/EntityBrowser.php index abfbe64..c8508d7 100644 --- a/src/Entity/EntityBrowser.php +++ b/src/Entity/EntityBrowser.php @@ -25,9 +25,22 @@ * label = @Translation("Entity browser"), * handlers = { * "form" = { - * "entity_browser" = "Drupal\entity_browser\Form\EntityBrowserForm" + * "entity_browser" = "Drupal\entity_browser\Form\EntityBrowserForm", + * "delete" = "Drupal\entity_browser\Form\EntityBrowserDeleteForm", + * }, + * "access" = "Drupal\Core\Entity\EntityAccessControlHandler", + * "list_builder" = "Drupal\entity_browser\Controllers\EntityBrowserListBuilder", + * "wizard" = { + * "add" = "Drupal\entity_browser\Wizard\EntityBrowserWizardAdd", + * "edit" = "Drupal\entity_browser\Wizard\EntityBrowserWizard", * } * }, + * links = { + * "canonical" = "/admin/config/content/entity_browser/{machine_name}/{step}", + * "collection" = "/admin/config/content/entity_browser", + * "edit-form" = "/admin/config/content/entity_browser/{machine_name}/{step}", + * "delete-form" = "/admin/config/content/entity_browser/{entity_browser}/delete", + * }, * admin_permission = "administer entity browsers", * config_prefix = "browser", * entity_keys = { @@ -89,7 +102,7 @@ class EntityBrowser extends ConfigEntityBase implements EntityBrowserInterface, * * @var array */ - protected $widgets; + protected $widgets = []; /** * Holds the collection of widgets that are used by this entity browser. @@ -172,7 +185,7 @@ public function getName() { * {@inheritdoc} */ public function setName($name) { - $this->set('name', $name); + $this->name = $name; return $this; } @@ -183,6 +196,44 @@ public function getDisplay() { return $this->displayPluginCollection()->get($this->display); } + /** + * {@inheritdoc} + */ + public function setLabel($label) { + $this->label = $label; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDisplay($display) { + $this->display = $display; + $this->displayPluginCollection = NULL; + $this->getDisplay(); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setWidgetSelector($widget_selector) { + $this->widget_selector = $widget_selector; + $this->widgetSelectorCollection = NULL; + $this->getWidgetSelector(); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setSelectionDisplay($selection_display) { + $this->selection_display = $selection_display; + $this->selectionDisplayCollection = NULL; + $this->getSelectionDisplay(); + return $this; + } + /** * Returns display plugin collection. * @@ -247,7 +298,7 @@ public function addWidget(array $configuration) { * {@inheritdoc} */ public function deleteWidget(WidgetInterface $widget) { - $this->getWidgets()->removeInstanceId($widget->getUuid()); + $this->getWidgets()->removeInstanceId($widget->uuid()); $this->save(); return $this; } @@ -371,7 +422,7 @@ public function __sleep() { // Save configuration for all plugins. $this->widgets = $this->getWidgets()->getConfiguration(); $this->widget_selector_configuration = $this->widgetSelectorPluginCollection()->getConfiguration(); - $this->display_configuration = $this->widgetSelectorPluginCollection()->getConfiguration(); + $this->display_configuration = $this->displayPluginCollection()->getConfiguration(); $this->selection_display_configuration = $this->selectionDisplayPluginCollection()->getConfiguration(); return array_diff( @@ -406,4 +457,21 @@ public function getFormObject() { return $form_class; } + /** + * {@inheritdoc} + */ + protected function urlRouteParameters($rel) { + $uri_route_parameters = parent::urlRouteParameters($rel); + + // Form wizard expects step argument and uses machine_name instead of + // entity_browser. + if ($rel == 'edit-form') { + $uri_route_parameters['step'] = 'general'; + $uri_route_parameters['machine_name'] = $uri_route_parameters['entity_browser']; + unset($uri_route_parameters['entity_browser']); + } + + return $uri_route_parameters; + } + } diff --git a/src/EntityBrowserInterface.php b/src/EntityBrowserInterface.php index 70623f8..cc5c614 100644 --- a/src/EntityBrowserInterface.php +++ b/src/EntityBrowserInterface.php @@ -16,7 +16,7 @@ interface EntityBrowserInterface extends ConfigEntityInterface { /** - * Returns the entity browser name. + * Gets the entity browser name. * * @return string * The name of the entity browser. @@ -34,6 +34,50 @@ public function getName(); */ public function setName($name); + /** + * Sets the label of the entity browser. + * + * @param string $label + * The label of the entity browser. + * + * @return \Drupal\entity_browser\EntityBrowserInterface + * The class instance this method is called on. + */ + public function setLabel($label); + + /** + * Sets the id of the display plugin + * + * @param string $display + * The id of the display plugin. + * + * @return \Drupal\entity_browser\EntityBrowserInterface + * The class instance this method is called on. + */ + public function setDisplay($display); + + /** + * Sets the id of the widget selector plugin + * + * @param string $display + * The id of the widget selector plugin. + * + * @return \Drupal\entity_browser\EntityBrowserInterface + * The class instance this method is called on. + */ + public function setWidgetSelector($widget_selector); + + /** + * Sets the id of the selection display plugin + * + * @param string $display + * The id of the selection display plugin. + * + * @return \Drupal\entity_browser\EntityBrowserInterface + * The class instance this method is called on. + */ + public function setSelectionDisplay($selection_display); + /** * Returns the display. * diff --git a/src/Form/DisplayConfig.php b/src/Form/DisplayConfig.php new file mode 100644 index 0000000..2804718 --- /dev/null +++ b/src/Form/DisplayConfig.php @@ -0,0 +1,31 @@ +getDisplay(); + } + +} diff --git a/src/Form/EntityBrowserDeleteForm.php b/src/Form/EntityBrowserDeleteForm.php new file mode 100644 index 0000000..24de209 --- /dev/null +++ b/src/Form/EntityBrowserDeleteForm.php @@ -0,0 +1,52 @@ +t( + 'Are you sure you want to delete entity browser %label?', + ['%label' => $this->entity->label()] + ); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Delete Entity Browser'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('entity.entity_browser.collection'); + } + + /** + * {@inheritdoc} + */ + protected function getDeletionMessage() { + return $this->t( + 'Entity browser %label was deleted.', + ['%label' => $this->entity->label()] + ); + } + +} diff --git a/src/Form/GeneralInfoConfig.php b/src/Form/GeneralInfoConfig.php new file mode 100644 index 0000000..9369c58 --- /dev/null +++ b/src/Form/GeneralInfoConfig.php @@ -0,0 +1,150 @@ +displayManager = $display_manager; + $this->selectionDisplayManager = $selection_display_manager; + $this->widgetSelectorManager = $widget_selector_manager; + $this->widgetManager = $widget_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity_browser.display'), + $container->get('plugin.manager.entity_browser.widget_selector'), + $container->get('plugin.manager.entity_browser.selection_display'), + $container->get('plugin.manager.entity_browser.widget') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'entity_browser_general_info_config_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $cached_values['entity_browser']; + + $displays = []; + foreach ($this->displayManager->getDefinitions() as $plugin_id => $plugin_definition) { + $displays[$plugin_id] = $plugin_definition['label']; + } + $form['display'] = [ + '#type' => 'select', + '#title' => $this->t('Display plugin'), + '#default_value' => $entity_browser->get('display') ? $entity_browser->getDisplay()->getPluginId() : NULL, + '#options' => $displays, + '#required' => TRUE, + ]; + + $widget_selectors = []; + foreach ($this->widgetSelectorManager->getDefinitions() as $plugin_id => $plugin_definition) { + $widget_selectors[$plugin_id] = $plugin_definition['label']; + } + $form['widget_selector'] = [ + '#type' => 'select', + '#title' => $this->t('Widget selector plugin'), + '#default_value' => $entity_browser->get('widget_selector') ? $entity_browser->getWidgetSelector()->getPluginId() : NULL, + '#options' => $widget_selectors, + '#required' => TRUE, + ]; + + $selection_display = []; + foreach ($this->selectionDisplayManager->getDefinitions() as $plugin_id => $plugin_definition) { + $selection_display[$plugin_id] = $plugin_definition['label']; + } + $form['selection_display'] = [ + '#type' => 'select', + '#title' => $this->t('Selection display plugin'), + '#default_value' => $entity_browser->get('selection_display') ? $entity_browser->getSelectionDisplay()->getPluginId() : NULL, + '#options' => $selection_display, + '#required' => TRUE, + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $form_state->getTemporaryValue('wizard')['entity_browser']; + $entity_browser->setName($form_state->getValue('id')) + ->setLabel($form_state->getValue('label')) + ->setDisplay($form_state->getValue('display')) + ->setWidgetSelector($form_state->getValue('widget_selector')) + ->setSelectionDisplay($form_state->getValue('selection_display')); + } + +} diff --git a/src/Form/PluginConfigFormBase.php b/src/Form/PluginConfigFormBase.php new file mode 100644 index 0000000..1a09667 --- /dev/null +++ b/src/Form/PluginConfigFormBase.php @@ -0,0 +1,55 @@ +getTemporaryValue('wizard')['entity_browser']; + $form = $this->getPlugin($entity_browser)->buildConfigurationForm($form, $form_state); + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $form_state->getTemporaryValue('wizard')['entity_browser']; + $this->getPlugin($entity_browser)->validateConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $form_state->getTemporaryValue('wizard')['entity_browser']; + $this->getPlugin($entity_browser)->submitConfigurationForm($form, $form_state); + } + + /** + * Gets plugin that form operates with. + * + * @return \Drupal\Core\Plugin\PluginFormInterface + * Plugin instance. + */ + abstract public function getPlugin(EntityBrowserInterface $entity_browser); + +} diff --git a/src/Form/SelectionDisplayConfig.php b/src/Form/SelectionDisplayConfig.php new file mode 100644 index 0000000..da1195a --- /dev/null +++ b/src/Form/SelectionDisplayConfig.php @@ -0,0 +1,31 @@ +getSelectionDisplay(); + } + +} diff --git a/src/Form/WidgetSelectorConfig.php b/src/Form/WidgetSelectorConfig.php new file mode 100644 index 0000000..275e420 --- /dev/null +++ b/src/Form/WidgetSelectorConfig.php @@ -0,0 +1,31 @@ +getWidgetSelector(); + } + +} diff --git a/src/Form/WidgetsConfig.php b/src/Form/WidgetsConfig.php new file mode 100644 index 0000000..6f696f4 --- /dev/null +++ b/src/Form/WidgetsConfig.php @@ -0,0 +1,222 @@ +widgetManager = $widget_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity_browser.widget') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'entity_browser_widgets_config_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $form_state->getTemporaryValue('wizard')['entity_browser']; + + $widgets = []; + foreach ($this->widgetManager->getDefinitions() as $plugin_id => $plugin_definition) { + $widgets[$plugin_id] = $plugin_definition['label']; + } + $default_widgets = []; + foreach ($entity_browser->getWidgets() as $widget) { + /** @var \Drupal\entity_browser\WidgetInterface $widget */ + $default_widgets[] = $widget->id(); + } + $form['widget'] = [ + '#type' => 'select', + '#title' => $this->t('Add widget plugin'), + '#options' => ['_none_' => '- ' . $this->t('Select a widget to add it') . ' -'] + $widgets, + '#ajax' => [ + 'callback' => [get_class($this), 'tableUpdatedAjaxCallback'], + 'wrapper' => 'widgets', + 'event' => 'change' + ], + '#executes_submit_callback' => TRUE, + '#submit' => [[get_class($this), 'submitAddWidget']], + '#limit_validation_errors' => [['widget']], + ]; + $form_state->unsetValue('widget'); + + $form['widgets'] = [ + '#type' => 'container', + '#attributes' => ['id' => 'widgets'], + ]; + + $form['widgets']['table'] = [ + '#type' => 'table', + '#header' => [ + $this->t('Form'), + $this->t('Operations'), + $this->t('Actions'), + $this->t('Weight'), + ], + '#empty' => $this->t('There are no widgets.'), + '#tabledrag' => [[ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'variant-weight', + ]], + ]; + + /** @var \Drupal\entity_browser\WidgetInterface $widget */ + foreach ($entity_browser->getWidgets() as $uuid => $widget) { + $row = [ + '#attributes' => [ + 'class' => ['draggable'], + ], + ]; + $row['label'] = [ + '#type' => 'textfield', + '#default_value' => $widget->label(), + '#title' => $this->t('Label'), + ]; + $row['form'] = []; + $row['form'] = $widget->buildConfigurationForm($row['form'], $form_state); + $row['remove'] = [ + '#type' => 'submit', + '#value' => $this->t('Delete'), + '#name' => 'remove' . $uuid, + '#ajax' => [ + 'callback' => [get_class($this), 'tableUpdatedAjaxCallback'], + 'wrapper' => 'widgets', + 'event' => 'click' + ], + '#executes_submit_callback' => TRUE, + '#submit' => [[get_class($this), 'submitDeleteWidget']], + '#arguments' => $uuid, + ]; + $row['weight'] = [ + '#type' => 'weight', + '#default_value' => $widget->getWeight(), + '#title' => $this->t('Weight for @widget widget', ['@widget' => $widget->label()]), + '#title_display' => 'invisible', + '#attributes' => [ + 'class' => ['variant-weight'], + ], + ]; + $form['widgets']['table'][$uuid] = $row; + } + return $form; + } + + /** + * AJAX submit callback for adding widgets to the entity browser. + */ + public static function submitAddWidget($form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $cached_values['entity_browser']; + $widgets_num = count($entity_browser->getWidgets()); + $widget = $form_state->getValue('widget'); + $weight = $widgets_num + 1; + $entity_browser->addWidget([ + 'id' => $widget, + 'label' => $widget, + 'weight' => $weight, + // Configuration will be set on the widgets page. + 'settings' => [], + ]); + \Drupal::service('user.shared_tempstore') + ->get('entity_browser.config') + ->set($entity_browser->id(), $cached_values); + $form_state->setRebuild(); + } + + /** + * AJAX submit callback for removing widgets from the entity browser. + */ + public static function submitDeleteWidget($form, FormStateInterface $form_state) { + $cached_values = $form_state->getTemporaryValue('wizard'); + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $cached_values['entity_browser']; + $entity_browser->deleteWidget($entity_browser->getWidget($form_state->getTriggeringElement()['#arguments'])); + \Drupal::service('user.shared_tempstore') + ->get('entity_browser.config') + ->set($entity_browser->id(), $cached_values); + $form_state->setRebuild(); + } + + /** + * AJAX callback for all operations that update widgets table. + */ + public static function tableUpdatedAjaxCallback($form, $form_state) { + return $form['widgets']; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $form_state->getTemporaryValue('wizard')['entity_browser']; + /** @var \Drupal\entity_browser\WidgetInterface $widget */ + foreach ($entity_browser->getWidgets() as $widget) { + $widget->validateConfigurationForm($form, $form_state); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ + $entity_browser = $form_state->getTemporaryValue('wizard')['entity_browser']; + $table = $form_state->getValue('table'); + /** @var \Drupal\entity_browser\WidgetInterface $widget */ + foreach ($entity_browser->getWidgets() as $uuid => $widget) { + $widget->submitConfigurationForm($form, $form_state); + $widget->setConfiguration([ + 'settings' => !empty($table[$uuid]['form']) ? $table[$uuid]['form'] : [], + 'weight' => $table[$uuid]['weight'], + 'label' => $table[$uuid]['label'], + 'uuid' => $uuid, + 'id' => $widget->id(), + ]); + } + } + +} diff --git a/src/Plugin/EntityBrowser/Display/IFrame.php b/src/Plugin/EntityBrowser/Display/IFrame.php index 3a8205b..7ffe0ef 100644 --- a/src/Plugin/EntityBrowser/Display/IFrame.php +++ b/src/Plugin/EntityBrowser/Display/IFrame.php @@ -8,6 +8,7 @@ use Drupal\Component\Uuid\UuidInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\entity_browser\DisplayBase; @@ -234,4 +235,50 @@ public function path() { return '/entity-browser/iframe/' . $this->configuration['entity_browser_id']; } + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $configuration = $this->getConfiguration(); + $form['width'] = [ + '#type' => 'number', + '#title' => $this->t('Width of the iFrame'), + '#min' => 1, + '#default_value' => $configuration['width'], + ]; + + $form['height'] = [ + '#type' => 'number', + '#title' => $this->t('Height of the iFrame'), + '#min' => 1, + '#default_value' => $configuration['height'], + ]; + + $form['link_text'] = [ + '#type' => 'textfield', + '#title' => $this->t('Link text'), + '#default_value' => $configuration['link_text'], + ]; + + $form['auto_open'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Auto open entity browser'), + '#default_value' => $configuration['auto_open'], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + if ($form_state->getValue('width') <= 0) { + $form_state->setError($form['width'], $this->t('Width must be greather than 0.')); + } + if ($form_state->getValue('height') <= 0) { + $form_state->setError($form['height'], $this->t('Height must be greather than 0.')); + } + } + } diff --git a/src/Plugin/EntityBrowser/Display/Modal.php b/src/Plugin/EntityBrowser/Display/Modal.php index 78f5c3f..5e4e95b 100644 --- a/src/Plugin/EntityBrowser/Display/Modal.php +++ b/src/Plugin/EntityBrowser/Display/Modal.php @@ -353,7 +353,32 @@ public function setUuid($uuid) { * @inheritDoc */ public function __sleep() { - return array('configuration'); + return ['configuration']; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $configuration = $this->getConfiguration(); + $form['width'] = [ + '#type' => 'number', + '#title' => $this->t('Width of the modal'), + '#min' => 1, + '#default_value' => $configuration['width'], + ]; + $form['height'] = [ + '#type' => 'number', + '#title' => $this->t('Height of the modal'), + '#min' => 1, + '#default_value' => $configuration['height'], + ]; + $form['link_text'] = [ + '#type' => 'textfield', + '#title' => $this->t('Link text'), + '#default_value' => $configuration['link_text'], + ]; + return $form; } } diff --git a/src/Plugin/EntityBrowser/Widget/Upload.php b/src/Plugin/EntityBrowser/Widget/Upload.php index e34ae9e..913477f 100644 --- a/src/Plugin/EntityBrowser/Widget/Upload.php +++ b/src/Plugin/EntityBrowser/Widget/Upload.php @@ -102,4 +102,17 @@ protected function extractFiles(FormStateInterface $form_state) { return $files; } + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['upload_location'] = [ + '#type' => 'textfield', + '#title' => $this->t('Upload location'), + '#default_value' => $this->configuration['upload_location'], + ]; + + return $form; + } + } diff --git a/src/PluginConfigurationFormTrait.php b/src/PluginConfigurationFormTrait.php new file mode 100644 index 0000000..2b7afd6 --- /dev/null +++ b/src/PluginConfigurationFormTrait.php @@ -0,0 +1,51 @@ +getValues(); + + if ($this instanceof WidgetInterface) { + $values = $values['table'][$this->uuid()]; + } + + if (!empty($values)) { + foreach ($values as $key => $value) { + if (isset($this->configuration[$key])) { + $this->configuration[$key] = $value; + } + } + } + } + +} diff --git a/src/Routing/CtoolsFallbackRouteEnhancer.php b/src/Routing/CtoolsFallbackRouteEnhancer.php new file mode 100644 index 0000000..e2988e2 --- /dev/null +++ b/src/Routing/CtoolsFallbackRouteEnhancer.php @@ -0,0 +1,50 @@ +moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public function enhance(array $defaults, Request $request) { + if (!$this->moduleHandler->moduleExists('ctools')) { + $defaults['_controller'] = '\Drupal\entity_browser\Controllers\CtoolsFallback::displayMessage'; + } + + return $defaults; + } + + /** + * {@inheritdoc} + */ + public function applies(Route $route) { + return $route->hasDefault('_entity_wizard') && strpos($route->getDefault('_entity_wizard'), 'entity_browser.') === 0; + } + +} diff --git a/src/SelectionDisplayBase.php b/src/SelectionDisplayBase.php index 4879a98..a1491df 100644 --- a/src/SelectionDisplayBase.php +++ b/src/SelectionDisplayBase.php @@ -20,6 +20,8 @@ */ abstract class SelectionDisplayBase extends PluginBase implements SelectionDisplayInterface, ContainerFactoryPluginInterface { + use PluginConfigurationFormTrait; + /** * Plugin label. * @@ -85,7 +87,10 @@ public function defaultConfiguration() { * {@inheritdoc} */ public function getConfiguration() { - return $this->configuration; + return array_diff_key( + $this->configuration, + ['entity_browser_id' => 0] + ); } /** diff --git a/src/SelectionDisplayInterface.php b/src/SelectionDisplayInterface.php index b34ba96..ccc23ba 100644 --- a/src/SelectionDisplayInterface.php +++ b/src/SelectionDisplayInterface.php @@ -10,11 +10,12 @@ use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\PluginFormInterface; /** * Defines the interface for entity browser selection displays. */ -interface SelectionDisplayInterface extends PluginInspectionInterface, ConfigurablePluginInterface { +interface SelectionDisplayInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface { /** * Returns the selection display label. diff --git a/src/Tests/ConfigUITest.php b/src/Tests/ConfigUITest.php new file mode 100644 index 0000000..72743ad --- /dev/null +++ b/src/Tests/ConfigUITest.php @@ -0,0 +1,174 @@ +drupalPlaceBlock('local_actions_block'); + $this->adminUser = $this->drupalCreateUser([ + 'administer entity browsers', + ]); + } + + /** + * Tests the entity browser config UI. + */ + public function testConfigUI() { + $this->drupalGet('/admin/config/content/entity_browser'); + $this->assertResponse(403, "Anonymous user can't access entity browser listing page."); + $this->drupalGet('/admin/config/content/entity_browser/add'); + $this->assertResponse(403, "Anonymous user can't access entity browser add form."); + + // Listing is empty. + $this->drupalLogin($this->adminUser); + $this->drupalGet('/admin/config/content/entity_browser'); + $this->assertResponse(200, 'Admin user is able to navigate to the entity browser listing page.'); + $this->assertText('There is no Entity browser yet.', 'Entity browsers table is empty.'); + + // Add page. + $this->clickLink('Add Entity browser'); + $this->assertUrl('/admin/config/content/entity_browser/add'); + $edit = [ + 'label' => 'Test entity browser', + 'id' => 'test_entity_browser', + 'display' => 'iframe', + 'widget_selector' => 'tabs', + 'selection_display' => 'no_display', + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Display configuration step. + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/display', ['query' => ['js' => 'nojs']]); + $edit = [ + 'width' => 100, + 'height' => 100, + 'link_text' => 'All animals are created equal', + 'auto_open' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, 'Next'); + + // Widget selector step. + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/widget_selector', ['query' => ['js' => 'nojs']]); + $this->drupalPostForm(NULL, [], 'Next'); + + // Selection display step. + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/selection_display', ['query' => ['js' => 'nojs']]); + $this->drupalPostForm(NULL, [], 'Next'); + + // Widgets step. + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/widgets', ['query' => ['js' => 'nojs']]); + $this->drupalPostAjaxForm(NULL, ['widget' => 'upload'], 'widget'); + $this->drupalPostForm(NULL, [], 'Finish'); + + // Back on listing page. + $this->assertUrl('/admin/config/content/entity_browser'); + $this->assertText('Test entity browser', 'Entity browser label found on the listing page'); + $this->assertText('test_entity_browser', 'Entity browser ID found on the listing page.'); + + // Check structure of entity browser object. + /** @var \Drupal\entity_browser\EntityBrowserInterface $loaded_entity_browser */ + $loaded_entity_browser = $this->container->get('entity_type.manager') + ->getStorage('entity_browser') + ->load('test_entity_browser'); + $this->assertEqual('test_entity_browser', $loaded_entity_browser->id(), 'Entity browser ID was correctly saved.'); + $this->assertEqual('Test entity browser', $loaded_entity_browser->label(), 'Entity browser label was correctly saved.'); + $this->assertTrue($loaded_entity_browser->getDisplay() instanceof IFrame, 'Entity browser display was correctly saved.'); + $expected = [ + 'width' => '100', + 'height' => '100', + 'link_text' => 'All animals are created equal', + 'auto_open' => TRUE, + ]; + $this->assertEqual($expected, $loaded_entity_browser->getDisplay()->getConfiguration(), 'Entity browser display configuration was correctly saved.'); + $this->assertTrue($loaded_entity_browser->getSelectionDisplay() instanceof NoDisplay, 'Entity browser selection display was correctly saved.'); + $this->assertEqual([], $loaded_entity_browser->getSelectionDisplay()->getConfiguration(), 'Entity browser selection display configuration was correctly saved.'); + $this->assertEqual($loaded_entity_browser->getWidgetSelector() instanceof Tabs, 'Entity browser widget selector was correctly saved.'); + $this->assertEqual([], $loaded_entity_browser->getWidgetSelector()->getConfiguration(), 'Entity browser widget selector configuration was correctly saved.'); + + $widgets = $loaded_entity_browser->getWidgets(); + $uuid = current($widgets->getInstanceIds()); + /** @var \Drupal\entity_browser\WidgetInterface $widget */ + $widget = $widgets->get($uuid); + $this->assertEqual('upload', $widget->id(), 'Entity browser widget was correctly saved.'); + $this->assertEqual($uuid, $widget->uuid(), 'Entity browser widget uuid was correctly saved.'); + $configuration = $widget->getConfiguration()['settings']; + $this->assertEqual(['upload_location' => 'public://'], $configuration, 'Entity browser widget configuration was correctly saved.'); + $this->assertEqual(1, $widget->getWeight(), 'Entity browser widget weight was correctly saved.'); + + // Navigate to edit. + $this->clickLink('Edit'); + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/general'); + $this->assertFieldById('edit-label', 'Test entity browser', 'Correct label found.'); + $this->assertText('test_entity_browser', 'Correct id found.'); + $this->assertOptionSelected('edit-display', 'iframe', 'Correct display selected.'); + $this->assertOptionSelected('edit-widget-selector', 'tabs', 'Correct widget selector selected.'); + $this->assertOptionSelected('edit-selection-display', 'no_display', 'Correct selection display selected.'); + + $this->drupalPostForm(NULL,[], 'Next'); + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/display', ['query' => ['js' => 'nojs']]); + $this->assertFieldById('edit-width', '100', 'Correct value for width found.'); + $this->assertFieldById('edit-height', '100', 'Correct value for height found.'); + $this->assertFieldById('edit-link-text', 'All animals are created equal', 'Correct value for link text found.'); + $this->assertFieldChecked('edit-auto-open', 'Auto open is enabled.'); + + $this->drupalPostForm(NULL,[], 'Next'); + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/widget_selector', ['query' => ['js' => 'nojs']]); + + $this->drupalPostForm(NULL,[], 'Next'); + $this->assertUrl('/admin/config/content/entity_browser/test_entity_browser/selection_display', ['query' => ['js' => 'nojs']]); + + $this->drupalPostForm(NULL,[], 'Next'); + $this->assertFieldById('edit-table-' . $uuid . '-label', 'upload', 'Correct value for widget label found.'); + $this->assertFieldById('edit-table-' . $uuid . '-form-upload-location', 'public://', 'Correct value for upload location found.'); + + $this->drupalPostForm(NULL,[], 'Finish'); + + $this->drupalLogout(); + $this->drupalGet('/admin/config/content/entity_browser/test_entity_browser/general'); + $this->assertResponse(403, "Anonymous user can't access entity browser edit form."); + + $this->drupalLogin($this->adminUser); + $this->drupalGet('/admin/config/content/entity_browser'); + $this->clickLink('Delete'); + $this->assertText('This action cannot be undone.', 'Delete question found.'); + $this->drupalPostForm(NULL, [], 'Delete Entity Browser'); + + $this->assertText('Entity browser Test entity browser was deleted.', 'Confirmation message found.'); + $this->assertText('There is no Entity browser yet.', 'Entity browsers table is empty.'); + $this->drupalLogout(); + } + +} diff --git a/src/WidgetBase.php b/src/WidgetBase.php index 1fab349..e82ceac 100644 --- a/src/WidgetBase.php +++ b/src/WidgetBase.php @@ -20,6 +20,8 @@ */ abstract class WidgetBase extends PluginBase implements WidgetInterface, ContainerFactoryPluginInterface { + use PluginConfigurationFormTrait; + /** * Plugin id. * @@ -105,7 +107,10 @@ public function defaultConfiguration() { */ public function getConfiguration() { return [ - 'settings' => $this->configuration, + 'settings' => array_diff_key( + $this->configuration, + ['entity_browser_id' => 0] + ), 'uuid' => $this->uuid(), 'weight' => $this->getWeight(), 'label' => $this->label(), diff --git a/src/WidgetInterface.php b/src/WidgetInterface.php index 0dd58dd..339c712 100644 --- a/src/WidgetInterface.php +++ b/src/WidgetInterface.php @@ -10,11 +10,12 @@ use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\PluginFormInterface; /** * Defines the interface for entity browser widgets. */ -interface WidgetInterface extends PluginInspectionInterface, ConfigurablePluginInterface { +interface WidgetInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface { /** * Returns the widget id. diff --git a/src/WidgetSelectorBase.php b/src/WidgetSelectorBase.php index aef1971..5758d0e 100644 --- a/src/WidgetSelectorBase.php +++ b/src/WidgetSelectorBase.php @@ -9,13 +9,13 @@ use Drupal\Core\Plugin\PluginBase; use Drupal\Core\Form\FormStateInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Base class for widget selector plugins. */ abstract class WidgetSelectorBase extends PluginBase implements WidgetSelectorInterface { + use PluginConfigurationFormTrait; /** * Plugin label. @@ -46,6 +46,37 @@ public function __construct($configuration, $plugin_id, $plugin_definition) { $this->widget_ids = $this->configuration['widget_ids']; } + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return array_diff_key( + $this->configuration, + ['widget_ids' => 0] + ); + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = $configuration; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + /** * {@inheritdoc} */ diff --git a/src/WidgetSelectorInterface.php b/src/WidgetSelectorInterface.php index 9fc7a24..6ccbfaa 100644 --- a/src/WidgetSelectorInterface.php +++ b/src/WidgetSelectorInterface.php @@ -7,14 +7,15 @@ namespace Drupal\entity_browser; -use Drupal\Component\Plugin\LazyPluginCollection; +use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Plugin\PluginFormInterface; /** * Defines the interface for entity browser widget selectors. */ -interface WidgetSelectorInterface extends PluginInspectionInterface { +interface WidgetSelectorInterface extends PluginInspectionInterface, ConfigurablePluginInterface, PluginFormInterface { /** * Returns the widget selector label. diff --git a/src/Wizard/EntityBrowserWizard.php b/src/Wizard/EntityBrowserWizard.php new file mode 100644 index 0000000..e70d964 --- /dev/null +++ b/src/Wizard/EntityBrowserWizard.php @@ -0,0 +1,78 @@ +t('Entity browser'); + } + + /** + * {@inheritdoc} + */ + public function getMachineLabel() { + return $this->t('Label'); + } + + /** + * {@inheritdoc} + */ + public function getEntityType() { + return 'entity_browser'; + } + + /** + * {@inheritdoc} + */ + public function exists() { + return 'Drupal\entity_browser\Entity\EntityBrowser::load'; + } + + /** + * {@inheritdoc} + */ + public function getOperations($cached_values) { + return [ + 'general' => [ + 'title' => $this->t('General information'), + 'form' => GeneralInfoConfig::class, + ], + 'display' => [ + 'title' => $this->t('Display'), + 'form' => DisplayConfig::class, + ], + 'widget_selector' => [ + 'title' => $this->t('Widget selector'), + 'form' => WidgetSelectorConfig::class, + ], + 'selection_display' => [ + 'title' => $this->t('Selection display'), + 'form' => SelectionDisplayConfig::class, + ], + 'widgets' => [ + 'title' => $this->t('Widgets'), + 'form' => WidgetsConfig::class, + ], + ]; + } + +} diff --git a/src/Wizard/EntityBrowserWizardAdd.php b/src/Wizard/EntityBrowserWizardAdd.php new file mode 100644 index 0000000..9845a3c --- /dev/null +++ b/src/Wizard/EntityBrowserWizardAdd.php @@ -0,0 +1,21 @@ +randomString(4)); parent::setUp(); - $this->installSchema('system', 'router'); - $this->controller = $this->container->get('entity.manager')->getStorage('entity_browser'); $this->widgetUUID = $this->container->get('uuid')->generate(); $this->routeProvider = $this->container->get('router.route_provider');