bea8491576d8f0886a21dfa4bfa39ca60bcd0e29
[yaffs-website] / Plugin / views / display / EntityBrowser.php
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   protected function defineOptions() {
51     $options = parent::defineOptions();
52     $options['use_ajax']['default'] = TRUE;
53     return $options;
54   }
55
56   /**
57    * {@inheritdoc}
58    */
59   public function optionsSummary(&$categories, &$options) {
60     parent::optionsSummary($categories, $options);
61     if (isset($options['use_ajax'])) {
62       $options['use_ajax']['value'] = $this->t('Yes (Forced)');
63     }
64   }
65
66   /**
67    * {@inheritdoc}
68    */
69   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
70     parent::buildOptionsForm($form, $form_state);
71     // Disable the ability to toggle AJAX support, as we forcibly enable AJAX
72     // in our ajaxEnabled() implementation.
73     if (isset($form['use_ajax'])) {
74       $form['use_ajax'] = [
75         '#description' => $this->t('Entity Browser requires Views to use AJAX.'),
76         '#type' => 'checkbox',
77         '#title' => $this->t('Use AJAX'),
78         '#default_value' => 1,
79         '#disabled' => TRUE,
80       ];
81     }
82   }
83
84   /**
85    * {@inheritdoc}
86    */
87   public function preview() {
88     return $this->execute();
89   }
90
91   /**
92    * {@inheritdoc}
93    *
94    * Pre render callback for a view.
95    *
96    * Based on DisplayPluginBase::elementPreRender() except that we removed form
97    * part which need to handle by our own.
98    */
99   public function elementPreRender(array $element) {
100     $view = $element['#view'];
101     $empty = empty($view->result);
102
103     // Force a render array so CSS/JS can be attached.
104     if (!is_array($element['#rows'])) {
105       $element['#rows'] = ['#markup' => $element['#rows']];
106     }
107
108     $element['#header'] = $view->display_handler->renderArea('header', $empty);
109     $element['#footer'] = $view->display_handler->renderArea('footer', $empty);
110     $element['#empty'] = $empty ? $view->display_handler->renderArea('empty', $empty) : [];
111     $element['#exposed'] = !empty($view->exposed_widgets) ? $view->exposed_widgets : [];
112     $element['#more'] = $view->display_handler->renderMoreLink();
113     $element['#feed_icons'] = !empty($view->feedIcons) ? $view->feedIcons : [];
114
115     if ($view->display_handler->renderPager()) {
116       $exposed_input = isset($view->exposed_raw_input) ? $view->exposed_raw_input : NULL;
117       $element['#pager'] = $view->renderPager($exposed_input);
118     }
119
120     if (!empty($view->attachment_before)) {
121       $element['#attachment_before'] = $view->attachment_before;
122     }
123     if (!empty($view->attachment_after)) {
124       $element['#attachment_after'] = $view->attachment_after;
125     }
126
127     return $element;
128   }
129
130   /**
131    * Handles form elements on a view.
132    *
133    * @param array $render
134    *   Rendered content.
135    */
136   protected function handleForm(array &$render) {
137     if (!empty($this->view->field['entity_browser_select'])) {
138
139       /** @var \Drupal\entity_browser\Plugin\views\field\SelectForm $select */
140       $select = $this->view->field['entity_browser_select'];
141       $select->viewsForm($render);
142
143       $render['#post_render'][] = [get_class($this), 'postRender'];
144       $substitutions = [];
145       foreach ($this->view->result as $row) {
146         $form_element_row_id = $select->getRowId($row);
147
148         $substitutions[] = [
149           'placeholder' => '<!--form-item-entity_browser_select--' . $form_element_row_id . '-->',
150           'field_name' => 'entity_browser_select',
151           'row_id' => $form_element_row_id,
152         ];
153       }
154
155       $render['#substitutions'] = [
156         '#type' => 'value',
157         '#value' => $substitutions,
158       ];
159     }
160   }
161
162   /**
163    * Post render callback that moves form elements into the view.
164    *
165    * Form elements need to be added out of view to be correctly detected by Form
166    * API and then added into the view afterwards. Views use the same approach
167    * for bulk operations.
168    *
169    * @param string $content
170    *   Rendered content.
171    * @param array $element
172    *   Render array.
173    *
174    * @return string
175    *   Rendered content.
176    */
177   public static function postRender($content, array $element) {
178     // Placeholders and their substitutions (usually rendered form elements).
179     $search = $replace = [];
180
181     // Add in substitutions provided by the form.
182     foreach ($element['#substitutions']['#value'] as $substitution) {
183       $field_name = $substitution['field_name'];
184       $row_id = $substitution['row_id'];
185
186       $search[] = $substitution['placeholder'];
187       $replace[] = isset($element[$field_name][$row_id]) ? \Drupal::service('renderer')->render($element[$field_name][$row_id]) : '';
188     }
189     // Add in substitutions from hook_views_form_substitutions().
190     $substitutions = \Drupal::moduleHandler()->invokeAll('views_form_substitutions');
191     foreach ($substitutions as $placeholder => $substitution) {
192       $search[] = $placeholder;
193       $replace[] = $substitution;
194     }
195
196     // We cannot render exposed form within the View, as nested forms are not
197     // standard and will break entity selection.
198     $search[] = '<form';
199     $replace[] = '<div';
200     $search[] = '</form>';
201     $replace[] = '</div>';
202
203     $content = str_replace($search, $replace, $content);
204
205     return $content;
206   }
207
208 }