| 1 |
<?php |
| 2 |
|
| 3 |
if (!defined('ABSPATH')) { |
| 4 |
exit; |
| 5 |
} |
| 6 |
|
| 7 |
/** |
| 8 |
* Renders edit-redirect form templates and form-table rows. |
| 9 |
*/ |
| 10 |
class ABJ_404_Solution_RedirectEditFormPresenter { |
| 11 |
|
| 12 |
/** @var ABJ_404_Solution_Functions */ |
| 13 |
private $functions; |
| 14 |
|
| 15 |
/** @var ABJ_404_Solution_RedirectEngineLabeler */ |
| 16 |
private $engineLabeler; |
| 17 |
|
| 18 |
/** |
| 19 |
* @param ABJ_404_Solution_Functions $functions |
| 20 |
* @param ABJ_404_Solution_RedirectEngineLabeler $engineLabeler |
| 21 |
*/ |
| 22 |
public function __construct($functions, ABJ_404_Solution_RedirectEngineLabeler $engineLabeler) { |
| 23 |
$this->functions = $functions; |
| 24 |
$this->engineLabeler = $engineLabeler; |
| 25 |
} |
| 26 |
|
| 27 |
/** |
| 28 |
* Build the redirect-to autocomplete dropdown HTML from the template. |
| 29 |
* |
| 30 |
* @param string $pageTitle |
| 31 |
* @param string $pageIDAndType |
| 32 |
* @return string |
| 33 |
*/ |
| 34 |
public function buildRedirectToDropdownHtml(string $pageTitle, string $pageIDAndType): string { |
| 35 |
$html = $this->readTemplate('addManualRedirectPageSearchDropdown.html'); |
| 36 |
$html = $this->functions->str_replace('{redirect_to_label}', __('Redirect to', '404-solution'), $html); |
| 37 |
$html = $this->functions->str_replace('{TOOLTIP_POPUP_EXPLANATION_EMPTY}', |
| 38 |
__('(Type a page name or an external URL)', '404-solution'), $html); |
| 39 |
$html = $this->functions->str_replace('{TOOLTIP_POPUP_EXPLANATION_PAGE}', |
| 40 |
__('(A page has been selected.)', '404-solution'), $html); |
| 41 |
$html = $this->functions->str_replace('{TOOLTIP_POPUP_EXPLANATION_CUSTOM_STRING}', |
| 42 |
__('(A custom string has been entered.)', '404-solution'), $html); |
| 43 |
$html = $this->functions->str_replace('{TOOLTIP_POPUP_EXPLANATION_URL}', |
| 44 |
__('(An external URL will be used.)', '404-solution'), $html); |
| 45 |
$html = $this->functions->str_replace('{REDIRECT_TO_USER_FIELD_WARNING}', '', $html); |
| 46 |
$html = $this->functions->str_replace('{redirectPageTitle}', esc_attr($pageTitle), $html); |
| 47 |
$html = $this->functions->str_replace('{pageIDAndType}', esc_attr($pageIDAndType), $html); |
| 48 |
$html = $this->functions->str_replace('{data-url}', |
| 49 |
"admin-ajax.php?action=echoRedirectToPages&includeDefault404Page=true&includeSpecial=true&nonce=" . wp_create_nonce('abj404_ajax'), $html); |
| 50 |
return $this->functions->doNormalReplacements($html); |
| 51 |
} |
| 52 |
|
| 53 |
/** |
| 54 |
* @param array<int, array<string, mixed>> $redirects |
| 55 |
* @return string |
| 56 |
*/ |
| 57 |
public function buildBulkUrlsRowHtml(array $redirects): string { |
| 58 |
$items = ''; |
| 59 |
$itemTemplate = $this->readTemplate('redirectEditBulkUrlItem.html'); |
| 60 |
foreach ($redirects as $bulkRedirect) { |
| 61 |
$bulkUrl = is_string($bulkRedirect['url'] ?? '') ? (string)($bulkRedirect['url'] ?? '') : ''; |
| 62 |
$items .= $this->functions->str_replace('{url}', esc_html($bulkUrl), $itemTemplate); |
| 63 |
} |
| 64 |
|
| 65 |
$rowHtml = $this->readTemplate('editRedirectBulkUrls.html'); |
| 66 |
$rowHtml = $this->functions->str_replace('{bulk_urls_label}', esc_html__('URLs to redirect', '404-solution'), $rowHtml); |
| 67 |
$rowHtml = $this->functions->str_replace('{bulk_count}', (string)count($redirects), $rowHtml); |
| 68 |
return $this->functions->str_replace('{bulk_url_items}', $items, $rowHtml); |
| 69 |
} |
| 70 |
|
| 71 |
/** |
| 72 |
* @param array<int, int> $ids |
| 73 |
* @return string |
| 74 |
*/ |
| 75 |
public function buildIdsMultipleHiddenInput(array $ids): string { |
| 76 |
$hiddenInput = $this->readTemplate('editRedirectIdsMultipleHiddenInput.html'); |
| 77 |
return $this->functions->str_replace('{ids_multiple}', esc_attr(implode(',', $ids)), $hiddenInput); |
| 78 |
} |
| 79 |
|
| 80 |
/** |
| 81 |
* Build the suggestion block HTML for a captured URL's best match. |
| 82 |
* |
| 83 |
* @param array{title: string, score: int, id_and_type: string, type_label: string} $suggestion |
| 84 |
* @return string |
| 85 |
*/ |
| 86 |
public function buildSuggestionBlockHtml(array $suggestion): string { |
| 87 |
$bucket = $suggestion['score'] >= 75 ? 'high' : ($suggestion['score'] >= 50 ? 'medium' : 'low'); |
| 88 |
$typeLabel = ''; |
| 89 |
if (!empty($suggestion['type_label'])) { |
| 90 |
$typeLabel = $this->readTemplate('editRedirectSuggestionTypeLabel.html'); |
| 91 |
$typeLabel = $this->functions->str_replace('{type_label}', esc_html($suggestion['type_label']), $typeLabel); |
| 92 |
} |
| 93 |
|
| 94 |
$html = $this->readTemplate('editRedirectSuggestionBlock.html'); |
| 95 |
$html = $this->functions->str_replace('{suggestion_label}', esc_html__('Suggested destination', '404-solution'), $html); |
| 96 |
$html = $this->functions->str_replace('{suggestion_title}', esc_html($suggestion['title']), $html); |
| 97 |
$html = $this->functions->str_replace('{suggestion_title_attr}', esc_attr($suggestion['title']), $html); |
| 98 |
$html = $this->functions->str_replace('{suggestion_type_label}', $typeLabel, $html); |
| 99 |
$html = $this->functions->str_replace('{suggestion_score_bucket}', $bucket, $html); |
| 100 |
$html = $this->functions->str_replace('{suggestion_score}', esc_html((string)$suggestion['score']), $html); |
| 101 |
$html = $this->functions->str_replace('{suggestion_id_and_type}', esc_attr($suggestion['id_and_type']), $html); |
| 102 |
$html = $this->functions->str_replace('{match_text}', esc_html__('match', '404-solution'), $html); |
| 103 |
$html = $this->functions->str_replace('{accept_label}', esc_html__('Accept Suggestion', '404-solution'), $html); |
| 104 |
return $this->functions->str_replace('{pick_different_label}', esc_html__('Pick a Different Page', '404-solution'), $html); |
| 105 |
} |
| 106 |
|
| 107 |
/** |
| 108 |
* @param string $redirectId |
| 109 |
* @return string |
| 110 |
*/ |
| 111 |
public function buildRedirectIdHiddenInput(string $redirectId): string { |
| 112 |
$hiddenInputs = $this->readTemplate('editRedirectIdHiddenInput.html'); |
| 113 |
return $this->functions->str_replace('{redirect_id}', esc_attr($redirectId), $hiddenInputs); |
| 114 |
} |
| 115 |
|
| 116 |
/** |
| 117 |
* Build the URL form-table row with an optional "Auto-matched by" note. |
| 118 |
* |
| 119 |
* @return string |
| 120 |
*/ |
| 121 |
public function buildUrlRowHtml(string $redirectUrl, string $redirectEngine): string { |
| 122 |
$matchedByNote = ''; |
| 123 |
if ($redirectEngine !== '') { |
| 124 |
$matchedByNote = $this->readTemplate('editRedirectMatchedByNote.html'); |
| 125 |
$matchedByNote = $this->functions->str_replace('{matched_by_label}', esc_html__('Auto-matched by:', '404-solution'), $matchedByNote); |
| 126 |
$matchedByNote = $this->functions->str_replace('{engine_name}', esc_html($this->engineLabeler->humanize($redirectEngine)), $matchedByNote); |
| 127 |
} |
| 128 |
$urlBody = $this->readTemplate('editRedirectUrlRowBody.html'); |
| 129 |
$urlBody = $this->functions->str_replace('{url_value}', esc_attr($redirectUrl), $urlBody); |
| 130 |
$urlBody = $this->functions->str_replace('{matched_by_note}', $matchedByNote, $urlBody); |
| 131 |
return $this->buildFieldRowHtml('url', $this->buildRequiredLabel(__('URL', '404-solution')), $urlBody); |
| 132 |
} |
| 133 |
|
| 134 |
/** |
| 135 |
* Build the "regular expression" form-table row. |
| 136 |
* |
| 137 |
* @param string $isRegexChecked ' checked' or '' |
| 138 |
* @return string |
| 139 |
*/ |
| 140 |
public function buildRegexRowHtml(string $isRegexChecked): string { |
| 141 |
$regexLabel = __('Treat this URL as a regular expression', '404-solution'); |
| 142 |
$body = $this->readTemplate('editRedirectRegexBody.html'); |
| 143 |
$body = $this->functions->str_replace('{regex_label}', esc_html($regexLabel), $body); |
| 144 |
$body = $this->functions->str_replace('{is_regex_checked}', $isRegexChecked, $body); |
| 145 |
$body = $this->functions->str_replace('{regex_explain_link}', esc_html__('(Explain)', '404-solution'), $body); |
| 146 |
$body = $this->functions->str_replace('{regex_explain_text}', esc_html__('When checked, the text is treated as a regular expression. Note that including a bad regular expression or one that takes too long will break your website. So please use caution and test them elsewhere before trying them here. If you don\'t know what you\'re doing please don\'t use this option (as it\'s not necessary for the functioning of the plugin).', '404-solution'), $body); |
| 147 |
$body = $this->functions->str_replace('{regex_example_label}', esc_html__('Example:', '404-solution'), $body); |
| 148 |
$body = $this->functions->str_replace('{regex_example_text}', esc_html__('/events/(.+) will match any URL that begins with /events/ and redirect to the specified page. Since a capture group is used, you can use a $1 replacement in the destination string of an external URL.', '404-solution'), $body); |
| 149 |
return $this->buildFieldRowHtml('is_regex_url', ' ', $body); |
| 150 |
} |
| 151 |
|
| 152 |
/** |
| 153 |
* Build hidden `source_*` inputs that preserve the originating list-table view. |
| 154 |
* |
| 155 |
* @return string |
| 156 |
*/ |
| 157 |
public function buildSourceHiddenInputs(string $sourcePage, string $filter, string $orderby, string $order, string $paged): string { |
| 158 |
$pairs = array( |
| 159 |
'source_page' => $sourcePage, |
| 160 |
'source_filter' => $filter, |
| 161 |
'source_orderby' => $orderby, |
| 162 |
'source_order' => $order, |
| 163 |
'source_paged' => $paged, |
| 164 |
); |
| 165 |
$template = $this->readTemplate('editRedirectSourceHiddenInput.html'); |
| 166 |
$html = ''; |
| 167 |
foreach ($pairs as $name => $value) { |
| 168 |
if ($value === '') { |
| 169 |
continue; |
| 170 |
} |
| 171 |
$line = $this->functions->str_replace('{name}', esc_attr($name), $template); |
| 172 |
$line = $this->functions->str_replace('{value}', esc_attr($value), $line); |
| 173 |
$html .= $line; |
| 174 |
} |
| 175 |
return $html; |
| 176 |
} |
| 177 |
|
| 178 |
/** |
| 179 |
* Build the back-to-list cancel URL with preserved filter/orderby/order params. |
| 180 |
* |
| 181 |
* @return string |
| 182 |
*/ |
| 183 |
public function buildCancelUrl(string $sourcePage, string $filter, string $orderby, string $order): string { |
| 184 |
$url = '?page=' . ABJ404_PP; |
| 185 |
$pairs = array( |
| 186 |
'subpage' => $sourcePage, |
| 187 |
'filter' => $filter, |
| 188 |
'orderby' => $orderby, |
| 189 |
'order' => $order, |
| 190 |
); |
| 191 |
foreach ($pairs as $name => $value) { |
| 192 |
if ($value === '') { |
| 193 |
continue; |
| 194 |
} |
| 195 |
$url .= '&' . $name . '=' . $value; |
| 196 |
} |
| 197 |
return $url; |
| 198 |
} |
| 199 |
|
| 200 |
/** |
| 201 |
* Build the Advanced Options section HTML using the template. |
| 202 |
* |
| 203 |
* @param string $startDate ISO date for "Active From", or empty. |
| 204 |
* @param string $endDate ISO date for "Active Until", or empty. |
| 205 |
* @param string $conditionsHtml Already-rendered conditions section. |
| 206 |
* @param bool $isOpen Whether the details element starts open. |
| 207 |
* @return string |
| 208 |
*/ |
| 209 |
public function buildAdvancedOptionsHtml(string $startDate, string $endDate, string $conditionsHtml, bool $isOpen): string { |
| 210 |
$html = $this->readTemplate('editRedirectAdvancedOptions.html'); |
| 211 |
$html = $this->functions->str_replace('{advanced_options_label}', esc_html__('Advanced Options', '404-solution'), $html); |
| 212 |
$html = $this->functions->str_replace('{open_attr}', $isOpen ? ' open' : '', $html); |
| 213 |
$html = $this->functions->str_replace('{start_date_label}', esc_html__('Active From (optional)', '404-solution'), $html); |
| 214 |
$html = $this->functions->str_replace('{start_date_value}', esc_attr($startDate), $html); |
| 215 |
$html = $this->functions->str_replace('{start_date_help}', esc_html__('Leave blank to activate immediately', '404-solution'), $html); |
| 216 |
$html = $this->functions->str_replace('{end_date_label}', esc_html__('Active Until (optional)', '404-solution'), $html); |
| 217 |
$html = $this->functions->str_replace('{end_date_value}', esc_attr($endDate), $html); |
| 218 |
$html = $this->functions->str_replace('{end_date_help}', esc_html__('Leave blank to never expire', '404-solution'), $html); |
| 219 |
return $this->functions->str_replace('{conditions_section}', $conditionsHtml, $html); |
| 220 |
} |
| 221 |
|
| 222 |
/** |
| 223 |
* @return string |
| 224 |
*/ |
| 225 |
public function buildRequiredLabel(string $baseLabel): string { |
| 226 |
$html = $this->readTemplate('editRedirectRequiredLabelSuffix.html'); |
| 227 |
$html = $this->functions->str_replace('{base_label}', esc_html($baseLabel), $html); |
| 228 |
return $this->functions->str_replace('{required_label}', esc_html__('(Required)', '404-solution'), $html); |
| 229 |
} |
| 230 |
|
| 231 |
/** |
| 232 |
* Build a single form-table row using the field-row template. |
| 233 |
* |
| 234 |
* @param string $fieldId Form-control id used in the label's `for` attribute. |
| 235 |
* @param string $labelHtml Already-escaped label HTML. |
| 236 |
* @param string $bodyHtml Already-built input/markup for the td cell. |
| 237 |
* @return string |
| 238 |
*/ |
| 239 |
public function buildFieldRowHtml(string $fieldId, string $labelHtml, string $bodyHtml): string { |
| 240 |
$row = $this->readTemplate('editRedirectFieldRow.html'); |
| 241 |
$row = $this->functions->str_replace('{field_id}', esc_attr($fieldId), $row); |
| 242 |
$row = $this->functions->str_replace('{field_label}', $labelHtml, $row); |
| 243 |
return $this->functions->str_replace('{field_body}', $bodyHtml, $row); |
| 244 |
} |
| 245 |
|
| 246 |
/** |
| 247 |
* @return string |
| 248 |
*/ |
| 249 |
public function buildManualPickerWrapperHtml(string $hiddenClass, string $innerHtml): string { |
| 250 |
$redirectToBody = $this->readTemplate('editRedirectManualPickerWrapper.html'); |
| 251 |
$redirectToBody = $this->functions->str_replace('{hidden_class}', $hiddenClass, $redirectToBody); |
| 252 |
return $this->functions->str_replace('{inner_html}', $innerHtml, $redirectToBody); |
| 253 |
} |
| 254 |
|
| 255 |
/** |
| 256 |
* @param array<string, string> $parts |
| 257 |
* @return string |
| 258 |
*/ |
| 259 |
public function buildShellHtml(array $parts): string { |
| 260 |
$shell = $this->readTemplate('editRedirectFormShell.html'); |
| 261 |
foreach ($parts as $placeholder => $value) { |
| 262 |
$shell = $this->functions->str_replace($placeholder, $value, $shell); |
| 263 |
} |
| 264 |
return $shell; |
| 265 |
} |
| 266 |
|
| 267 |
/** |
| 268 |
* @param string $name |
| 269 |
* @return string |
| 270 |
*/ |
| 271 |
private function readTemplate(string $name): string { |
| 272 |
return ABJ_404_Solution_FileSystemService::readFileContents(dirname(__DIR__) . '/html/' . $name); |
| 273 |
} |
| 274 |
} |
| 275 |
|