Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
129 / 129 |
|
100.00% |
5 / 5 |
CRAP | |
100.00% |
1 / 1 |
VisitorsDisplayLink | |
100.00% |
129 / 129 |
|
100.00% |
5 / 5 |
24 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
create | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
buildOptionsForm | |
100.00% |
41 / 41 |
|
100.00% |
1 / 1 |
4 | |||
validate | |
100.00% |
40 / 40 |
|
100.00% |
1 / 1 |
10 | |||
render | |
100.00% |
40 / 40 |
|
100.00% |
1 / 1 |
8 |
1 | <?php |
2 | |
3 | namespace Drupal\visitors\Plugin\views\area; |
4 | |
5 | use Drupal\Core\Config\ImmutableConfig; |
6 | use Drupal\Core\EventSubscriber\AjaxResponseSubscriber; |
7 | use Drupal\Core\EventSubscriber\MainContentViewSubscriber; |
8 | use Drupal\Core\Form\FormBuilderInterface; |
9 | use Drupal\Core\Form\FormStateInterface; |
10 | use Drupal\Core\Url; |
11 | use Drupal\views\Attribute\ViewsArea; |
12 | use Drupal\views\Plugin\views\area\DisplayLink; |
13 | use Symfony\Component\DependencyInjection\ContainerInterface; |
14 | |
15 | /** |
16 | * Views area display_link handler. |
17 | * |
18 | * @ingroup views_area_handlers |
19 | */ |
20 | #[ViewsArea("visitors_display_link")] |
21 | final class VisitorsDisplayLink extends DisplayLink { |
22 | |
23 | /** |
24 | * The view settings. |
25 | * |
26 | * @var \Drupal\Core\Config\ImmutableConfig |
27 | */ |
28 | protected $viewSettings; |
29 | |
30 | /** |
31 | * Constructs a new VisitorsDisplayLink object. |
32 | * |
33 | * @param array $configuration |
34 | * The plugin configuration. |
35 | * @param string $plugin_id |
36 | * The plugin ID. |
37 | * @param mixed $plugin_definition |
38 | * The plugin definition. |
39 | * @param \Drupal\Core\Config\ImmutableConfig $view_settings |
40 | * The view settings. |
41 | */ |
42 | public function __construct(array $configuration, $plugin_id, $plugin_definition, ImmutableConfig $view_settings) { |
43 | parent::__construct($configuration, $plugin_id, $plugin_definition); |
44 | $this->viewSettings = $view_settings; |
45 | } |
46 | |
47 | /** |
48 | * {@inheritdoc} |
49 | */ |
50 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { |
51 | return new self( |
52 | $configuration, |
53 | $plugin_id, |
54 | $plugin_definition, |
55 | $container->get('config.factory')->get('views.settings') |
56 | ); |
57 | } |
58 | |
59 | /** |
60 | * {@inheritdoc} |
61 | */ |
62 | public function buildOptionsForm(&$form, FormStateInterface $form_state) { |
63 | parent::buildOptionsForm($form, $form_state); |
64 | |
65 | $allowed_displays = []; |
66 | $displays = $this->view->storage->get('display'); |
67 | foreach ($displays as $display_id => $display) { |
68 | if ($this->isPathBasedDisplay($display_id)) { |
69 | unset($displays[$display_id]); |
70 | continue; |
71 | } |
72 | $allowed_displays[$display_id] = $display['display_title']; |
73 | } |
74 | |
75 | $form['description'] = [ |
76 | [ |
77 | '#markup' => $this->t('To make sure the results are the same when switching to the other display, it is recommended to make sure the display:'), |
78 | ], |
79 | [ |
80 | '#theme' => 'item_list', |
81 | '#items' => [ |
82 | $this->t('Has a path.'), |
83 | $this->t('Has the same filter criteria.'), |
84 | $this->t('Has the same sort criteria.'), |
85 | $this->t('Has the same pager settings.'), |
86 | $this->t('Has the same contextual filters.'), |
87 | ], |
88 | ], |
89 | ]; |
90 | |
91 | if (!$allowed_displays) { |
92 | $form['empty_message'] = [ |
93 | '#markup' => '<p><em>' . $this->t('There are no path-based displays available.') . '</em></p>', |
94 | ]; |
95 | } |
96 | else { |
97 | $form['display_id'] = [ |
98 | '#title' => $this->t('Display'), |
99 | '#type' => 'select', |
100 | '#options' => $allowed_displays, |
101 | '#default_value' => $this->options['display_id'], |
102 | '#required' => TRUE, |
103 | ]; |
104 | $form['label'] = [ |
105 | '#title' => $this->t('Label'), |
106 | '#description' => $this->t('The text of the link.'), |
107 | '#type' => 'textfield', |
108 | '#default_value' => $this->options['label'], |
109 | '#required' => TRUE, |
110 | ]; |
111 | } |
112 | } |
113 | |
114 | /** |
115 | * {@inheritdoc} |
116 | */ |
117 | public function validate() { |
118 | $errors = []; |
119 | |
120 | // Do not add errors for the default display if it is not displayed in the |
121 | // UI. |
122 | if ($this->displayHandler->isDefaultDisplay() && !$this->viewSettings->get('ui.show.default_display')) { |
123 | return $errors; |
124 | } |
125 | |
126 | // Ajax errors can cause the plugin to be added without any settings. |
127 | $linked_display_id = !empty($this->options['display_id']) ? $this->options['display_id'] : NULL; |
128 | if (!$linked_display_id) { |
129 | $errors[] = $this->t('%current_display: The link in the %area area has no configured display.', [ |
130 | '%current_display' => $this->displayHandler->display['display_title'], |
131 | '%area' => $this->areaType, |
132 | ]); |
133 | return $errors; |
134 | } |
135 | |
136 | // Check if the linked display hasn't been removed. |
137 | if (!$this->view->displayHandlers->get($linked_display_id)) { |
138 | $errors[] = $this->t('%current_display: The link in the %area area points to the %linked_display display which no longer exists.', [ |
139 | '%current_display' => $this->displayHandler->display['display_title'], |
140 | '%area' => $this->areaType, |
141 | '%linked_display' => $this->options['display_id'], |
142 | ]); |
143 | return $errors; |
144 | } |
145 | |
146 | // Check if the linked display is a path-based display. |
147 | if ($this->isPathBasedDisplay($linked_display_id)) { |
148 | $errors[] = $this->t('%current_display: The link in the %area area points to the %linked_display display which does not have a path.', [ |
149 | '%current_display' => $this->displayHandler->display['display_title'], |
150 | '%area' => $this->areaType, |
151 | '%linked_display' => $this->view->displayHandlers->get($linked_display_id)->display['display_title'], |
152 | ]); |
153 | return $errors; |
154 | } |
155 | |
156 | // Check if options of the linked display are equal to the options of the |
157 | // current display. We "only" show a warning here, because even though we |
158 | // recommend keeping the display options equal, we do not want to enforce |
159 | // this. |
160 | $unequal_options = [ |
161 | 'filters' => $this->t('Filter criteria'), |
162 | 'arguments' => $this->t('Contextual filters'), |
163 | ]; |
164 | foreach (array_keys($unequal_options) as $option) { |
165 | if ($this->hasEqualOptions($linked_display_id, $option)) { |
166 | unset($unequal_options[$option]); |
167 | } |
168 | } |
169 | |
170 | if ($unequal_options) { |
171 | $warning = $this->t('%current_display: The link in the %area area points to the %linked_display display which uses different settings than the %current_display display for: %unequal_options. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', [ |
172 | '%current_display' => $this->displayHandler->display['display_title'], |
173 | '%area' => $this->areaType, |
174 | '%linked_display' => $this->view->displayHandlers->get($linked_display_id)->display['display_title'], |
175 | '%unequal_options' => implode(', ', $unequal_options), |
176 | ]); |
177 | $this->messenger()->addWarning($warning); |
178 | } |
179 | return $errors; |
180 | } |
181 | |
182 | /** |
183 | * {@inheritdoc} |
184 | */ |
185 | public function render($empty = FALSE) { |
186 | |
187 | if (($empty && empty($this->options['empty'])) || empty($this->options['display_id'])) { |
188 | return []; |
189 | } |
190 | |
191 | if ($this->isPathBasedDisplay($this->options['display_id'])) { |
192 | return []; |
193 | } |
194 | |
195 | // Get query parameters from the exposed input and pager. |
196 | $query = $this->view->getExposedInput(); |
197 | if ($current_page = $this->view->getCurrentPage()) { |
198 | $query['page'] = $current_page; |
199 | } |
200 | |
201 | // @todo Remove this parsing once these are removed from the request in |
202 | // https://www.drupal.org/node/2504709. |
203 | foreach ([ |
204 | 'view_name', |
205 | 'view_display_id', |
206 | 'view_args', |
207 | 'view_path', |
208 | 'view_dom_id', |
209 | 'pager_element', |
210 | 'view_base_path', |
211 | AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER, |
212 | FormBuilderInterface::AJAX_FORM_REQUEST, |
213 | MainContentViewSubscriber::WRAPPER_FORMAT, |
214 | ] as $key) { |
215 | unset($query[$key]); |
216 | } |
217 | |
218 | // Set default classes. |
219 | $classes = [ |
220 | 'views-display-link', |
221 | 'views-display-link-' . $this->options['display_id'], |
222 | ]; |
223 | if ($this->options['display_id'] === $this->view->current_display) { |
224 | $classes[] = 'is-active'; |
225 | } |
226 | $classes[] = 'use-ajax'; |
227 | $storage_id = $this->view->storage->id(); |
228 | $path = 'internal:/admin/visitors/_report/' . $storage_id . '/' . $this->options['display_id']; |
229 | $query_class = '.view-id-' . $storage_id . '.view-display-id-' . $this->view->current_display; |
230 | |
231 | return [ |
232 | '#type' => 'link', |
233 | '#title' => $this->options['label'], |
234 | '#url' => Url::fromUri($path, [ |
235 | 'query' => ['class' => $query_class], |
236 | ]), |
237 | '#options' => [ |
238 | 'attributes' => ['class' => $classes], |
239 | ], |
240 | ]; |
241 | } |
242 | |
243 | } |