diff --git a/includes/abstracts/abstract-wc-rest-crud-controller.php b/includes/abstracts/abstract-wc-rest-crud-controller.php deleted file mode 100644 index 83392dfbd0e..00000000000 --- a/includes/abstracts/abstract-wc-rest-crud-controller.php +++ /dev/null @@ -1,627 +0,0 @@ - 405 ) ); - } - - /** - * Check if a given request has access to read an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_item_permissions_check( $request ) { - $object = $this->get_object( (int) $request['id'] ); - - if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'read', $object->get_id() ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to update an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_item_permissions_check( $request ) { - $object = $this->get_object( (int) $request['id'] ); - - if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $object->get_id() ) ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to delete an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return bool|WP_Error - */ - public function delete_item_permissions_check( $request ) { - $object = $this->get_object( (int) $request['id'] ); - - if ( $object && 0 !== $object->get_id() && ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { - return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Get object permalink. - * - * @param object $object Object. - * @return string - */ - protected function get_permalink( $object ) { - return ''; - } - - /** - * Prepares the object for the REST response. - * - * @since 3.0.0 - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. - */ - protected function prepare_object_for_response( $object, $request ) { - // translators: %s: Class method name. - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); - } - - /** - * Prepares one object for create or update operation. - * - * @since 3.0.0 - * @param WP_REST_Request $request Request object. - * @param bool $creating If is creating a new object. - * @return WP_Error|WC_Data The prepared item, or WP_Error object on failure. - */ - protected function prepare_object_for_database( $request, $creating = false ) { - // translators: %s: Class method name. - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); - } - - /** - * Get a single item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_item( $request ) { - $object = $this->get_object( (int) $request['id'] ); - - if ( ! $object || 0 === $object->get_id() ) { - return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $data = $this->prepare_object_for_response( $object, $request ); - $response = rest_ensure_response( $data ); - - if ( $this->public ) { - $response->link_header( 'alternate', $this->get_permalink( $object ), array( 'type' => 'text/html' ) ); - } - - return $response; - } - - /** - * Save an object data. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @param bool $creating If is creating a new object. - * @return WC_Data|WP_Error - */ - protected function save_object( $request, $creating = false ) { - try { - $object = $this->prepare_object_for_database( $request, $creating ); - - if ( is_wp_error( $object ) ) { - return $object; - } - - $object->save(); - - return $this->get_object( $object->get_id() ); - } catch ( WC_Data_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); - } catch ( WC_REST_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - } - - /** - * Create a single item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function create_item( $request ) { - if ( ! empty( $request['id'] ) ) { - /* translators: %s: post type */ - return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) ); - } - - $object = $this->save_object( $request, true ); - - if ( is_wp_error( $object ) ) { - return $object; - } - - try { - $this->update_additional_fields_for_object( $object, $request ); - - /** - * Fires after a single object is created or updated via the REST API. - * - * @param WC_Data $object Inserted object. - * @param WP_REST_Request $request Request object. - * @param boolean $creating True when creating object, false when updating. - */ - do_action( "woocommerce_rest_insert_{$this->post_type}_object", $object, $request, true ); - } catch ( WC_Data_Exception $e ) { - $object->delete(); - return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); - } catch ( WC_REST_Exception $e ) { - $object->delete(); - return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_object_for_response( $object, $request ); - $response = rest_ensure_response( $response ); - $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ) ); - - return $response; - } - - /** - * Update a single post. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function update_item( $request ) { - $object = $this->get_object( (int) $request['id'] ); - - if ( ! $object || 0 === $object->get_id() ) { - return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 400 ) ); - } - - $object = $this->save_object( $request, false ); - - if ( is_wp_error( $object ) ) { - return $object; - } - - try { - $this->update_additional_fields_for_object( $object, $request ); - - /** - * Fires after a single object is created or updated via the REST API. - * - * @param WC_Data $object Inserted object. - * @param WP_REST_Request $request Request object. - * @param boolean $creating True when creating object, false when updating. - */ - do_action( "woocommerce_rest_insert_{$this->post_type}_object", $object, $request, false ); - } catch ( WC_Data_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() ); - } catch ( WC_REST_Exception $e ) { - return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_object_for_response( $object, $request ); - return rest_ensure_response( $response ); - } - - /** - * Prepare objects query. - * - * @since 3.0.0 - * @param WP_REST_Request $request Full details about the request. - * @return array - */ - protected function prepare_objects_query( $request ) { - $args = array(); - $args['offset'] = $request['offset']; - $args['order'] = $request['order']; - $args['orderby'] = $request['orderby']; - $args['paged'] = $request['page']; - $args['post__in'] = $request['include']; - $args['post__not_in'] = $request['exclude']; - $args['posts_per_page'] = $request['per_page']; - $args['name'] = $request['slug']; - $args['post_parent__in'] = $request['parent']; - $args['post_parent__not_in'] = $request['parent_exclude']; - $args['s'] = $request['search']; - - if ( 'date' === $args['orderby'] ) { - $args['orderby'] = 'date ID'; - } - - $args['date_query'] = array(); - // Set before into date query. Date query must be specified as an array of an array. - if ( isset( $request['before'] ) ) { - $args['date_query'][0]['before'] = $request['before']; - } - - // Set after into date query. Date query must be specified as an array of an array. - if ( isset( $request['after'] ) ) { - $args['date_query'][0]['after'] = $request['after']; - } - - // Force the post_type argument, since it's not a user input variable. - $args['post_type'] = $this->post_type; - - /** - * Filter the query arguments for a request. - * - * Enables adding extra arguments or setting defaults for a post - * collection request. - * - * @param array $args Key value array of query var to query value. - * @param WP_REST_Request $request The request used. - */ - $args = apply_filters( "woocommerce_rest_{$this->post_type}_object_query", $args, $request ); - - return $this->prepare_items_query( $args, $request ); - } - - /** - * Get objects. - * - * @since 3.0.0 - * @param array $query_args Query args. - * @return array - */ - protected function get_objects( $query_args ) { - $query = new WP_Query(); - $result = $query->query( $query_args ); - - $total_posts = $query->found_posts; - if ( $total_posts < 1 ) { - // Out-of-bounds, run the query again without LIMIT for total count. - unset( $query_args['paged'] ); - $count_query = new WP_Query(); - $count_query->query( $query_args ); - $total_posts = $count_query->found_posts; - } - - return array( - 'objects' => array_map( array( $this, 'get_object' ), $result ), - 'total' => (int) $total_posts, - 'pages' => (int) ceil( $total_posts / (int) $query->query_vars['posts_per_page'] ), - ); - } - - /** - * Get a collection of posts. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $query_args = $this->prepare_objects_query( $request ); - $query_results = $this->get_objects( $query_args ); - - $objects = array(); - foreach ( $query_results['objects'] as $object ) { - if ( ! wc_rest_check_post_permissions( $this->post_type, 'read', $object->get_id() ) ) { - continue; - } - - $data = $this->prepare_object_for_response( $object, $request ); - $objects[] = $this->prepare_response_for_collection( $data ); - } - - $page = (int) $query_args['paged']; - $max_pages = $query_results['pages']; - - $response = rest_ensure_response( $objects ); - $response->header( 'X-WP-Total', $query_results['total'] ); - $response->header( 'X-WP-TotalPages', (int) $max_pages ); - - $base = $this->rest_base; - $attrib_prefix = '(?P<'; - if ( strpos( $base, $attrib_prefix ) !== false ) { - $attrib_names = array(); - preg_match( '/\(\?P<[^>]+>.*\)/', $base, $attrib_names, PREG_OFFSET_CAPTURE ); - foreach ( $attrib_names as $attrib_name_match ) { - $beginning_offset = strlen( $attrib_prefix ); - $attrib_name_end = strpos( $attrib_name_match[0], '>', $attrib_name_match[1] ); - $attrib_name = substr( $attrib_name_match[0], $beginning_offset, $attrib_name_end - $beginning_offset ); - if ( isset( $request[ $attrib_name ] ) ) { - $base = str_replace( "(?P<$attrib_name>[\d]+)", $request[ $attrib_name ], $base ); - } - } - } - $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ) ); - - if ( $page > 1 ) { - $prev_page = $page - 1; - if ( $prev_page > $max_pages ) { - $prev_page = $max_pages; - } - $prev_link = add_query_arg( 'page', $prev_page, $base ); - $response->link_header( 'prev', $prev_link ); - } - if ( $max_pages > $page ) { - $next_page = $page + 1; - $next_link = add_query_arg( 'page', $next_page, $base ); - $response->link_header( 'next', $next_link ); - } - - return $response; - } - - /** - * Delete a single item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error - */ - public function delete_item( $request ) { - $force = (bool) $request['force']; - $object = $this->get_object( (int) $request['id'] ); - $result = false; - - if ( ! $object || 0 === $object->get_id() ) { - return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); - - /** - * Filter whether an object is trashable. - * - * Return false to disable trash support for the object. - * - * @param boolean $supports_trash Whether the object type support trashing. - * @param WC_Data $object The object being considered for trashing support. - */ - $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); - - if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { - /* translators: %s: post type */ - return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code() ) ); - } - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_object_for_response( $object, $request ); - - // If we're forcing, then delete permanently. - if ( $force ) { - $object->delete( true ); - $result = 0 === $object->get_id(); - } else { - // If we don't support trashing for this type, error out. - if ( ! $supports_trash ) { - /* translators: %s: post type */ - return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) ); - } - - // Otherwise, only trash if we haven't already. - if ( is_callable( array( $object, 'get_status' ) ) ) { - if ( 'trash' === $object->get_status() ) { - /* translators: %s: post type */ - return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) ); - } - - $object->delete(); - $result = 'trash' === $object->get_status(); - } - } - - if ( ! $result ) { - /* translators: %s: post type */ - return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500 ) ); - } - - /** - * Fires after a single object is deleted or trashed via the REST API. - * - * @param WC_Data $object The deleted or trashed object. - * @param WP_REST_Response $response The response data. - * @param WP_REST_Request $request The request sent to the API. - */ - do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); - - return $response; - } - - /** - * Prepare links for the request. - * - * @param WC_Data $object Object data. - * @param WP_REST_Request $request Request object. - * @return array Links for the given post. - */ - protected function prepare_links( $object, $request ) { - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), - ), - ); - - return $links; - } - - /** - * Get the query params for collections of attachments. - * - * @return array - */ - public function get_collection_params() { - $params = array(); - $params['context'] = $this->get_context_param(); - $params['context']['default'] = 'view'; - - $params['page'] = array( - 'description' => __( 'Current page of the collection.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 1, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - 'minimum' => 1, - ); - $params['per_page'] = array( - 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 10, - 'minimum' => 1, - 'maximum' => 100, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['search'] = array( - 'description' => __( 'Limit results to those matching a string.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['after'] = array( - 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['before'] = array( - 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['exclude'] = array( - 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', - ); - $params['include'] = array( - 'description' => __( 'Limit result set to specific ids.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', - ); - $params['offset'] = array( - 'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'date', - 'enum' => array( - 'date', - 'id', - 'include', - 'title', - 'slug', - ), - 'validate_callback' => 'rest_validate_request_arg', - ); - - if ( $this->hierarchical ) { - $params['parent'] = array( - 'description' => __( 'Limit result set to those of particular parent IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'sanitize_callback' => 'wp_parse_id_list', - 'default' => array(), - ); - $params['parent_exclude'] = array( - 'description' => __( 'Limit result set to all items except those of a particular parent ID.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'sanitize_callback' => 'wp_parse_id_list', - 'default' => array(), - ); - } - - /** - * Filter collection parameters for the posts controller. - * - * The dynamic part of the filter `$this->post_type` refers to the post - * type slug for the controller. - * - * This filter registers the collection parameter, but does not map the - * collection parameter to an internal WP_Query parameter. Use the - * `rest_{$this->post_type}_query` filter to set WP_Query parameters. - * - * @param array $query_params JSON Schema-formatted collection parameters. - * @param WP_Post_Type $post_type Post type object. - */ - return apply_filters( "rest_{$this->post_type}_collection_params", $params, $this->post_type ); - } -} diff --git a/includes/abstracts/abstract-wc-rest-posts-controller.php b/includes/abstracts/abstract-wc-rest-posts-controller.php deleted file mode 100644 index bffc4a6843e..00000000000 --- a/includes/abstracts/abstract-wc-rest-posts-controller.php +++ /dev/null @@ -1,723 +0,0 @@ -post_type, 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to create an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function create_item_permissions_check( $request ) { - if ( ! wc_rest_check_post_permissions( $this->post_type, 'create' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to read an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_item_permissions_check( $request ) { - $post = get_post( (int) $request['id'] ); - - if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'read', $post->ID ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to update an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_item_permissions_check( $request ) { - $post = get_post( (int) $request['id'] ); - - if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'edit', $post->ID ) ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to delete an item. - * - * @param WP_REST_Request $request Full details about the request. - * @return bool|WP_Error - */ - public function delete_item_permissions_check( $request ) { - $post = get_post( (int) $request['id'] ); - - if ( $post && ! wc_rest_check_post_permissions( $this->post_type, 'delete', $post->ID ) ) { - return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access batch create, update and delete items. - * - * @param WP_REST_Request $request Full details about the request. - * - * @return boolean|WP_Error - */ - public function batch_items_permissions_check( $request ) { - if ( ! wc_rest_check_post_permissions( $this->post_type, 'batch' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Get a single item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_item( $request ) { - $id = (int) $request['id']; - $post = get_post( $id ); - - if ( ! empty( $post->post_type ) && 'product_variation' === $post->post_type && 'product' === $this->post_type ) { - return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404 ) ); - } elseif ( empty( $id ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) { - return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $data = $this->prepare_item_for_response( $post, $request ); - $response = rest_ensure_response( $data ); - - if ( $this->public ) { - $response->link_header( 'alternate', get_permalink( $id ), array( 'type' => 'text/html' ) ); - } - - return $response; - } - - /** - * Create a single item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function create_item( $request ) { - if ( ! empty( $request['id'] ) ) { - /* translators: %s: post type */ - return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) ); - } - - $post = $this->prepare_item_for_database( $request ); - if ( is_wp_error( $post ) ) { - return $post; - } - - $post->post_type = $this->post_type; - $post_id = wp_insert_post( $post, true ); - - if ( is_wp_error( $post_id ) ) { - - if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ) ) ) { - $post_id->add_data( array( 'status' => 500 ) ); - } else { - $post_id->add_data( array( 'status' => 400 ) ); - } - return $post_id; - } - $post->ID = $post_id; - $post = get_post( $post_id ); - - $this->update_additional_fields_for_object( $post, $request ); - - // Add meta fields. - $meta_fields = $this->add_post_meta_fields( $post, $request ); - if ( is_wp_error( $meta_fields ) ) { - // Remove post. - $this->delete_post( $post ); - - return $meta_fields; - } - - /** - * Fires after a single item is created or updated via the REST API. - * - * @param WP_Post $post Post object. - * @param WP_REST_Request $request Request object. - * @param boolean $creating True when creating item, false when updating. - */ - do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true ); - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_item_for_response( $post, $request ); - $response = rest_ensure_response( $response ); - $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) ); - - return $response; - } - - /** - * Add post meta fields. - * - * @param WP_Post $post Post Object. - * @param WP_REST_Request $request WP_REST_Request Object. - * @return bool|WP_Error - */ - protected function add_post_meta_fields( $post, $request ) { - return true; - } - - /** - * Delete post. - * - * @param WP_Post $post Post object. - */ - protected function delete_post( $post ) { - wp_delete_post( $post->ID, true ); - } - - /** - * Update a single post. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function update_item( $request ) { - $id = (int) $request['id']; - $post = get_post( $id ); - - if ( ! empty( $post->post_type ) && 'product_variation' === $post->post_type && 'product' === $this->post_type ) { - return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404 ) ); - } elseif ( empty( $id ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) { - return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) ); - } - - $post = $this->prepare_item_for_database( $request ); - if ( is_wp_error( $post ) ) { - return $post; - } - // Convert the post object to an array, otherwise wp_update_post will expect non-escaped input. - $post_id = wp_update_post( (array) $post, true ); - if ( is_wp_error( $post_id ) ) { - if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) { - $post_id->add_data( array( 'status' => 500 ) ); - } else { - $post_id->add_data( array( 'status' => 400 ) ); - } - return $post_id; - } - - $post = get_post( $post_id ); - $this->update_additional_fields_for_object( $post, $request ); - - // Update meta fields. - $meta_fields = $this->update_post_meta_fields( $post, $request ); - if ( is_wp_error( $meta_fields ) ) { - return $meta_fields; - } - - /** - * Fires after a single item is created or updated via the REST API. - * - * @param WP_Post $post Post object. - * @param WP_REST_Request $request Request object. - * @param boolean $creating True when creating item, false when updating. - */ - do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false ); - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_item_for_response( $post, $request ); - return rest_ensure_response( $response ); - } - - /** - * Get a collection of posts. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|WP_REST_Response - */ - public function get_items( $request ) { - $args = array(); - $args['offset'] = $request['offset']; - $args['order'] = $request['order']; - $args['orderby'] = $request['orderby']; - $args['paged'] = $request['page']; - $args['post__in'] = $request['include']; - $args['post__not_in'] = $request['exclude']; - $args['posts_per_page'] = $request['per_page']; - $args['name'] = $request['slug']; - $args['post_parent__in'] = $request['parent']; - $args['post_parent__not_in'] = $request['parent_exclude']; - $args['s'] = $request['search']; - - $args['date_query'] = array(); - // Set before into date query. Date query must be specified as an array of an array. - if ( isset( $request['before'] ) ) { - $args['date_query'][0]['before'] = $request['before']; - } - - // Set after into date query. Date query must be specified as an array of an array. - if ( isset( $request['after'] ) ) { - $args['date_query'][0]['after'] = $request['after']; - } - - if ( 'wc/v1' === $this->namespace ) { - if ( is_array( $request['filter'] ) ) { - $args = array_merge( $args, $request['filter'] ); - unset( $args['filter'] ); - } - } - - // Force the post_type argument, since it's not a user input variable. - $args['post_type'] = $this->post_type; - - /** - * Filter the query arguments for a request. - * - * Enables adding extra arguments or setting defaults for a post - * collection request. - * - * @param array $args Key value array of query var to query value. - * @param WP_REST_Request $request The request used. - */ - $args = apply_filters( "woocommerce_rest_{$this->post_type}_query", $args, $request ); - $query_args = $this->prepare_items_query( $args, $request ); - - $posts_query = new WP_Query(); - $query_result = $posts_query->query( $query_args ); - - $posts = array(); - foreach ( $query_result as $post ) { - if ( ! wc_rest_check_post_permissions( $this->post_type, 'read', $post->ID ) ) { - continue; - } - - $data = $this->prepare_item_for_response( $post, $request ); - $posts[] = $this->prepare_response_for_collection( $data ); - } - - $page = (int) $query_args['paged']; - $total_posts = $posts_query->found_posts; - - if ( $total_posts < 1 ) { - // Out-of-bounds, run the query again without LIMIT for total count. - unset( $query_args['paged'] ); - $count_query = new WP_Query(); - $count_query->query( $query_args ); - $total_posts = $count_query->found_posts; - } - - $max_pages = ceil( $total_posts / (int) $query_args['posts_per_page'] ); - - $response = rest_ensure_response( $posts ); - $response->header( 'X-WP-Total', (int) $total_posts ); - $response->header( 'X-WP-TotalPages', (int) $max_pages ); - - $request_params = $request->get_query_params(); - if ( ! empty( $request_params['filter'] ) ) { - // Normalize the pagination params. - unset( $request_params['filter']['posts_per_page'] ); - unset( $request_params['filter']['paged'] ); - } - $base = add_query_arg( $request_params, rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); - - if ( $page > 1 ) { - $prev_page = $page - 1; - if ( $prev_page > $max_pages ) { - $prev_page = $max_pages; - } - $prev_link = add_query_arg( 'page', $prev_page, $base ); - $response->link_header( 'prev', $prev_link ); - } - if ( $max_pages > $page ) { - $next_page = $page + 1; - $next_link = add_query_arg( 'page', $next_page, $base ); - $response->link_header( 'next', $next_link ); - } - - return $response; - } - - /** - * Delete a single item. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error - */ - public function delete_item( $request ) { - $id = (int) $request['id']; - $force = (bool) $request['force']; - $post = get_post( $id ); - - if ( empty( $id ) || empty( $post->ID ) || $post->post_type !== $this->post_type ) { - return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - $supports_trash = EMPTY_TRASH_DAYS > 0; - - /** - * Filter whether an item is trashable. - * - * Return false to disable trash support for the item. - * - * @param boolean $supports_trash Whether the item type support trashing. - * @param WP_Post $post The Post object being considered for trashing support. - */ - $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_trashable", $supports_trash, $post ); - - if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $post->ID ) ) { - /* translators: %s: post type */ - return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code() ) ); - } - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_item_for_response( $post, $request ); - - // If we're forcing, then delete permanently. - if ( $force ) { - $result = wp_delete_post( $id, true ); - } else { - // If we don't support trashing for this type, error out. - if ( ! $supports_trash ) { - /* translators: %s: post type */ - return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) ); - } - - // Otherwise, only trash if we haven't already. - if ( 'trash' === $post->post_status ) { - /* translators: %s: post type */ - return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) ); - } - - // (Note that internally this falls through to `wp_delete_post` if - // the trash is disabled.) - $result = wp_trash_post( $id ); - } - - if ( ! $result ) { - /* translators: %s: post type */ - return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500 ) ); - } - - /** - * Fires after a single item is deleted or trashed via the REST API. - * - * @param object $post The deleted or trashed item. - * @param WP_REST_Response $response The response data. - * @param WP_REST_Request $request The request sent to the API. - */ - do_action( "woocommerce_rest_delete_{$this->post_type}", $post, $response, $request ); - - return $response; - } - - /** - * Prepare links for the request. - * - * @param WP_Post $post Post object. - * @param WP_REST_Request $request Request object. - * @return array Links for the given post. - */ - protected function prepare_links( $post, $request ) { - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post->ID ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), - ), - ); - - return $links; - } - - /** - * Determine the allowed query_vars for a get_items() response and - * prepare for WP_Query. - * - * @param array $prepared_args Prepared arguments. - * @param WP_REST_Request $request Request object. - * @return array $query_args - */ - protected function prepare_items_query( $prepared_args = array(), $request = null ) { - - $valid_vars = array_flip( $this->get_allowed_query_vars() ); - $query_args = array(); - foreach ( $valid_vars as $var => $index ) { - if ( isset( $prepared_args[ $var ] ) ) { - /** - * Filter the query_vars used in `get_items` for the constructed query. - * - * The dynamic portion of the hook name, $var, refers to the query_var key. - * - * @param mixed $prepared_args[ $var ] The query_var value. - */ - $query_args[ $var ] = apply_filters( "woocommerce_rest_query_var-{$var}", $prepared_args[ $var ] ); - } - } - - $query_args['ignore_sticky_posts'] = true; - - if ( 'include' === $query_args['orderby'] ) { - $query_args['orderby'] = 'post__in'; - } elseif ( 'id' === $query_args['orderby'] ) { - $query_args['orderby'] = 'ID'; // ID must be capitalized. - } elseif ( 'slug' === $query_args['orderby'] ) { - $query_args['orderby'] = 'name'; - } - - return $query_args; - } - - /** - * Get all the WP Query vars that are allowed for the API request. - * - * @return array - */ - protected function get_allowed_query_vars() { - global $wp; - - /** - * Filter the publicly allowed query vars. - * - * Allows adjusting of the default query vars that are made public. - * - * @param array Array of allowed WP_Query query vars. - */ - $valid_vars = apply_filters( 'query_vars', $wp->public_query_vars ); - - $post_type_obj = get_post_type_object( $this->post_type ); - if ( current_user_can( $post_type_obj->cap->edit_posts ) ) { - /** - * Filter the allowed 'private' query vars for authorized users. - * - * If the user has the `edit_posts` capability, we also allow use of - * private query parameters, which are only undesirable on the - * frontend, but are safe for use in query strings. - * - * To disable anyway, use - * `add_filter( 'woocommerce_rest_private_query_vars', '__return_empty_array' );` - * - * @param array $private_query_vars Array of allowed query vars for authorized users. - * } - */ - $private = apply_filters( 'woocommerce_rest_private_query_vars', $wp->private_query_vars ); - $valid_vars = array_merge( $valid_vars, $private ); - } - // Define our own in addition to WP's normal vars. - $rest_valid = array( - 'date_query', - 'ignore_sticky_posts', - 'offset', - 'post__in', - 'post__not_in', - 'post_parent', - 'post_parent__in', - 'post_parent__not_in', - 'posts_per_page', - 'meta_query', - 'tax_query', - 'meta_key', - 'meta_value', - 'meta_compare', - 'meta_value_num', - ); - $valid_vars = array_merge( $valid_vars, $rest_valid ); - - /** - * Filter allowed query vars for the REST API. - * - * This filter allows you to add or remove query vars from the final allowed - * list for all requests, including unauthenticated ones. To alter the - * vars for editors only. - * - * @param array { - * Array of allowed WP_Query query vars. - * - * @param string $allowed_query_var The query var to allow. - * } - */ - $valid_vars = apply_filters( 'woocommerce_rest_query_vars', $valid_vars ); - - return $valid_vars; - } - - /** - * Get the query params for collections of attachments. - * - * @return array - */ - public function get_collection_params() { - $params = parent::get_collection_params(); - - $params['context']['default'] = 'view'; - - $params['after'] = array( - 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['before'] = array( - 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['exclude'] = array( - 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', - ); - $params['include'] = array( - 'description' => __( 'Limit result set to specific ids.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', - ); - $params['offset'] = array( - 'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'date', - 'enum' => array( - 'date', - 'id', - 'include', - 'title', - 'slug', - ), - 'validate_callback' => 'rest_validate_request_arg', - ); - - $post_type_obj = get_post_type_object( $this->post_type ); - - if ( isset( $post_type_obj->hierarchical ) && $post_type_obj->hierarchical ) { - $params['parent'] = array( - 'description' => __( 'Limit result set to those of particular parent IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'sanitize_callback' => 'wp_parse_id_list', - 'default' => array(), - ); - $params['parent_exclude'] = array( - 'description' => __( 'Limit result set to all items except those of a particular parent ID.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'sanitize_callback' => 'wp_parse_id_list', - 'default' => array(), - ); - } - - if ( 'wc/v1' === $this->namespace ) { - $params['filter'] = array( - 'type' => 'object', - 'description' => __( 'Use WP Query arguments to modify the response; private query vars require appropriate authorization.', 'woocommerce' ), - ); - } - - return $params; - } - - /** - * Update post meta fields. - * - * @param WP_Post $post Post object. - * @param WP_REST_Request $request Request object. - * @return bool|WP_Error - */ - protected function update_post_meta_fields( $post, $request ) { - return true; - } -} diff --git a/includes/abstracts/abstract-wc-rest-shipping-zones-controller.php b/includes/abstracts/abstract-wc-rest-shipping-zones-controller.php deleted file mode 100644 index b89a5a442c8..00000000000 --- a/includes/abstracts/abstract-wc-rest-shipping-zones-controller.php +++ /dev/null @@ -1,125 +0,0 @@ - 404 ) ); - } - - return $zone; - } - - /** - * Check whether a given request has permission to read Shipping Zones. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - if ( ! wc_shipping_enabled() ) { - return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to create Shipping Zones. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function create_item_permissions_check( $request ) { - if ( ! wc_shipping_enabled() ) { - return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check whether a given request has permission to edit Shipping Zones. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_items_permissions_check( $request ) { - if ( ! wc_shipping_enabled() ) { - return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check whether a given request has permission to delete Shipping Zones. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function delete_items_permissions_check( $request ) { - if ( ! wc_shipping_enabled() ) { - return new WP_Error( 'rest_no_route', __( 'Shipping is disabled.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - if ( ! wc_rest_check_manager_permissions( 'settings', 'delete' ) ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - -} diff --git a/includes/abstracts/abstract-wc-rest-terms-controller.php b/includes/abstracts/abstract-wc-rest-terms-controller.php deleted file mode 100644 index 4d20b07ea34..00000000000 --- a/includes/abstracts/abstract-wc-rest-terms-controller.php +++ /dev/null @@ -1,806 +0,0 @@ -namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), - 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => array_merge( - $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), - array( - 'name' => array( - 'type' => 'string', - 'description' => __( 'Name for the resource.', 'woocommerce' ), - 'required' => true, - ), - ) - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[\d]+)', - array( - 'args' => array( - 'id' => array( - 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ), - 'type' => 'integer', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ), - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - array( - 'methods' => WP_REST_Server::DELETABLE, - 'callback' => array( $this, 'delete_item' ), - 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - 'args' => array( - 'force' => array( - 'default' => false, - 'type' => 'boolean', - 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ), - ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/batch', - array( - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'batch_items' ), - 'permission_callback' => array( $this, 'batch_items_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - 'schema' => array( $this, 'get_public_batch_schema' ), - ) - ); - } - - /** - * Check if a given request has access to read the terms. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_items_permissions_check( $request ) { - $permissions = $this->check_permissions( $request, 'read' ); - if ( is_wp_error( $permissions ) ) { - return $permissions; - } - - if ( ! $permissions ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to create a term. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function create_item_permissions_check( $request ) { - $permissions = $this->check_permissions( $request, 'create' ); - if ( is_wp_error( $permissions ) ) { - return $permissions; - } - - if ( ! $permissions ) { - return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to read a term. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function get_item_permissions_check( $request ) { - $permissions = $this->check_permissions( $request, 'read' ); - if ( is_wp_error( $permissions ) ) { - return $permissions; - } - - if ( ! $permissions ) { - return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to update a term. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_item_permissions_check( $request ) { - $permissions = $this->check_permissions( $request, 'edit' ); - if ( is_wp_error( $permissions ) ) { - return $permissions; - } - - if ( ! $permissions ) { - return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access to delete a term. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function delete_item_permissions_check( $request ) { - $permissions = $this->check_permissions( $request, 'delete' ); - if ( is_wp_error( $permissions ) ) { - return $permissions; - } - - if ( ! $permissions ) { - return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check if a given request has access batch create, update and delete items. - * - * @param WP_REST_Request $request Full details about the request. - * @return boolean|WP_Error - */ - public function batch_items_permissions_check( $request ) { - $permissions = $this->check_permissions( $request, 'batch' ); - if ( is_wp_error( $permissions ) ) { - return $permissions; - } - - if ( ! $permissions ) { - return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Check permissions. - * - * @param WP_REST_Request $request Full details about the request. - * @param string $context Request context. - * @return bool|WP_Error - */ - protected function check_permissions( $request, $context = 'read' ) { - // Get taxonomy. - $taxonomy = $this->get_taxonomy( $request ); - if ( ! $taxonomy || ! taxonomy_exists( $taxonomy ) ) { - return new WP_Error( 'woocommerce_rest_taxonomy_invalid', __( 'Taxonomy does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - // Check permissions for a single term. - $id = intval( $request['id'] ); - if ( $id ) { - $term = get_term( $id, $taxonomy ); - - if ( is_wp_error( $term ) || ! $term || $term->taxonomy !== $taxonomy ) { - return new WP_Error( 'woocommerce_rest_term_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) ); - } - - return wc_rest_check_product_term_permissions( $taxonomy, $context, $term->term_id ); - } - - return wc_rest_check_product_term_permissions( $taxonomy, $context ); - } - - /** - * Get terms associated with a taxonomy. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error - */ - public function get_items( $request ) { - $taxonomy = $this->get_taxonomy( $request ); - $prepared_args = array( - 'exclude' => $request['exclude'], - 'include' => $request['include'], - 'order' => $request['order'], - 'orderby' => $request['orderby'], - 'product' => $request['product'], - 'hide_empty' => $request['hide_empty'], - 'number' => $request['per_page'], - 'search' => $request['search'], - 'slug' => $request['slug'], - ); - - if ( ! empty( $request['offset'] ) ) { - $prepared_args['offset'] = $request['offset']; - } else { - $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; - } - - $taxonomy_obj = get_taxonomy( $taxonomy ); - - if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) { - if ( 0 === $request['parent'] ) { - // Only query top-level terms. - $prepared_args['parent'] = 0; - } else { - if ( $request['parent'] ) { - $prepared_args['parent'] = $request['parent']; - } - } - } - - /** - * Filter the query arguments, before passing them to `get_terms()`. - * - * Enables adding extra arguments or setting defaults for a terms - * collection request. - * - * @see https://developer.wordpress.org/reference/functions/get_terms/ - * - * @param array $prepared_args Array of arguments to be - * passed to get_terms. - * @param WP_REST_Request $request The current request. - */ - $prepared_args = apply_filters( "woocommerce_rest_{$taxonomy}_query", $prepared_args, $request ); - - if ( ! empty( $prepared_args['product'] ) ) { - $query_result = $this->get_terms_for_product( $prepared_args, $request ); - $total_terms = $this->total_terms; - } else { - $query_result = get_terms( $taxonomy, $prepared_args ); - - $count_args = $prepared_args; - unset( $count_args['number'] ); - unset( $count_args['offset'] ); - $total_terms = wp_count_terms( $taxonomy, $count_args ); - - // Ensure we don't return results when offset is out of bounds. - // See https://core.trac.wordpress.org/ticket/35935. - if ( $prepared_args['offset'] && $prepared_args['offset'] >= $total_terms ) { - $query_result = array(); - } - - // wp_count_terms can return a falsy value when the term has no children. - if ( ! $total_terms ) { - $total_terms = 0; - } - } - $response = array(); - foreach ( $query_result as $term ) { - $data = $this->prepare_item_for_response( $term, $request ); - $response[] = $this->prepare_response_for_collection( $data ); - } - - $response = rest_ensure_response( $response ); - - // Store pagination values for headers then unset for count query. - $per_page = (int) $prepared_args['number']; - $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); - - $response->header( 'X-WP-Total', (int) $total_terms ); - $max_pages = ceil( $total_terms / $per_page ); - $response->header( 'X-WP-TotalPages', (int) $max_pages ); - - $base = str_replace( '(?P[\d]+)', $request['attribute_id'], $this->rest_base ); - $base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $base ) ); - if ( $page > 1 ) { - $prev_page = $page - 1; - if ( $prev_page > $max_pages ) { - $prev_page = $max_pages; - } - $prev_link = add_query_arg( 'page', $prev_page, $base ); - $response->link_header( 'prev', $prev_link ); - } - if ( $max_pages > $page ) { - $next_page = $page + 1; - $next_link = add_query_arg( 'page', $next_page, $base ); - $response->link_header( 'next', $next_link ); - } - - return $response; - } - - /** - * Create a single term for a taxonomy. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Request|WP_Error - */ - public function create_item( $request ) { - $taxonomy = $this->get_taxonomy( $request ); - $name = $request['name']; - $args = array(); - $schema = $this->get_item_schema(); - - if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) { - $args['description'] = $request['description']; - } - if ( isset( $request['slug'] ) ) { - $args['slug'] = $request['slug']; - } - if ( isset( $request['parent'] ) ) { - if ( ! is_taxonomy_hierarchical( $taxonomy ) ) { - return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) ); - } - $args['parent'] = $request['parent']; - } - - $term = wp_insert_term( $name, $taxonomy, $args ); - if ( is_wp_error( $term ) ) { - $error_data = array( 'status' => 400 ); - - // If we're going to inform the client that the term exists, - // give them the identifier they can actually use. - $term_id = $term->get_error_data( 'term_exists' ); - if ( $term_id ) { - $error_data['resource_id'] = $term_id; - } - - return new WP_Error( $term->get_error_code(), $term->get_error_message(), $error_data ); - } - - $term = get_term( $term['term_id'], $taxonomy ); - - $this->update_additional_fields_for_object( $term, $request ); - - // Add term data. - $meta_fields = $this->update_term_meta_fields( $term, $request ); - if ( is_wp_error( $meta_fields ) ) { - wp_delete_term( $term->term_id, $taxonomy ); - - return $meta_fields; - } - - /** - * Fires after a single term is created or updated via the REST API. - * - * @param WP_Term $term Inserted Term object. - * @param WP_REST_Request $request Request object. - * @param boolean $creating True when creating term, false when updating. - */ - do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, true ); - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_item_for_response( $term, $request ); - $response = rest_ensure_response( $response ); - $response->set_status( 201 ); - - $base = '/' . $this->namespace . '/' . $this->rest_base; - if ( ! empty( $request['attribute_id'] ) ) { - $base = str_replace( '(?P[\d]+)', (int) $request['attribute_id'], $base ); - } - - $response->header( 'Location', rest_url( $base . '/' . $term->term_id ) ); - - return $response; - } - - /** - * Get a single term from a taxonomy. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Request|WP_Error - */ - public function get_item( $request ) { - $taxonomy = $this->get_taxonomy( $request ); - $term = get_term( (int) $request['id'], $taxonomy ); - - if ( is_wp_error( $term ) ) { - return $term; - } - - $response = $this->prepare_item_for_response( $term, $request ); - - return rest_ensure_response( $response ); - } - - /** - * Update a single term from a taxonomy. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Request|WP_Error - */ - public function update_item( $request ) { - $taxonomy = $this->get_taxonomy( $request ); - $term = get_term( (int) $request['id'], $taxonomy ); - $schema = $this->get_item_schema(); - $prepared_args = array(); - - if ( isset( $request['name'] ) ) { - $prepared_args['name'] = $request['name']; - } - if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) { - $prepared_args['description'] = $request['description']; - } - if ( isset( $request['slug'] ) ) { - $prepared_args['slug'] = $request['slug']; - } - if ( isset( $request['parent'] ) ) { - if ( ! is_taxonomy_hierarchical( $taxonomy ) ) { - return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) ); - } - $prepared_args['parent'] = $request['parent']; - } - - // Only update the term if we haz something to update. - if ( ! empty( $prepared_args ) ) { - $update = wp_update_term( $term->term_id, $term->taxonomy, $prepared_args ); - if ( is_wp_error( $update ) ) { - return $update; - } - } - - $term = get_term( (int) $request['id'], $taxonomy ); - - $this->update_additional_fields_for_object( $term, $request ); - - // Update term data. - $meta_fields = $this->update_term_meta_fields( $term, $request ); - if ( is_wp_error( $meta_fields ) ) { - return $meta_fields; - } - - /** - * Fires after a single term is created or updated via the REST API. - * - * @param WP_Term $term Inserted Term object. - * @param WP_REST_Request $request Request object. - * @param boolean $creating True when creating term, false when updating. - */ - do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, false ); - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_item_for_response( $term, $request ); - return rest_ensure_response( $response ); - } - - /** - * Delete a single term from a taxonomy. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error - */ - public function delete_item( $request ) { - $taxonomy = $this->get_taxonomy( $request ); - $force = isset( $request['force'] ) ? (bool) $request['force'] : false; - - // We don't support trashing for this type, error out. - if ( ! $force ) { - return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) ); - } - - $term = get_term( (int) $request['id'], $taxonomy ); - // Get default category id. - $default_category_id = absint( get_option( 'default_product_cat', 0 ) ); - - // Prevent deleting the default product category. - if ( $default_category_id === (int) $request['id'] ) { - return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Default product category cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) ); - } - - $request->set_param( 'context', 'edit' ); - $response = $this->prepare_item_for_response( $term, $request ); - - $retval = wp_delete_term( $term->term_id, $term->taxonomy ); - if ( ! $retval ) { - return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) ); - } - - /** - * Fires after a single term is deleted via the REST API. - * - * @param WP_Term $term The deleted term. - * @param WP_REST_Response $response The response data. - * @param WP_REST_Request $request The request sent to the API. - */ - do_action( "woocommerce_rest_delete_{$taxonomy}", $term, $response, $request ); - - return $response; - } - - /** - * Prepare links for the request. - * - * @param object $term Term object. - * @param WP_REST_Request $request Full details about the request. - * @return array Links for the given term. - */ - protected function prepare_links( $term, $request ) { - $base = '/' . $this->namespace . '/' . $this->rest_base; - - if ( ! empty( $request['attribute_id'] ) ) { - $base = str_replace( '(?P[\d]+)', (int) $request['attribute_id'], $base ); - } - - $links = array( - 'self' => array( - 'href' => rest_url( trailingslashit( $base ) . $term->term_id ), - ), - 'collection' => array( - 'href' => rest_url( $base ), - ), - ); - - if ( $term->parent ) { - $parent_term = get_term( (int) $term->parent, $term->taxonomy ); - if ( $parent_term ) { - $links['up'] = array( - 'href' => rest_url( trailingslashit( $base ) . $parent_term->term_id ), - ); - } - } - - return $links; - } - - /** - * Update term meta fields. - * - * @param WP_Term $term Term object. - * @param WP_REST_Request $request Full details about the request. - * @return bool|WP_Error - */ - protected function update_term_meta_fields( $term, $request ) { - return true; - } - - /** - * Get the terms attached to a product. - * - * This is an alternative to `get_terms()` that uses `get_the_terms()` - * instead, which hits the object cache. There are a few things not - * supported, notably `include`, `exclude`. In `self::get_items()` these - * are instead treated as a full query. - * - * @param array $prepared_args Arguments for `get_terms()`. - * @param WP_REST_Request $request Full details about the request. - * @return array List of term objects. (Total count in `$this->total_terms`). - */ - protected function get_terms_for_product( $prepared_args, $request ) { - $taxonomy = $this->get_taxonomy( $request ); - - $query_result = get_the_terms( $prepared_args['product'], $taxonomy ); - if ( empty( $query_result ) ) { - $this->total_terms = 0; - return array(); - } - - // get_items() verifies that we don't have `include` set, and default. - // ordering is by `name`. - if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ), true ) ) { - switch ( $prepared_args['orderby'] ) { - case 'id': - $this->sort_column = 'term_id'; - break; - case 'slug': - case 'term_group': - case 'description': - case 'count': - $this->sort_column = $prepared_args['orderby']; - break; - } - usort( $query_result, array( $this, 'compare_terms' ) ); - } - if ( strtolower( $prepared_args['order'] ) !== 'asc' ) { - $query_result = array_reverse( $query_result ); - } - - // Pagination. - $this->total_terms = count( $query_result ); - $query_result = array_slice( $query_result, $prepared_args['offset'], $prepared_args['number'] ); - - return $query_result; - } - - /** - * Comparison function for sorting terms by a column. - * - * Uses `$this->sort_column` to determine field to sort by. - * - * @param stdClass $left Term object. - * @param stdClass $right Term object. - * @return int <0 if left is higher "priority" than right, 0 if equal, >0 if right is higher "priority" than left. - */ - protected function compare_terms( $left, $right ) { - $col = $this->sort_column; - $left_val = $left->$col; - $right_val = $right->$col; - - if ( is_int( $left_val ) && is_int( $right_val ) ) { - return $left_val - $right_val; - } - - return strcmp( $left_val, $right_val ); - } - - /** - * Get the query params for collections - * - * @return array - */ - public function get_collection_params() { - $params = parent::get_collection_params(); - - if ( '' !== $this->taxonomy && taxonomy_exists( $this->taxonomy ) ) { - $taxonomy = get_taxonomy( $this->taxonomy ); - } else { - $taxonomy = new stdClass(); - $taxonomy->hierarchical = true; - } - - $params['context']['default'] = 'view'; - - $params['exclude'] = array( - 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', - ); - $params['include'] = array( - 'description' => __( 'Limit result set to specific ids.', 'woocommerce' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', - ); - if ( ! $taxonomy->hierarchical ) { - $params['offset'] = array( - 'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - } - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_key', - 'default' => 'asc', - 'enum' => array( - 'asc', - 'desc', - ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by resource attribute.', 'woocommerce' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_key', - 'default' => 'name', - 'enum' => array( - 'id', - 'include', - 'name', - 'slug', - 'term_group', - 'description', - 'count', - ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['hide_empty'] = array( - 'description' => __( 'Whether to hide resources not assigned to any products.', 'woocommerce' ), - 'type' => 'boolean', - 'default' => false, - 'validate_callback' => 'rest_validate_request_arg', - ); - if ( $taxonomy->hierarchical ) { - $params['parent'] = array( - 'description' => __( 'Limit result set to resources assigned to a specific parent.', 'woocommerce' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - } - $params['product'] = array( - 'description' => __( 'Limit result set to resources assigned to a specific product.', 'woocommerce' ), - 'type' => 'integer', - 'default' => null, - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['slug'] = array( - 'description' => __( 'Limit result set to resources with a specific slug.', 'woocommerce' ), - 'type' => 'string', - 'validate_callback' => 'rest_validate_request_arg', - ); - - return $params; - } - - /** - * Get taxonomy. - * - * @param WP_REST_Request $request Full details about the request. - * @return int|WP_Error - */ - protected function get_taxonomy( $request ) { - // Check if taxonomy is defined. - // Prevents check for attribute taxonomy more than one time for each query. - if ( '' !== $this->taxonomy ) { - return $this->taxonomy; - } - - if ( ! empty( $request['attribute_id'] ) ) { - $taxonomy = wc_attribute_taxonomy_name_by_id( (int) $request['attribute_id'] ); - - $this->taxonomy = $taxonomy; - } - - return $this->taxonomy; - } -}