PluginProbe
WooCommerce / 11.0.0
WooCommerce v11.0.0
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 All 648 releases
woocommerce / src / Internal / RestApi / Routes / V4 / OrderNotes / Controller.php
Controller.php
403 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * REST API Order Notes controller
4 *
5 * Handles route registration, permissions, CRUD operations, and schema definition.
6 *
7 * @package WooCommerce\RestApi
8 */
9
10 declare( strict_types=1 );
11
12 namespace Automattic\WooCommerce\Internal\RestApi\Routes\V4\OrderNotes;
13
14 defined( 'ABSPATH' ) || exit;
15
16 use Automattic\WooCommerce\Internal\RestApi\Routes\V4\AbstractController;
17 use Automattic\WooCommerce\Internal\RestApi\Routes\V4\OrderNotes\Schema\OrderNoteSchema;
18 use WP_Http;
19 use WP_Error;
20 use WP_Comment;
21 use WC_Order;
22 use WP_REST_Request;
23 use WP_REST_Response;
24 use WP_REST_Server;
25
26 /**
27 * OrdersNotes Controller.
28 */
29 class Controller extends AbstractController {
30 /**
31 * Route base.
32 *
33 * @var string
34 */
35 protected $rest_base = 'order-notes';
36
37 /**
38 * Schema class for this route.
39 *
40 * @var OrderNoteSchema
41 */
42 protected $item_schema;
43
44 /**
45 * Query utils class.
46 *
47 * @var QueryUtils
48 */
49 protected $query_utils;
50
51 /**
52 * Initialize the controller.
53 *
54 * @param OrderNoteSchema $item_schema Order schema class.
55 * @param CollectionQuery $query_utils Query utils class.
56 * @internal
57 */
58 final public function init( OrderNoteSchema $item_schema, CollectionQuery $query_utils ) {
59 $this->item_schema = $item_schema;
60 $this->collection_query = $query_utils;
61 }
62
63 /**
64 * Get the schema for the current resource. This use consumed by the AbstractController to generate the item schema
65 * after running various hooks on the response.
66 */
67 protected function get_schema(): array {
68 return $this->item_schema->get_item_schema();
69 }
70
71 /**
72 * Get the collection args schema.
73 *
74 * @return array
75 */
76 protected function get_query_schema(): array {
77 return $this->collection_query->get_query_schema();
78 }
79
80 /**
81 * Register the routes for orders.
82 */
83 public function register_routes() {
84 register_rest_route(
85 $this->namespace,
86 '/' . $this->rest_base,
87 array(
88 'schema' => array( $this, 'get_public_item_schema' ),
89 'args' => array(
90 'order_id' => array(
91 'description' => __( 'The order ID that notes belong to.', 'woocommerce' ),
92 'type' => 'integer',
93 'validate_callback' => function ( $value ) {
94 return $this->is_valid_order_id( $value );
95 },
96 'required' => true,
97 ),
98 ),
99 array(
100 'methods' => WP_REST_Server::READABLE,
101 'callback' => array( $this, 'get_items' ),
102 'permission_callback' => array( $this, 'get_items_permissions_check' ),
103 'args' => $this->get_collection_params(),
104 ),
105 array(
106 'methods' => WP_REST_Server::CREATABLE,
107 'callback' => array( $this, 'create_item' ),
108 'permission_callback' => array( $this, 'create_item_permissions_check' ),
109 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
110 ),
111 )
112 );
113 register_rest_route(
114 $this->namespace,
115 '/' . $this->rest_base . '/(?P<id>[\d]+)',
116 array(
117 'schema' => array( $this, 'get_public_item_schema' ),
118 'args' => array(
119 'id' => array(
120 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
121 'type' => 'integer',
122 ),
123 ),
124 array(
125 'methods' => WP_REST_Server::READABLE,
126 'callback' => array( $this, 'get_item' ),
127 'permission_callback' => array( $this, 'get_item_permissions_check' ),
128 'args' => array(
129 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
130 ),
131 ),
132 array(
133 'methods' => WP_REST_Server::DELETABLE,
134 'callback' => array( $this, 'delete_item' ),
135 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
136 ),
137 )
138 );
139 }
140
141 /**
142 * Prepare links for the request.
143 *
144 * @param mixed $item WordPress representation of the item.
145 * @param WP_REST_Request $request Request object.
146 * @param WP_REST_Response $response Response object.
147 * @return array
148 */
149 protected function prepare_links( $item, WP_REST_Request $request, WP_REST_Response $response ): array {
150 return array(
151 'self' => array(
152 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, (int) $item->comment_ID ) ),
153 ),
154 'collection' => array(
155 'href' => add_query_arg(
156 array( 'order_id' => (int) $item->comment_post_ID ),
157 rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) )
158 ),
159 ),
160 );
161 }
162
163 /**
164 * Prepare a single order note item for response.
165 *
166 * @param WP_Comment $note Note object.
167 * @param WP_REST_Request $request Request object.
168 * @return array
169 */
170 protected function get_item_response( $note, WP_REST_Request $request ): array {
171 return $this->item_schema->get_item_response( $note, $request );
172 }
173
174 /**
175 * Check if a given request has access to read an item.
176 *
177 * @param WP_REST_Request $request The request object.
178 * @return WP_Error|boolean
179 */
180 public function get_item_permissions_check( $request ) {
181 $order = $this->get_order_by_note_id( (int) $request['id'] );
182
183 if ( ! $order ) {
184 return $this->get_route_error_by_code( self::INVALID_ID );
185 }
186
187 if ( ! wc_rest_check_post_permissions( 'shop_order', 'read', $order->get_id() ) ) {
188 return $this->get_authentication_error_by_method( $request->get_method() );
189 }
190 return true;
191 }
192
193 /**
194 * Check if a given request has access to read items.
195 *
196 * @param WP_REST_Request $request Full details about the request.
197 * @return WP_Error|boolean
198 */
199 public function get_items_permissions_check( $request ) {
200 if ( ! wc_rest_check_post_permissions( 'shop_order', 'read', (int) $request['order_id'] ) ) {
201 return $this->get_authentication_error_by_method( $request->get_method() );
202 }
203 return true;
204 }
205
206 /**
207 * Check if a given request has access to create an item.
208 *
209 * @param WP_REST_Request $request The request object.
210 * @return WP_Error|boolean
211 */
212 public function create_item_permissions_check( $request ) {
213 if ( ! wc_rest_check_post_permissions( 'shop_order', 'create', (int) $request['order_id'] ) ) {
214 return $this->get_authentication_error_by_method( $request->get_method() );
215 }
216 return true;
217 }
218
219 /**
220 * Check if a given request has access to delete an item.
221 *
222 * @param WP_REST_Request $request The request object.
223 * @return bool|WP_Error
224 */
225 public function delete_item_permissions_check( $request ) {
226 $order = $this->get_order_by_note_id( (int) $request['id'] );
227
228 if ( ! $order ) {
229 return $this->get_route_error_by_code( self::INVALID_ID );
230 }
231
232 if ( ! wc_rest_check_post_permissions( 'shop_order', 'delete', $order->get_id() ) ) {
233 return $this->get_authentication_error_by_method( $request->get_method() );
234 }
235 return true;
236 }
237
238 /**
239 * Get a single item.
240 *
241 * @param WP_REST_Request $request Full details about the request.
242 * @return WP_Error|WP_REST_Response
243 */
244 public function get_item( $request ) {
245 $note = $this->get_note_by_id( (int) $request['id'] );
246
247 if ( ! $note ) {
248 return $this->get_route_error_by_code( self::INVALID_ID );
249 }
250
251 return $this->prepare_item_for_response( $note, $request );
252 }
253
254 /**
255 * Get collection of orders.
256 *
257 * @param WP_REST_Request $request Full details about the request.
258 * @return WP_Error|WP_REST_Response
259 */
260 public function get_items( $request ) {
261 $order = $this->get_order_by_id( (int) $request['order_id'] );
262
263 if ( ! $order ) {
264 return $this->get_route_error_by_code( self::INVALID_ID );
265 }
266
267 $query_args = $this->collection_query->get_query_args( $request );
268 $results = $this->collection_query->get_query_results( $query_args, $request );
269 $items = array();
270
271 foreach ( $results as $result ) {
272 $items[] = $this->prepare_response_for_collection( $this->prepare_item_for_response( $result, $request ) );
273 }
274
275 return rest_ensure_response( $items );
276 }
277
278 /**
279 * Create a single item.
280 *
281 * @param WP_REST_Request $request Full details about the request.
282 * @return WP_Error|WP_REST_Response
283 */
284 public function create_item( $request ) {
285 if ( ! empty( $request['id'] ) ) {
286 return $this->get_route_error_by_code( self::RESOURCE_EXISTS );
287 }
288
289 $order = $this->get_order_by_id( (int) $request['order_id'] );
290 $note_id = $order ? $order->add_order_note( wp_kses_post( $request['note'] ), $request['is_customer_note'], true ) : null;
291
292 if ( ! $note_id ) {
293 return $this->get_route_error_by_code( self::CANNOT_CREATE );
294 }
295
296 $note = get_comment( $note_id );
297 $this->update_additional_fields_for_object( $note, $request );
298
299 /**
300 * Fires after a single object is created via the REST API.
301 *
302 * @param WP_Comment $note Inserted object.
303 * @param WP_REST_Request $request Request object.
304 * @since 10.2.0
305 */
306 do_action( $this->get_hook_prefix() . 'created', $note, $request );
307
308 $request->set_param( 'context', 'edit' );
309 $response = $this->prepare_item_for_response( $note, $request );
310 $response->set_status( WP_Http::CREATED );
311 $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $note_id ) ) );
312
313 return $response;
314 }
315
316 /**
317 * Delete a single item.
318 *
319 * @param WP_REST_Request $request Full details about the request.
320 * @return WP_REST_Response|WP_Error
321 */
322 public function delete_item( $request ) {
323 $note = $this->get_note_by_id( (int) $request['id'] );
324
325 if ( empty( $note ) ) {
326 return $this->get_route_error_by_code( self::INVALID_ID );
327 }
328
329 $request->set_param( 'context', 'edit' );
330 $response = $this->prepare_item_for_response( $note, $request );
331
332 $result = wc_delete_order_note( (int) $note->comment_ID );
333
334 if ( ! $result ) {
335 return $this->get_route_error_by_code( self::CANNOT_DELETE );
336 }
337
338 /**
339 * Fires after a single object is deleted or trashed via the REST API.
340 *
341 * @param WP_Comment $note The deleted or trashed object.
342 * @param WP_REST_Response $response The response data.
343 * @param WP_REST_Request $request The request sent to the API.
344 * @since 10.2.0
345 */
346 do_action( $this->get_hook_prefix() . 'deleted', $note, $response, $request );
347
348 return $response;
349 }
350
351 /**
352 * Check if an order is valid.
353 *
354 * @param mixed $order_id The order ID.
355 * @return bool True if the order is valid, false otherwise.
356 */
357 protected function is_valid_order_id( $order_id ): bool {
358 $order = $this->get_order_by_id( (int) $order_id );
359 return $order && $order instanceof WC_Order;
360 }
361
362 /**
363 * Get an order by ID.
364 *
365 * @param int $order_id The order ID.
366 * @return WC_Order|null
367 */
368 protected function get_order_by_id( int $order_id ) {
369 if ( ! $order_id ) {
370 return null;
371 }
372 $order = wc_get_order( $order_id );
373 return $order && 'shop_order' === $order->get_type() ? $order : null;
374 }
375 /**
376 * Get the parent order of a note.
377 *
378 * @param int|WP_Comment $note_id The note ID or note object.
379 * @return WC_Order|null
380 */
381 protected function get_order_by_note_id( $note_id ) {
382 $note = $note_id instanceof WP_Comment ? $note_id : $this->get_note_by_id( (int) $note_id );
383 if ( ! $note ) {
384 return null;
385 }
386 return $this->get_order_by_id( (int) $note->comment_post_ID );
387 }
388
389 /**
390 * Get a note by ID.
391 *
392 * @param int $note_id The note ID.
393 * @return WP_Comment|null
394 */
395 protected function get_note_by_id( int $note_id ) {
396 if ( ! $note_id ) {
397 return null;
398 }
399 $note = get_comment( $note_id );
400 return $note && 'order_note' === $note->comment_type ? $note : null;
401 }
402 }
403