Make WordPress Core


Ignore:
Timestamp:
11/09/2016 07:02:53 AM (9 years ago)
Author:
westonruter
Message:

Customize: Store modifying user ID with setting change written into changeset and restore current user when setting is being saved.

Restoring the current user context when saving a setting ensures filters apply as expected, such as Kses. When a user is not associated with a given setting change, continue to override capability to be exist when saving. Skip overwriting setting values in a changeset that have not changed, facilitating concurrent editing and amending a changeset by a user with fewer privileges.

See #30937.
Fixes #38705.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r39175 r39181  
    18281828     *     @type string $title    Post title. Optional.
    18291829     *     @type string $date_gmt Date in GMT. Optional.
     1830     *     @type int    $user_id  ID for user who is saving the changeset. Optional, defaults to the current user ID.
    18301831     * }
    18311832     *
     
    18401841                'data' => array(),
    18411842                'date_gmt' => null,
     1843                'user_id' => get_current_user_id(),
    18421844            ),
    18431845            $args
     
    18451847
    18461848        $changeset_post_id = $this->changeset_post_id();
     1849        $existing_changeset_data = array();
     1850        if ( $changeset_post_id ) {
     1851            $existing_changeset_data = $this->get_changeset_post_data( $changeset_post_id );
     1852        }
    18471853
    18481854        // The request was made via wp.customize.previewer.save().
     
    18631869        ) );
    18641870        $this->add_dynamic_settings( array_keys( $post_values ) ); // Ensure settings get created even if they lack an input value.
     1871
     1872        /*
     1873         * Get list of IDs for settings that have values different from what is currently
     1874         * saved in the changeset. By skipping any values that are already the same, the
     1875         * subset of changed settings can be passed into validate_setting_values to prevent
     1876         * an underprivileged modifying a single setting for which they have the capability
     1877         * from being blocked from saving. This also prevents a user from touching of the
     1878         * previous saved settings and overriding the associated user_id if they made no change.
     1879         */
     1880        $changed_setting_ids = array();
     1881        foreach ( $post_values as $setting_id => $setting_value ) {
     1882            $setting = $this->get_setting( $setting_id );
     1883
     1884            if ( $setting && 'theme_mod' === $setting->type ) {
     1885                $prefixed_setting_id = $this->get_stylesheet() . '::' . $setting->id;
     1886            } else {
     1887                $prefixed_setting_id = $setting_id;
     1888            }
     1889
     1890            $is_value_changed = (
     1891                ! isset( $existing_changeset_data[ $prefixed_setting_id ] )
     1892                ||
     1893                ! array_key_exists( 'value', $existing_changeset_data[ $prefixed_setting_id ] )
     1894                ||
     1895                $existing_changeset_data[ $prefixed_setting_id ]['value'] !== $setting_value
     1896            );
     1897            if ( $is_value_changed ) {
     1898                $changed_setting_ids[] = $setting_id;
     1899            }
     1900        }
     1901        $post_values = wp_array_slice_assoc( $post_values, $changed_setting_ids );
    18651902
    18661903        /**
     
    19441981                    $data[ $changeset_setting_id ],
    19451982                    $setting_params,
    1946                     array( 'type' => $setting->type )
     1983                    array(
     1984                        'type' => $setting->type,
     1985                        'user_id' => $args['user_id'],
     1986                    )
    19471987                );
    19481988            }
     
    21222162        $this->_changeset_data      = $publishing_changeset_data;
    21232163
    2124         // Ensure that other theme mods are stashed.
    2125         $other_theme_mod_settings = array();
    2126         if ( did_action( 'switch_theme' ) ) {
    2127             $namespace_pattern = '/^(?P<stylesheet>.+?)::(?P<setting_id>.+)$/';
    2128             $matches = array();
    2129             foreach ( $this->_changeset_data as $raw_setting_id => $setting_params ) {
    2130                 $is_other_theme_mod = (
    2131                     isset( $setting_params['value'] )
    2132                     &&
    2133                     isset( $setting_params['type'] )
    2134                     &&
    2135                     'theme_mod' === $setting_params['type']
    2136                     &&
    2137                     preg_match( $namespace_pattern, $raw_setting_id, $matches )
    2138                     &&
    2139                     $this->get_stylesheet() !== $matches['stylesheet']
    2140                 );
    2141                 if ( $is_other_theme_mod ) {
    2142                     if ( ! isset( $other_theme_mod_settings[ $matches['stylesheet'] ] ) ) {
    2143                         $other_theme_mod_settings[ $matches['stylesheet'] ] = array();
    2144                     }
    2145                     $other_theme_mod_settings[ $matches['stylesheet'] ][ $matches['setting_id'] ] = $setting_params;
     2164        // Parse changeset data to identify theme mod settings and user IDs associated with settings to be saved.
     2165        $setting_user_ids = array();
     2166        $theme_mod_settings = array();
     2167        $namespace_pattern = '/^(?P<stylesheet>.+?)::(?P<setting_id>.+)$/';
     2168        $matches = array();
     2169        foreach ( $this->_changeset_data as $raw_setting_id => $setting_params ) {
     2170            $actual_setting_id = null;
     2171            $is_theme_mod_setting = (
     2172                isset( $setting_params['value'] )
     2173                &&
     2174                isset( $setting_params['type'] )
     2175                &&
     2176                'theme_mod' === $setting_params['type']
     2177                &&
     2178                preg_match( $namespace_pattern, $raw_setting_id, $matches )
     2179            );
     2180            if ( $is_theme_mod_setting ) {
     2181                if ( ! isset( $theme_mod_settings[ $matches['stylesheet'] ] ) ) {
     2182                    $theme_mod_settings[ $matches['stylesheet'] ] = array();
    21462183                }
     2184                $theme_mod_settings[ $matches['stylesheet'] ][ $matches['setting_id'] ] = $setting_params;
     2185
     2186                if ( $this->get_stylesheet() === $matches['stylesheet'] ) {
     2187                    $actual_setting_id = $matches['setting_id'];
     2188                }
     2189            } else {
     2190                $actual_setting_id = $raw_setting_id;
     2191            }
     2192
     2193            // Keep track of the user IDs for settings actually for this theme.
     2194            if ( $actual_setting_id && isset( $setting_params['user_id'] ) ) {
     2195                $setting_user_ids[ $actual_setting_id ] = $setting_params['user_id'];
    21472196            }
    21482197        }
     
    21742223        foreach ( $changeset_setting_ids as $setting_id ) {
    21752224            $setting = $this->get_setting( $setting_id );
    2176             if ( $setting ) {
     2225            if ( $setting && ! isset( $setting_user_ids[ $setting_id ] ) ) {
    21772226                $original_setting_capabilities[ $setting->id ] = $setting->capability;
    21782227                $setting->capability = 'exist';
     
    21802229        }
    21812230
     2231        $original_user_id = get_current_user_id();
    21822232        foreach ( $changeset_setting_ids as $setting_id ) {
    21832233            $setting = $this->get_setting( $setting_id );
    21842234            if ( $setting ) {
     2235                /*
     2236                 * Set the current user to match the user who saved the value into
     2237                 * the changeset so that any filters that apply during the save
     2238                 * process will respect the original user's capabilities. This
     2239                 * will ensure, for example, that KSES won't strip unsafe HTML
     2240                 * when a scheduled changeset publishes via WP Cron.
     2241                 */
     2242                if ( isset( $setting_user_ids[ $setting_id ] ) ) {
     2243                    wp_set_current_user( $setting_user_ids[ $setting_id ] );
     2244                } else {
     2245                    wp_set_current_user( $original_user_id );
     2246                }
     2247
    21852248                $setting->save();
    21862249            }
    21872250        }
     2251        wp_set_current_user( $original_user_id );
    21882252
    21892253        // Update the stashed theme mod settings, removing the active theme's stashed settings, if activated.
    21902254        if ( did_action( 'switch_theme' ) ) {
     2255            $other_theme_mod_settings = $theme_mod_settings;
     2256            unset( $other_theme_mod_settings[ $this->get_stylesheet() ] );
    21912257            $this->update_stashed_theme_mod_settings( $other_theme_mod_settings );
    21922258        }
Note: See TracChangeset for help on using the changeset viewer.