1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Drupal\entity_browser\Plugin\EntityBrowser\Widget; |
4
|
|
|
|
5
|
|
|
use Drupal\Component\Plugin\Exception\PluginNotFoundException; |
6
|
|
|
use Drupal\Core\Access\AccessResult; |
7
|
|
|
use Drupal\Core\Form\FormStateInterface; |
8
|
|
|
use Drupal\Core\Render\Element; |
9
|
|
|
use Drupal\entity_browser\WidgetBase; |
10
|
|
|
use Drupal\Core\Url; |
11
|
|
|
use Drupal\entity_browser\WidgetValidationManager; |
12
|
|
|
use Drupal\views\Entity\View as ViewEntity; |
13
|
|
|
use Drupal\views\Views; |
14
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
15
|
|
|
use Drupal\Core\Session\AccountInterface; |
16
|
|
|
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; |
17
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
18
|
|
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Uses a view to provide entity listing in a browser's widget. |
22
|
|
|
* |
23
|
|
|
* @EntityBrowserWidget( |
24
|
|
|
* id = "view", |
25
|
|
|
* label = @Translation("View"), |
26
|
|
|
* provider = "views", |
27
|
|
|
* description = @Translation("Uses a view to provide entity listing in a browser's widget."), |
28
|
|
|
* auto_select = TRUE |
29
|
|
|
* ) |
30
|
|
|
*/ |
31
|
|
|
class View extends WidgetBase implements ContainerFactoryPluginInterface { |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* The current user. |
35
|
|
|
* |
36
|
|
|
* @var \Drupal\Core\Session\AccountInterface |
37
|
|
|
*/ |
38
|
|
|
protected $currentUser; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* {@inheritdoc} |
42
|
|
|
*/ |
43
|
|
|
public function defaultConfiguration() { |
44
|
|
|
return [ |
45
|
|
|
'view' => NULL, |
46
|
|
|
'view_display' => NULL, |
47
|
|
|
] + parent::defaultConfiguration(); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* {@inheritdoc} |
52
|
|
|
*/ |
53
|
|
View Code Duplication |
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { |
|
|
|
|
54
|
|
|
return new static( |
55
|
|
|
$configuration, |
56
|
|
|
$plugin_id, |
57
|
|
|
$plugin_definition, |
58
|
|
|
$container->get('event_dispatcher'), |
59
|
|
|
$container->get('entity_type.manager'), |
60
|
|
|
$container->get('plugin.manager.entity_browser.widget_validation'), |
61
|
|
|
$container->get('current_user') |
62
|
|
|
); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Constructs a new View object. |
67
|
|
|
* |
68
|
|
|
* @param array $configuration |
69
|
|
|
* A configuration array containing information about the plugin instance. |
70
|
|
|
* @param string $plugin_id |
71
|
|
|
* The plugin_id for the plugin instance. |
72
|
|
|
* @param mixed $plugin_definition |
73
|
|
|
* The plugin implementation definition. |
74
|
|
|
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher |
75
|
|
|
* Event dispatcher service. |
76
|
|
|
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager |
77
|
|
|
* The entity type manager. |
78
|
|
|
* @param \Drupal\entity_browser\WidgetValidationManager $validation_manager |
79
|
|
|
* The Widget Validation Manager service. |
80
|
|
|
* @param \Drupal\Core\Session\AccountInterface $current_user |
81
|
|
|
* The current user. |
82
|
|
|
*/ |
83
|
|
|
public function __construct(array $configuration, $plugin_id, $plugin_definition, EventDispatcherInterface $event_dispatcher, EntityTypeManagerInterface $entity_type_manager, WidgetValidationManager $validation_manager, AccountInterface $current_user) { |
84
|
|
|
parent::__construct($configuration, $plugin_id, $plugin_definition, $event_dispatcher, $entity_type_manager, $validation_manager); |
85
|
|
|
$this->currentUser = $current_user; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* {@inheritdoc} |
90
|
|
|
*/ |
91
|
|
|
public function getForm(array &$original_form, FormStateInterface $form_state, array $additional_widget_parameters) { |
92
|
|
|
$form = parent::getForm($original_form, $form_state, $additional_widget_parameters); |
93
|
|
|
// TODO - do we need better error handling for view and view_display (in case |
94
|
|
|
// either of those is nonexistent or display not of correct type)? |
95
|
|
|
$form['#attached']['library'] = ['entity_browser/view']; |
96
|
|
|
|
97
|
|
|
/** @var \Drupal\views\ViewExecutable $view */ |
98
|
|
|
$view = $this->entityTypeManager |
99
|
|
|
->getStorage('view') |
100
|
|
|
->load($this->configuration['view']) |
101
|
|
|
->getExecutable(); |
102
|
|
|
|
103
|
|
|
// Check if the current user has access to this view. |
104
|
|
|
if (!$view->access($this->configuration['view_display'])) { |
105
|
|
|
return [ |
106
|
|
|
'#markup' => $this->t('You do not have access to this View.'), |
107
|
|
|
]; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
if (!empty($this->configuration['arguments'])) { |
111
|
|
|
if (!empty($additional_widget_parameters['path_parts'])) { |
112
|
|
|
$arguments = []; |
113
|
|
|
// Map configuration arguments with original path parts. |
114
|
|
|
foreach ($this->configuration['arguments'] as $argument) { |
115
|
|
|
$arguments[] = isset($additional_widget_parameters['path_parts'][$argument]) ? $additional_widget_parameters['path_parts'][$argument] : ''; |
116
|
|
|
} |
117
|
|
|
$view->setArguments(array_values($arguments)); |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$form['view'] = $view->executeDisplay($this->configuration['view_display']); |
122
|
|
|
|
123
|
|
|
if (empty($view->field['entity_browser_select'])) { |
124
|
|
|
$url = Url::fromRoute('entity.view.edit_form', ['view' => $this->configuration['view']])->toString(); |
125
|
|
|
if ($this->currentUser->hasPermission('administer views')) { |
126
|
|
|
return [ |
127
|
|
|
'#markup' => $this->t('Entity browser select form field not found on a view. <a href=":link">Go fix it</a>!', [':link' => $url]), |
128
|
|
|
]; |
129
|
|
|
} |
130
|
|
|
else { |
131
|
|
|
return [ |
132
|
|
|
'#markup' => $this->t('Entity browser select form field not found on a view. Go fix it!'), |
133
|
|
|
]; |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
// When rebuilding makes no sense to keep checkboxes that were previously |
138
|
|
|
// selected. |
139
|
|
|
if (!empty($form['view']['entity_browser_select']) && $form_state->isRebuilding()) { |
140
|
|
|
foreach (Element::children($form['view']['entity_browser_select']) as $child) { |
141
|
|
|
$form['view']['entity_browser_select'][$child]['#process'][] = ['\Drupal\entity_browser\Plugin\EntityBrowser\Widget\View', 'processCheckbox']; |
142
|
|
|
$form['view']['entity_browser_select'][$child]['#process'][] = ['\Drupal\Core\Render\Element\Checkbox', 'processAjaxForm']; |
143
|
|
|
$form['view']['entity_browser_select'][$child]['#process'][] = ['\Drupal\Core\Render\Element\Checkbox', 'processGroup']; |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
$form['view']['view'] = [ |
148
|
|
|
'#markup' => \Drupal::service('renderer')->render($form['view']['view']), |
149
|
|
|
]; |
150
|
|
|
|
151
|
|
|
return $form; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Sets the #checked property when rebuilding form. |
156
|
|
|
* |
157
|
|
|
* Every time when we rebuild we want all checkboxes to be unchecked. |
158
|
|
|
* |
159
|
|
|
* @see \Drupal\Core\Render\Element\Checkbox::processCheckbox() |
160
|
|
|
*/ |
161
|
|
|
public static function processCheckbox(&$element, FormStateInterface $form_state, &$complete_form) { |
162
|
|
|
$element['#checked'] = FALSE; |
163
|
|
|
return $element; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* {@inheritdoc} |
168
|
|
|
*/ |
169
|
|
|
public function validate(array &$form, FormStateInterface $form_state) { |
170
|
|
|
$user_input = $form_state->getUserInput(); |
171
|
|
|
if (isset($user_input['entity_browser_select'])) { |
172
|
|
|
$selected_rows = array_values(array_filter($user_input['entity_browser_select'])); |
173
|
|
|
foreach ($selected_rows as $row) { |
174
|
|
|
// Verify that the user input is a string and split it. |
175
|
|
|
// Each $row is in the format entity_type:id. |
176
|
|
|
if (is_string($row) && $parts = explode(':', $row, 2)) { |
177
|
|
|
// Make sure we have a type and id present. |
178
|
|
|
if (count($parts) == 2) { |
179
|
|
|
try { |
180
|
|
|
$storage = $this->entityTypeManager->getStorage($parts[0]); |
181
|
|
|
if (!$storage->load($parts[1])) { |
182
|
|
|
$message = $this->t('The @type Entity @id does not exist.', [ |
183
|
|
|
'@type' => $parts[0], |
184
|
|
|
'@id' => $parts[1], |
185
|
|
|
]); |
186
|
|
|
$form_state->setError($form['widget']['view']['entity_browser_select'], $message); |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
catch (PluginNotFoundException $e) { |
|
|
|
|
190
|
|
|
$message = $this->t('The Entity Type @type does not exist.', [ |
191
|
|
|
'@type' => $parts[0], |
192
|
|
|
]); |
193
|
|
|
$form_state->setError($form['widget']['view']['entity_browser_select'], $message); |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
// If there weren't any errors set, run the normal validators. |
200
|
|
|
if (empty($form_state->getErrors())) { |
201
|
|
|
parent::validate($form, $form_state); |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* {@inheritdoc} |
208
|
|
|
*/ |
209
|
|
|
protected function prepareEntities(array $form, FormStateInterface $form_state) { |
210
|
|
|
$selected_rows = array_values(array_filter($form_state->getUserInput()['entity_browser_select'])); |
211
|
|
|
$entities = []; |
212
|
|
|
foreach ($selected_rows as $row) { |
213
|
|
|
list($type, $id) = explode(':', $row); |
214
|
|
|
$storage = $this->entityTypeManager->getStorage($type); |
215
|
|
|
if ($entity = $storage->load($id)) { |
216
|
|
|
$entities[] = $entity; |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
return $entities; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* {@inheritdoc} |
224
|
|
|
*/ |
225
|
|
|
public function submit(array &$element, array &$form, FormStateInterface $form_state) { |
226
|
|
|
$entities = $this->prepareEntities($form, $form_state); |
227
|
|
|
$this->selectEntities($entities, $form_state); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* {@inheritdoc} |
232
|
|
|
*/ |
233
|
|
|
public function buildConfigurationForm(array $form, FormStateInterface $form_state) { |
234
|
|
|
$form = parent::buildConfigurationForm($form, $form_state); |
235
|
|
|
|
236
|
|
|
$options = []; |
237
|
|
|
// Get only those enabled Views that have entity_browser displays. |
238
|
|
|
$displays = Views::getApplicableViews('entity_browser_display'); |
239
|
|
|
foreach ($displays as $display) { |
240
|
|
|
list($view_id, $display_id) = $display; |
241
|
|
|
$view = $this->entityTypeManager->getStorage('view')->load($view_id); |
242
|
|
|
$options[$view_id . '.' . $display_id] = $this->t('@view : @display', ['@view' => $view->label(), '@display' => $view->get('display')[$display_id]['display_title']]); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
$form['view'] = [ |
246
|
|
|
'#type' => 'select', |
247
|
|
|
'#title' => $this->t('View : View display'), |
248
|
|
|
'#default_value' => $this->configuration['view'] . '.' . $this->configuration['view_display'], |
249
|
|
|
'#options' => $options, |
250
|
|
|
'#empty_option' => $this->t('- Select a view -'), |
251
|
|
|
'#required' => TRUE, |
252
|
|
|
]; |
253
|
|
|
|
254
|
|
|
return $form; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* {@inheritdoc} |
259
|
|
|
*/ |
260
|
|
|
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { |
261
|
|
|
$values = $form_state->getValues()['table'][$this->uuid()]['form']; |
262
|
|
|
$this->configuration['submit_text'] = $values['submit_text']; |
263
|
|
|
$this->configuration['auto_select'] = $values['auto_select']; |
264
|
|
View Code Duplication |
if (!empty($values['view'])) { |
|
|
|
|
265
|
|
|
list($view_id, $display_id) = explode('.', $values['view']); |
266
|
|
|
$this->configuration['view'] = $view_id; |
267
|
|
|
$this->configuration['view_display'] = $display_id; |
268
|
|
|
} |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* {@inheritdoc} |
273
|
|
|
*/ |
274
|
|
View Code Duplication |
public function calculateDependencies() { |
|
|
|
|
275
|
|
|
$dependencies = []; |
276
|
|
|
if ($this->configuration['view']) { |
277
|
|
|
$view = ViewEntity::load($this->configuration['view']); |
278
|
|
|
$dependencies[$view->getConfigDependencyKey()] = [$view->getConfigDependencyName()]; |
279
|
|
|
} |
280
|
|
|
return $dependencies; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* {@inheritdoc} |
285
|
|
|
*/ |
286
|
|
|
public function access() { |
287
|
|
|
// Mark the widget as not visible if the user has no access to the view. |
288
|
|
|
/** @var \Drupal\views\ViewExecutable $view */ |
289
|
|
|
$view = $this->entityTypeManager |
290
|
|
|
->getStorage('view') |
291
|
|
|
->load($this->configuration['view']) |
292
|
|
|
->getExecutable(); |
293
|
|
|
|
294
|
|
|
|
295
|
|
|
// Check if the current user has access to this view. |
296
|
|
|
return AccessResult::allowedIf($view->access($this->configuration['view_display'])); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
} |
300
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.