PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.8.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.8.0
2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 All 49 releases
← All changes | includes/api/class-email-report-endpoint.php +32 -80 2.1.12.8.0 View file →
@@ -2,16 +2,13 @@
2 2 /**
3 3 * Email Report REST Endpoint
4 4 *
5 5 * Three routes:
6 - * GET /thinkrank/v1/email-report/config — returns config + capability map + section catalog
7 - * POST /thinkrank/v1/email-report/config — saves config (sanitized + capability-clamped)
6 + * GET /thinkrank/v1/email-report/config — returns the resolved config + section catalog
7 + * POST /thinkrank/v1/email-report/config — switches the report on or off
8 8 * POST /thinkrank/v1/email-report/test-send — triggers an immediate one-off send
9 9 *
10 - * Permissions: admin (`manage_options`) + valid REST nonce. Pro-only
11 - * fields submitted on a free plan are silently dropped by
12 - * Email_Report_Config::sanitize() — we don't 403 on those, since the
13 - * client may not know its current capabilities yet (e.g. mid-downgrade).
10 + * Permissions: admin (`manage_options`) + valid REST nonce.
14 11 *
15 12 * @package ThinkRank
16 13 * @subpackage API
17 14 * @since 1.9.0
@@ -21,9 +18,8 @@
21 18
22 19 namespace ThinkRank\API;
23 20
24 21 use ThinkRank\API\Traits\CSRF_Protection;
25 -use ThinkRank\Core\Plan_Config;
26 22 use ThinkRank\SEO\Email_Report_Manager;
27 23 use WP_REST_Controller;
28 24 use WP_REST_Request;
29 25 use WP_REST_Response;
@@ -61,9 +57,14 @@
61 57 [
62 58 'methods' => 'POST',
63 59 'callback' => [$this, 'save_config'],
64 60 'permission_callback' => [$this, 'check_admin_csrf_permissions'],
65 - 'args' => $this->save_args(),
61 + 'args' => [
62 + 'enabled' => [
63 + 'type' => 'boolean',
64 + 'sanitize_callback' => 'rest_sanitize_boolean',
65 + ],
66 + ],
66 67 ],
67 68 ]
68 69 );
69 70
@@ -82,11 +83,13 @@
82 83
83 84 /**
84 85 * GET /email-report/config
85 86 *
86 - * Returns the current per-site config along with the plan's capability
87 - * map and the section catalog so the React panel can render the right
88 - * fields without a second round-trip.
87 + * Returns the resolved config (on/off, frequency, recipients, sections,
88 + * last skip reason) along with the section catalog, the next scheduled
89 + * run and the readiness of the data sources — whether Search Console is
90 + * connected, so the panel can say a report is paused rather than let it
91 + * look healthy (#742).
89 92 */
90 93 public function get_config(WP_REST_Request $request): WP_REST_Response {
91 94 $manager = $this->resolve_manager();
92 95 if ($manager === null) {
@@ -95,13 +98,12 @@
95 98 ], 500);
96 99 }
97 100
98 101 return new WP_REST_Response([
99 - 'config' => $manager->config()->get(),
100 - 'capabilities' => Plan_Config::email_report(),
101 - 'sections' => $manager->registry()->describe_for_ui(),
102 - 'next_run' => $manager->scheduler()->next_run_iso(),
103 - 'tokens' => $this->supported_tokens(),
102 + 'config' => $manager->config()->get(),
103 + 'sections' => $manager->registry()->describe_for_ui(),
104 + 'next_run' => $manager->scheduler()->next_run_iso(),
105 + 'readiness' => $manager->data_provider()->readiness(),
104 106 ]);
105 107 }
106 108
107 109 /**
@@ -115,20 +117,20 @@
115 117 'message' => __('Email Report Manager unavailable.', 'thinkrank'),
116 118 ], 500);
117 119 }
118 120
119 - $input = $request->get_json_params();
120 - if (!is_array($input)) {
121 - $input = $request->get_params();
121 + $input = [];
122 + if ($request->has_param('enabled')) {
123 + $input['enabled'] = (bool) $request->get_param('enabled');
122 124 }
123 125
124 - $saved = $manager->config()->save(is_array($input) ? $input : []);
126 + $saved = $manager->config()->save($input);
125 127
126 128 return new WP_REST_Response([
127 - 'success' => true,
128 - 'config' => $saved,
129 - 'capabilities' => Plan_Config::email_report(),
130 - 'next_run' => $manager->scheduler()->next_run_iso(),
129 + 'success' => true,
130 + 'config' => $saved,
131 + 'next_run' => $manager->scheduler()->next_run_iso(),
132 + 'readiness' => $manager->data_provider()->readiness(),
131 133 ]);
132 134 }
133 135
134 136 /**
@@ -146,10 +148,13 @@
146 148 $result = $manager->generator()->generate_test();
147 149
148 150 $status = !empty($result['success']) ? 200 : 400;
149 151 return new WP_REST_Response([
150 - 'success' => (bool) ($result['success'] ?? false),
151 - 'result' => $result,
152 + 'success' => (bool) ($result['success'] ?? false),
153 + // True when the test went out as the "connect Search Console"
154 + // email rather than a report, so the panel can say so.
155 + 'not_connected' => !empty($result['not_connected']),
156 + 'result' => $result,
152 157 ], $status);
153 158 }
154 159
155 160 /**
@@ -189,10 +194,9 @@
189 194 }
190 195
191 196 /**
192 197 * Reach into the plugin DI container for the Email_Report_Manager
193 - * instance built at boot. Falls back to creating one on demand if
194 - * the function doesn't exist yet (defensive — shouldn't happen).
198 + * instance built at boot.
195 199 */
196 200 private function resolve_manager(): ?Email_Report_Manager {
197 201 if ($this->manager !== null) {
198 202 return $this->manager;
@@ -204,58 +208,6 @@
204 208 return $this->manager;
205 209 }
206 210 }
207 211 return null;
208 - }
209 -
210 - /**
211 - * REST args: permissive on type so we accept the full config object
212 - * the panel sends back (including nulls for paid fields the user
213 - * isn't allowed to set). Heavy sanitization happens in
214 - * Email_Report_Config::sanitize() so the cron path benefits too.
215 - *
216 - * Don't add `sanitize_callback` here for nullable fields — the
217 - * sanitized value is what reaches the handler, and
218 - * `esc_url_raw(null)` coerces to '', defeating the point of
219 - * preserving "unset". (WP_REST_Server::respond_to_request runs
220 - * has_valid_params() first and sanitize_params() second, so the
221 - * ['string','null'] type above is what admits the null; sanitizing
222 - * afterwards would throw it away.)
223 - */
224 - private function save_args(): array {
225 - $nullable_string = ['type' => ['string', 'null']];
226 - return [
227 - 'enabled' => [
228 - 'type' => 'boolean',
229 - 'sanitize_callback' => 'rest_sanitize_boolean',
230 - ],
231 - 'frequency_days' => [
232 - 'type' => 'integer',
233 - 'sanitize_callback' => 'absint',
234 - ],
235 - 'recipients' => [
236 - 'type' => ['array', 'string', 'null'],
237 - ],
238 - 'subject_template' => $nullable_string,
239 - 'logo_url' => $nullable_string,
240 - 'logo_link' => $nullable_string,
241 - 'header_background' => $nullable_string,
242 - 'link_to_full_report' => [
243 - 'type' => 'boolean',
244 - 'sanitize_callback' => 'rest_sanitize_boolean',
245 - ],
246 - 'intro_text' => $nullable_string,
247 - 'sections_enabled' => [
248 - 'type' => ['array', 'null'],
249 - ],
250 - 'footer_text' => $nullable_string,
251 - 'additional_css' => $nullable_string,
252 - ];
253 - }
254 -
255 - private function supported_tokens(): array {
256 - if (!function_exists('thinkrank_get_email_report_tokens')) {
257 - require_once THINKRANK_PLUGIN_DIR . 'includes/config/email-report-settings-config.php';
258 - }
259 - return thinkrank_get_email_report_tokens();
260 212 }
261 213 }