PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / trunk
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO vtrunk
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 1.11.0 All 47 releases
thinkrank / includes / api / class-external-links-endpoint.php

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

192 lines 5.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * External Links API Endpoints Class
5 *
6 * REST API endpoints for the outbound-link rewriting settings (#659).
7 *
8 * @package ThinkRank
9 * @subpackage API
10 * @since 2.5.0
11 */
12
13 declare(strict_types=1);
14
15 namespace ThinkRank\API;
16
17 use ThinkRank\SEO\External_Links_Manager;
18 use ThinkRank\API\Traits\CSRF_Protection;
19 use WP_REST_Controller;
20 use WP_REST_Request;
21 use WP_REST_Response;
22
23 // Prevent direct access
24 if (!defined('ABSPATH')) {
25 exit;
26 }
27
28 /**
29 * External Links API Endpoints Class
30 *
31 * @since 2.5.0
32 */
33 class External_Links_Endpoint extends WP_REST_Controller {
34 use CSRF_Protection;
35
36 /**
37 * External Links Manager instance
38 *
39 * @since 2.5.0
40 * @var External_Links_Manager
41 */
42 private External_Links_Manager $manager;
43
44 /**
45 * API namespace
46 *
47 * @since 2.5.0
48 * @var string
49 */
50 protected $namespace = 'thinkrank/v1';
51
52 /**
53 * API resource base
54 *
55 * @since 2.5.0
56 * @var string
57 */
58 protected $rest_base = 'external-links';
59
60 /**
61 * Constructor
62 *
63 * @since 2.5.0
64 */
65 public function __construct() {
66 if (!class_exists('ThinkRank\\SEO\\External_Links_Manager')) {
67 require_once THINKRANK_PLUGIN_DIR . 'includes/seo/class-external-links-manager.php';
68 }
69
70 $this->manager = new External_Links_Manager();
71 }
72
73 /**
74 * Register API routes
75 *
76 * @since 2.5.0
77 * @return void
78 */
79 public function register_routes(): void {
80 register_rest_route(
81 $this->namespace,
82 '/' . $this->rest_base . '/settings',
83 [
84 [
85 'methods' => 'GET',
86 'callback' => [$this, 'get_settings'],
87 'permission_callback' => [$this, 'check_permissions'],
88 ],
89 [
90 'methods' => 'POST',
91 'callback' => [$this, 'update_settings'],
92 'permission_callback' => [$this, 'check_permissions'],
93 'args' => $this->get_settings_args(),
94 ],
95 ]
96 );
97 }
98
99 /**
100 * Check if user has required permissions
101 *
102 * @since 2.5.0
103 * @return bool
104 */
105 public function check_permissions(): bool {
106 return \ThinkRank\Core\Capability_Manager::current_user_can('thinkrank_external_links');
107 }
108
109 /**
110 * Read the site-wide external link settings
111 *
112 * @since 2.5.0
113 * @param WP_REST_Request $request API request object
114 * @return WP_REST_Response
115 */
116 public function get_settings(WP_REST_Request $request): WP_REST_Response {
117 $settings = $this->manager->get_settings('site');
118
119 // The list is stored as hosts; the UI edits it as lines of text.
120 $settings['external_link_exceptions'] = array_values((array) ($settings['external_link_exceptions'] ?? []));
121
122 return new WP_REST_Response($settings, 200);
123 }
124
125 /**
126 * Persist the site-wide external link settings
127 *
128 * @since 2.5.0
129 * @param WP_REST_Request $request API request object
130 * @return WP_REST_Response
131 */
132 public function update_settings(WP_REST_Request $request): WP_REST_Response {
133 $allowed_keys = array_keys($this->manager->get_settings_schema('site'));
134 $settings = array_intersect_key($request->get_params(), array_flip($allowed_keys));
135
136 // Normalize on the way in, so a domain typed as a full URL, with
137 // "www.", or with a trailing path still matches at render time —
138 // and the stored value is the same shape the matcher compares.
139 if (isset($settings['external_link_exceptions'])) {
140 $settings['external_link_exceptions'] = $this->manager->normalize_exceptions(
141 $settings['external_link_exceptions']
142 );
143 }
144
145 $success = $this->manager->save_settings('site', 0, $settings);
146
147 if ($success) {
148 return new WP_REST_Response([
149 'success' => true,
150 'message' => __('Settings updated successfully', 'thinkrank'),
151 'settings' => $this->manager->get_settings('site'),
152 ], 200);
153 }
154
155 $error = $this->manager->get_last_save_error();
156
157 return new WP_REST_Response([
158 'success' => false,
159 'message' => $error !== '' ? $error : __('Failed to update settings', 'thinkrank'),
160 ], 500);
161 }
162
163 /**
164 * REST args derived from the settings schema
165 *
166 * @since 2.5.0
167 * @return array
168 */
169 private function get_settings_args(): array {
170 return [
171 'nofollow_external' => [
172 'type' => 'boolean',
173 'required' => false,
174 'sanitize_callback' => 'rest_sanitize_boolean',
175 ],
176 'open_external_in_new_tab' => [
177 'type' => 'boolean',
178 'required' => false,
179 'sanitize_callback' => 'rest_sanitize_boolean',
180 ],
181 // No sanitize_callback on the array: core falls back to
182 // rest_parse_request_arg, which sanitizes against the shape below
183 // instead of flattening the list to the string "Array".
184 'external_link_exceptions' => [
185 'type' => 'array',
186 'required' => false,
187 'items' => ['type' => 'string'],
188 ],
189 ];
190 }
191 }
192