3 namespace Drupal\entity_browser;
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
7 use Drupal\Core\Plugin\PluginBase;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10 use Drupal\entity_browser\Events\EntitySelectionEvent;
11 use Drupal\entity_browser\Events\Events;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14 use Symfony\Component\Validator\ConstraintViolationList;
17 * Base class for widget plugins.
19 abstract class WidgetBase extends PluginBase implements WidgetInterface, ContainerFactoryPluginInterface {
21 use PluginConfigurationFormTrait;
51 * Event dispatcher service.
53 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
55 protected $eventDispatcher;
58 * Entity type manager service.
60 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
62 protected $entityTypeManager;
65 * The Widget Validation Manager service.
67 * @var \Drupal\entity_browser\WidgetValidationManager
69 protected $validationManager;
72 * WidgetBase constructor.
74 * @param array $configuration
75 * A configuration array containing information about the plugin instance.
76 * @param string $plugin_id
77 * The plugin_id for the plugin instance.
78 * @param mixed $plugin_definition
79 * The plugin implementation definition.
80 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
81 * Event dispatcher service.
82 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
83 * The entity type manager service.
84 * @param \Drupal\entity_browser\WidgetValidationManager $validation_manager
85 * The Widget Validation Manager service.
87 public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityTypeManagerInterface $entity_type_manager, WidgetValidationManager $validation_manager) {
88 parent::__construct($configuration, $plugin_id, $plugin_definition);
89 $this->eventDispatcher = $event_dispatcher;
90 $this->entityTypeManager = $entity_type_manager;
91 $this->validationManager = $validation_manager;
92 $this->setConfiguration($configuration);
98 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
103 $container->get('event_dispatcher'),
104 $container->get('entity_type.manager'),
105 $container->get('plugin.manager.entity_browser.widget_validation')
112 public function getForm(array &$original_form, FormStateInterface $form_state, array $additional_widget_parameters) {
115 if ($form_state->has(['entity_browser', 'widget_context'])) {
116 $this->handleWidgetContext($form_state->get(['entity_browser', 'widget_context']));
119 // Check if widget supports auto select functionality and expose config to
120 // front-end javascript.
122 if ($this->getPluginDefinition()['auto_select']) {
123 $autoSelect = $this->configuration['auto_select'];
124 $form['#attached']['drupalSettings']['entity_browser_widget']['auto_select'] = $autoSelect;
127 // In case of auto select, widget will handle adding entities in JS.
130 '#type' => 'actions',
133 '#value' => $this->configuration['submit_text'],
134 '#eb_widget_main_submit' => TRUE,
135 '#attributes' => ['class' => ['is-entity-browser-submit']],
136 '#button_type' => 'primary',
147 public function defaultConfiguration() {
149 'submit_text' => $this->t('Select entities'),
152 // If auto select is supported by Widget, append default configuration.
153 if ($this->getPluginDefinition()['auto_select']) {
154 $defaultConfig['auto_select'] = FALSE;
157 return $defaultConfig;
163 public function getConfiguration() {
165 'settings' => array_diff_key(
166 $this->configuration,
167 ['entity_browser_id' => 0]
169 'uuid' => $this->uuid(),
170 'weight' => $this->getWeight(),
171 'label' => $this->label(),
179 public function setConfiguration(array $configuration) {
188 $this->configuration = NestedArray::mergeDeep(
189 $this->defaultConfiguration(),
190 $configuration['settings']
192 $this->label = $configuration['label'];
193 $this->weight = $configuration['weight'];
194 $this->uuid = $configuration['uuid'];
195 $this->id = $configuration['id'];
201 public function calculateDependencies() {
208 public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
209 $form['submit_text'] = [
210 '#type' => 'textfield',
211 '#title' => $this->t('Submit button text'),
212 '#default_value' => $this->configuration['submit_text'],
215 // Allow "auto_select" setting when auto_select is supported by widget.
216 if ($this->getPluginDefinition()['auto_select']) {
217 $form['auto_select'] = [
218 '#type' => 'checkbox',
219 '#title' => $this->t('Automatically submit selection'),
220 '#default_value' => $this->configuration['auto_select'],
230 public function id() {
237 public function uuid() {
244 public function label() {
251 public function setLabel($label) {
252 $this->label = $label;
259 public function getWeight() {
260 return $this->weight;
266 public function setWeight($weight) {
267 $this->weight = $weight;
272 * Prepares the entities without saving them.
274 * We need this method when we want to validate or perform other operations
279 * @param \Drupal\Core\Form\FormStateInterface $form_state
280 * The form state object.
282 * @return \Drupal\Core\Entity\EntityInterface[]
285 abstract protected function prepareEntities(array $form, FormStateInterface $form_state);
290 public function validate(array &$form, FormStateInterface $form_state) {
291 $entities = $this->prepareEntities($form, $form_state);
292 $validators = $form_state->get(['entity_browser', 'validators']);
294 $violations = $this->runWidgetValidators($entities, $validators);
295 foreach ($violations as $violation) {
296 $form_state->setError($form['widget'], $violation->getMessage());
302 * Run widget validators.
304 * @param array $entities
305 * Array of entity ids to validate.
306 * @param array $validators
307 * Array of widget validator ids.
309 * @return \Symfony\Component\Validator\ConstraintViolationListInterface
310 * A list of constraint violations. If the list is empty, validation
313 protected function runWidgetValidators(array $entities, $validators = []) {
314 $violations = new ConstraintViolationList();
315 foreach ($validators as $validator_id => $options) {
316 /** @var \Drupal\entity_browser\WidgetValidationInterface $widget_validator */
317 $widget_validator = $this->validationManager->createInstance($validator_id, []);
318 if ($widget_validator) {
319 $violations->addAll($widget_validator->validate($entities, $options));
329 public function submit(array &$element, array &$form, FormStateInterface $form_state) {}
332 * Dispatches event that informs all subscribers about new selected entities.
334 * @param array $entities
337 protected function selectEntities(array $entities, FormStateInterface $form_state) {
338 $selected_entities = &$form_state->get(['entity_browser', 'selected_entities']);
339 $selected_entities = array_merge($selected_entities, $entities);
341 $this->eventDispatcher->dispatch(
343 new EntitySelectionEvent(
344 $this->configuration['entity_browser_id'],
345 $form_state->get(['entity_browser', 'instance_uuid']),
353 public function requiresJsCommands() {
354 return $this->getPluginDefinition()['auto_select'] && $this->getConfiguration()['settings']['auto_select'];
358 * Allow configuration overrides at runtime based on widget context passed to
359 * this widget from the Entity Browser element.
361 * Widgets can override this method to replace the default behavior of
362 * replacing configuration with widget context if array keys match.
364 * @param array $widget_context
365 * The widget context.
367 protected function handleWidgetContext($widget_context) {
368 foreach ($this->defaultConfiguration() as $key => $value) {
369 if (isset($widget_context[$key]) && isset($this->configuration[$key])) {
370 $this->configuration[$key] = $widget_context[$key];