Version 1
[yaffs-website] / web / modules / contrib / entityqueue / src / Plugin / Field / FieldWidget / EntityqueueDragtableWidget.php
1 <?php
2
3 namespace Drupal\entityqueue\Plugin\Field\FieldWidget;
4
5 use Drupal\Component\Utility\Html;
6 use Drupal\Component\Utility\NestedArray;
7 use Drupal\Core\Entity\Element\EntityAutocomplete;
8 use Drupal\Core\Field\FieldItemListInterface;
9 use Drupal\Core\Field\FieldStorageDefinitionInterface;
10 use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
11 use Drupal\Core\Form\FormStateInterface;
12
13 /**
14  * Plugin implementation of the 'entityqueue_dragtable' widget.
15  *
16  * @FieldWidget(
17  *   id = "entityqueue_dragtable",
18  *   label = @Translation("Autocomplete (draggable table) - Experimental"),
19  *   description = @Translation("An autocomplete text field with a draggable table."),
20  *   field_types = {
21  *     "entity_reference"
22  *   }
23  * )
24  */
25 class EntityqueueDragtableWidget extends EntityReferenceAutocompleteWidget {
26
27   /**
28    * The unique HTML ID of the widget's wrapping element.
29    *
30    * @var string
31    */
32   protected $wrapperId;
33
34   /**
35    * {@inheritdoc}
36    */
37   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
38     $field_name = $this->fieldDefinition->getName();
39     $parents = $form['#parents'];
40     $referenced_entities = $items->referencedEntities();
41
42     if (isset($referenced_entities[$delta])) {
43       $entity_label = EntityAutocomplete::getEntityLabels([$referenced_entities[$delta]]);
44       $id_prefix = implode('-', array_merge($parents, [$field_name, $delta]));
45
46       $element += [
47         '#type' => 'container',
48         '#attributes' => ['class' => ['form--inline']],
49         'target_id' => [
50           '#type' => 'item',
51           '#markup' => $entity_label,
52           '#default_value' => !$referenced_entities[$delta]->isNew() ? $referenced_entities[$delta]->id() : NULL,
53         ],
54         'entity' => [
55           '#type' => 'value',
56           '#default_value' => $referenced_entities[$delta],
57         ],
58         'remove' => [
59           '#type' => 'submit',
60           '#name' => strtr($id_prefix, '-', '_') . '_remove',
61           '#value' => $this->t('Remove'),
62           '#attributes' => ['class' => ['remove-item-submit', 'align-right']],
63           '#submit' => [[get_class($this), 'removeSubmit']],
64           '#ajax' => [
65             'callback' => [get_class($this), 'getWidgetElementAjax'],
66             'wrapper' => $this->getWrapperId(),
67             'effect' => 'fade',
68           ],
69         ],
70       ];
71     }
72
73     return $element;
74   }
75
76   /**
77    * {@inheritdoc}
78    */
79   protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
80     $field_name = $this->fieldDefinition->getName();
81     $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
82     $parents = $form['#parents'];
83
84     // Assign a unique identifier to each widget.
85     $id_prefix = implode('-', array_merge($parents, [$field_name]));
86     $wrapper_id = Html::getUniqueId($id_prefix . '-add-more-wrapper');
87     $this->setWrapperId($wrapper_id);
88
89     // Load the items for form rebuilds from the field state as they might not
90     // be in $form_state->getValues() because of validation limitations. Also,
91     // they are only passed in as $items when editing existing entities.
92     $field_state = static::getWidgetState($parents, $field_name, $form_state);
93     if (isset($field_state['items'])) {
94       $items->setValue($field_state['items']);
95     }
96
97     // Lower the 'items_count' field state property in order to prevent the
98     // parent implementation to append an extra empty item.
99     if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
100       $field_state['items_count'] = (count($items) > 1) ? count($items) - 1 : 0;
101       static::setWidgetState($parents, $field_name, $form_state, $field_state);
102     }
103
104     $elements = parent::formMultipleElements($items, $form, $form_state);
105
106     if ($elements) {
107       if (isset($elements['add_more'])) {
108         // Update the HTML wrapper ID with the one generated by us.
109         $elements['#prefix'] = '<div id="' . $this->getWrapperId() . '">';
110
111         $add_more_button = $elements['add_more'];
112         $add_more_button['#value'] = $this->t('Add item');
113         $add_more_button['#ajax']['callback'] = [get_class($this), 'getWidgetElementAjax'];
114         $add_more_button['#ajax']['wrapper'] = $this->getWrapperId();
115
116         $elements['add_more'] = [
117           '#type' => 'container',
118           '#tree' => TRUE,
119           '#attributes' => ['class' => ['form--inline']],
120           'new_item' => parent::formElement($items, -1, [], $form, $form_state),
121           'submit' => $add_more_button,
122         ];
123       }
124     }
125
126     return $elements;
127   }
128
129   /**
130    * {@inheritdoc}
131    */
132   public static function getWidgetElementAjax(array $form, FormStateInterface $form_state) {
133     $button = $form_state->getTriggeringElement();
134
135     // Go two levels up in the form, to the widgets container.
136     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
137
138     // Ensure the widget allows adding additional items.
139     if ($element['#cardinality'] != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
140       return;
141     }
142
143     // Add a DIV around the delta receiving the Ajax effect.
144     $delta = $element['#max_delta'];
145     $element[$delta]['#prefix'] = '<div class="ajax-new-content">' . (isset($element[$delta]['#prefix']) ? $element[$delta]['#prefix'] : '');
146     $element[$delta]['#suffix'] = (isset($element[$delta]['#suffix']) ? $element[$delta]['#suffix'] : '') . '</div>';
147
148     return $element;
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   public static function addMoreSubmit(array $form, FormStateInterface $form_state) {
155     // During the form rebuild, formElement() will create field item widget
156     // elements using re-indexed deltas, so clear out FormState::$input to
157     // avoid a mismatch between old and new deltas. The rebuilt elements will
158     // have #default_value set appropriately for the current state of the field,
159     // so nothing is lost in doing this.
160     $button = $form_state->getTriggeringElement();
161     $parents = array_slice($button['#parents'], 0, -2);
162     NestedArray::setValue($form_state->getUserInput(), $parents, NULL);
163
164     // Go two levels up in the form, to the widgets container.
165     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
166     $field_name = $element['#field_name'];
167     $parents = $element['#field_parents'];
168
169     $submitted_values = NestedArray::getValue($form_state->getValues(), array_slice($button['#parents'], 0, -2));
170
171     // Check submitted values for empty items.
172     $new_values = array();
173     foreach ($submitted_values as $delta => $submitted_value) {
174       if ($delta !== 'add_more' && (isset($submitted_value['target_id']) || isset($submitted_value['entity']))) {
175         $new_values[] = $submitted_value;
176       }
177
178       if ($delta === 'add_more' && (isset($submitted_value['new_item']['target_id']) || isset($submitted_value['new_item']['entity']))) {
179         $new_values[] = $submitted_value['new_item'];
180       }
181     }
182
183     // Re-index deltas after removing empty items.
184     $submitted_values = array_values($new_values);
185
186     // Update form_state values.
187     NestedArray::setValue($form_state->getValues(), array_slice($button['#parents'], 0, -2), $submitted_values);
188
189     // Update items.
190     $field_state = static::getWidgetState($parents, $field_name, $form_state);
191     $field_state['items'] = $submitted_values;
192     static::setWidgetState($parents, $field_name, $form_state, $field_state);
193
194     $form_state->setRebuild();
195   }
196
197   /**
198    * Submission handler for the "Remove" button.
199    */
200   public static function removeSubmit(array $form, FormStateInterface $form_state) {
201     $button = $form_state->getTriggeringElement();
202
203     // Go one level up in the form, to the single field item element container.
204     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
205
206     $form_state->setValueForElement($element, ['target_id' => NULL, 'entity' => NULL]);
207
208     // Call the generic submit handler which takes care of removing the item.
209     static::addMoreSubmit($form, $form_state);
210   }
211
212   /**
213    * Sets the unique HTML ID of the widget's wrapping element.
214    *
215    * @param string $wrapperId
216    *   The unique HTML ID.
217    */
218   public function setWrapperId($wrapperId) {
219     if (!$this->wrapperId) {
220       $this->wrapperId = $wrapperId;
221     }
222   }
223
224   /**
225    * Gets the unique HTML ID of the widget's wrapping element.
226    *
227    * @return string
228    *   The unique HTML ID.
229    */
230   public function getWrapperId() {
231     return $this->wrapperId;
232   }
233
234 }