Editor: Persist preferences in user meta.

Adds a new feature to persist editor UI preferences between page loads and browsers.

* Adds a new preferences persistence API.
* Saves editor preferences in user meta instead of in browser's local storage.

Why?
Due to the transient nature of browser storage, this persistence is not as sticky as it is expected to be, including: switching browsers (unique storage between browsers), or using private browsing tabs (storage cleared between sessions), or the same user across a network of sites (storage unique by domain).

This is a backport from Gutenberg.[https://github.com/WordPress/gutenberg/pull/39795 See WordPress/gutenberg PR 39795].

Props talldanwp, youknowriad, noisysocks, mamaduka, costdev, ironprogrammer, hellofromTonya.
See #56467.

git-svn-id: https://develop.svn.wordpress.org/trunk@54182 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Tonya Mork 2022-09-15 16:43:39 +00:00
parent 2b4d385298
commit fc6bc030d4
7 changed files with 3542 additions and 147 deletions

3540
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -116,7 +116,8 @@
"@wordpress/notices": "3.6.1",
"@wordpress/nux": "5.4.5",
"@wordpress/plugins": "4.4.3",
"@wordpress/preferences": "1.2.5",
"@wordpress/preferences": "2.8.0",
"@wordpress/preferences-persistence": "1.8.0",
"@wordpress/primitives": "3.4.1",
"@wordpress/priority-queue": "2.6.1",
"@wordpress/redux-routine": "4.6.1",

File diff suppressed because one or more lines are too long

View File

@ -683,4 +683,7 @@ add_action( 'wp_footer', 'the_block_template_skip_link' );
add_action( 'setup_theme', 'wp_enable_block_templates' );
add_action( 'wp_loaded', '_add_template_loader_filters' );
// User preferences.
add_action( 'init', 'wp_register_persisted_preferences_meta' );
unset( $filter, $action );

View File

@ -294,6 +294,9 @@ function wp_default_packages_scripts( $scripts ) {
case 'wp-edit-post':
array_push( $dependencies, 'media-models', 'media-views', 'postbox', 'wp-dom-ready' );
break;
case 'wp-preferences':
array_push( $dependencies, 'wp-preferences-persistence' );
break;
}
$scripts->add( $handle, $path, $dependencies, $package_data['version'], 1 );
@ -324,11 +327,12 @@ function wp_default_packages_scripts( $scripts ) {
* @since 5.0.0
*
* @global WP_Locale $wp_locale WordPress date and time locale object.
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param WP_Scripts $scripts WP_Scripts object.
*/
function wp_default_packages_inline_scripts( $scripts ) {
global $wp_locale;
global $wp_locale, $wpdb;
if ( isset( $scripts->registered['wp-api-fetch'] ) ) {
$scripts->registered['wp-api-fetch']->deps[] = 'wp-hooks';
@ -360,19 +364,22 @@ function wp_default_packages_inline_scripts( $scripts ) {
),
'after'
);
$meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences';
$user_id = get_current_user_ID();
$preload_data = get_user_meta( $user_id, $meta_key, true );
$scripts->add_inline_script(
'wp-data',
implode(
"\n",
array(
'( function() {',
' var userId = ' . get_current_user_ID() . ';',
' var storageKey = "WP_DATA_USER_" + userId;',
' wp.data',
' .use( wp.data.plugins.persistence, { storageKey: storageKey } );',
' wp.data.plugins.persistence.__unstableMigrate( { storageKey: storageKey } );',
'} )();',
)
'wp-preferences',
sprintf(
'( function() {
var serverData = %s;
var userId = "%d";
var persistenceLayer = wp.preferencesPersistence.__unstableCreatePersistenceLayer( serverData, userId );
var preferencesStore = wp.preferences.store;
wp.data.dispatch( preferencesStore ).setPersistenceLayer( persistenceLayer );
} ) ();',
wp_json_encode( $preload_data ),
$user_id
)
);

View File

@ -4951,3 +4951,50 @@ function wp_is_application_passwords_available_for_user( $user ) {
*/
return apply_filters( 'wp_is_application_passwords_available_for_user', true, $user );
}
/**
* Registers the user meta property for persisted preferences.
*
* This property is used to store user preferences across page reloads and is
* currently used by the block editor for preferences like 'fullscreenMode' and
* 'fixedToolbar'.
*
* @since 6.1.0
* @access private
*
* @global wpdb $wpdb WordPress database abstraction object.
*/
function wp_register_persisted_preferences_meta() {
/*
* Create a meta key that incorporates the blog prefix so that each site
* on a multisite can have distinct user preferences.
*/
global $wpdb;
$meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences';
register_meta(
'user',
$meta_key,
array(
'type' => 'object',
'single' => true,
'show_in_rest' => array(
'name' => 'persisted_preferences',
'type' => 'object',
'context' => array( 'edit' ),
'schema' => array(
'type' => 'object',
'properties' => array(
'_modified' => array(
'description' => __( 'The date and time the preferences were updated.' ),
'type' => 'string',
'format' => 'date-time',
'readonly' => false,
),
),
'additionalProperties' => true,
),
),
)
);
}

View File

@ -0,0 +1,61 @@
<?php
/**
* @group user
*
* @covers ::wp_register_persisted_preferences_meta
*/
class Tests_User_WpRegisterPersistedPreferencesMeta extends WP_UnitTestCase {
/**
* Test that user persisted preferences meta is registered.
*
* @ticket 56467
*/
public function test_should_register_persisted_preferences_meta() {
global $wpdb, $wp_meta_keys;
$meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences';
// Test that meta key is registered.
unregister_meta_key( 'user', $meta_key );
wp_register_persisted_preferences_meta();
$this->assertIsArray( $wp_meta_keys, 'No meta keys exist' );
$this->assertArrayHasKey(
$meta_key,
$wp_meta_keys['user'][''],
'The expected meta key was not registered'
);
// Test to detect changes in meta key structure.
$this->assertSame(
array(
'type' => 'object',
'description' => '',
'single' => true,
'sanitize_callback' => null,
'auth_callback' => '__return_true',
'show_in_rest' => array(
'name' => 'persisted_preferences',
'type' => 'object',
'context' => array( 'edit' ),
'schema' => array(
'type' => 'object',
'properties' => array(
'_modified' => array(
'description' => __( 'The date and time the preferences were updated.' ),
'type' => 'string',
'format' => 'date-time',
'readonly' => false,
),
),
'additionalProperties' => true,
),
),
),
$wp_meta_keys['user'][''][ $meta_key ],
'The registered metadata did not have the expected structure'
);
}
}