Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
DisplayLinkController
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 2
342
0.00% covered (danger)
0.00%
0 / 1
 __invoke
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 1
240
 extractViewAndDisplayFromClass
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace Drupal\visitors\Controller;
4
5use Drupal\Core\Ajax\AjaxResponse;
6use Drupal\Core\Ajax\ReplaceCommand;
7use Drupal\Core\Controller\ControllerBase;
8use Drupal\views\Views;
9use Symfony\Component\HttpFoundation\Request;
10
11/**
12 * Display Link Controller.
13 *
14 * Returns the display link for a given view and display.
15 */
16class DisplayLinkController extends ControllerBase {
17
18  /**
19   * Single-action controller.
20   */
21  public function __invoke(Request $request, string $view_id, string $display_id) {
22    $blocks['path'] = [
23      '#view_id'      => $view_id,
24      '#view_display' => $display_id,
25    ];
26
27    // Store AJAX parameters to exclude from sort links in a static variable
28    // that can be accessed by our theme preprocessing hook.
29    $ajax_params_to_remove = [
30      'class',
31      '_wrapper_format',
32      'ajax_form',
33      '_drupal_ajax',
34    ];
35
36    $bidirectional_label = $request->query->get('bidirectional_label');
37    if ($bidirectional_label) {
38      [$source_view_id, $source_display] = $this->extractViewAndDisplayFromClass($request->query->get('class'));
39      // Set bidirectional linking information in drupal_static for
40      // VisitorsDisplayLink to use.
41      $bidirectional_info = &drupal_static('visitors_bidirectional_linking', []);
42      $bidirectional_info[$bidirectional_label] = [
43        'view_id' => $source_view_id,
44        'display_id' => $source_display,
45      ];
46    }
47
48    // Store these in a static variable for the theme hook.
49    $excluded_params = &drupal_static('visitors_excluded_sort_params', []);
50    $excluded_params = $ajax_params_to_remove;
51
52    // Get the referrer URL and extract sort/order parameters.
53    $referrer = $request->headers->get('referer');
54    $referrer_path = '';
55    $sort_args = [];
56
57    if ($referrer) {
58      $referrer_parts = parse_url($referrer);
59      $referrer_path = $referrer_parts['path'] ?? '';
60
61      // Extract query parameters from referrer.
62      if (!empty($referrer_parts['query'])) {
63        parse_str($referrer_parts['query'], $referrer_query);
64
65        // Check if referrer has sort/order parameters.
66        if (isset($referrer_query['order'])) {
67          $sort_args['order'] = $referrer_query['order'];
68        }
69        if (isset($referrer_query['sort'])) {
70          $sort_args['sort'] = $referrer_query['sort'];
71        }
72      }
73
74      $referrer_path_static = &drupal_static('visitors_referrer_path', '');
75      $referrer_path_static = $referrer_path;
76    }
77
78    // Apply sort parameters from referrer to the view.
79    if (!empty($sort_args)) {
80      // Load the view manually to apply sorting.
81      $view = Views::getView($view_id);
82      if ($view && $view->access($display_id)) {
83        $view->setDisplay($display_id);
84
85        // Apply the sort parameters to the view.
86        if (isset($sort_args['order']) && isset($sort_args['sort'])) {
87          $order_field = $sort_args['order'];
88          $sort_direction = strtoupper($sort_args['sort']);
89
90          // Set the sort on the view's table style plugin.
91          if ($view->display_handler->getPlugin('style') &&
92              method_exists($view->display_handler->getPlugin('style'), 'options')) {
93            $style_plugin = $view->display_handler->getPlugin('style');
94            $style_plugin->active = $order_field;
95            $style_plugin->order = $sort_direction;
96          }
97
98          // Also set it via the request for the table style to pick up.
99          $temp_query = $sort_args;
100          $temp_request = $request->duplicate($temp_query);
101          $view->setRequest($temp_request);
102        }
103
104        // Build the renderable array.
105        $rendered = $view->buildRenderable($display_id);
106      }
107      else {
108        // Fallback to normal embed if view access fails.
109        $rendered = \views_embed_view($view_id, $display_id);
110      }
111    }
112    else {
113      $rendered = \views_embed_view($view_id, $display_id);
114    }
115
116    // Set live preview mode to ensure sort links use current route.
117    if (isset($rendered['view_build']['#view'])) {
118      $view = $rendered['view_build']['#view'];
119      $view->live_preview = TRUE;
120      if (!empty($referrer_path)) {
121        $view->override_path = $referrer_path;
122      }
123    }
124
125    $settings = NULL;
126    $selector = $request->query->get('class');
127
128    $response = new AjaxResponse();
129    $response->addCommand(new ReplaceCommand($selector, $rendered, $settings));
130
131    return $response;
132  }
133
134  /**
135   * Extract view ID and display ID from the class parameter.
136   *
137   * The class parameter contains the format:
138   *  .view-id-{view_id}.view-display-id-{display_id}
139   * This method extracts both the view ID and display ID from this format.
140   *
141   * @param string|null $class_selector
142   *   The class selector string.
143   *
144   * @return array|null
145   *   Array containing [view_id, display_id] if found, null otherwise.
146   */
147  protected function extractViewAndDisplayFromClass(?string $class_selector): ?array {
148    if (empty($class_selector)) {
149      return NULL;
150    }
151
152    // Extract view ID and display ID from the format:
153    // .view-id-{view_id}.view-display-id-{display_id}.
154    if (preg_match('/\.view-id-([^.\s]+)\.view-display-id-([^.\s]+)/', $class_selector, $matches)) {
155      return [$matches[1], $matches[2]];
156    }
157
158    return NULL;
159  }
160
161}