PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.7
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.7
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / jwt / rest-api / version1 / class-lp-rest-posts-controller.php

class-lp-rest-posts-controller.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.1.7, at inc/jwt/rest-api/version1/class-lp-rest-posts-controller.php

640 lines 21.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 abstract class LP_REST_Jwt_Posts_Controller extends LP_REST_Jwt_Controller {
3 protected $namespace = 'learnpress/v1';
4
5 protected $rest_base = '';
6
7 protected $post_type = '';
8
9 protected $public = false;
10
11 protected $hierarchical = false;
12
13 protected function get_object( $id ) {
14 return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'learnpress' ), __METHOD__ ), array( 'status' => 405 ) );
15 }
16
17 public function get_items_permissions_check( $request ) {
18 if ( ! lp_rest_check_post_permissions( $this->post_type, 'read' ) ) {
19 return new WP_Error( 'lp_jwt_rest_cannot_view', __( 'Sorry, you cannot get lists', 'learnpress' ), array( 'status' => rest_authorization_required_code() ) );
20 }
21
22 return true;
23 }
24
25 /**
26 * Check if a given request has access to create an item.
27 *
28 * @param WP_REST_Request $request Full details about the request.
29 * @return WP_Error|boolean
30 */
31 public function create_item_permissions_check( $request ) {
32 if ( ! lp_rest_check_post_permissions( $this->post_type, 'create' ) ) {
33 return new WP_Error( 'lp_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'learnpress' ), array( 'status' => rest_authorization_required_code() ) );
34 }
35
36 return true;
37 }
38
39 public function get_item_permissions_check( $request ) {
40 if ( $this->post_type !== get_post_type( (int) $request['id'] ) ) {
41 return new WP_Error( 'lp_jwt_rest_not_in_post_type', __( 'Sorry, You cannot view this item.', 'learnpress' ), array( 'status' => rest_authorization_required_code() ) );
42 }
43
44 $object = $this->get_object( (int) $request['id'] );
45
46 if ( $object && 0 !== $object->get_id() && ! $this->check_read_permission( $object->get_id() ) ) {
47 return new WP_Error( 'lp_jwt_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'learnpress' ), array( 'status' => rest_authorization_required_code() ) );
48 }
49
50 return true;
51 }
52
53 public function check_read_permission( $post_id ) {
54 if ( ! lp_rest_check_post_permissions( $this->post_type, 'read', $post_id ) ) {
55 return false;
56 }
57
58 return true;
59 }
60
61 public function get_item( $request ) {
62 $object = $this->get_object( (int) $request['id'] );
63
64 if ( ! $object || 0 === $object->get_id() ) {
65 return new WP_Error( "lp_rest_{$this->post_type}_invalid_id", esc_html__( 'Invalid ID.', 'learnpress' ), array( 'status' => 404 ) );
66 }
67
68 $data = $this->prepare_object_for_response( $object, $request );
69 $response = rest_ensure_response( $data );
70
71 if ( $this->public ) {
72 $response->link_header( 'alternate', $this->get_permalink( $object ), array( 'type' => 'text/html' ) );
73 }
74
75 return $response;
76 }
77
78 public function get_items( $request ) {
79 $query_args = $this->prepare_objects_query( $request );
80 $query_results = $this->get_objects( $query_args );
81
82 $objects = array();
83 foreach ( $query_results['objects'] as $object ) {
84 $object_id = ! empty( $object->ID ) ? $object->ID : $object->get_id();
85
86 if ( ! $this->check_read_permission( $object_id ) ) {
87 continue;
88 }
89
90 $data = $this->prepare_object_for_response( $object, $request );
91 $objects[] = $this->prepare_response_for_collection( $data );
92 }
93
94 $page = (int) $query_args['paged'];
95 $max_pages = $query_results['pages'];
96
97 $response = rest_ensure_response( $objects );
98 $response->header( 'X-WP-Total', $query_results['total'] );
99 $response->header( 'X-WP-TotalPages', (int) $max_pages );
100
101 $base = $this->rest_base;
102 $attrib_prefix = '(?P<';
103
104 if ( strpos( $base, $attrib_prefix ) !== false ) {
105 $attrib_names = array();
106 preg_match( '/\(\?P<[^>]+>.*\)/', $base, $attrib_names, PREG_OFFSET_CAPTURE );
107
108 foreach ( $attrib_names as $attrib_name_match ) {
109 $beginning_offset = strlen( $attrib_prefix );
110 $attrib_name_end = strpos( $attrib_name_match[0], '>', $attrib_name_match[1] );
111 $attrib_name = substr( $attrib_name_match[0], $beginning_offset, $attrib_name_end - $beginning_offset );
112
113 if ( isset( $request[ $attrib_name ] ) ) {
114 $base = str_replace( "(?P<$attrib_name>[\d]+)", $request[ $attrib_name ], $base );
115 }
116 }
117 }
118
119 $base = esc_url_raw( add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ) ) );
120
121 if ( $page > 1 ) {
122 $prev_page = $page - 1;
123 if ( $prev_page > $max_pages ) {
124 $prev_page = $max_pages;
125 }
126 $prev_link = esc_url_raw( add_query_arg( 'page', $prev_page, $base ) );
127 $response->link_header( 'prev', $prev_link );
128 }
129
130 if ( $max_pages > $page ) {
131 $next_page = $page + 1;
132 $next_link = esc_url_raw( add_query_arg( 'page', $next_page, $base ) );
133 $response->link_header( 'next', $next_link );
134 }
135
136 return $response;
137 }
138
139 /**
140 * Create a single item.
141 *
142 * @param WP_REST_Request $request Full details about the request.
143 * @return WP_Error|WP_REST_Response
144 */
145 public function create_item( $request ) {
146 if ( ! empty( $request['id'] ) ) {
147 /* translators: %s: post type */
148 return new WP_Error( "lp_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'learnpress' ), $this->post_type ), array( 'status' => 400 ) );
149 }
150
151 $post = $this->prepare_item_for_database( $request );
152 if ( is_wp_error( $post ) ) {
153 return $post;
154 }
155
156 $post->post_type = $this->post_type;
157 $post_id = wp_insert_post( $post, true );
158
159 if ( is_wp_error( $post_id ) ) {
160 if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ) ) ) {
161 $post_id->add_data( array( 'status' => 500 ) );
162 } else {
163 $post_id->add_data( array( 'status' => 400 ) );
164 }
165 return $post_id;
166 }
167
168 $post->ID = $post_id;
169 $post = get_post( $post_id );
170
171 $this->update_additional_fields_for_object( $post, $request );
172
173 // Add meta fields.
174 $meta_fields = $this->add_post_meta_fields( $post, $request );
175 if ( is_wp_error( $meta_fields ) ) {
176 // Remove post.
177 $this->delete_post( $post );
178
179 return $meta_fields;
180 }
181
182 /**
183 * Fires after a single item is created or updated via the REST API.
184 *
185 * @param WP_Post $post Post object.
186 * @param WP_REST_Request $request Request object.
187 * @param boolean $creating True when creating item, false when updating.
188 */
189 do_action( "lp_rest_insert_{$this->post_type}", $post, $request, true );
190
191 $request->set_param( 'context', 'edit' );
192 $response = $this->prepare_item_for_response( $post, $request );
193 $response = rest_ensure_response( $response );
194 $response->set_status( 201 );
195 $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) );
196
197 return $response;
198 }
199
200 public function prepare_item_for_database( $request, $creating = false ) {
201 $prepared_post = new stdClass();
202 $current_status = '';
203
204 $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
205
206 if ( isset( $request['id'] ) ) {
207 $existing_post = get_post( $id );
208
209 if ( empty( $existing_post ) || empty( $existing_post->ID ) || $this->post_type !== $existing_post->post_type ) {
210 return new WP_Error( 'rest_invalid_course', __( 'Invalid Course' ), array( 'status' => 404 ) );
211 }
212
213 $prepared_post->ID = $existing_post->ID;
214 $current_status = $existing_post->post_status;
215 }
216
217 $schema = $this->get_item_schema();
218
219 // Post title.
220 if ( ! empty( $schema['properties']['name'] ) && isset( $request['name'] ) ) {
221 if ( is_string( $request['name'] ) ) {
222 $prepared_post->post_title = $request['name'];
223 } elseif ( ! empty( $request['name']['raw'] ) ) {
224 $prepared_post->post_title = $request['name']['raw'];
225 }
226 }
227
228 // Post content.
229 if ( ! empty( $schema['properties']['content'] ) && isset( $request['content'] ) ) {
230 if ( is_string( $request['content'] ) ) {
231 $prepared_post->post_content = $request['content'];
232 } elseif ( isset( $request['content']['raw'] ) ) {
233 $prepared_post->post_content = $request['content']['raw'];
234 }
235 }
236
237 // Post excerpt.
238 if ( ! empty( $schema['properties']['excerpt'] ) && isset( $request['excerpt'] ) ) {
239 if ( is_string( $request['excerpt'] ) ) {
240 $prepared_post->post_excerpt = $request['excerpt'];
241 } elseif ( isset( $request['excerpt']['raw'] ) ) {
242 $prepared_post->post_excerpt = $request['excerpt']['raw'];
243 }
244 }
245
246 // Post type.
247 if ( empty( $request['id'] ) ) {
248 // Creating new post, use default type for the controller.
249 $prepared_post->post_type = $this->post_type;
250 } else {
251 // Updating a post, use previous type.
252 $prepared_post->post_type = get_post_type( $request['id'] );
253 }
254
255 $post_type = get_post_type_object( $prepared_post->post_type );
256
257 // Post status.
258 if (
259 ! empty( $schema['properties']['status'] ) &&
260 isset( $request['status'] ) &&
261 ( ! $current_status || $current_status !== $request['status'] )
262 ) {
263 $status = $this->handle_status_param( $request['status'], $post_type );
264
265 if ( is_wp_error( $status ) ) {
266 return $status;
267 }
268
269 $prepared_post->post_status = $status;
270 }
271
272 // Post date.
273 if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) {
274 $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date : false;
275 $date_data = rest_get_date_with_gmt( $request['date'] );
276
277 if ( ! empty( $date_data ) && $current_date !== $date_data[0] ) {
278 list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
279 $prepared_post->edit_date = true;
280 }
281 } elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) {
282 $current_date = isset( $prepared_post->ID ) ? get_post( $prepared_post->ID )->post_date_gmt : false;
283 $date_data = rest_get_date_with_gmt( $request['date_gmt'], true );
284
285 if ( ! empty( $date_data ) && $current_date !== $date_data[1] ) {
286 list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data;
287 $prepared_post->edit_date = true;
288 }
289 }
290
291 // Sending a null date or date_gmt value resets date and date_gmt to their
292 // default values (`0000-00-00 00:00:00`).
293 if (
294 ( ! empty( $schema['properties']['date_gmt'] ) && $request->has_param( 'date_gmt' ) && null === $request['date_gmt'] ) ||
295 ( ! empty( $schema['properties']['date'] ) && $request->has_param( 'date' ) && null === $request['date'] )
296 ) {
297 $prepared_post->post_date_gmt = null;
298 $prepared_post->post_date = null;
299 }
300
301 // Post slug.
302 if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) {
303 $prepared_post->post_name = $request['slug'];
304 }
305
306 // Author.
307 if ( ! empty( $schema['properties']['author'] ) && ! empty( $request['author'] ) ) {
308 $post_author = (int) $request['author'];
309
310 if ( get_current_user_id() !== $post_author ) {
311 $user_obj = get_userdata( $post_author );
312
313 if ( ! $user_obj ) {
314 return new WP_Error(
315 'rest_invalid_author',
316 __( 'Invalid author ID.' ),
317 array( 'status' => 400 )
318 );
319 }
320 }
321
322 $prepared_post->post_author = $post_author;
323 }
324
325 // Post password.
326 if ( ! empty( $schema['properties']['password'] ) && isset( $request['password'] ) ) {
327 $prepared_post->post_password = $request['password'];
328
329 if ( '' !== $request['password'] ) {
330 if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) {
331 return new WP_Error(
332 'rest_invalid_field',
333 __( 'A post can not be sticky and have a password.' ),
334 array( 'status' => 400 )
335 );
336 }
337
338 if ( ! empty( $prepared_post->ID ) && is_sticky( $prepared_post->ID ) ) {
339 return new WP_Error(
340 'rest_invalid_field',
341 __( 'A sticky post can not be password protected.' ),
342 array( 'status' => 400 )
343 );
344 }
345 }
346 }
347
348 return apply_filters( "lp_rest_pre_insert_{$this->post_type}", $prepared_post, $request );
349 }
350
351 /**
352 * Determines validity and normalizes the given status parameter.
353 *
354 * @since 4.7.0
355 *
356 * @param string $post_status Post status.
357 * @param WP_Post_Type $post_type Post type.
358 * @return string|WP_Error Post status or WP_Error if lacking the proper permission.
359 */
360 protected function handle_status_param( $post_status, $post_type ) {
361
362 switch ( $post_status ) {
363 case 'draft':
364 case 'pending':
365 break;
366 case 'private':
367 if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
368 return new WP_Error(
369 'rest_cannot_publish',
370 __( 'Sorry, you are not allowed to create private posts in this post type.' ),
371 array( 'status' => rest_authorization_required_code() )
372 );
373 }
374 break;
375 case 'publish':
376 case 'future':
377 if ( ! current_user_can( $post_type->cap->publish_posts ) ) {
378 return new WP_Error(
379 'rest_cannot_publish',
380 __( 'Sorry, you are not allowed to publish posts in this post type.' ),
381 array( 'status' => rest_authorization_required_code() )
382 );
383 }
384 break;
385 default:
386 if ( ! get_post_status_object( $post_status ) ) {
387 $post_status = 'draft';
388 }
389 break;
390 }
391
392 return $post_status;
393 }
394
395 protected function prepare_objects_query( $request ) {
396 global $wpdb;
397
398 $args = array();
399 $args['offset'] = $request['offset'];
400 $args['order'] = $request['order'];
401 $args['orderby'] = $request['orderby'];
402 $args['paged'] = $request['page'];
403 $args['post__in'] = $request['include'];
404 $args['post__not_in'] = $request['exclude'];
405 $args['posts_per_page'] = $request['per_page'];
406 $args['name'] = $request['slug'];
407 $args['post_parent__in'] = $request['parent'];
408 $args['post_parent__not_in'] = $request['parent_exclude'];
409 $args['s'] = $request['search'];
410 $args['author__in'] = ! empty( $request['user'] ) ? $request['user'] : $request['author'];
411 $args['author__not_in'] = $request['author_exclude'];
412 $args['fields'] = $this->get_fields_for_response( $request );
413
414 if ( 'date' === $args['orderby'] ) {
415 $args['orderby'] = 'date ID';
416 }
417
418 $args['date_query'] = array();
419
420 if ( isset( $request['before'] ) ) {
421 $args['date_query'][0]['before'] = $request['before'];
422 }
423
424 if ( isset( $request['after'] ) ) {
425 $args['date_query'][0]['after'] = $request['after'];
426 }
427
428 // Get item user is learned
429 if ( ! empty( $request['learned'] ) ) {
430 $item_ids = $this->get_item_learned_ids( $request );
431
432 // Force WP_Query return empty if don't found any order.
433 $item_ids = ! empty( $item_ids ) ? $item_ids : array( 0 );
434
435 $args['post__in'] = $item_ids;
436 }
437
438 $args['post_type'] = $this->post_type;
439
440 $args = apply_filters( "lp_jwt_rest_{$this->post_type}_object_query", $args, $request );
441
442 return $this->prepare_items_query( $args, $request );
443 }
444
445 /** Get all items has in database learnpress_user_items by post_type */
446 public function get_item_learned_ids( $request ) {
447 return array();
448 }
449
450 protected function get_objects( $query_args ) {
451 $query = new WP_Query();
452 $result = $query->query( $query_args );
453
454 $total_posts = $query->found_posts;
455
456 if ( $total_posts < 1 ) {
457 unset( $query_args['paged'] );
458 $count_query = new WP_Query();
459 $count_query->query( $query_args );
460 $total_posts = $count_query->found_posts;
461 }
462
463 return array(
464 'objects' => array_filter( array_map( array( $this, 'get_object' ), $result ) ),
465 'total' => (int) $total_posts,
466 'pages' => (int) ceil( $total_posts / (int) $query->query_vars['posts_per_page'] ),
467 );
468 }
469
470 protected function add_post_meta_fields( $post, $request ) {
471 return true;
472 }
473
474 protected function prepare_object_for_response( $object, $request ) {
475 return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'learnpress' ), __METHOD__ ), array( 'status' => 405 ) );
476 }
477
478 protected function prepare_items_query( $prepared_args = array(), $request = null ) {
479 $query_args = array();
480
481 foreach ( $prepared_args as $key => $value ) {
482 $query_args[ $key ] = apply_filters( "rest_query_var-{$key}", $value ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
483 }
484
485 if ( 'post' !== $this->post_type || ! isset( $query_args['ignore_sticky_posts'] ) ) {
486 $query_args['ignore_sticky_posts'] = true;
487 }
488
489 if ( 'include' === $query_args['orderby'] ) {
490 $query_args['orderby'] = 'post__in';
491 } elseif ( 'id' === $query_args['orderby'] ) {
492 $query_args['orderby'] = 'ID';
493 } elseif ( 'slug' === $query_args['orderby'] ) {
494 $query_args['orderby'] = 'name';
495 }
496
497 return $query_args;
498 }
499
500 public function get_collection_params() {
501 $params = array();
502 $params['context'] = $this->get_context_param();
503 $params['context']['default'] = 'view';
504
505 $params['page'] = array(
506 'description' => __( 'Current page of the collection.', 'learnpress' ),
507 'type' => 'integer',
508 'default' => 1,
509 'sanitize_callback' => 'absint',
510 'validate_callback' => 'rest_validate_request_arg',
511 'minimum' => 1,
512 );
513 $params['per_page'] = array(
514 'description' => __( 'Maximum number of items to be returned in result set.', 'learnpress' ),
515 'type' => 'integer',
516 'default' => 10,
517 'minimum' => 1,
518 'maximum' => 100,
519 'sanitize_callback' => 'absint',
520 'validate_callback' => 'rest_validate_request_arg',
521 );
522 $params['search'] = array(
523 'description' => __( 'Limit results to those matching a string.', 'learnpress' ),
524 'type' => 'string',
525 'sanitize_callback' => 'sanitize_text_field',
526 'validate_callback' => 'rest_validate_request_arg',
527 );
528 $params['after'] = array(
529 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'learnpress' ),
530 'type' => 'string',
531 'format' => 'date-time',
532 'validate_callback' => 'rest_validate_request_arg',
533 );
534 $params['before'] = array(
535 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'learnpress' ),
536 'type' => 'string',
537 'format' => 'date-time',
538 'validate_callback' => 'rest_validate_request_arg',
539 );
540 $params['exclude'] = array(
541 'description' => __( 'Ensure result set excludes specific IDs.', 'learnpress' ),
542 'type' => 'array',
543 'items' => array(
544 'type' => 'integer',
545 ),
546 'default' => array(),
547 'sanitize_callback' => 'wp_parse_id_list',
548 );
549 $params['include'] = array(
550 'description' => __( 'Limit result set to specific ids.', 'learnpress' ),
551 'type' => 'array',
552 'items' => array(
553 'type' => 'integer',
554 ),
555 'default' => array(),
556 'sanitize_callback' => 'wp_parse_id_list',
557 );
558 $params['offset'] = array(
559 'description' => __( 'Offset the result set by a specific number of items.', 'learnpress' ),
560 'type' => 'integer',
561 'sanitize_callback' => 'absint',
562 'validate_callback' => 'rest_validate_request_arg',
563 );
564 $params['order'] = array(
565 'description' => __( 'Order sort attribute ascending or descending.', 'learnpress' ),
566 'type' => 'string',
567 'default' => 'desc',
568 'enum' => array( 'asc', 'desc' ),
569 'validate_callback' => 'rest_validate_request_arg',
570 );
571 $params['orderby'] = array(
572 'description' => __( 'Sort collection by object attribute.', 'learnpress' ),
573 'type' => 'string',
574 'default' => 'date',
575 'enum' => array(
576 'date',
577 'id',
578 'include',
579 'title',
580 'slug',
581 'modified',
582 ),
583 'validate_callback' => 'rest_validate_request_arg',
584 );
585 $params['author'] = array(
586 'description' => __( 'Limit result set to posts assigned to specific authors.', 'learnpress' ),
587 'type' => 'array',
588 'items' => array(
589 'type' => 'integer',
590 ),
591 'default' => array(),
592 );
593 $params['author_exclude'] = array(
594 'description' => __( 'Ensure result set excludes posts assigned to specific authors.', 'learnpress' ),
595 'type' => 'array',
596 'items' => array(
597 'type' => 'integer',
598 ),
599 'default' => array(),
600 );
601 $params['user'] = array(
602 'description' => __( 'Limit result set to posts assigned to specific authors.', 'learnpress' ),
603 'type' => 'array',
604 'items' => array(
605 'type' => 'integer',
606 ),
607 'default' => array(),
608 );
609 $params['learned'] = array(
610 'description' => __( 'Get item learned by user.', 'learnpress' ),
611 'type' => 'boolean',
612 'default' => false,
613 'validate_callback' => 'rest_validate_request_arg',
614 );
615
616 if ( $this->hierarchical ) {
617 $params['parent'] = array(
618 'description' => __( 'Limit result set to those of particular parent IDs.', 'learnpress' ),
619 'type' => 'array',
620 'items' => array(
621 'type' => 'integer',
622 ),
623 'sanitize_callback' => 'wp_parse_id_list',
624 'default' => array(),
625 );
626 $params['parent_exclude'] = array(
627 'description' => __( 'Limit result set to all items except those of a particular parent ID.', 'learnpress' ),
628 'type' => 'array',
629 'items' => array(
630 'type' => 'integer',
631 ),
632 'sanitize_callback' => 'wp_parse_id_list',
633 'default' => array(),
634 );
635 }
636
637 return apply_filters( "rest_{$this->post_type}_collection_params", $params, $this->post_type );
638 }
639 }
640