Make WordPress Core

Ticket #48885: patch.2.diff

File patch.2.diff, 11.8 KB (added by scruffian, 5 years ago)

Fix the update method

  • src/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php

     
    4141                        array(
    4242                                array(
    4343                                        'methods'             => WP_REST_Server::READABLE,
    44                                         'callback'            => array( $this, 'get_item' ),
     44                                        'callback'            => array( $this, 'get_items' ),
    4545                                        'args'                => array(),
    46                                         'permission_callback' => array( $this, 'get_item_permissions_check' ),
     46                                        'permission_callback' => array( $this, 'get_items_permissions_check' ),
    4747                                ),
    4848                                array(
    4949                                        'methods'             => WP_REST_Server::EDITABLE,
    50                                         'callback'            => array( $this, 'update_item' ),
     50                                        'callback'            => array( $this, 'update_items' ),
    5151                                        'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
     52                                        'permission_callback' => array( $this, 'get_items_permissions_check' ),
     53                                ),
     54                                'schema' => array( $this, 'get_public_item_schema' ),
     55                        )
     56                );
     57
     58                register_rest_route(
     59                        $this->namespace,
     60                        '/' . $this->rest_base . '/(?P<option>.+)',
     61                        array(
     62                                'args'   => array(
     63                                        'id' => array(
     64                                                'description' => __( 'The option to fetch.' ),
     65                                                'type'        => 'string',
     66                                        ),
     67                                ),
     68                                array(
     69                                        'methods'             => WP_REST_Server::READABLE,
     70                                        'callback'            => array( $this, 'get_item' ),
    5271                                        'permission_callback' => array( $this, 'get_item_permissions_check' ),
     72                                        'args'                => array(),
    5373                                ),
     74                                array(
     75                                        'methods'             => WP_REST_Server::EDITABLE,
     76                                        'callback'            => array( $this, 'update_item' ),
     77                                        'permission_callback' => array( $this, 'update_item_permissions_check' ),
     78                                        'args'                => array(),
     79                                ),
    5480                                'schema' => array( $this, 'get_public_item_schema' ),
    5581                        )
    5682                );
    57 
    5883        }
    5984
    6085        /**
     
    6590         * @param WP_REST_Request $request Full details about the request.
    6691         * @return bool True if the request has read access for the item, otherwise false.
    6792         */
     93        public function get_items_permissions_check( $request ) {
     94                return true;
     95                return current_user_can( 'manage_options' );
     96        }
     97
     98        /**
     99         * Checks if a given request has access to read a setting.
     100         *
     101         * @since 4.7.0
     102         *
     103         * @param WP_REST_Request $request Full details about the request.
     104         * @return bool True if the request has read access for the item, otherwise false.
     105         */
    68106        public function get_item_permissions_check( $request ) {
     107                // to make testing easier
     108                return true;
     109                return current_user_can( 'edit_posts' );
     110        }
     111
     112        /**
     113         * Checks if a given request has access to update a setting.
     114         *
     115         * @since 4.7.0
     116         *
     117         * @param WP_REST_Request $request Full details about the request.
     118         * @return bool True if the request has update access for the item, otherwise false.
     119         */
     120        public function update_item_permissions_check( $request ) {
    69121                return current_user_can( 'manage_options' );
    70122        }
    71123
     124
    72125        /**
     126         * Filters the value of a setting recognized by the REST API.
     127         *
     128         * Allow hijacking the setting value and overriding the built-in behavior by returning a
     129         * non-null value.  The returned value will be presented as the setting value instead.
     130         *
     131         * @since 4.7.0
     132         *
     133         * @param mixed  $result Value to use for the requested setting. Can be a scalar
     134         *                       matching the registered schema for the setting, or null to
     135         *                       follow the default get_option() behavior.
     136         * @param string $name   Setting name (as shown in REST API responses).
     137         * @param array  $args   Arguments passed to register_setting() for this setting.
     138         */
     139        private function get_filtered_setting( $name, $args ) {
     140                $response = apply_filters( 'rest_pre_get_setting', null, $name, $args );
     141
     142                if ( is_null( $response) ) {
     143                        // Default to a null value as "null" in the response means "not set".
     144                        $response = get_option( $args['option_name'], $args['schema']['default'] );
     145                }
     146
     147                /*
     148                        * Because get_option() is lossy, we have to
     149                        * cast values to the type they are registered with.
     150                        */
     151                return $this->prepare_value( $response, $args['schema'] );
     152        }
     153
     154        /**
    73155         * Retrieves the settings.
    74156         *
    75157         * @since 4.7.0
     
    77159         * @param WP_REST_Request $request Full details about the request.
    78160         * @return array|WP_Error Array on success, or WP_Error object on failure.
    79161         */
    80         public function get_item( $request ) {
     162        public function get_items( $request ) {
    81163                $options  = $this->get_registered_options();
    82164                $response = array();
    83165
    84166                foreach ( $options as $name => $args ) {
    85                         /**
    86                          * Filters the value of a setting recognized by the REST API.
    87                          *
    88                          * Allow hijacking the setting value and overriding the built-in behavior by returning a
    89                          * non-null value.  The returned value will be presented as the setting value instead.
    90                          *
    91                          * @since 4.7.0
    92                          *
    93                          * @param mixed  $result Value to use for the requested setting. Can be a scalar
    94                          *                       matching the registered schema for the setting, or null to
    95                          *                       follow the default get_option() behavior.
    96                          * @param string $name   Setting name (as shown in REST API responses).
    97                          * @param array  $args   Arguments passed to register_setting() for this setting.
    98                          */
    99                         $response[ $name ] = apply_filters( 'rest_pre_get_setting', null, $name, $args );
    100 
    101                         if ( is_null( $response[ $name ] ) ) {
    102                                 // Default to a null value as "null" in the response means "not set".
    103                                 $response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] );
    104                         }
    105 
    106                         /*
    107                          * Because get_option() is lossy, we have to
    108                          * cast values to the type they are registered with.
    109                          */
    110                         $response[ $name ] = $this->prepare_value( $response[ $name ], $args['schema'] );
     167                        $response[ $name ] = $this->get_filtered_setting( $name, $args );
    111168                }
    112169
    113170                return $response;
     
    135192        }
    136193
    137194        /**
     195         * Filters whether to preempt a setting value update.
     196         *
     197         * Allows hijacking the setting update logic and overriding the built-in behavior by
     198         * returning true.
     199         *
     200         * @since 4.7.0
     201         *
     202         * @param bool   $result Whether to override the default behavior for updating the
     203         *                       value of a setting.
     204         * @param string $name   Setting name (as shown in REST API responses).
     205         * @param mixed  $value  Updated setting value.
     206         * @param array  $args   Arguments passed to register_setting() for this setting.
     207         */
     208        private function filtered_update_option( $name, $value, $args ) {
     209                $updated = apply_filters( 'rest_pre_update_setting', false, $name, $value, $args );
     210
     211                if ( $updated ) {
     212                        return;
     213                }
     214
     215                /*
     216                * A null value for an option would have the same effect as
     217                * deleting the option from the database, and relying on the
     218                * default value.
     219                */
     220                if ( is_null( $value ) ) {
     221                        /*
     222                        * A null value is returned in the response for any option
     223                        * that has a non-scalar value.
     224                        *
     225                        * To protect clients from accidentally including the null
     226                        * values from a response object in a request, we do not allow
     227                        * options with values that don't pass validation to be updated to null.
     228                        * Without this added protection a client could mistakenly
     229                        * delete all options that have invalid values from the
     230                        * database.
     231                        */
     232                        if ( is_wp_error( rest_validate_value_from_schema( get_option( $args['option_name'], false ), $args['schema'] ) ) ) {
     233                                return new WP_Error(
     234                                        'rest_invalid_stored_value',
     235                                        /* translators: %s: Property name. */
     236                                        sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ),
     237                                        array( 'status' => 500 )
     238                                );
     239                        }
     240
     241                        delete_option( $args['option_name'] );
     242                } else {
     243                        update_option( $args['option_name'], $value );
     244                }
     245        }
     246
     247        /**
    138248         * Updates settings for the settings object.
    139249         *
    140250         * @since 4.7.0
     
    142252         * @param WP_REST_Request $request Full details about the request.
    143253         * @return array|WP_Error Array on success, or error object on failure.
    144254         */
    145         public function update_item( $request ) {
     255        public function update_items( $request ) {
    146256                $options = $this->get_registered_options();
    147257
    148258                $params = $request->get_params();
     
    152262                                continue;
    153263                        }
    154264
    155                         /**
    156                          * Filters whether to preempt a setting value update.
    157                          *
    158                          * Allows hijacking the setting update logic and overriding the built-in behavior by
    159                          * returning true.
    160                          *
    161                          * @since 4.7.0
    162                          *
    163                          * @param bool   $result Whether to override the default behavior for updating the
    164                          *                       value of a setting.
    165                          * @param string $name   Setting name (as shown in REST API responses).
    166                          * @param mixed  $value  Updated setting value.
    167                          * @param array  $args   Arguments passed to register_setting() for this setting.
    168                          */
    169                         $updated = apply_filters( 'rest_pre_update_setting', false, $name, $request[ $name ], $args );
    170 
    171                         if ( $updated ) {
    172                                 continue;
    173                         }
    174 
    175                         /*
    176                          * A null value for an option would have the same effect as
    177                          * deleting the option from the database, and relying on the
    178                          * default value.
    179                          */
    180                         if ( is_null( $request[ $name ] ) ) {
    181                                 /*
    182                                  * A null value is returned in the response for any option
    183                                  * that has a non-scalar value.
    184                                  *
    185                                  * To protect clients from accidentally including the null
    186                                  * values from a response object in a request, we do not allow
    187                                  * options with values that don't pass validation to be updated to null.
    188                                  * Without this added protection a client could mistakenly
    189                                  * delete all options that have invalid values from the
    190                                  * database.
    191                                  */
    192                                 if ( is_wp_error( rest_validate_value_from_schema( get_option( $args['option_name'], false ), $args['schema'] ) ) ) {
    193                                         return new WP_Error(
    194                                                 'rest_invalid_stored_value',
    195                                                 /* translators: %s: Property name. */
    196                                                 sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ),
    197                                                 array( 'status' => 500 )
    198                                         );
    199                                 }
    200 
    201                                 delete_option( $args['option_name'] );
    202                         } else {
    203                                 update_option( $args['option_name'], $request[ $name ] );
    204                         }
     265                        $this->filtered_update_option( $name, $request[ $name ], $args );
    205266                }
    206267
    207                 return $this->get_item( $request );
     268                return $this->get_items( $request );
    208269        }
    209270
    210271        /**
     
    345406
    346407                return $schema;
    347408        }
     409
     410        /**
     411         * Retrieves a single option
     412         *
     413         * @since ?
     414         *
     415         * @param WP_REST_Request $request Full details about the request.
     416         * @return array|WP_Error Array on success, or WP_Error object on failure.
     417         */
     418        public function get_item( $request ) {
     419                $name = $request['option'];
     420                $options  = $this->get_registered_options();
     421                if ( ! isset( $options[ $name ] ) ) {
     422                        return new WP_Error(
     423                                'rest_invalid_setting',
     424                                /* translators: %s: Settings name. */
     425                                sprintf( __( '%s is not a registered setting.' ), $name ),
     426                                array( 'status' => 500 )
     427                        );
     428                }
     429
     430                $response = array();
     431                $response[ $name ] = $this->get_filtered_setting( $name, $options[ $name ] );
     432                return $response;
     433        }
     434
     435        /**
     436         * Updates a given setting
     437         *
     438         * @since 4.7.0
     439         *
     440         * @param WP_REST_Request $request Full details about the request.
     441         * @return array|WP_Error Array on success, or error object on failure.
     442         */
     443        public function update_item( $request ) {
     444                $name = $request['option'];
     445                $options = $this->get_registered_options();
     446
     447                $params = $request->get_params();
     448
     449                if ( ! array_key_exists( $name, $params ) ) {
     450                        return new WP_Error(
     451                                'rest_invalid_setting',
     452                                /* translators: %s: Settings name. */
     453                                sprintf( __( '%s is not a registered setting.' ), $name ),
     454                                array( 'status' => 500 )
     455                        );
     456                }
     457
     458                $this->filtered_update_option( $name, $request[ $name ], $options[ $name ] );
     459
     460                return $this->get_item( $request );
     461        }
    348462}