PluginProbe
404 Solution / trunk
404 Solution vtrunk
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 / view / CapturedPageChromeRenderer.php

CapturedPageChromeRenderer.php in 404 Solution trunk, at includes/view/CapturedPageChromeRenderer.php

192 lines 9.5 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 the Captured 404 URLs admin page wrapper/chrome: the subsubsub
9 * filter row, subtitle, empty-trash button, bulk-action buttons, bulk-form
10 * action/nonce, and the warmup placeholder that surrounds the AJAX-refreshed
11 * table body. Extracted from View_CapturedURLsTable because page-chrome
12 * rendering (page-level wrapper, filters, bulk actions) is a distinct
13 * responsibility from table-body-row rendering.
14 */
15 class ABJ_404_Solution_CapturedPageChromeRenderer {
16
17 /** @var ABJ_404_Solution_PluginLogic */
18 private $logic;
19
20 /** @var ABJ_404_Solution_View_ListTableChrome */
21 private $listTableChrome;
22
23 /** @var ABJ_404_Solution_Functions */
24 private $f;
25
26 public function __construct(
27 ABJ_404_Solution_PluginLogic $logic,
28 ABJ_404_Solution_View_ListTableChrome $listTableChrome,
29 ABJ_404_Solution_Functions $functions
30 ) {
31 $this->logic = $logic;
32 $this->listTableChrome = $listTableChrome;
33 $this->f = $functions;
34 }
35
36 private function tpl(string $name): string {
37 $raw = ABJ_404_Solution_FileSystemService::readFileContents(dirname(__DIR__) . '/html/' . $name, false);
38 return rtrim((string)$raw, "\n");
39 }
40
41 /** @param array<string,string> $vars */
42 private function fillTpl(string $name, array $vars): string {
43 return (string)$this->f->str_replace(array_keys($vars), array_values($vars), $this->tpl($name));
44 }
45
46 public function render(): string {
47 $sub = 'abj404_captured';
48
49 $tableOptions = $this->logic->settingsUpdate()->getTableOptions($sub);
50 $isSimpleMode = abj_service('settings_mode_preference')->getMode() === 'simple';
51
52 $subsubsubHtml = $this->buildSubsubsubHtml($sub, $isSimpleMode, $tableOptions);
53 $display = $this->readDisplayOptions($tableOptions);
54
55 $paginationNonce = wp_create_nonce('abj404_updatePaginationLink');
56 $lazyBackfillNonce = wp_create_nonce('abj404_runLazyBackfill');
57
58 $subtitleHtml = $this->buildSubtitleHtml($isSimpleMode);
59 $emptyTrashHtml = $this->buildEmptyTrashButtonHtml($display['currentFilter']);
60 $bulkButtons = $this->buildBulkButtonsHtml($isSimpleMode, $display['currentFilter']);
61
62 // Bulk form action URL + nonce field
63 $formAction = $this->listTableChrome->getBulkOperationsFormURL($sub, $tableOptions);
64 ob_start();
65 wp_nonce_field('abj404_bulkProcess');
66 $nonceField = (string)ob_get_clean();
67
68 $warmup = ABJ_404_Solution_FileSystemService::readFileContents(dirname(__DIR__) . "/html/tableWarmupPlaceholder.html");
69
70 $refresh = $this->listTableChrome->paginationRefreshStrings();
71
72 return $this->fillTpl('viewRedirectsTableCapturedPageWrapper.html', array(
73 '{captured_title}' => __('Captured 404 URLs', '404-solution'),
74 '{subtitle}' => $subtitleHtml,
75 '{subsubsub}' => $subsubsubHtml,
76 '{data-pagination-ajax-url}' => esc_attr(admin_url('admin-ajax.php')),
77 '{data-lazy-backfill-ajax-url}' => esc_attr(admin_url('admin-ajax.php')),
78 '{data-lazy-backfill-nonce}' => esc_attr($lazyBackfillNonce),
79 '{data-pagination-ajax-subpage}' => esc_attr($sub),
80 '{data-pagination-ajax-nonce}' => esc_attr($paginationNonce),
81 '{data-pagination-current-orderby}' => esc_attr($display['currentOrderBy']),
82 '{data-pagination-current-order}' => esc_attr($display['currentOrder']),
83 '{data-pagination-current-filter}' => esc_attr((string)$display['currentFilter']),
84 '{data-pagination-current-paged}' => esc_attr((string)$display['currentPaged']),
85 '{data-pagination-current-score-range}' => esc_attr($display['currentScoreRange']),
86 '{data-pagination-auto-refresh}' => esc_attr('1'),
87 '{data-pagination-refresh-available-text}' => esc_attr($refresh['available']),
88 '{search_placeholder}' => esc_attr__('Type to filter URLs... (press Enter)', '404-solution'),
89 '{filter_text}' => esc_attr($display['filterText']),
90 '{rows_per_page_label}' => esc_html__('Rows per page:', '404-solution'),
91 '{perpage_options}' => $this->listTableChrome->buildPerpageOptions($display['perPage']),
92 '{empty_trash_button}' => $emptyTrashHtml,
93 '{selected_label}' => __('selected', '404-solution'),
94 '{bulk_buttons}' => $bulkButtons,
95 '{clear_label}' => __('Clear', '404-solution'),
96 '{form_action}' => esc_url($formAction),
97 '{nonce_field}' => $nonceField,
98 '{warmup_placeholder}' => $warmup,
99 '{loading_badge_text}' => esc_html__('Loading...', '404-solution'),
100 ));
101 }
102
103 /** @param array<string, mixed> $tableOptions */
104 private function buildSubsubsubHtml(string $sub, bool $isSimpleMode, array $tableOptions): string {
105 // Filter row (native WP subsubsub). Counts are placeholders, populated via AJAX.
106 if ($isSimpleMode) {
107 $items = array(
108 array(ABJ404_STATUS_CAPTURED, __('Needs Review', '404-solution')),
109 array(ABJ404_HANDLED_FILTER, __('Handled', '404-solution')),
110 );
111 } else {
112 $items = array(
113 array(0, __('All', '404-solution')),
114 array(ABJ404_STATUS_CAPTURED, __('Captured', '404-solution')),
115 array(ABJ404_STATUS_IGNORED, __('Ignored', '404-solution')),
116 array(ABJ404_STATUS_LATER, __('Later', '404-solution')),
117 array(ABJ404_TRASH_FILTER, __('Trash', '404-solution')),
118 );
119 }
120 return $this->listTableChrome->buildSubsubsubFilters($sub, $items, $tableOptions);
121 }
122
123 /**
124 * Reads the raw table options into typed, defaulted display values.
125 *
126 * @param array<string, mixed> $tableOptions
127 * @return array{filterText: string, perPage: int, currentFilter: bool|float|int|string, currentOrderBy: string, currentOrder: string, currentPaged: int, currentScoreRange: string}
128 */
129 private function readDisplayOptions(array $tableOptions): array {
130 $rawPerPage = $tableOptions['perpage'] ?? 25;
131 $rawFilter = $tableOptions['filter'] ?? 0;
132 $rawOrderBy = $tableOptions['orderby'] ?? '';
133 $rawOrder = $tableOptions['order'] ?? '';
134 $rawPaged = $tableOptions['paged'] ?? 1;
135 $currentPaged = is_scalar($rawPaged) ? intval($rawPaged) : 1;
136 if ($currentPaged < 1) {
137 $currentPaged = 1;
138 }
139 $rawScoreRange = $tableOptions['score_range'] ?? 'all';
140
141 return array(
142 'filterText' => is_string($tableOptions['filterText'] ?? '') ? (string)($tableOptions['filterText'] ?? '') : '',
143 'perPage' => is_scalar($rawPerPage) ? (int)$rawPerPage : 25,
144 'currentFilter' => is_scalar($rawFilter) ? $rawFilter : 0,
145 'currentOrderBy' => is_string($rawOrderBy) ? $rawOrderBy : 'url',
146 'currentOrder' => is_string($rawOrder) ? $rawOrder : 'ASC',
147 'currentPaged' => $currentPaged,
148 'currentScoreRange' => is_string($rawScoreRange) ? $rawScoreRange : 'all',
149 );
150 }
151
152 private function buildSubtitleHtml(bool $isSimpleMode): string {
153 if (!$isSimpleMode) {
154 return '';
155 }
156 return $this->f->str_replace(
157 '{text}',
158 esc_html__('Broken links visitors tried to reach. Create Redirect for important ones, Dismiss the rest.', '404-solution'),
159 $this->tpl('viewRedirectsTableSubtitle.html')
160 ) . "\n";
161 }
162
163 /** @param bool|float|int|string $currentFilter */
164 private function buildEmptyTrashButtonHtml($currentFilter): string {
165 if ($currentFilter != ABJ404_TRASH_FILTER) {
166 return '';
167 }
168 $eturl = wp_nonce_url("?page=" . ABJ404_PP . "&subpage=abj404_captured&filter=" . ABJ404_TRASH_FILTER, 'abj404_bulkProcess');
169 return $this->fillTpl('viewRedirectsTableEmptyTrashButton.html', array(
170 '{href}' => esc_url($eturl . '&abj404action=emptyCapturedTrash'),
171 '{confirm_js}' => esc_js(__('Are you sure you want to permanently delete all items in trash?', '404-solution')),
172 '{label}' => esc_html__('Empty Trash', '404-solution'),
173 )) . "\n";
174 }
175
176 /** @param bool|float|int|string $currentFilter */
177 private function buildBulkButtonsHtml(bool $isSimpleMode, $currentFilter): string {
178 if ($isSimpleMode) {
179 return $this->listTableChrome->buildBulkButton('bulkignore', __('Dismiss', '404-solution'))
180 . $this->listTableChrome->buildBulkButton('editRedirect', __('Create Redirect', '404-solution'));
181 }
182
183 $bulkButtons = '';
184 if ($currentFilter != ABJ404_STATUS_CAPTURED) { $bulkButtons .= $this->listTableChrome->buildBulkButton('bulkcaptured', __('Mark Captured', '404-solution')); }
185 if ($currentFilter != ABJ404_STATUS_IGNORED) { $bulkButtons .= $this->listTableChrome->buildBulkButton('bulkignore', __('Mark Ignored', '404-solution')); }
186 if ($currentFilter != ABJ404_STATUS_LATER) { $bulkButtons .= $this->listTableChrome->buildBulkButton('bulklater', __('Organize Later', '404-solution')); }
187 if ($currentFilter != ABJ404_TRASH_FILTER) { $bulkButtons .= $this->listTableChrome->buildBulkButton('bulktrash', __('Move to Trash', '404-solution')); }
188 $bulkButtons .= $this->listTableChrome->buildBulkButton('editRedirect', __('Create Redirect', '404-solution'));
189 return $bulkButtons;
190 }
191 }
192