Completed
Push — 8.x-1.x ( b45449...fc4997 )
by Janez
02:00
created

EntityBrowser   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 200
rs 10
c 0
b 0
f 0
wmc 26
lcom 1
cbo 1

10 Methods

Rating   Name   Duplication   Size   Complexity  
A execute() 0 6 1
A ajaxEnabled() 0 5 1
A getOption() 0 12 2
A defineOptions() 0 5 1
A optionsSummary() 0 6 2
A buildOptionsForm() 0 14 2
A preview() 0 3 1
D elementPreRender() 0 30 9
B handleForm() 0 25 3
B postRender() 0 30 4
1
<?php
2
3
namespace Drupal\entity_browser\Plugin\views\display;
4
5
use Drupal\Core\Form\FormStateInterface;
6
use Drupal\views\Plugin\views\display\DisplayPluginBase;
7
8
/**
9
 * The plugin that handles entity browser display.
10
 *
11
 * "entity_browser_display" is a custom property, used with
12
 * \Drupal\views\Views::getApplicableViews() to retrieve all views with a
13
 * 'Entity Browser' display.
14
 *
15
 * @ingroup views_display_plugins
16
 *
17
 * @ViewsDisplay(
18
 *   id = "entity_browser",
19
 *   title = @Translation("Entity browser"),
20
 *   help = @Translation("Displays a view as Entity browser widget."),
21
 *   theme = "views_view",
22
 *   admin = @Translation("Entity browser"),
23
 *   entity_browser_display = TRUE
24
 * )
25
 */
26
class EntityBrowser extends DisplayPluginBase {
27
28
  /**
29
   * {@inheritdoc}
30
   */
31
  public function execute() {
32
    parent::execute();
33
    $render = ['view' => $this->view->render()];
34
    $this->handleForm($render);
35
    return $render;
36
  }
37
38
  /**
39
   * {@inheritdoc}
40
   */
41
  public function ajaxEnabled() {
42
    // Force AJAX as this Display Plugin will almost always be embedded inside
43
    // EntityBrowserForm, which breaks normal exposed form submits.
44
    return TRUE;
45
  }
46
47
  /**
48
   * {@inheritdoc}
49
   */
50
  public function getOption($option) {
51
    // @todo remove upon resolution of https://www.drupal.org/node/2904798
52
    // This overrides getOption() instead of ajaxEnabled() because
53
    // \Drupal\views\Controller\ViewAjaxController::ajaxView() currently calls
54
    // that directly.
55
    if ($option == 'use_ajax') {
56
      return TRUE;
57
    }
58
    else {
59
      return parent::getOption($option);
60
    }
61
  }
62
63
64
  /**
65
   * {@inheritdoc}
66
   */
67
  protected function defineOptions() {
68
    $options = parent::defineOptions();
69
    $options['use_ajax']['default'] = TRUE;
70
    return $options;
71
  }
72
73
  /**
74
   * {@inheritdoc}
75
   */
76
  public function optionsSummary(&$categories, &$options) {
77
    parent::optionsSummary($categories, $options);
78
    if (isset($options['use_ajax'])) {
79
      $options['use_ajax']['value'] = $this->t('Yes (Forced)');
80
    }
81
  }
82
83
  /**
84
   * {@inheritdoc}
85
   */
86
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
87
    parent::buildOptionsForm($form, $form_state);
88
    // Disable the ability to toggle AJAX support, as we forcibly enable AJAX
89
    // in our ajaxEnabled() implementation.
90
    if (isset($form['use_ajax'])) {
91
      $form['use_ajax'] = [
92
        '#description' => $this->t('Entity Browser requires Views to use AJAX.'),
93
        '#type' => 'checkbox',
94
        '#title' => $this->t('Use AJAX'),
95
        '#default_value' => 1,
96
        '#disabled' => TRUE,
97
      ];
98
    }
99
  }
100
101
  /**
102
   * {@inheritdoc}
103
   */
104
  public function preview() {
105
    return $this->execute();
106
  }
107
108
  /**
109
   * {@inheritdoc}
110
   *
111
   * Pre render callback for a view.
112
   *
113
   * Based on DisplayPluginBase::elementPreRender() except that we removed form
114
   * part which need to handle by our own.
115
   */
116
  public function elementPreRender(array $element) {
117
    $view = $element['#view'];
118
    $empty = empty($view->result);
119
120
    // Force a render array so CSS/JS can be attached.
121
    if (!is_array($element['#rows'])) {
122
      $element['#rows'] = ['#markup' => $element['#rows']];
123
    }
124
125
    $element['#header'] = $view->display_handler->renderArea('header', $empty);
126
    $element['#footer'] = $view->display_handler->renderArea('footer', $empty);
127
    $element['#empty'] = $empty ? $view->display_handler->renderArea('empty', $empty) : [];
128
    $element['#exposed'] = !empty($view->exposed_widgets) ? $view->exposed_widgets : [];
129
    $element['#more'] = $view->display_handler->renderMoreLink();
130
    $element['#feed_icons'] = !empty($view->feedIcons) ? $view->feedIcons : [];
131
132
    if ($view->display_handler->renderPager()) {
133
      $exposed_input = isset($view->exposed_raw_input) ? $view->exposed_raw_input : NULL;
134
      $element['#pager'] = $view->renderPager($exposed_input);
135
    }
136
137
    if (!empty($view->attachment_before)) {
138
      $element['#attachment_before'] = $view->attachment_before;
139
    }
140
    if (!empty($view->attachment_after)) {
141
      $element['#attachment_after'] = $view->attachment_after;
142
    }
143
144
    return $element;
145
  }
146
147
  /**
148
   * Handles form elements on a view.
149
   *
150
   * @param array $render
151
   *   Rendered content.
152
   */
153
  protected function handleForm(array &$render) {
154
    if (!empty($this->view->field['entity_browser_select'])) {
155
156
      /** @var \Drupal\entity_browser\Plugin\views\field\SelectForm $select */
157
      $select = $this->view->field['entity_browser_select'];
158
      $select->viewsForm($render);
159
160
      $render['#post_render'][] = [get_class($this), 'postRender'];
161
      $substitutions = [];
162
      foreach ($this->view->result as $row) {
163
        $form_element_row_id = $select->getRowId($row);
164
165
        $substitutions[] = [
166
          'placeholder' => '<!--form-item-entity_browser_select--' . $form_element_row_id . '-->',
167
          'field_name' => 'entity_browser_select',
168
          'row_id' => $form_element_row_id,
169
        ];
170
      }
171
172
      $render['#substitutions'] = [
173
        '#type' => 'value',
174
        '#value' => $substitutions,
175
      ];
176
    }
177
  }
178
179
  /**
180
   * Post render callback that moves form elements into the view.
181
   *
182
   * Form elements need to be added out of view to be correctly detected by Form
183
   * API and then added into the view afterwards. Views use the same approach
184
   * for bulk operations.
185
   *
186
   * @param string $content
187
   *   Rendered content.
188
   * @param array $element
189
   *   Render array.
190
   *
191
   * @return string
192
   *   Rendered content.
193
   */
194
  public static function postRender($content, array $element) {
195
    // Placeholders and their substitutions (usually rendered form elements).
196
    $search = $replace = [];
197
198
    // Add in substitutions provided by the form.
199
    foreach ($element['#substitutions']['#value'] as $substitution) {
200
      $field_name = $substitution['field_name'];
201
      $row_id = $substitution['row_id'];
202
203
      $search[] = $substitution['placeholder'];
204
      $replace[] = isset($element[$field_name][$row_id]) ? \Drupal::service('renderer')->render($element[$field_name][$row_id]) : '';
205
    }
206
    // Add in substitutions from hook_views_form_substitutions().
207
    $substitutions = \Drupal::moduleHandler()->invokeAll('views_form_substitutions');
208
    foreach ($substitutions as $placeholder => $substitution) {
209
      $search[] = $placeholder;
210
      $replace[] = $substitution;
211
    }
212
213
    // We cannot render exposed form within the View, as nested forms are not
214
    // standard and will break entity selection.
215
    $search[] = '<form';
216
    $replace[] = '<div';
217
    $search[] = '</form>';
218
    $replace[] = '</div>';
219
220
    $content = str_replace($search, $replace, $content);
221
222
    return $content;
223
  }
224
225
}
226