PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.10
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.10
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / includes / class-metasync-rest-seo-inventory.php

class-metasync-rest-seo-inventory.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.10, at includes/class-metasync-rest-seo-inventory.php

145 lines 5.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * REST Endpoint: SEO Inventory
4 *
5 * Registers GET /wp-json/metasync/v1/seo-inventory
6 * Returns all published posts/pages with their SEO metadata in bulk.
7 *
8 * @package MetaSync
9 * @subpackage Includes
10 */
11
12 if (!defined('ABSPATH')) {
13 exit;
14 }
15
16 class Metasync_REST_SEO_Inventory {
17
18 const REST_NAMESPACE = 'metasync/v1';
19 const REST_ROUTE = '/seo-inventory';
20
21 /**
22 * Register the REST route on rest_api_init.
23 */
24 public function __construct() {
25 add_action('rest_api_init', [$this, 'register_routes']);
26 }
27
28 /**
29 * Register routes.
30 */
31 public function register_routes(): void {
32 register_rest_route(self::REST_NAMESPACE, self::REST_ROUTE, [
33 'methods' => 'GET',
34 'callback' => [$this, 'handle_request'],
35 'permission_callback' => [$this, 'check_permissions'],
36 'args' => $this->get_endpoint_args(),
37 ]);
38 }
39
40 /**
41 * Permission check — delegates to the MCP server's check_permissions().
42 *
43 * This reuses the exact same auth logic (API key, JWT, nonce) and ensures
44 * constants like METASYNC_MCP_API_KEY_AUTH are defined correctly.
45 *
46 * @param WP_REST_Request $request
47 * @return bool|WP_Error
48 */
49 public function check_permissions(WP_REST_Request $request) {
50 global $metasync_mcp_server;
51
52 if ($metasync_mcp_server && method_exists($metasync_mcp_server, 'check_permissions')) {
53 return $metasync_mcp_server->check_permissions($request);
54 }
55
56 return new WP_Error(
57 'rest_forbidden',
58 'MCP server not available for authentication.',
59 ['status' => 500]
60 );
61 }
62
63 /**
64 * Handle the inventory request.
65 *
66 * @param WP_REST_Request $request
67 * @return WP_REST_Response
68 */
69 public function handle_request(WP_REST_Request $request): WP_REST_Response {
70 $post_status = $request->get_param('post_status') ?? 'publish';
71
72 // Sensitive statuses require edit_posts capability (matches MCP tool guard)
73 $sensitive_statuses = ['private', 'draft', 'pending', 'any'];
74 if (in_array($post_status, $sensitive_statuses, true) && !current_user_can('edit_posts')) {
75 return new WP_REST_Response([
76 'code' => 'rest_forbidden',
77 'message' => 'You need edit_posts capability to query non-public post statuses.',
78 ], 403);
79 }
80
81 $args = [
82 'post_type' => $request->get_param('post_type') ?? 'any',
83 'post_status' => $post_status,
84 'limit' => $request->get_param('limit') ?? Metasync_SEO_Inventory_Builder::DEFAULT_LIMIT,
85 'cursor' => $request->get_param('cursor') ?? 0,
86 'include_issues' => $request->get_param('include_issues') ?? true,
87 'modified_after' => $request->get_param('modified_after') ?? '',
88 ];
89
90 $result = Metasync_SEO_Inventory_Builder::build($args);
91
92 return new WP_REST_Response($result, 200);
93 }
94
95 /**
96 * Define query parameter schema for the endpoint.
97 *
98 * @return array
99 */
100 private function get_endpoint_args(): array {
101 return [
102 'post_type' => [
103 'type' => 'string',
104 'enum' => ['post', 'page', 'any'],
105 'default' => 'any',
106 'sanitize_callback' => 'sanitize_text_field',
107 'description' => 'Filter by post type.',
108 ],
109 'post_status' => [
110 'type' => 'string',
111 'enum' => ['publish', 'draft', 'pending', 'private', 'any'],
112 'default' => 'publish',
113 'sanitize_callback' => 'sanitize_text_field',
114 'description' => 'Filter by post status.',
115 ],
116 'limit' => [
117 'type' => 'integer',
118 'default' => Metasync_SEO_Inventory_Builder::DEFAULT_LIMIT,
119 'minimum' => 1,
120 'maximum' => Metasync_SEO_Inventory_Builder::MAX_LIMIT,
121 'sanitize_callback' => 'absint',
122 'description' => 'Number of items per page (max 500).',
123 ],
124 'cursor' => [
125 'type' => 'integer',
126 'default' => 0,
127 'minimum' => 0,
128 'sanitize_callback' => 'absint',
129 'description' => 'Post ID to start after (keyset pagination).',
130 ],
131 'include_issues' => [
132 'type' => 'boolean',
133 'default' => true,
134 'description' => 'Include pre-computed SEO issue flags.',
135 ],
136 'modified_after' => [
137 'type' => 'string',
138 'default' => '',
139 'sanitize_callback' => 'sanitize_text_field',
140 'description' => 'Only return posts modified after this ISO 8601 date.',
141 ],
142 ];
143 }
144 }
145