3 namespace Drupal\entity_browser\Plugin\views\display;
5 use Drupal\Core\Form\FormStateInterface;
6 use Drupal\views\Plugin\views\display\DisplayPluginBase;
9 * The plugin that handles entity browser display.
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.
15 * @ingroup views_display_plugins
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
26 class EntityBrowser extends DisplayPluginBase {
31 public function execute() {
33 $render = ['view' => $this->view->render()];
34 $this->handleForm($render);
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.
50 protected function defineOptions() {
51 $options = parent::defineOptions();
52 $options['use_ajax']['default'] = TRUE;
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)');
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'])) {
75 '#description' => $this->t('Entity Browser requires Views to use AJAX.'),
76 '#type' => 'checkbox',
77 '#title' => $this->t('Use AJAX'),
78 '#default_value' => 1,
87 public function preview() {
88 return $this->execute();
94 * Pre render callback for a view.
96 * Based on DisplayPluginBase::elementPreRender() except that we removed form
97 * part which need to handle by our own.
99 public function elementPreRender(array $element) {
100 $view = $element['#view'];
101 $empty = empty($view->result);
103 // Force a render array so CSS/JS can be attached.
104 if (!is_array($element['#rows'])) {
105 $element['#rows'] = ['#markup' => $element['#rows']];
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 : [];
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);
120 if (!empty($view->attachment_before)) {
121 $element['#attachment_before'] = $view->attachment_before;
123 if (!empty($view->attachment_after)) {
124 $element['#attachment_after'] = $view->attachment_after;
131 * Handles form elements on a view.
133 * @param array $render
136 protected function handleForm(array &$render) {
137 if (!empty($this->view->field['entity_browser_select'])) {
139 /** @var \Drupal\entity_browser\Plugin\views\field\SelectForm $select */
140 $select = $this->view->field['entity_browser_select'];
141 $select->viewsForm($render);
143 $render['#post_render'][] = [get_class($this), 'postRender'];
145 foreach ($this->view->result as $row) {
146 $form_element_row_id = $select->getRowId($row);
149 'placeholder' => '<!--form-item-entity_browser_select--' . $form_element_row_id . '-->',
150 'field_name' => 'entity_browser_select',
151 'row_id' => $form_element_row_id,
155 $render['#substitutions'] = [
157 '#value' => $substitutions,
163 * Post render callback that moves form elements into the view.
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.
169 * @param string $content
171 * @param array $element
177 public static function postRender($content, array $element) {
178 // Placeholders and their substitutions (usually rendered form elements).
179 $search = $replace = [];
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'];
186 $search[] = $substitution['placeholder'];
187 $replace[] = isset($element[$field_name][$row_id]) ? \Drupal::service('renderer')->render($element[$field_name][$row_id]) : '';
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;
196 // We cannot render exposed form within the View, as nested forms are not
197 // standard and will break entity selection.
200 $search[] = '</form>';
201 $replace[] = '</div>';
203 $content = str_replace($search, $replace, $content);