| 1 |
<?php |
| 2 |
|
| 3 |
if (!defined('ABSPATH')) { |
| 4 |
exit; |
| 5 |
} |
| 6 |
|
| 7 |
/** |
| 8 |
* Presents the Add Manual Redirect modal on the Page Redirects admin table. |
| 9 |
*/ |
| 10 |
class ABJ_404_Solution_RedirectAddModalPresenter { |
| 11 |
|
| 12 |
/** @var ABJ_404_Solution_Functions */ |
| 13 |
private $functions; |
| 14 |
|
| 15 |
/** @var ABJ_404_Solution_View_OptionsPresenter */ |
| 16 |
private $optionsPresenter; |
| 17 |
|
| 18 |
/** @var ABJ_404_Solution_View_RedirectTypeUI */ |
| 19 |
private $redirectTypeUI; |
| 20 |
|
| 21 |
/** @var ABJ_404_Solution_View_RedirectConditions */ |
| 22 |
private $redirectConditions; |
| 23 |
|
| 24 |
public function __construct(ABJ_404_Solution_Functions $functions, ABJ_404_Solution_View_OptionsPresenter $optionsPresenter, |
| 25 |
ABJ_404_Solution_View_RedirectTypeUI $redirectTypeUI, |
| 26 |
ABJ_404_Solution_View_RedirectConditions $redirectConditions) { |
| 27 |
$this->functions = $functions; |
| 28 |
$this->optionsPresenter = $optionsPresenter; |
| 29 |
$this->redirectTypeUI = $redirectTypeUI; |
| 30 |
$this->redirectConditions = $redirectConditions; |
| 31 |
} |
| 32 |
|
| 33 |
/** |
| 34 |
* @param array<string, mixed> $tableOptions |
| 35 |
*/ |
| 36 |
public function render(array $tableOptions): string { |
| 37 |
$options = $this->optionsPresenter->getOptionsWithDefaults(); |
| 38 |
$link = wp_nonce_url($this->formUrl($tableOptions), 'abj404addRedirect'); |
| 39 |
$urlPlaceholder = parse_url(get_home_url(), PHP_URL_PATH) . '/example'; |
| 40 |
$prefilledUrl = $this->prefilledUrl(); |
| 41 |
|
| 42 |
return $this->fillTpl('viewRedirectsTableAddModal.html', array( |
| 43 |
'{modal_title}' => esc_html__('Add Manual Redirect', '404-solution'), |
| 44 |
'{form_action}' => esc_url($link), |
| 45 |
'{modal_active_class}' => $prefilledUrl === '' ? '' : ' active', |
| 46 |
'{url_value}' => esc_attr($prefilledUrl), |
| 47 |
'{url_label}' => esc_html__('URL', '404-solution'), |
| 48 |
'{url_placeholder}' => esc_attr($urlPlaceholder), |
| 49 |
'{url_help}' => esc_html__('The URL path that should be redirected (without domain)', '404-solution'), |
| 50 |
'{regex_label}' => esc_html__('Treat this URL as a regular expression', '404-solution'), |
| 51 |
'{explain_label}' => esc_html__('(Explain)', '404-solution'), |
| 52 |
'{regex_help_1}' => 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'), |
| 53 |
'{example_label}' => esc_html__('Example:', '404-solution'), |
| 54 |
'{regex_help_2}' => esc_html__('/events/(.+) will match any URL that begins with /events/. Use $1 in the destination to insert the captured text. Site-relative paths such as /archive/$1 and full HTTP(S) URLs are supported.', '404-solution'), |
| 55 |
'{regex_help_3}' => esc_html__('First, all of the normal "exact match" URLs are checked, then all of the regular expression URLs are checked.', '404-solution'), |
| 56 |
'{redirect_to_html}' => $this->redirectToHtml(), |
| 57 |
'{redirect_type_grid}' => $this->redirectTypeGrid($options), |
| 58 |
'{advanced_options}' => $this->advancedOptionsHtml(), |
| 59 |
'{cancel_label}' => esc_html__('Cancel', '404-solution'), |
| 60 |
'{add_redirect_label}' => esc_html__('Add', '404-solution'), |
| 61 |
)); |
| 62 |
} |
| 63 |
|
| 64 |
/** |
| 65 |
* The URL an inbound "redirect this URL" link asked us to pre-fill. |
| 66 |
* |
| 67 |
* The admin-only note on the front-end suggestions page links here with |
| 68 |
* the path that 404'd, so the admin lands on the form with the URL |
| 69 |
* already in it rather than having to retype what they were just looking |
| 70 |
* at. Only a site-relative path is accepted; anything else (an absolute |
| 71 |
* URL, a scheme, a stray fragment) is reduced to its path component or |
| 72 |
* dropped, so the query arg cannot seed the form with an off-site value. |
| 73 |
* |
| 74 |
* @return string Empty when the request carries no usable path, which |
| 75 |
* also leaves the modal closed. |
| 76 |
*/ |
| 77 |
private function prefilledUrl(): string { |
| 78 |
$raw = ABJ_404_Solution_RequestInputNormalizer::readText( |
| 79 |
$_GET, array('name' => 'abj404_add_url')); |
| 80 |
if ($raw === '') { |
| 81 |
return ''; |
| 82 |
} |
| 83 |
$path = parse_url($raw, PHP_URL_PATH); |
| 84 |
if (!is_string($path) || $path === '') { |
| 85 |
return ''; |
| 86 |
} |
| 87 |
return $path[0] === '/' ? $path : '/' . $path; |
| 88 |
} |
| 89 |
|
| 90 |
/** |
| 91 |
* @param array<string, mixed> $tableOptions |
| 92 |
*/ |
| 93 |
private function formUrl(array $tableOptions): string { |
| 94 |
$url = '?page=' . ABJ404_PP . '&subpage=abj404_redirects'; |
| 95 |
$orderby = array_key_exists('orderby', $tableOptions) && is_string($tableOptions['orderby']) ? $tableOptions['orderby'] : 'url'; |
| 96 |
$order = array_key_exists('order', $tableOptions) && is_string($tableOptions['order']) ? $tableOptions['order'] : 'ASC'; |
| 97 |
if (!($orderby == 'url' && $order == 'ASC')) { |
| 98 |
$url .= '&orderby=' . sanitize_text_field($orderby) . '&order=' . sanitize_text_field($order); |
| 99 |
} |
| 100 |
$filter = array_key_exists('filter', $tableOptions) && is_scalar($tableOptions['filter']) ? $tableOptions['filter'] : 0; |
| 101 |
if ($filter != 0) { |
| 102 |
$url .= '&filter=' . $filter; |
| 103 |
} |
| 104 |
return $url; |
| 105 |
} |
| 106 |
|
| 107 |
private function redirectToHtml(): string { |
| 108 |
$redirectHtml = $this->tpl('addManualRedirectPageSearchDropdown.html'); |
| 109 |
$redirectHtml = $this->functions->str_replace( |
| 110 |
array( |
| 111 |
'{redirect_to_label}', |
| 112 |
'{TOOLTIP_POPUP_EXPLANATION_EMPTY}', |
| 113 |
'{TOOLTIP_POPUP_EXPLANATION_PAGE}', |
| 114 |
'{TOOLTIP_POPUP_EXPLANATION_CUSTOM_STRING}', |
| 115 |
'{TOOLTIP_POPUP_EXPLANATION_URL}', |
| 116 |
'{REDIRECT_TO_USER_FIELD_WARNING}', |
| 117 |
'{redirectPageTitle}', |
| 118 |
'{pageIDAndType}', |
| 119 |
'{data-url}', |
| 120 |
), |
| 121 |
array( |
| 122 |
esc_html__('Redirect to', '404-solution') . ' *', |
| 123 |
__('(Type a page name or an external URL)', '404-solution'), |
| 124 |
__('(A page has been selected.)', '404-solution'), |
| 125 |
__('(A custom string has been entered.)', '404-solution'), |
| 126 |
__('(An external URL will be used.)', '404-solution'), |
| 127 |
'', |
| 128 |
'', |
| 129 |
'', |
| 130 |
'admin-ajax.php?action=echoRedirectToPages&includeDefault404Page=true&includeSpecial=true&nonce=' . |
| 131 |
wp_create_nonce('abj404_ajax'), |
| 132 |
), |
| 133 |
$redirectHtml |
| 134 |
); |
| 135 |
return $this->functions->doNormalReplacements($redirectHtml); |
| 136 |
} |
| 137 |
|
| 138 |
/** |
| 139 |
* @param array<string, mixed> $options |
| 140 |
*/ |
| 141 |
private function redirectTypeGrid(array $options): string { |
| 142 |
$rawDefault = $options['default_redirect'] ?? '301'; |
| 143 |
$defaultCode = is_string($rawDefault) ? $rawDefault : '301'; |
| 144 |
ob_start(); |
| 145 |
$this->redirectTypeUI->echoRedirectTypeButtonGrid($defaultCode); |
| 146 |
return (string)ob_get_clean(); |
| 147 |
} |
| 148 |
|
| 149 |
private function advancedOptionsHtml(): string { |
| 150 |
ob_start(); |
| 151 |
$this->redirectConditions->echoRedirectConditionsSection(); |
| 152 |
$conditionsSection = (string)ob_get_clean(); |
| 153 |
|
| 154 |
return $this->fillTpl('viewRedirectsTableAdvancedOptions.html', array( |
| 155 |
'{open_attr}' => '', |
| 156 |
'{advanced_options_label}' => esc_html__('Advanced Options', '404-solution'), |
| 157 |
'{active_from_label}' => esc_html__('Active From (optional)', '404-solution'), |
| 158 |
'{start_date_value}' => '', |
| 159 |
'{active_from_help}' => esc_html__('Leave blank to activate immediately', '404-solution'), |
| 160 |
'{active_until_label}' => esc_html__('Active Until (optional)', '404-solution'), |
| 161 |
'{end_date_value}' => '', |
| 162 |
'{active_until_help}' => esc_html__('Leave blank to never expire', '404-solution'), |
| 163 |
'{conditions_section}' => $conditionsSection, |
| 164 |
)); |
| 165 |
} |
| 166 |
|
| 167 |
/** @param array<string, string> $vars */ |
| 168 |
private function fillTpl(string $name, array $vars): string { |
| 169 |
return (string)$this->functions->str_replace(array_keys($vars), array_values($vars), $this->tpl($name)); |
| 170 |
} |
| 171 |
|
| 172 |
private function tpl(string $name): string { |
| 173 |
$raw = ABJ_404_Solution_FileSystemService::readFileContents(dirname(__DIR__) . '/html/' . $name, false); |
| 174 |
return rtrim((string)$raw, "\n"); |
| 175 |
} |
| 176 |
} |
| 177 |
|