PluginProbe
SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking / trunk
SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking vtrunk
1.5.0 1.4.0 1.3.0 1.3.1 trunk 0.0.0-alpha.1 0.0.0-alpha.2 0.0.0-alpha.3 0.0.1-beta.1 0.0.1-beta.2 0.0.1-beta.3 0.0.1-beta.4 1.0.0 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4
surecookie / inc / api / posts.php

posts.php in SureCookie – GDPR Cookie Consent Banner, Cookie Scanner & Script Blocking trunk, at inc/api/posts.php

167 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Posts API.
4 *
5 * REST endpoints for searching posts for the Cookie Policy and Site Scanner
6 * page pickers and fetching a single post by ID. Both surfaces default to the
7 * `page` post type; developers extend the searchable list per surface (via the
8 * request `context` arg) using the `surecookie_searchable_post_types` filter -
9 * see Get::searchable_post_types().
10 *
11 * @package SureCookie\Inc\API
12 * @since 0.0.1-beta.2
13 */
14
15 namespace SureCookie\Inc\API;
16
17 use SureCookie\Inc\Functions\SendJson;
18 use SureCookie\Inc\Services\ContentService;
19 use SureCookie\Inc\Traits\GetInstance;
20 use WP_REST_Request;
21 use WP_REST_Server;
22
23 if ( ! defined( 'ABSPATH' ) ) {
24 exit; // Exit if accessed directly.
25 }
26
27 /**
28 * Class Posts
29 *
30 * @since 0.0.1-beta.2
31 */
32 class Posts extends Base {
33 use GetInstance;
34
35 /**
36 * Register API routes.
37 *
38 * @since 0.0.1-beta.2
39 * @return void
40 */
41 public function register_routes(): void {
42 register_rest_route(
43 $this->get_api_namespace(),
44 '/posts/search',
45 [
46 'methods' => WP_REST_Server::READABLE,
47 'callback' => [ $this, 'search_posts' ],
48 'permission_callback' => [ $this, 'validate_permission' ],
49 'args' => [
50 'search' => [
51 'type' => 'string',
52 'default' => '',
53 'sanitize_callback' => 'sanitize_text_field',
54 'validate_callback' => 'rest_validate_request_arg',
55 ],
56 'per_page' => [
57 'type' => 'integer',
58 'default' => 20,
59 'minimum' => 1,
60 'maximum' => 50,
61 'sanitize_callback' => 'absint',
62 'validate_callback' => 'rest_validate_request_arg',
63 'description' => 'Maximum number of results returned per post type.',
64 ],
65 'context' => [
66 'type' => 'string',
67 'default' => 'policy',
68 'enum' => [ 'policy', 'scanner' ],
69 'sanitize_callback' => 'sanitize_key',
70 'validate_callback' => 'rest_validate_request_arg',
71 ],
72 ],
73 ]
74 );
75
76 register_rest_route(
77 $this->get_api_namespace(),
78 '/posts/(?P<id>\d+)',
79 [
80 'methods' => WP_REST_Server::READABLE,
81 'callback' => [ $this, 'get_post_by_id' ],
82 'permission_callback' => [ $this, 'validate_permission' ],
83 'args' => [
84 'id' => [
85 'required' => true,
86 'type' => 'integer',
87 'minimum' => 1,
88 'sanitize_callback' => 'absint',
89 'validate_callback' => 'rest_validate_request_arg',
90 ],
91 'context' => [
92 'type' => 'string',
93 'default' => 'policy',
94 'enum' => [ 'policy', 'scanner' ],
95 'sanitize_callback' => 'sanitize_key',
96 'validate_callback' => 'rest_validate_request_arg',
97 ],
98 ],
99 ]
100 );
101 }
102
103 /**
104 * Search published posts for a page picker, grouped by post type.
105 *
106 * Both the Cookie Policy picker (context = 'policy') and the Site Scanner
107 * picker (context = 'scanner') default to the `page` post type; the allowed
108 * list is extensible per surface via the `surecookie_searchable_post_types`
109 * filter. One capped query runs per allowed post type so every type is
110 * represented in the results, and each row is tagged with its post type so
111 * the client can group the dropdown. Within a type, results are ordered
112 * alphabetically when no search term is given, or by relevance otherwise.
113 *
114 * @param WP_REST_Request $request Full data about the request.
115 * @since 0.0.1-beta.2
116 * @return void
117 */
118 public function search_posts( WP_REST_Request $request ): void {
119 // Values are already sanitized and validated by route arg definitions.
120 // The query itself lives in ContentService so the content-lookup ability
121 // runs exactly the same lookup; this route's JSON shape is unchanged.
122 $result = ( new ContentService() )->search_posts(
123 (string) $request->get_param( 'search' ),
124 (int) $request->get_param( 'per_page' ),
125 (string) $request->get_param( 'context' )
126 );
127
128 SendJson::success( [ 'data' => $result['posts'] ] );
129 }
130
131 /**
132 * Return basic data for a single published post regardless of post type.
133 *
134 * @param WP_REST_Request $request Full data about the request.
135 * @since 0.0.1-beta.2
136 * @return void
137 */
138 public function get_post_by_id( WP_REST_Request $request ): void {
139 $post_id = $request->get_param( 'id' ); // Already absint by sanitize_callback.
140 $service = new ContentService();
141 $post = $service->get_post( (int) $post_id );
142
143 // Return 404 for missing, non-published, OR disallowed post type so that draft/private/
144 // structural IDs (attachments, nav items, blocks) are not confirmed to exist. Using the
145 // same 404 for every disallowed case preserves the non-enumeration property. The service
146 // deliberately does not apply the post-type gate, so this route keeps it.
147 if (
148 $post === null
149 || ! in_array( $post['type'], $service->lookup_post_types( (string) $request->get_param( 'context' ) ), true )
150 ) {
151 SendJson::error( [ 'message' => __( 'Post not found.', 'surecookie' ) ], 404 );
152 return;
153 }
154
155 SendJson::success(
156 [
157 'id' => $post['id'],
158 'title' => $post['title'],
159 'status' => $post['status'],
160 'link' => $post['link'],
161 'type' => $post['type'],
162 'type_label' => $post['type_label'],
163 ]
164 );
165 }
166 }
167