PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.26.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.26.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 1.10.0 All 48 releases
thinkrank / includes / api / class-social-platforms-endpoint.php

class-social-platforms-endpoint.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.26.0, at includes/api/class-social-platforms-endpoint.php

362 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Social Platforms API Endpoints Class
4 *
5 * Provides REST API endpoints for social platform verification codes and IDs
6 * with selective encryption for sensitive verification codes.
7 *
8 * @package ThinkRank\API
9 * @since 1.0.0
10 */
11
12 declare(strict_types=1);
13
14 namespace ThinkRank\API;
15
16 use WP_REST_Controller;
17 use WP_REST_Request;
18 use WP_REST_Response;
19 use WP_Error;
20 use ThinkRank\Core\Settings;
21
22 // Prevent direct access
23 if (!defined('ABSPATH')) {
24 exit;
25 }
26
27 /**
28 * Social Platforms API Endpoints Class
29 *
30 * Handles social platform verification codes and IDs with selective encryption.
31 * Encrypts sensitive verification codes while keeping public IDs visible.
32 *
33 * @since 1.0.0
34 */
35 class Social_Platforms_Endpoint extends WP_REST_Controller {
36
37 /**
38 * API namespace
39 *
40 * @since 1.0.0
41 * @var string
42 */
43 protected $namespace = 'thinkrank/v1';
44
45 /**
46 * REST base
47 *
48 * @since 1.0.0
49 * @var string
50 */
51 protected $rest_base = 'social-platforms';
52
53 /**
54 * Settings instance
55 *
56 * @since 1.0.0
57 * @var Settings
58 */
59 private Settings $settings;
60
61 /**
62 * Sensitive keys that should be encrypted
63 *
64 * @since 1.0.0
65 * @var array
66 */
67 private array $sensitive_keys = [
68 'pinterest_site_verification',
69 'instagram_verification',
70 'tiktok_verification'
71 ];
72
73 /**
74 * Public keys that remain visible
75 *
76 * @since 1.0.0
77 * @var array
78 */
79 private array $public_keys = [
80 'facebook_app_id',
81 'facebook_admins',
82 'youtube_channel_id',
83 'whatsapp_business_id'
84 ];
85
86 /**
87 * Constructor
88 *
89 * @since 1.0.0
90 */
91 public function __construct() {
92 $this->settings = Settings::instance();
93 }
94
95 /**
96 * Register API routes
97 *
98 * @since 1.0.0
99 */
100 public function register_routes(): void {
101 // Social platform settings management
102 register_rest_route(
103 $this->namespace,
104 '/' . $this->rest_base . '/settings',
105 [
106 [
107 'methods' => 'GET',
108 'callback' => [$this, 'get_settings'],
109 'permission_callback' => [$this, 'check_read_permissions']
110 ],
111 [
112 'methods' => 'POST',
113 'callback' => [$this, 'update_settings'],
114 'permission_callback' => [$this, 'check_manage_permissions'],
115 'args' => $this->get_settings_args()
116 ]
117 ]
118 );
119 }
120
121 /**
122 * Get social platform settings
123 *
124 * @since 1.0.0
125 *
126 * @param WP_REST_Request $request Request object
127 * @return WP_REST_Response Response object
128 */
129 public function get_settings(WP_REST_Request $request): WP_REST_Response {
130 try {
131 $settings = $this->get_social_platform_settings();
132
133 return new WP_REST_Response([
134 'success' => true,
135 'data' => [
136 'settings' => $settings
137 ],
138 'message' => 'Social platform settings retrieved successfully'
139 ], 200);
140
141 } catch (\Exception $e) {
142 return new WP_REST_Response([
143 'success' => false,
144 'message' => 'Failed to retrieve social platform settings: ' . $e->getMessage()
145 ], 500);
146 }
147 }
148
149 /**
150 * Update social platform settings
151 *
152 * @since 1.0.0
153 *
154 * @param WP_REST_Request $request Request object
155 * @return WP_REST_Response Response object
156 */
157 public function update_settings(WP_REST_Request $request): WP_REST_Response {
158 try {
159 $settings = $request->get_param('settings');
160
161 if (empty($settings) || !is_array($settings)) {
162 return new WP_REST_Response([
163 'success' => false,
164 'message' => 'Invalid settings data provided'
165 ], 400);
166 }
167
168 // Sanitize input, then enforce the shared format rules before saving.
169 // Sanitization only strips unsafe characters; without this, malformed
170 // verification codes / IDs would be stored and later emitted verbatim.
171 $sanitized_settings = $this->sanitize_settings($settings);
172
173 $validation_errors = $this->validate_settings_format($sanitized_settings);
174 if (!empty($validation_errors)) {
175 return new WP_REST_Response([
176 'success' => false,
177 'message' => 'One or more social platform values are in an invalid format',
178 'errors' => $validation_errors
179 ], 400);
180 }
181
182 $success = $this->save_social_platform_settings($sanitized_settings);
183
184 if ($success) {
185 return new WP_REST_Response([
186 'success' => true,
187 'data' => [
188 'settings' => $this->get_social_platform_settings()
189 ],
190 'message' => 'Social platform settings saved successfully'
191 ], 200);
192 } else {
193 return new WP_REST_Response([
194 'success' => false,
195 'message' => 'Failed to save social platform settings'
196 ], 500);
197 }
198
199 } catch (\Exception $e) {
200 return new WP_REST_Response([
201 'success' => false,
202 'message' => 'Failed to update social platform settings: ' . $e->getMessage()
203 ], 500);
204 }
205 }
206
207 /**
208 * Get social platform settings from Settings class
209 *
210 * @since 1.0.0
211 * @return array Settings array
212 */
213 private function get_social_platform_settings(): array {
214 $settings = [];
215
216 // Get public IDs (visible)
217 foreach ($this->public_keys as $key) {
218 $settings[$key] = $this->settings->get($key, '');
219 }
220
221 // Get sensitive verification codes (encrypted, masked for display)
222 foreach ($this->sensitive_keys as $key) {
223 $value = $this->settings->get($key, '');
224 $settings[$key] = $this->mask_verification_code($value);
225 }
226
227 return $settings;
228 }
229
230 /**
231 * Save social platform settings using Settings class
232 *
233 * @since 1.0.0
234 * @param array $settings Settings to save
235 * @return bool Success status
236 */
237 private function save_social_platform_settings(array $settings): bool {
238 $success = true;
239
240 // Save each setting individually using the Settings class
241 // This ensures proper encryption for sensitive verification codes
242 foreach ($settings as $key => $value) {
243 // Only save if the key is in our allowed lists and has a value
244 if (in_array($key, array_merge($this->public_keys, $this->sensitive_keys)) && !empty($value)) {
245 if (!$this->settings->set($key, $value)) {
246 $success = false;
247 }
248 }
249 }
250
251 return $success;
252 }
253
254 /**
255 * Sanitize settings data
256 *
257 * @since 1.0.0
258 * @param array $settings Raw settings
259 * @return array Sanitized settings
260 */
261 private function sanitize_settings(array $settings): array {
262 $sanitized = [];
263
264 // Sanitize public IDs (only if not empty)
265 foreach ($this->public_keys as $key) {
266 if (!empty($settings[$key])) {
267 $sanitized[$key] = sanitize_text_field($settings[$key]);
268 }
269 }
270
271 // Sanitize sensitive verification codes (only if not empty)
272 foreach ($this->sensitive_keys as $key) {
273 if (!empty($settings[$key])) {
274 $sanitized[$key] = sanitize_text_field($settings[$key]);
275 }
276 }
277
278 return $sanitized;
279 }
280
281 /**
282 * Validate sanitized settings against the shared social platform format rules.
283 *
284 * Reuses Social_Meta_Manager's per-field rules (single source of truth) so the
285 * REST save path rejects malformed verification codes / IDs instead of storing
286 * them and letting them render as broken verification meta tags.
287 *
288 * @since 1.14.0
289 *
290 * @param array $settings Sanitized settings.
291 * @return array<string, string> Map of field key => error message; empty when all valid.
292 */
293 private function validate_settings_format(array $settings): array {
294 $errors = [];
295
296 foreach ($settings as $key => $value) {
297 $error = \ThinkRank\SEO\Social_Meta_Manager::validate_platform_field($key, $value);
298 if ($error !== null) {
299 $errors[$key] = $error;
300 }
301 }
302
303 return $errors;
304 }
305
306 /**
307 * Mask verification code for security display (XXX pattern)
308 *
309 * @since 1.0.0
310 * @param string $code Verification code to mask
311 * @return string Masked code or empty string
312 */
313 private function mask_verification_code(string $code): string {
314 if (empty($code)) {
315 return '';
316 }
317
318 // Show first 4 characters + XXXX suffix (like placeholders)
319 if (strlen($code) > 8) {
320 return substr($code, 0, 4) . 'XXXX';
321 }
322
323 return 'XXXX';
324 }
325
326 /**
327 * Get settings arguments for REST API
328 *
329 * @since 1.0.0
330 * @return array Settings arguments
331 */
332 private function get_settings_args(): array {
333 return [
334 'settings' => [
335 'required' => true,
336 'type' => 'object',
337 'description' => 'Social platform settings object'
338 ]
339 ];
340 }
341
342 /**
343 * Check read permissions
344 *
345 * @since 1.0.0
346 * @return bool Permission status
347 */
348 public function check_read_permissions(): bool {
349 return \ThinkRank\Core\Capability_Manager::current_user_can('thinkrank_settings');
350 }
351
352 /**
353 * Check manage permissions
354 *
355 * @since 1.0.0
356 * @return bool Permission status
357 */
358 public function check_manage_permissions(): bool {
359 return \ThinkRank\Core\Capability_Manager::current_user_can('thinkrank_settings');
360 }
361 }
362