| 1 |
<?php |
| 2 |
|
| 3 |
if (!defined('ABSPATH')) { |
| 4 |
exit; |
| 5 |
} |
| 6 |
|
| 7 |
/** |
| 8 |
* Presents option values for admin settings views. |
| 9 |
* |
| 10 |
* Owns view-only defaults, checked-state helpers, safe string extraction, and |
| 11 |
* the behavior-tile markup for the default 404 destination setting. |
| 12 |
*/ |
| 13 |
class ABJ_404_Solution_View_OptionsPresenter extends ABJ_404_Solution_ViewComponent { |
| 14 |
|
| 15 |
/** Get the 'checked' attribute for a checkbox based on option value. |
| 16 |
* @param array<string, mixed> $options The options array |
| 17 |
* @param string $key The option key to check |
| 18 |
* @return string Returns ' checked' if option is '1', empty string otherwise |
| 19 |
*/ |
| 20 |
public function getCheckedAttr($options, $key) { |
| 21 |
return (array_key_exists($key, $options) && $options[$key] == '1') ? " checked" : ""; |
| 22 |
} |
| 23 |
|
| 24 |
/** @return array<string, string> */ |
| 25 |
public function getFallbackOptionDefaults() { |
| 26 |
return array( |
| 27 |
'default_redirect' => '301', |
| 28 |
'DB_VERSION' => defined('ABJ404_VERSION') ? ABJ404_VERSION : '', |
| 29 |
'menuLocation' => 'optionsLevel', |
| 30 |
'admin_theme' => 'default', |
| 31 |
'capture_deletion' => '0', |
| 32 |
'admin_notification' => '0', |
| 33 |
'maximum_log_disk_usage' => '0', |
| 34 |
'admin_notification_email' => '', |
| 35 |
'suggest_cats' => '0', |
| 36 |
'suggest_tags' => '0', |
| 37 |
'update_suggest_url' => '0', |
| 38 |
'suggest_max' => '5', |
| 39 |
'suggest_title' => '', |
| 40 |
'suggest_before' => '', |
| 41 |
'suggest_after' => '', |
| 42 |
'suggest_entrybefore' => '', |
| 43 |
'suggest_entryafter' => '', |
| 44 |
'suggest_noresults' => '', |
| 45 |
'ignore_doprocess' => '', |
| 46 |
'ignore_dontprocess' => '', |
| 47 |
'recognized_post_types' => 'page', |
| 48 |
'recognized_categories' => '', |
| 49 |
'folders_files_ignore' => '', |
| 50 |
'suggest_regex_exclusions' => '', |
| 51 |
'plugin_admin_users' => '', |
| 52 |
'auto_score' => '0', |
| 53 |
'template_redirect_priority' => '9', |
| 54 |
'days_wait_before_major_update' => '0', |
| 55 |
'excludePages[]' => '', |
| 56 |
// These are used by other option cards; harmless defaults. |
| 57 |
'auto_deletion' => '0', |
| 58 |
'manual_deletion' => '0', |
| 59 |
); |
| 60 |
} |
| 61 |
|
| 62 |
/** |
| 63 |
* @param array<string, mixed> $options |
| 64 |
* @return array<string, mixed> |
| 65 |
*/ |
| 66 |
public function normalizeOptionsForView($options) { |
| 67 |
return array_merge($this->getFallbackOptionDefaults(), $options); |
| 68 |
} |
| 69 |
|
| 70 |
/** |
| 71 |
* Get plugin options merged with defaults. |
| 72 |
* |
| 73 |
* Some tests use partial/mocked PluginLogic instances; this method must not |
| 74 |
* assume getDefaultOptions() is available or safe to call. |
| 75 |
* |
| 76 |
* @return array<string, mixed> |
| 77 |
*/ |
| 78 |
public function getOptionsWithDefaults() { |
| 79 |
$options = abj_service('options_repository')->getOptions(); |
| 80 |
if (!is_array($options)) { |
| 81 |
$options = array(); |
| 82 |
} |
| 83 |
|
| 84 |
$defaults = array(); |
| 85 |
if (is_object($this->logic) && method_exists($this->logic, 'getDefaultOptions')) { |
| 86 |
try { |
| 87 |
$defaults = ABJ_404_Solution_PluginLogicDefaults::defaults(); |
| 88 |
} catch (Throwable $e) { // allow-silent-catch: getDefaultOptions() is best-effort; empty array merges with getFallbackOptionDefaults() below |
| 89 |
$defaults = array(); |
| 90 |
} |
| 91 |
} |
| 92 |
|
| 93 |
$defaults = is_array($defaults) && !empty($defaults) |
| 94 |
? array_merge($this->getFallbackOptionDefaults(), $defaults) |
| 95 |
: $this->getFallbackOptionDefaults(); |
| 96 |
|
| 97 |
return array_merge($defaults, $options); |
| 98 |
} |
| 99 |
|
| 100 |
/** |
| 101 |
* Build the behavior tiles HTML for the 404 destination setting. |
| 102 |
* Used by both simple and advanced mode. |
| 103 |
* |
| 104 |
* @param array<string, mixed> $options |
| 105 |
* @return string |
| 106 |
*/ |
| 107 |
public function getBehaviorTilesHTML($options) { |
| 108 |
$behavior = isset($options['dest404_behavior']) && is_string($options['dest404_behavior']) |
| 109 |
? $options['dest404_behavior'] : 'theme_default'; |
| 110 |
|
| 111 |
$userSelectedDefault404PageRaw = (array_key_exists('dest404page', $options) && |
| 112 |
isset($options['dest404page']) ? $options['dest404page'] : null); |
| 113 |
$userSelectedDefault404Page = is_string($userSelectedDefault404PageRaw) ? $userSelectedDefault404PageRaw : ''; |
| 114 |
$urlDestinationRaw = (array_key_exists('dest404pageURL', $options) && |
| 115 |
isset($options['dest404pageURL']) ? $options['dest404pageURL'] : null); |
| 116 |
$urlDestination = is_string($urlDestinationRaw) ? $urlDestinationRaw : ''; |
| 117 |
|
| 118 |
$pageTitle = $this->logic->pageOrdering()->getPageTitleFromIDAndType($userSelectedDefault404Page, $urlDestination); |
| 119 |
$pageMissingWarning = ""; |
| 120 |
if ($behavior === 'custom' && $userSelectedDefault404Page !== '') { |
| 121 |
$permalink = ABJ_404_Solution_PermalinkResolver::permalinkInfoToArray($userSelectedDefault404Page, 0); |
| 122 |
if (!in_array($permalink['status'], array('publish', 'published'))) { |
| 123 |
$pageMissingWarning = __("(The specified page doesn't exist. Please update this setting.)", '404-solution'); |
| 124 |
} |
| 125 |
} |
| 126 |
|
| 127 |
$customDropdown = ABJ_404_Solution_FileSystemService::readFileContents(dirname(__DIR__) . |
| 128 |
"/html/addManualRedirectPageSearchDropdown.html"); |
| 129 |
$customDropdown = $this->f->str_replace('{redirect_to_label}', '', $customDropdown); |
| 130 |
$customDropdown = $this->f->str_replace('{TOOLTIP_POPUP_EXPLANATION_EMPTY}', |
| 131 |
__('(Type a page name or an external URL)', '404-solution'), $customDropdown); |
| 132 |
$customDropdown = $this->f->str_replace('{TOOLTIP_POPUP_EXPLANATION_PAGE}', |
| 133 |
__('(A page has been selected.)', '404-solution'), $customDropdown); |
| 134 |
$customDropdown = $this->f->str_replace('{TOOLTIP_POPUP_EXPLANATION_CUSTOM_STRING}', |
| 135 |
__('(A custom string has been entered.)', '404-solution'), $customDropdown); |
| 136 |
$customDropdown = $this->f->str_replace('{TOOLTIP_POPUP_EXPLANATION_URL}', |
| 137 |
__('(An external URL will be used.)', '404-solution'), $customDropdown); |
| 138 |
$customDropdown = $this->f->str_replace('{REDIRECT_TO_USER_FIELD_WARNING}', $pageMissingWarning, $customDropdown); |
| 139 |
$customDropdown = $this->f->str_replace('{redirectPageTitle}', esc_attr($pageTitle), $customDropdown); |
| 140 |
$customDropdown = $this->f->str_replace('{pageIDAndType}', esc_attr($userSelectedDefault404Page), $customDropdown); |
| 141 |
$customDropdown = $this->f->str_replace('{data-url}', |
| 142 |
"admin-ajax.php?action=echoRedirectToPages&includeDefault404Page=true&includeSpecial=true&nonce=" . wp_create_nonce('abj404_ajax'), $customDropdown); |
| 143 |
$customDropdown = $this->f->doNormalReplacements($customDropdown); |
| 144 |
|
| 145 |
$html = ABJ_404_Solution_FileSystemService::readFileContents(dirname(__DIR__) . "/html/behaviorTiles.html"); |
| 146 |
|
| 147 |
$behaviors = array('suggest', 'homepage', 'custom', 'theme_default'); |
| 148 |
foreach ($behaviors as $b) { |
| 149 |
$isSelected = ($behavior === $b); |
| 150 |
$html = $this->f->str_replace('{tile_' . $b . '_selected}', $isSelected ? ' selected' : '', $html); |
| 151 |
$html = $this->f->str_replace('{' . $b . '_aria_checked}', $isSelected ? 'true' : 'false', $html); |
| 152 |
} |
| 153 |
|
| 154 |
$html = $this->f->str_replace('{selected_behavior}', esc_attr($behavior), $html); |
| 155 |
$html = $this->f->str_replace('{pageIDAndType}', esc_attr($userSelectedDefault404Page), $html); |
| 156 |
$html = $this->f->str_replace('{custom_picker_display}', $behavior === 'custom' ? '' : 'none', $html); |
| 157 |
$html = $this->f->str_replace('{customPageDropdown}', $customDropdown, $html); |
| 158 |
|
| 159 |
$html = $this->f->str_replace('{Recommended}', __('Recommended', '404-solution'), $html); |
| 160 |
$html = $this->f->str_replace('{Suggest similar pages}', __('Suggest similar pages', '404-solution'), $html); |
| 161 |
$html = $this->f->str_replace('{Shows visitors a list of pages matching the URL they were looking for}', |
| 162 |
__('Shows visitors a list of pages matching the URL they were looking for', '404-solution'), $html); |
| 163 |
$html = $this->f->str_replace('{Redirect to homepage}', __('Redirect to homepage', '404-solution'), $html); |
| 164 |
$html = $this->f->str_replace('{Sends all 404 visitors to the site front page}', |
| 165 |
__('Sends all 404 visitors to the site front page', '404-solution'), $html); |
| 166 |
$html = $this->f->str_replace('{Custom page}', __('Custom page', '404-solution'), $html); |
| 167 |
$html = $this->f->str_replace('{Choose a specific page to show for all 404 errors}', |
| 168 |
__('Choose a specific page to show for all 404 errors', '404-solution'), $html); |
| 169 |
$html = $this->f->str_replace('{Theme default}', __('Theme default', '404-solution'), $html); |
| 170 |
$html = $this->f->str_replace('{Uses the theme built-in 404 page, no redirect}', |
| 171 |
__('Uses the theme built-in 404 page, no redirect', '404-solution'), $html); |
| 172 |
$html = $this->f->str_replace('{Select a page}', __('Select a page', '404-solution'), $html); |
| 173 |
|
| 174 |
return $html; |
| 175 |
} |
| 176 |
|
| 177 |
/** |
| 178 |
* Safely extract a string value from an options array. |
| 179 |
* |
| 180 |
* @param array<string, mixed> $options |
| 181 |
* @param string $key |
| 182 |
* @param string $default |
| 183 |
* @return string |
| 184 |
*/ |
| 185 |
public function optStr($options, $key, $default = '') { |
| 186 |
if (!array_key_exists($key, $options)) { |
| 187 |
return $default; |
| 188 |
} |
| 189 |
$val = $options[$key]; |
| 190 |
if (is_string($val)) { |
| 191 |
return $val; |
| 192 |
} |
| 193 |
if (is_scalar($val)) { |
| 194 |
return (string)$val; |
| 195 |
} |
| 196 |
return $default; |
| 197 |
} |
| 198 |
} |
| 199 |
|