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 // Allow configuration overrides at runtime based on form state to enable
116 // use cases where the instance of a widget may have contextual
117 // configuration like field settings. "widget_context" doesn't have to be
118 // used in this way, if a widget doesn't want its default configuration
119 // overwritten it can not call this method and implement its own logic.
120 foreach ($this->defaultConfiguration() as $key => $value) {
121 if ($form_state->has(['entity_browser', 'widget_context', $key]) && isset($this->configuration[$key])) {
122 $this->configuration[$key] = $form_state->get(['entity_browser', 'widget_context', $key]);
126 // Check if widget supports auto select functionality and expose config to
127 // front-end javascript.
129 if ($this->getPluginDefinition()['auto_select']) {
130 $autoSelect = $this->configuration['auto_select'];
131 $form['#attached']['drupalSettings']['entity_browser_widget']['auto_select'] = $autoSelect;
134 // In case of auto select, widget will handle adding entities in JS.
137 '#type' => 'actions',
140 '#value' => $this->configuration['submit_text'],
141 '#eb_widget_main_submit' => TRUE,
142 '#attributes' => ['class' => ['is-entity-browser-submit']],
143 '#button_type' => 'primary',
154 public function defaultConfiguration() {
156 'submit_text' => $this->t('Select entities'),
159 // If auto select is supported by Widget, append default configuration.
160 if ($this->getPluginDefinition()['auto_select']) {
161 $defaultConfig['auto_select'] = FALSE;
164 return $defaultConfig;
170 public function getConfiguration() {
172 'settings' => array_diff_key(
173 $this->configuration,
174 ['entity_browser_id' => 0]
176 'uuid' => $this->uuid(),
177 'weight' => $this->getWeight(),
178 'label' => $this->label(),
186 public function setConfiguration(array $configuration) {
195 $this->configuration = NestedArray::mergeDeep(
196 $this->defaultConfiguration(),
197 $configuration['settings']
199 $this->label = $configuration['label'];
200 $this->weight = $configuration['weight'];
201 $this->uuid = $configuration['uuid'];
202 $this->id = $configuration['id'];
208 public function calculateDependencies() {
215 public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
216 $form['submit_text'] = [
217 '#type' => 'textfield',
218 '#title' => $this->t('Submit button text'),
219 '#default_value' => $this->configuration['submit_text'],
222 // Allow "auto_select" setting when auto_select is supported by widget.
223 if ($this->getPluginDefinition()['auto_select']) {
224 $form['auto_select'] = [
225 '#type' => 'checkbox',
226 '#title' => $this->t('Automatically submit selection'),
227 '#default_value' => $this->configuration['auto_select'],
237 public function id() {
244 public function uuid() {
251 public function label() {
258 public function setLabel($label) {
259 $this->label = $label;
266 public function getWeight() {
267 return $this->weight;
273 public function setWeight($weight) {
274 $this->weight = $weight;
279 * Prepares the entities without saving them.
281 * We need this method when we want to validate or perform other operations
286 * @param \Drupal\Core\Form\FormStateInterface $form_state
287 * The form state object.
289 * @return \Drupal\Core\Entity\EntityInterface[]
292 abstract protected function prepareEntities(array $form, FormStateInterface $form_state);
297 public function validate(array &$form, FormStateInterface $form_state) {
298 $entities = $this->prepareEntities($form, $form_state);
299 $validators = $form_state->get(['entity_browser', 'validators']);
301 $violations = $this->runWidgetValidators($entities, $validators);
302 foreach ($violations as $violation) {
303 $form_state->setError($form['widget'], $violation->getMessage());
309 * Run widget validators.
311 * @param array $entities
312 * Array of entity ids to validate.
313 * @param array $validators
314 * Array of widget validator ids.
316 * @return \Symfony\Component\Validator\ConstraintViolationListInterface
317 * A list of constraint violations. If the list is empty, validation
320 protected function runWidgetValidators(array $entities, $validators = []) {
321 $violations = new ConstraintViolationList();
322 foreach ($validators as $validator_id => $options) {
323 /** @var \Drupal\entity_browser\WidgetValidationInterface $widget_validator */
324 $widget_validator = $this->validationManager->createInstance($validator_id, []);
325 if ($widget_validator) {
326 $violations->addAll($widget_validator->validate($entities, $options));
336 public function submit(array &$element, array &$form, FormStateInterface $form_state) {}
339 * Dispatches event that informs all subscribers about new selected entities.
341 * @param array $entities
344 protected function selectEntities(array $entities, FormStateInterface $form_state) {
345 $selected_entities = &$form_state->get(['entity_browser', 'selected_entities']);
346 $selected_entities = array_merge($selected_entities, $entities);
348 $this->eventDispatcher->dispatch(
350 new EntitySelectionEvent(
351 $this->configuration['entity_browser_id'],
352 $form_state->get(['entity_browser', 'instance_uuid']),
360 public function requiresJsCommands() {
361 return $this->getPluginDefinition()['auto_select'] && $this->getConfiguration()['settings']['auto_select'];