3 namespace Drupal\entity_browser;
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Core\Access\AccessResult;
7 use Drupal\Core\Entity\EntityTypeManagerInterface;
8 use Drupal\Core\Plugin\PluginBase;
9 use Drupal\Core\Form\FormStateInterface;
10 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
11 use Drupal\entity_browser\Events\EntitySelectionEvent;
12 use Drupal\entity_browser\Events\Events;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15 use Symfony\Component\Validator\ConstraintViolationList;
18 * Base class for widget plugins.
20 abstract class WidgetBase extends PluginBase implements WidgetInterface, ContainerFactoryPluginInterface {
22 use PluginConfigurationFormTrait;
52 * Event dispatcher service.
54 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
56 protected $eventDispatcher;
59 * Entity type manager service.
61 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
63 protected $entityTypeManager;
66 * The Widget Validation Manager service.
68 * @var \Drupal\entity_browser\WidgetValidationManager
70 protected $validationManager;
73 * WidgetBase constructor.
75 * @param array $configuration
76 * A configuration array containing information about the plugin instance.
77 * @param string $plugin_id
78 * The plugin_id for the plugin instance.
79 * @param mixed $plugin_definition
80 * The plugin implementation definition.
81 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
82 * Event dispatcher service.
83 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
84 * The entity type manager service.
85 * @param \Drupal\entity_browser\WidgetValidationManager $validation_manager
86 * The Widget Validation Manager service.
88 public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityTypeManagerInterface $entity_type_manager, WidgetValidationManager $validation_manager) {
89 parent::__construct($configuration, $plugin_id, $plugin_definition);
90 $this->eventDispatcher = $event_dispatcher;
91 $this->entityTypeManager = $entity_type_manager;
92 $this->validationManager = $validation_manager;
93 $this->setConfiguration($configuration);
99 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
104 $container->get('event_dispatcher'),
105 $container->get('entity_type.manager'),
106 $container->get('plugin.manager.entity_browser.widget_validation')
113 public function getForm(array &$original_form, FormStateInterface $form_state, array $additional_widget_parameters) {
116 if ($form_state->has(['entity_browser', 'widget_context'])) {
117 $this->handleWidgetContext($form_state->get(['entity_browser', 'widget_context']));
120 // Check if widget supports auto select functionality and expose config to
121 // front-end javascript.
123 if ($this->getPluginDefinition()['auto_select']) {
124 $autoSelect = $this->configuration['auto_select'];
125 $form['#attached']['drupalSettings']['entity_browser_widget']['auto_select'] = $autoSelect;
128 // In case of auto select, widget will handle adding entities in JS.
131 '#type' => 'actions',
134 '#value' => $this->configuration['submit_text'],
135 '#eb_widget_main_submit' => TRUE,
136 '#attributes' => ['class' => ['is-entity-browser-submit']],
137 '#button_type' => 'primary',
148 public function defaultConfiguration() {
150 'submit_text' => $this->t('Select entities'),
153 // If auto select is supported by Widget, append default configuration.
154 if ($this->getPluginDefinition()['auto_select']) {
155 $defaultConfig['auto_select'] = FALSE;
158 return $defaultConfig;
164 public function getConfiguration() {
166 'settings' => array_diff_key(
167 $this->configuration,
168 ['entity_browser_id' => 0]
170 'uuid' => $this->uuid(),
171 'weight' => $this->getWeight(),
172 'label' => $this->label(),
180 public function setConfiguration(array $configuration) {
189 $this->configuration = NestedArray::mergeDeep(
190 $this->defaultConfiguration(),
191 $configuration['settings']
193 $this->label = $configuration['label'];
194 $this->weight = $configuration['weight'];
195 $this->uuid = $configuration['uuid'];
196 $this->id = $configuration['id'];
202 public function calculateDependencies() {
209 public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
210 $form['submit_text'] = [
211 '#type' => 'textfield',
212 '#title' => $this->t('Submit button text'),
213 '#default_value' => $this->configuration['submit_text'],
216 // Allow "auto_select" setting when auto_select is supported by widget.
217 if ($this->getPluginDefinition()['auto_select']) {
218 $form['auto_select'] = [
219 '#type' => 'checkbox',
220 '#title' => $this->t('Automatically submit selection'),
221 '#default_value' => $this->configuration['auto_select'],
231 public function id() {
238 public function uuid() {
245 public function label() {
252 public function setLabel($label) {
253 $this->label = $label;
260 public function getWeight() {
261 return $this->weight;
267 public function setWeight($weight) {
268 $this->weight = $weight;
273 * Prepares the entities without saving them.
275 * We need this method when we want to validate or perform other operations
280 * @param \Drupal\Core\Form\FormStateInterface $form_state
281 * The form state object.
283 * @return \Drupal\Core\Entity\EntityInterface[]
286 abstract protected function prepareEntities(array $form, FormStateInterface $form_state);
291 public function validate(array &$form, FormStateInterface $form_state) {
292 $entities = $this->prepareEntities($form, $form_state);
293 $validators = $form_state->get(['entity_browser', 'validators']);
295 $violations = $this->runWidgetValidators($entities, $validators);
296 foreach ($violations as $violation) {
297 $form_state->setError($form['widget'], $violation->getMessage());
303 * Run widget validators.
305 * @param array $entities
306 * Array of entity ids to validate.
307 * @param array $validators
308 * Array of widget validator ids.
310 * @return \Symfony\Component\Validator\ConstraintViolationListInterface
311 * A list of constraint violations. If the list is empty, validation
314 protected function runWidgetValidators(array $entities, $validators = []) {
315 $violations = new ConstraintViolationList();
316 foreach ($validators as $validator_id => $options) {
317 /** @var \Drupal\entity_browser\WidgetValidationInterface $widget_validator */
318 $widget_validator = $this->validationManager->createInstance($validator_id, []);
319 if ($widget_validator) {
320 $violations->addAll($widget_validator->validate($entities, $options));
330 public function submit(array &$element, array &$form, FormStateInterface $form_state) {}
333 * Dispatches event that informs all subscribers about new selected entities.
335 * @param array $entities
338 protected function selectEntities(array $entities, FormStateInterface $form_state) {
339 $selected_entities = &$form_state->get(['entity_browser', 'selected_entities']);
340 $selected_entities = array_merge($selected_entities, $entities);
342 $this->eventDispatcher->dispatch(
344 new EntitySelectionEvent(
345 $this->configuration['entity_browser_id'],
346 $form_state->get(['entity_browser', 'instance_uuid']),
354 public function requiresJsCommands() {
355 return $this->getPluginDefinition()['auto_select'] && $this->getConfiguration()['settings']['auto_select'];
359 * Allow configuration overrides at runtime based on widget context passed to
360 * this widget from the Entity Browser element.
362 * Widgets can override this method to replace the default behavior of
363 * replacing configuration with widget context if array keys match.
365 * @param array $widget_context
366 * The widget context.
368 protected function handleWidgetContext($widget_context) {
369 foreach ($this->defaultConfiguration() as $key => $value) {
370 if (isset($widget_context[$key]) && isset($this->configuration[$key])) {
371 $this->configuration[$key] = $widget_context[$key];
379 public function access() {
380 return AccessResult::allowed();