PluginProbe
404 Solution / 4.3.0
404 Solution v4.3.0
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / admin / AdminStatusFilterTabs.php

AdminStatusFilterTabs.php in 404 Solution 4.3.0, at includes/admin/AdminStatusFilterTabs.php

210 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Renders redirect and captured URL status filter links.
9 */
10 class ABJ_404_Solution_AdminStatusFilterTabs {
11
12 /** @var ABJ_404_Solution_Functions */
13 private $f;
14 /** @var ABJ_404_Solution_PluginLogic */
15 private $logic;
16 /** @var ABJ_404_Solution_Logging */
17 private $logger;
18 /** @var ABJ_404_Solution_ViewReadServiceInterface */
19 private $viewReadService;
20 /** @var ABJ_404_Solution_View_ListTableChrome */
21 private $listTableChrome;
22
23 /** @param ABJ_404_Solution_ViewReadServiceInterface $viewReadService */
24 public function __construct(ABJ_404_Solution_Functions $functions, ABJ_404_Solution_PluginLogic $pluginLogic,
25 ABJ_404_Solution_Logging $logging, $viewReadService,
26 ABJ_404_Solution_View_ListTableChrome $listTableChrome) {
27 $this->f = $functions;
28 $this->logic = $pluginLogic;
29 $this->logger = $logging;
30 $this->viewReadService = $viewReadService;
31 $this->listTableChrome = $listTableChrome;
32 }
33
34 /**
35 * @param string $sub
36 * @param array<string, mixed> $tableOptions
37 */
38 public function render(string $sub, array $tableOptions): string {
39 if (empty($tableOptions)) {
40 $tableOptions = $this->logic->settingsUpdate()->getTableOptions($sub);
41 }
42
43 return $this->tpl('viewLogsTabFiltersClearBar.html')
44 . $this->renderList($sub)
45 . $this->tpl('viewLogsTabFiltersClearBarClose.html');
46 }
47
48 public function renderList(string $sub): string {
49 $tableOptions = $this->logic->settingsUpdate()->getTableOptions($sub);
50 $filter = isset($tableOptions['filter']) ? intval(is_scalar($tableOptions['filter']) ? $tableOptions['filter'] : 0) : 0;
51 $url = $this->baseUrl($sub, $tableOptions);
52 $filterState = $this->filterState($sub);
53 $types = $filterState['types'];
54 $counts = $filterState['counts'];
55
56 $html = $this->tpl('viewLogsTabFiltersListOpen.html');
57 if ($sub != 'abj404_captured') {
58 $html .= $this->filterItem('', $url, ($filter == 0) ? ' class="current"' : '', __('All', '404-solution'), (int)($counts['all'] ?? 0));
59 }
60 foreach ($types as $type) {
61 $item = $this->typedFilterItem($sub, $url, $filter, (int)$type, $counts);
62 if ($item !== '') {
63 $html .= $item;
64 }
65 }
66
67 $trashurl = $url . '&filter=' . ABJ404_TRASH_FILTER;
68 $trashClass = (($tableOptions['filter'] ?? 0) == ABJ404_TRASH_FILTER) ? ' class="current"' : '';
69 $html .= $this->filterItem(' | ', $trashurl, $trashClass, __('Trash', '404-solution'), (int)($counts['trash'] ?? 0));
70 $html .= $this->tpl('viewLogsTabFiltersListClose.html');
71 $html .= $this->tpl('viewLogsTabFiltersFormGap.html');
72
73 return $html . $this->f->str_replace(
74 '{action_url}',
75 $this->listTableChrome->getBulkOperationsFormURL($sub, $tableOptions),
76 $this->tpl('viewLogsTabFiltersBulkForm.html')
77 );
78 }
79
80 /** @param array<string, mixed> $tableOptions */
81 private function baseUrl(string $sub, array $tableOptions): string {
82 $orderby = isset($tableOptions['orderby']) && is_string($tableOptions['orderby']) ? $tableOptions['orderby'] : 'url';
83 $order = isset($tableOptions['order']) && is_string($tableOptions['order']) ? $tableOptions['order'] : 'ASC';
84 $url = '?page=' . ABJ404_PP;
85 if ($sub == 'abj404_captured') {
86 $url .= '&subpage=abj404_captured';
87 } else if ($sub == 'abj404_redirects') {
88 $url .= '&subpage=abj404_redirects';
89 } else {
90 $this->logger->errorMessage('Unexpected sub page: ' . $sub);
91 }
92 $url .= '&orderby=' . sanitize_text_field($orderby);
93 $url .= '&order=' . sanitize_text_field($order);
94
95 return $url;
96 }
97
98 /**
99 * @return array{types: array<int, int>, counts: array<string, int>}
100 */
101 private function filterState(string $sub): array {
102 global $abj404_redirect_types;
103 global $abj404_captured_types;
104
105 if ($sub == 'abj404_redirects') {
106 $types = $this->numericTypes($abj404_redirect_types, array(ABJ404_STATUS_MANUAL, ABJ404_STATUS_AUTO, ABJ404_STATUS_REGEX));
107 return array('types' => $types, 'counts' => $this->viewReadService->getRedirectStatusCounts());
108 }
109 if ($sub == 'abj404_captured') {
110 $types = $this->numericTypes($abj404_captured_types, array(ABJ404_STATUS_CAPTURED, ABJ404_STATUS_IGNORED, ABJ404_STATUS_LATER));
111 return array('types' => $types, 'counts' => $this->viewReadService->getCapturedStatusCounts());
112 }
113
114 $this->logger->debugMessage('Unexpected sub type for tab filter: ' . $sub);
115 return array('types' => array(ABJ404_STATUS_CAPTURED, ABJ404_STATUS_IGNORED, ABJ404_STATUS_LATER), 'counts' => array());
116 }
117
118 /**
119 * @param mixed $candidate
120 * @param array<int, int> $defaults
121 * @return array<int, int>
122 */
123 private function numericTypes($candidate, array $defaults): array {
124 if (!is_array($candidate)) {
125 return $defaults;
126 }
127 $values = array_values($candidate);
128 foreach ($values as $value) {
129 if (!is_int($value) && !(is_string($value) && ctype_digit($value))) {
130 return $defaults;
131 }
132 }
133 if (empty($values)) {
134 return $defaults;
135 }
136 $ints = array();
137 foreach ($values as $value) {
138 if (is_int($value)) {
139 $ints[] = $value;
140 continue;
141 }
142 if (is_string($value) && ctype_digit($value)) {
143 $ints[] = intval($value);
144 continue;
145 }
146 return $defaults;
147 }
148 return $ints;
149 }
150
151 /** @param array<string, int> $counts */
152 private function typedFilterItem(string $sub, string $url, int $filter, int $type, array $counts): string {
153 if ($type == ABJ404_STATUS_REGEX) {
154 return '';
155 }
156 $definition = $this->filterDefinition($type, $counts);
157 if ($definition === null) {
158 $this->logger->errorMessage('Unrecognized redirect type in View: ' . esc_html((string)$type));
159 $definition = array('title' => __('Unknown', '404-solution'), 'count' => 0);
160 }
161
162 $prefix = ($sub != 'abj404_captured' || $type != ABJ404_STATUS_CAPTURED) ? ' | ' : '';
163 $class = ($filter == $type) ? ' class="current"' : '';
164 return $this->filterItem($prefix, $url . '&filter=' . $type, $class, $definition['title'], $definition['count']);
165 }
166
167 /**
168 * @param array<string, int> $counts
169 * @return array{title: string, count: int}|null
170 */
171 private function filterDefinition(int $type, array $counts): ?array {
172 $definitions = array(
173 ABJ404_STATUS_MANUAL => array(
174 'title' => __('Manual Redirects', '404-solution'),
175 'count' => intval($counts['manual'] ?? 0) + intval($counts['regex'] ?? 0),
176 ),
177 ABJ404_STATUS_AUTO => array(
178 'title' => __('Automatic Redirects', '404-solution'),
179 'count' => intval($counts['auto'] ?? 0),
180 ),
181 ABJ404_STATUS_CAPTURED => array(
182 'title' => 'Captured URLs',
183 'count' => intval($counts['captured'] ?? 0),
184 ),
185 ABJ404_STATUS_IGNORED => array(
186 'title' => 'Ignored 404s',
187 'count' => intval($counts['ignored'] ?? 0),
188 ),
189 ABJ404_STATUS_LATER => array(
190 'title' => 'Organize Later',
191 'count' => intval($counts['later'] ?? 0),
192 ),
193 );
194 return $definitions[$type] ?? null;
195 }
196
197 private function filterItem(string $prefix, string $url, string $class, string $title, int $count): string {
198 return $this->f->str_replace(
199 array('{prefix}', '{url}', '{class_attr}', '{title}', '{count}'),
200 array($prefix, esc_url($url), $class, $title, esc_html((string)$count)),
201 $this->tpl('viewLogsTabFilterItem.html')
202 );
203 }
204
205 private function tpl(string $name): string {
206 $raw = ABJ_404_Solution_FileSystemService::readFileContents(__DIR__ . '/../html/' . $name, false);
207 return rtrim((string)$raw, "\n");
208 }
209 }
210