View::buildConfigurationForm()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 16
nc 2
nop 2
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
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) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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) {
0 ignored issues
show
Bug introduced by
The class Drupal\Component\Plugin\...PluginNotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
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'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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