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 / policies / SetupWizardAnswerPolicy.php

SetupWizardAnswerPolicy.php in 404 Solution trunk, at includes/policies/SetupWizardAnswerPolicy.php

164 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // allow-no-test-found: covered by tests/SetupWizardTest.php through setup wizard admin entry points.
4
5 if (!defined('ABSPATH')) {
6 exit;
7 }
8
9 /**
10 * Validates setup wizard answers and maps them to setup decisions.
11 */
12 class ABJ_404_Solution_SetupWizardAnswerPolicy {
13
14 /** @var array<string,string> */
15 private const DEFAULT_ANSWERS = array(
16 'q1' => 'redirect',
17 'q2' => 'yes',
18 'q3' => 'yes',
19 );
20
21 /** @var array<string,array<int,string>> */
22 private const ALLOWED_VALUES = array(
23 'q1' => array('redirect', 'default'),
24 'q2' => array('yes', 'no'),
25 'q3' => array('yes', 'no'),
26 );
27
28 /**
29 * Return validated answers from a submitted setup wizard request.
30 *
31 * @param array<string,mixed> $post Raw POST payload.
32 * @return array{q1:string,q2:string,q3:string}
33 */
34 public static function answersFromRequest(array $post): array {
35 return self::answersFromRaw(array(
36 'q1' => self::readText($post, 'abj404_setup_q1', self::DEFAULT_ANSWERS['q1']),
37 'q2' => self::readText($post, 'abj404_setup_q2', self::DEFAULT_ANSWERS['q2']),
38 'q3' => self::readText($post, 'abj404_setup_q3', self::DEFAULT_ANSWERS['q3']),
39 ));
40 }
41
42 /**
43 * Return validated default answers for the rendered wizard form.
44 *
45 * @return array{q1:string,q2:string,q3:string}
46 */
47 public static function defaultAnswers(): array {
48 $answers = self::DEFAULT_ANSWERS;
49
50 if (function_exists('apply_filters')) {
51 $filtered = apply_filters('abj404_setup_wizard_default_answers', $answers);
52 if (is_array($filtered)) {
53 $answers = array_merge($answers, $filtered);
54 }
55 }
56
57 return self::answersFromRaw($answers);
58 }
59
60 /**
61 * Apply validated setup answers to an existing options array.
62 *
63 * @param array<string,mixed> $options Existing plugin options.
64 * @param array{q1:string,q2:string,q3:string} $answers Validated setup answers.
65 * @param string $adminEmail Site admin email address.
66 * @return array<string,mixed> Updated plugin options.
67 */
68 public static function applyToOptions(array $options, array $answers, string $adminEmail): array {
69 $redirectOptions = array(
70 'redirect' => array(
71 'auto_redirects' => '1',
72 'auto_cats' => '1',
73 'auto_tags' => '1',
74 ),
75 'default' => array(
76 'auto_redirects' => '0',
77 'auto_cats' => '0',
78 'auto_tags' => '0',
79 ),
80 );
81
82 foreach ($redirectOptions[$answers['q1']] as $key => $value) {
83 $options[$key] = $value;
84 }
85
86 $options['dest404page'] = '0|' . ABJ404_TYPE_404_DISPLAYED;
87 $options['capture_404'] = self::captures404s($answers) ? '1' : '0';
88
89 if ($answers['q3'] === 'yes') {
90 $options['admin_notification'] = '50';
91 $options['admin_notification_frequency'] = 'weekly';
92 $options['admin_notification_email'] = $adminEmail;
93 }
94
95 return $options;
96 }
97
98 /**
99 * Build the admin redirect path after setup submission.
100 *
101 * @param array{q1:string,q2:string,q3:string} $answers Validated setup answers.
102 * @return string Relative admin path.
103 */
104 public static function redirectPath(array $answers): string {
105 $path = 'options-general.php?page=abj404_solution&setup_complete=1';
106 if (self::captures404s($answers)) {
107 $path .= '&subpage=abj404_captured';
108 }
109
110 return $path;
111 }
112
113 /**
114 * Return whether the validated answers enable captured 404 logging.
115 *
116 * @param array{q1:string,q2:string,q3:string} $answers Validated setup answers.
117 * @return bool
118 */
119 public static function captures404s(array $answers): bool {
120 return $answers['q2'] === 'yes';
121 }
122
123 /**
124 * @param array<string,mixed> $source
125 */
126 private static function readText(array $source, string $key, string $default): string {
127 if (!array_key_exists($key, $source)) {
128 return $default;
129 }
130
131 $value = $source[$key];
132 if (!is_scalar($value)) {
133 return $default;
134 }
135
136 $text = (string)$value;
137 if (function_exists('sanitize_text_field')) {
138 $text = sanitize_text_field($text);
139 }
140
141 return $text;
142 }
143
144 /**
145 * @param array<string,mixed> $raw
146 * @return array{q1:string,q2:string,q3:string}
147 */
148 private static function answersFromRaw(array $raw): array {
149 return array(
150 'q1' => self::validAnswerValue($raw['q1'] ?? '', self::ALLOWED_VALUES['q1'], self::DEFAULT_ANSWERS['q1']),
151 'q2' => self::validAnswerValue($raw['q2'] ?? '', self::ALLOWED_VALUES['q2'], self::DEFAULT_ANSWERS['q2']),
152 'q3' => self::validAnswerValue($raw['q3'] ?? '', self::ALLOWED_VALUES['q3'], self::DEFAULT_ANSWERS['q3']),
153 );
154 }
155
156 /**
157 * @param mixed $value Candidate value.
158 * @param array<int,string> $allowed Allowed values.
159 */
160 private static function validAnswerValue($value, array $allowed, string $default): string {
161 return is_string($value) && in_array($value, $allowed, true) ? $value : $default;
162 }
163 }
164