Skip to content

Commit

Permalink
Display settings 'label' defined by the 'register_setting' method (Wo…
Browse files Browse the repository at this point in the history
…rdPress#59243)

* Try: Add 'label' argument to the 'register_setting' method
* Use title keyword
* Fix route override
* Use just a filter
* Update getOrLoadEntitiesConfig
* Temp update selectors
* Finalize first iteration
* Fix unit test
* Feedback
* Restore site entity shortcuts
* Update 'getOrLoadEntitiesConfig' doc-block

Unlinked contributors: itsmewatermelon.

Co-authored-by: Mamaduka <[email protected]>
Co-authored-by: ramonjd <[email protected]>
Co-authored-by: tyxla <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: adamziel <[email protected]>
Co-authored-by: richtabor <[email protected]>
  • Loading branch information
7 people authored and carstingaxion committed Mar 27, 2024
1 parent 6ff0c17 commit ff1a5e0
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 76 deletions.
50 changes: 50 additions & 0 deletions lib/compat/wordpress-6.6/option.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* Options API changes for the Gutenberg plugin.
*
* @package gutenberg
*/

/**
* Updates arguments for default settings available in WordPress.
*
* Note: During the core sync, the updates will be made to `register_initial_settings`.
*/
function gutenberg_update_initial_settings( $args, $defaults, $option_group, $option_name ) {
$settings_label_map = array(
'blogname' => __( 'Title' ),
'blogdescription' => __( 'Tagline' ),
'site_logo' => __( 'Logo' ),
'site_icon' => __( 'Icon' ),
'show_on_front' => __( 'Show on front' ),
'page_on_front' => __( 'Page on front' ),
'posts_per_page' => __( 'Maximum posts per page' ),
'default_comment_status' => __( 'Allow comments on new posts' ),
);

if ( isset( $settings_label_map[ $option_name ] ) ) {
$args['label'] = $settings_label_map[ $option_name ];
}

// Don't update schema when label isn't provided.
if ( ! isset( $args['label'] ) ) {
return $args;
}

$schema = array( 'title' => $args['label'] );
if ( ! is_array( $args['show_in_rest'] ) ) {
$args['show_in_rest'] = array(
'schema' => $schema,
);
return $args;
}

if ( ! empty( $args['show_in_rest']['schema'] ) ) {
$args['show_in_rest']['schema'] = array_merge( $args['show_in_rest']['schema'], $schema );
} else {
$args['show_in_rest']['schema'] = $schema;
}

return $args;
}
add_filter( 'register_setting_args', 'gutenberg_update_initial_settings', 10, 4 );
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ function gutenberg_is_experiment_enabled( $name ) {

// WordPress 6.6 compat.
require __DIR__ . '/compat/wordpress-6.6/block-bindings/pattern-overrides.php';
require __DIR__ . '/compat/wordpress-6.6/option.php';

// Experimental features.
require __DIR__ . '/experimental/block-editor-settings-mobile.php';
Expand Down
10 changes: 5 additions & 5 deletions packages/core-data/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export const deleteEntityRecord =
{ __unstableFetch = apiFetch, throwOnError = false } = {}
) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -503,7 +503,7 @@ export const saveEntityRecord =
} = {}
) =>
async ( { select, resolveSelect, dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -780,7 +780,7 @@ export const saveEditedEntityRecord =
if ( ! select.hasEditsForEntityRecord( kind, name, recordId ) ) {
return;
}
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -824,7 +824,7 @@ export const __experimentalSaveSpecifiedEntityEdits =
setNestedValue( editsToSave, item, getNestedValue( edits, item ) );
}

const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down Expand Up @@ -948,7 +948,7 @@ export function receiveDefaultTemplateId( query, templateId ) {
export const receiveRevisions =
( kind, name, recordKey, records, query, invalidateCache = false, meta ) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.kind === kind && config.name === name
);
Expand Down
108 changes: 71 additions & 37 deletions packages/core-data/src/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,36 +61,6 @@ export const rootEntitiesConfig = [
syncObjectType: 'root/base',
getSyncObjectId: () => 'index',
},
{
label: __( 'Site' ),
name: 'site',
kind: 'root',
baseURL: '/wp/v2/settings',
// The entity doesn't support selecting multiple records.
// The property is maintained for backward compatibility.
plural: 'sites',
getTitle: ( record ) => {
return record?.title ?? __( 'Site Title' );
},
syncConfig: {
fetch: async () => {
return apiFetch( { path: '/wp/v2/settings' } );
},
applyChangesToDoc: ( doc, changes ) => {
const document = doc.getMap( 'document' );
Object.entries( changes ).forEach( ( [ key, value ] ) => {
if ( document.get( key ) !== value ) {
document.set( key, value );
}
} );
},
fromCRDTDoc: ( doc ) => {
return doc.getMap( 'document' ).toJSON();
},
},
syncObjectType: 'root/site',
getSyncObjectId: () => 'index',
},
{
label: __( 'Post Type' ),
name: 'postType',
Expand Down Expand Up @@ -253,6 +223,12 @@ export const rootEntitiesConfig = [
export const additionalEntityConfigLoaders = [
{ kind: 'postType', loadEntities: loadPostTypeEntities },
{ kind: 'taxonomy', loadEntities: loadTaxonomyEntities },
{
kind: 'root',
name: 'site',
plural: 'sites',
loadEntities: loadSiteEntity,
},
];

/**
Expand Down Expand Up @@ -409,6 +385,56 @@ async function loadTaxonomyEntities() {
} );
}

/**
* Returns the Site entity.
*
* @return {Promise} Entity promise
*/
async function loadSiteEntity() {
const entity = {
label: __( 'Site' ),
name: 'site',
kind: 'root',
baseURL: '/wp/v2/settings',
syncConfig: {
fetch: async () => {
return apiFetch( { path: '/wp/v2/settings' } );
},
applyChangesToDoc: ( doc, changes ) => {
const document = doc.getMap( 'document' );
Object.entries( changes ).forEach( ( [ key, value ] ) => {
if ( document.get( key ) !== value ) {
document.set( key, value );
}
} );
},
fromCRDTDoc: ( doc ) => {
return doc.getMap( 'document' ).toJSON();
},
},
syncObjectType: 'root/site',
getSyncObjectId: () => 'index',
meta: {},
};

const site = await apiFetch( {
path: entity.baseURL,
method: 'OPTIONS',
} );

const labels = {};
Object.entries( site?.schema?.properties ?? {} ).forEach(
( [ key, value ] ) => {
// Ignore properties `title` and `type` keys.
if ( typeof value === 'object' && value.title ) {
labels[ key ] = value.title;
}
}
);

return [ { ...entity, meta: { labels } } ];
}

/**
* Returns the entity's getter method name given its kind and name or plural name.
*
Expand Down Expand Up @@ -443,17 +469,21 @@ function registerSyncConfigs( configs ) {
}

/**
* Loads the kind entities into the store.
* Loads the entities into the store.
*
* @param {string} kind Kind
* Note: The `name` argument is used for `root` entities requiring additional server data.
*
* @param {string} kind Kind
* @param {string} name Name
* @return {(thunkArgs: object) => Promise<Array>} Entities
*/
export const getOrLoadEntitiesConfig =
( kind ) =>
( kind, name ) =>
async ( { select, dispatch } ) => {
let configs = select.getEntitiesConfig( kind );
if ( configs && configs.length !== 0 ) {
const hasConfig = !! select.getEntityConfig( kind, name );

if ( configs?.length > 0 && hasConfig ) {
if ( window.__experimentalEnableSync ) {
if ( process.env.IS_GUTENBERG_PLUGIN ) {
registerSyncConfigs( configs );
Expand All @@ -463,9 +493,13 @@ export const getOrLoadEntitiesConfig =
return configs;
}

const loader = additionalEntityConfigLoaders.find(
( l ) => l.kind === kind
);
const loader = additionalEntityConfigLoaders.find( ( l ) => {
if ( ! name || ! l.name ) {
return l.kind === kind;
}

return l.kind === kind && l.name === name;
} );
if ( ! loader ) {
return [];
}
Expand Down
16 changes: 12 additions & 4 deletions packages/core-data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@ import * as privateSelectors from './private-selectors';
import * as actions from './actions';
import * as resolvers from './resolvers';
import createLocksActions from './locks/actions';
import { rootEntitiesConfig, getMethodName } from './entities';
import {
rootEntitiesConfig,
additionalEntityConfigLoaders,
getMethodName,
} from './entities';
import { STORE_NAME } from './name';
import { unlock } from './private-apis';

// The entity selectors/resolvers and actions are shortcuts to their generic equivalents
// (getEntityRecord, getEntityRecords, updateEntityRecord, updateEntityRecords)
// Instead of getEntityRecord, the consumer could use more user-friendly named selector: getPostType, getTaxonomy...
// The "kind" and the "name" of the entity are combined to generate these shortcuts.
const entitiesConfig = [
...rootEntitiesConfig,
...additionalEntityConfigLoaders.filter( ( config ) => !! config.name ),
];

const entitySelectors = rootEntitiesConfig.reduce( ( result, entity ) => {
const entitySelectors = entitiesConfig.reduce( ( result, entity ) => {
const { kind, name, plural } = entity;
result[ getMethodName( kind, name ) ] = ( state, key, query ) =>
selectors.getEntityRecord( state, kind, name, key, query );
Expand All @@ -33,7 +41,7 @@ const entitySelectors = rootEntitiesConfig.reduce( ( result, entity ) => {
return result;
}, {} );

const entityResolvers = rootEntitiesConfig.reduce( ( result, entity ) => {
const entityResolvers = entitiesConfig.reduce( ( result, entity ) => {
const { kind, name, plural } = entity;
result[ getMethodName( kind, name ) ] = ( key, query ) =>
resolvers.getEntityRecord( kind, name, key, query );
Expand All @@ -48,7 +56,7 @@ const entityResolvers = rootEntitiesConfig.reduce( ( result, entity ) => {
return result;
}, {} );

const entityActions = rootEntitiesConfig.reduce( ( result, entity ) => {
const entityActions = entitiesConfig.reduce( ( result, entity ) => {
const { kind, name } = entity;
result[ getMethodName( kind, name, 'save' ) ] = ( record, options ) =>
actions.saveEntityRecord( kind, name, record, options );
Expand Down
10 changes: 5 additions & 5 deletions packages/core-data/src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const getCurrentUser =
export const getEntityRecord =
( kind, name, key = '', query ) =>
async ( { select, dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -194,7 +194,7 @@ export const getEditedEntityRecord = forwardResolver( 'getEntityRecord' );
export const getEntityRecords =
( kind, name, query = {} ) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -429,7 +429,7 @@ export const canUser =
export const canUserEditEntityRecord =
( kind, name, recordId ) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -726,7 +726,7 @@ export const getDefaultTemplateId =
export const getRevisions =
( kind, name, recordKey, query = {} ) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down Expand Up @@ -851,7 +851,7 @@ getRevisions.shouldInvalidate = ( action, kind, name, recordKey ) =>
export const getRevision =
( kind, name, recordKey, revisionKey, query ) =>
async ( { dispatch } ) => {
const configs = await dispatch( getOrLoadEntitiesConfig( kind ) );
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
const entityConfig = configs.find(
( config ) => config.name === name && config.kind === kind
);
Expand Down
21 changes: 18 additions & 3 deletions packages/core-data/src/test/entities.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,29 @@ describe( 'getKindEntities', () => {
const dispatch = jest.fn();
const select = {
getEntitiesConfig: jest.fn( () => entities ),
getEntityConfig: jest.fn( () => ( {
kind: 'postType',
name: 'post',
} ) ),
};
const entities = [ { kind: 'postType' } ];
await getOrLoadEntitiesConfig( 'postType' )( { dispatch, select } );
await getOrLoadEntitiesConfig(
'postType',
'post'
)( { dispatch, select } );
expect( dispatch ).not.toHaveBeenCalled();
} );

it( 'shouldn’t do anything if there no defined kind config', async () => {
const dispatch = jest.fn();
const select = {
getEntitiesConfig: jest.fn( () => [] ),
getEntityConfig: jest.fn( () => undefined ),
};
await getOrLoadEntitiesConfig( 'unknownKind' )( { dispatch, select } );
await getOrLoadEntitiesConfig(
'unknownKind',
undefined
)( { dispatch, select } );
expect( dispatch ).not.toHaveBeenCalled();
} );

Expand All @@ -82,10 +93,14 @@ describe( 'getKindEntities', () => {
const dispatch = jest.fn();
const select = {
getEntitiesConfig: jest.fn( () => [] ),
getEntityConfig: jest.fn( () => undefined ),
};
triggerFetch.mockImplementation( () => fetchedEntities );

await getOrLoadEntitiesConfig( 'postType' )( { dispatch, select } );
await getOrLoadEntitiesConfig(
'postType',
'post'
)( { dispatch, select } );
expect( dispatch ).toHaveBeenCalledTimes( 1 );
expect( dispatch.mock.calls[ 0 ][ 0 ].type ).toBe( 'ADD_ENTITIES' );
expect( dispatch.mock.calls[ 0 ][ 0 ].entities.length ).toBe( 1 );
Expand Down
Loading

0 comments on commit ff1a5e0

Please sign in to comment.