*O*>䴾+[UO^5XY;aM_f ]t,tkNV n}F!XܯVcD*ł@:.REJ^my^M1F k_oZD2c :mW j&4o7$H@y+-.T,;] s+8az*^. 5-2F`|kw>&\ٝ'7M SyоPcv]?r'ȑ*գp=(Rl.CLsYlLu#ڏO} )FHj AxƉGXN:ۻj_i[Ppb9EyDy/{!1B.D7xnd(sq|ϧ7)R>:,]bDDRxFbRtNڤr֬#U{Sn)a"@goEև.5"'a~bV鿊LK)2K(Vx) <#zާcLׯܲhv &qo %:fvύp}U叠z.tU;%8 |=p["}ߝ6 pje]X_L@ḼJqOu@FtWg{p"7$whLqRyݙ,/5j#Mѱg[9 #<'v 畠i_Ӱ=ZlWUd^*cn}gp8in+cH H(w]auε酪=Hu K ّAdި||W8K[i#z[w.lAlnPiPO2elGcB8\5ۺb/6HOihnAHP2(]%f3MڮsLLm*l#l^-R28BWhgqչR`F;NVe`9L5,J*fle8+r tD zHzx̂*E RnH[.;vaL )EZX] Uv״~kWƺoҲJ' S{CdҐջ7mBs; l #(졑)~V0b$e<7)XG6pȡ|d(T+$TK t$ L-+5Ú)%3IaTq<oL2+LF.;ׯqOf ¼.Pz1Wlzz,6F38 ̀m5kl|kBeC4> ;1'cV󛔺ɋ^`)sG5R|) +k{ғgԨm"U j~R+B52NwZ1r;xy]ƾm )VO ҬHcm^b-VuZv.K)j]wf7KK@0@wi_>/Az!+ ՜"[/t~x)S[Wv@,ϪȲL?n5J8ŽjB&SX;#S&Ə;L0k{`^kDhhtrG' !52j"_)S!Y9tPhT ([B}el„LLJ'S4F$7JQ*{su޾8_$tNk!t8+WZS3Y,m$׋@aԾ;5΅$T0 NWk4R%L$ ؝ 5zUJ^8lRl1, ;KP0_a7yȍD?E#p fw~ eBW}a!՚=\gxK7"! Q{1 ='l-X6"?`%aaܬ<8)?Cbƅ|8::W8lͳ֊$ O`,3sU|#oNdCiJu(]-yC=H' ^;u>\C W\HoA%":n/t D߹g,ϿgZY얝{ t!uLN4PN4L05V= ~̝ ="c=&I, n<ܘ祧q=>c6jkqmkN a_&”P1rgwmwF(i"nqI^.|mCΝTV|߼Q d+DcnDЮjTyC"z&!/rɲmEuC' 4Pۃ bCeJƾ%5|7 (,qEG}1\?fcJ_. uCe-j51ޗ_pj:,Q#܏4*B{ oh!hʘ#g۬׎0%ysu`bO͋ >歨Ѯ1j>l ! c o-ܾ@d W2n}ƞ.SǧwR;t=bRi q12E)*F$2jIL3b:obd߳dY,8Tt+kءGkM}eR"?pH:+$ ]l{\lnkHmL f*X Or@3=mڠQVyuum !NZ9H^pJ> s+ J"%ЛuFUw:Qαa%&骏)>z_ej0:ZL}=KhVXfzNko3ŞU P/Oa)N(t I#؞}bih,XN͕_BϠh_(&nv3mc<#ZU?R!=%M9/V=!GAPjs5O)$75,FGΣքiHgRNͤNH͢ lJ.lZ 2=Q):M$R6Afn:sG jZBơԀIþ:|w=ۡOBL6e:?klz/Kycudd(!`!2z%=I`$T9y<5&!jΙz}8muc:24vKN.nCw{sX4a\i_]щ&2[ update a specific item. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise. */ public function update_item_permissions_check( $request ) { return new WP_Error( 'invalid-method', /* translators: %s: Method name. */ sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** * Updates one item from the collection. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function update_item( $request ) { return new WP_Error( 'invalid-method', /* translators: %s: Method name. */ sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** * Checks if a given request has access to delete a specific item. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. */ public function delete_item_permissions_check( $request ) { return new WP_Error( 'invalid-method', /* translators: %s: Method name. */ sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** * Deletes one item from the collection. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function delete_item( $request ) { return new WP_Error( 'invalid-method', /* translators: %s: Method name. */ sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** * Prepares one item for create or update operation. * * @since 4.7.0 * * @param WP_REST_Request $request Request object. * @return object|WP_Error The prepared item, or WP_Error object on failure. */ protected function prepare_item_for_database( $request ) { return new WP_Error( 'invalid-method', /* translators: %s: Method name. */ sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** * Prepares the item for the REST response. * * @since 4.7.0 * * @param mixed $item WordPress representation of the item. * @param WP_REST_Request $request Request object. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function prepare_item_for_response( $item, $request ) { return new WP_Error( 'invalid-method', /* translators: %s: Method name. */ sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** * Prepares a response for insertion into a collection. * * @since 4.7.0 * * @param WP_REST_Response $response Response object. * @return array|mixed Response data, ready for insertion into collection data. */ public function prepare_response_for_collection( $response ) { if ( ! ( $response instanceof WP_REST_Response ) ) { return $response; } $data = (array) $response->get_data(); $server = rest_get_server(); $links = $server::get_compact_response_links( $response ); if ( ! empty( $links ) ) { $data['_links'] = $links; } return $data; } /** * Filters a response based on the context defined in the schema. * * @since 4.7.0 * * @param array $data Response data to filter. * @param string $context Context defined in the schema. * @return array Filtered response. */ public function filter_response_by_context( $data, $context ) { $schema = $this->get_item_schema(); return rest_filter_response_by_context( $data, $schema, $context ); } /** * Retrieves the item's schema, conforming to JSON Schema. * * @since 4.7.0 * * @return array Item schema data. */ public function get_item_schema() { return $this->add_additional_fields_schema( array() ); } /** * Retrieves the item's schema for display / public consumption purposes. * * @since 4.7.0 * * @return array Public item schema data. */ public function get_public_item_schema() { $schema = $this->get_item_schema(); if ( ! empty( $schema['properties'] ) ) { foreach ( $schema['properties'] as &$property ) { unset( $property['arg_options'] ); } } return $schema; } /** * Retrieves the query params for the collections. * * @since 4.7.0 * * @return array Query parameters for the collection. */ public function get_collection_params() { return array( 'context' => $this->get_context_param(), 'page' => array( 'description' => __( 'Current page of the collection.' ), 'type' => 'integer', 'default' => 1, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', 'minimum' => 1, ), 'per_page' => array( 'description' => __( 'Maximum number of items to be returned in result set.' ), 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 100, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ), 'search' => array( 'description' => __( 'Limit results to those matching a string.' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'validate_callback' => 'rest_validate_request_arg', ), ); } /** * Retrieves the magical context param. * * Ensures consistent descriptions between endpoints, and populates enum from schema. * * @since 4.7.0 * * @param array $args Optional. Additional arguments for context parameter. Default empty array. * @return array Context parameter details. */ public function get_context_param( $args = array() ) { $param_details = array( 'description' => __( 'Scope under which the request is made; determines fields present in response.' ), 'type' => 'string', 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', ); $schema = $this->get_item_schema(); if ( empty( $schema['properties'] ) ) { return array_merge( $param_details, $args ); } $contexts = array(); foreach ( $schema['properties'] as $attributes ) { if ( ! empty( $attributes['context'] ) ) { $contexts = array_merge( $contexts, $attributes['context'] ); } } if ( ! empty( $contexts ) ) { $param_details['enum'] = array_unique( $contexts ); rsort( $param_details['enum'] ); } return array_merge( $param_details, $args ); } /** * Adds the values from additional fields to a data object. * * @since 4.7.0 * * @param array $prepared Prepared response array. * @param WP_REST_Request $request Full details about the request. * @return array Modified data object with additional fields. */ protected function add_additional_fields_to_object( $prepared, $request ) { $additional_fields = $this->get_additional_fields(); $requested_fields = $this->get_fields_for_response( $request ); foreach ( $additional_fields as $field_name => $field_options ) { if ( ! $field_options['get_callback'] ) { continue; } if ( ! rest_is_field_included( $field_name, $requested_fields ) ) { continue; } $prepared[ $field_name ] = call_user_func( $field_options['get_callback'], $prepared, $field_name, $request, $this->get_object_type() ); } return $prepared; } /** * Updates the values of additional fields added to a data object. * * @since 4.7.0 * * @param object $object Data model like WP_Term or WP_Post. * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True on success, WP_Error object if a field cannot be updated. */ protected function update_additional_fields_for_object( $object, $request ) { $additional_fields = $this->get_additional_fields(); foreach ( $additional_fields as $field_name => $field_options ) { if ( ! $field_options['update_callback'] ) { continue; } // Don't run the update callbacks if the data wasn't passed in the request. if ( ! isset( $request[ $field_name ] ) ) { continue; } $result = call_user_func( $field_options['update_callback'], $request[ $field_name ], $object, $field_name, $request, $this->get_object_type() ); if ( is_wp_error( $result ) ) { return $result; } } return true; } /** * Adds the schema from additional fields to a schema array. * * The type of object is inferred from the passed schema. * * @since 4.7.0 * * @param array $schema Schema array. * @return array Modified Schema array. */ protected function add_additional_fields_schema( $schema ) { if ( empty( $schema['title'] ) ) { return $schema; } // Can't use $this->get_object_type otherwise we cause an inf loop. $object_type = $schema['title']; $additional_fields = $this->get_additional_fields( $object_type ); foreach ( $additional_fields as $field_name => $field_options ) { if ( ! $field_options['schema'] ) { continue; } $schema['properties'][ $field_name ] = $field_options['schema']; } return $schema; } /** * Retrieves all of the registered additional fields for a given object-type. * * @since 4.7.0 * * @global array $wp_rest_additional_fields Holds registered fields, organized by object type. * * @param string $object_type Optional. The object type. * @return array Registered additional fields (if any), empty array if none or if the object type * could not be inferred. */ protected function get_additional_fields( $object_type = null ) { global $wp_rest_additional_fields; if ( ! $object_type ) { $object_type = $this->get_object_type(); } if ( ! $object_type ) { return array(); } if ( ! $wp_rest_additional_fields || ! isset( $wp_rest_additional_fields[ $object_type ] ) ) { return array(); } return $wp_rest_additional_fields[ $object_type ]; } /** * Retrieves the object type this controller is responsible for managing. * * @since 4.7.0 * * @return string Object type for the controller. */ protected function get_object_type() { $schema = $this->get_item_schema(); if ( ! $schema || ! isset( $schema['title'] ) ) { return null; } return $schema['title']; } /** * Gets an array of fields to be included on the response. * * Included fields are based on item schema and `_fields=` request argument. * * @since 4.9.6 * * @param WP_REST_Request $request Full details about the request. * @return string[] Fields to be included in the response. */ public function get_fields_for_response( $request ) { $schema = $this->get_item_schema(); $properties = isset( $schema['properties'] ) ? $schema['properties'] : array(); $additional_fields = $this->get_additional_fields(); foreach ( $additional_fields as $field_name => $field_options ) { // For back-compat, include any field with an empty schema // because it won't be present in $this->get_item_schema(). if ( is_null( $field_options['schema'] ) ) { $properties[ $field_name ] = $field_options; } } // Exclude fields that specify a different context than the request context. $context = $request['context']; if ( $context ) { foreach ( $properties as $name => $options ) { if ( ! empty( $options['context'] ) && ! in_array( $context, $options['context'], true ) ) { unset( $properties[ $name ] ); } } } $fields = array_keys( $properties ); if ( ! isset( $request['_fields'] ) ) { return $fields; } $requested_fields = wp_parse_list( $request['_fields'] ); if ( 0 === count( $requested_fields ) ) { return $fields; } // Trim off outside whitespace from the comma delimited list. $requested_fields = array_map( 'trim', $requested_fields ); // Always persist 'id', because it can be needed for add_additional_fields_to_object(). if ( in_array( 'id', $fields, true ) ) { $requested_fields[] = 'id'; } // Return the list of all requested fields which appear in the schema. return array_reduce( $requested_fields, static function( $response_fields, $field ) use ( $fields ) { if ( in_array( $field, $fields, true ) ) { $response_fields[] = $field; return $response_fields; } // Check for nested fields if $field is not a direct match. $nested_fields = explode( '.', $field ); // A nested field is included so long as its top-level property // is present in the schema. if ( in_array( $nested_fields[0], $fields, true ) ) { $response_fields[] = $field; } return $response_fields; }, array() ); } /** * Retrieves an array of endpoint arguments from the item schema for the controller. * * @since 4.7.0 * * @param string $method Optional. HTTP method of the request. The arguments for `CREATABLE` requests are * checked for required values and may fall-back to a given default, this is not done * on `EDITABLE` requests. Default WP_REST_Server::CREATABLE. * @return array Endpoint arguments. */ public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) { return rest_get_endpoint_args_for_schema( $this->get_item_schema(), $method ); } /** * Sanitizes the slug value. * * @since 4.7.0 * * @internal We can't use sanitize_title() directly, as the second * parameter is the fallback title, which would end up being set to the * request object. * * @see https://github.com/WP-API/WP-API/issues/1585 * * @todo Remove this in favour of https://core.trac.wordpress.org/ticket/34659 * * @param string $slug Slug value passed in request. * @return string Sanitized value for the slug. */ public function sanitize_slug( $slug ) { return sanitize_title( $slug ); } }