@wordpress/core-data 7.2.0 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +54 -6
- package/build/actions.js +6 -6
- package/build/actions.js.map +1 -1
- package/build/entity-context.js +13 -0
- package/build/entity-context.js.map +1 -0
- package/build/entity-provider.js +4 -189
- package/build/entity-provider.js.map +1 -1
- package/build/entity-types/menu-location.js.map +1 -1
- package/build/entity-types/settings.js.map +1 -1
- package/build/entity-types/theme.js.map +1 -1
- package/build/entity-types/widget-type.js.map +1 -1
- package/build/hooks/index.js +22 -0
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use-entity-block-editor.js +140 -0
- package/build/hooks/use-entity-block-editor.js.map +1 -0
- package/build/hooks/use-entity-id.js +28 -0
- package/build/hooks/use-entity-id.js.map +1 -0
- package/build/hooks/use-entity-prop.js +65 -0
- package/build/hooks/use-entity-prop.js.map +1 -0
- package/build/hooks/use-resource-permissions.js +25 -8
- package/build/hooks/use-resource-permissions.js.map +1 -1
- package/build/resolvers.js +81 -70
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +23 -9
- package/build/selectors.js.map +1 -1
- package/build/utils/index.js +19 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/user-permissions.js +32 -0
- package/build/utils/user-permissions.js.map +1 -0
- package/build-module/actions.js +6 -6
- package/build-module/actions.js.map +1 -1
- package/build-module/entity-context.js +6 -0
- package/build-module/entity-context.js.map +1 -0
- package/build-module/entity-provider.js +3 -185
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/entity-types/menu-location.js.map +1 -1
- package/build-module/entity-types/settings.js.map +1 -1
- package/build-module/entity-types/theme.js.map +1 -1
- package/build-module/entity-types/widget-type.js.map +1 -1
- package/build-module/hooks/index.js +3 -0
- package/build-module/hooks/index.js.map +1 -1
- package/build-module/hooks/use-entity-block-editor.js +132 -0
- package/build-module/hooks/use-entity-block-editor.js.map +1 -0
- package/build-module/hooks/use-entity-id.js +22 -0
- package/build-module/hooks/use-entity-id.js.map +1 -0
- package/build-module/hooks/use-entity-prop.js +58 -0
- package/build-module/hooks/use-entity-prop.js.map +1 -0
- package/build-module/hooks/use-resource-permissions.js +25 -8
- package/build-module/hooks/use-resource-permissions.js.map +1 -1
- package/build-module/resolvers.js +82 -71
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +24 -10
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/index.js +1 -0
- package/build-module/utils/index.js.map +1 -1
- package/build-module/utils/user-permissions.js +24 -0
- package/build-module/utils/user-permissions.js.map +1 -0
- package/build-types/actions.d.ts +3 -3
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/batch/create-batch.d.ts.map +1 -1
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/entity-context.d.ts +2 -0
- package/build-types/entity-context.d.ts.map +1 -0
- package/build-types/entity-provider.d.ts +0 -48
- package/build-types/entity-provider.d.ts.map +1 -1
- package/build-types/entity-types/menu-location.d.ts.map +1 -1
- package/build-types/entity-types/settings.d.ts.map +1 -1
- package/build-types/entity-types/theme.d.ts.map +1 -1
- package/build-types/entity-types/widget-type.d.ts.map +1 -1
- package/build-types/fetch/__experimental-fetch-url-data.d.ts.map +1 -1
- package/build-types/hooks/index.d.ts +3 -0
- package/build-types/hooks/index.d.ts.map +1 -1
- package/build-types/hooks/use-entity-block-editor.d.ts +22 -0
- package/build-types/hooks/use-entity-block-editor.d.ts.map +1 -0
- package/build-types/hooks/use-entity-id.d.ts +9 -0
- package/build-types/hooks/use-entity-id.d.ts.map +1 -0
- package/build-types/hooks/use-entity-prop.d.ts +19 -0
- package/build-types/hooks/use-entity-prop.d.ts.map +1 -0
- package/build-types/hooks/use-resource-permissions.d.ts +8 -70
- package/build-types/hooks/use-resource-permissions.d.ts.map +1 -1
- package/build-types/index.d.ts +35 -32
- package/build-types/index.d.ts.map +1 -1
- package/build-types/locks/reducer.d.ts +1 -1
- package/build-types/locks/reducer.d.ts.map +1 -1
- package/build-types/queried-data/actions.d.ts +1 -1
- package/build-types/queried-data/actions.d.ts.map +1 -1
- package/build-types/queried-data/get-query-parts.d.ts.map +1 -1
- package/build-types/queried-data/reducer.d.ts +1 -1
- package/build-types/queried-data/reducer.d.ts.map +1 -1
- package/build-types/queried-data/selectors.d.ts +0 -1
- package/build-types/queried-data/selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +13 -13
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +3 -2
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +11 -6
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/utils/get-nested-value.d.ts.map +1 -1
- package/build-types/utils/get-normalized-comma-separable.d.ts.map +1 -1
- package/build-types/utils/if-matching-action.d.ts +1 -1
- package/build-types/utils/index.d.ts +1 -0
- package/build-types/utils/on-sub-key.d.ts +1 -1
- package/build-types/utils/replace-action.d.ts +1 -1
- package/build-types/utils/set-nested-value.d.ts.map +1 -1
- package/build-types/utils/user-permissions.d.ts +4 -0
- package/build-types/utils/user-permissions.d.ts.map +1 -0
- package/package.json +18 -17
- package/src/actions.js +6 -6
- package/src/entity-context.js +6 -0
- package/src/entity-provider.js +2 -211
- package/src/entity-types/menu-location.ts +1 -0
- package/src/entity-types/settings.ts +1 -0
- package/src/entity-types/theme.ts +1 -0
- package/src/entity-types/widget-type.ts +1 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/test/use-entity-record.js +5 -3
- package/src/hooks/test/use-resource-permissions.js +96 -5
- package/src/hooks/use-entity-block-editor.js +148 -0
- package/src/hooks/use-entity-id.js +21 -0
- package/src/hooks/use-entity-prop.js +60 -0
- package/src/hooks/use-resource-permissions.ts +46 -9
- package/src/resolvers.js +102 -78
- package/src/selectors.ts +24 -9
- package/src/test/entity-provider.js +6 -2
- package/src/test/resolvers.js +221 -50
- package/src/test/selectors.js +18 -55
- package/src/utils/index.js +5 -0
- package/src/utils/user-permissions.js +39 -0
- package/tsconfig.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useCallback, useMemo } from '@wordpress/element';
|
|
5
|
+
import { useDispatch, useSelect } from '@wordpress/data';
|
|
6
|
+
import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Internal dependencies
|
|
10
|
+
*/
|
|
11
|
+
import { STORE_NAME } from '../name';
|
|
12
|
+
import useEntityId from './use-entity-id';
|
|
13
|
+
import { updateFootnotesFromMeta } from '../footnotes';
|
|
14
|
+
|
|
15
|
+
const EMPTY_ARRAY = [];
|
|
16
|
+
const parsedBlocksCache = new WeakMap();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Hook that returns block content getters and setters for
|
|
20
|
+
* the nearest provided entity of the specified type.
|
|
21
|
+
*
|
|
22
|
+
* The return value has the shape `[ blocks, onInput, onChange ]`.
|
|
23
|
+
* `onInput` is for block changes that don't create undo levels
|
|
24
|
+
* or dirty the post, non-persistent changes, and `onChange` is for
|
|
25
|
+
* persistent changes. They map directly to the props of a
|
|
26
|
+
* `BlockEditorProvider` and are intended to be used with it,
|
|
27
|
+
* or similar components or hooks.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} kind The entity kind.
|
|
30
|
+
* @param {string} name The entity name.
|
|
31
|
+
* @param {Object} options
|
|
32
|
+
* @param {string} [options.id] An entity ID to use instead of the context-provided one.
|
|
33
|
+
*
|
|
34
|
+
* @return {[unknown[], Function, Function]} The block array and setters.
|
|
35
|
+
*/
|
|
36
|
+
export default function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
|
|
37
|
+
const providerId = useEntityId( kind, name );
|
|
38
|
+
const id = _id ?? providerId;
|
|
39
|
+
const { getEntityRecord, getEntityRecordEdits } = useSelect( STORE_NAME );
|
|
40
|
+
const { content, editedBlocks, meta } = useSelect(
|
|
41
|
+
( select ) => {
|
|
42
|
+
if ( ! id ) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
const { getEditedEntityRecord } = select( STORE_NAME );
|
|
46
|
+
const editedRecord = getEditedEntityRecord( kind, name, id );
|
|
47
|
+
return {
|
|
48
|
+
editedBlocks: editedRecord.blocks,
|
|
49
|
+
content: editedRecord.content,
|
|
50
|
+
meta: editedRecord.meta,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
[ kind, name, id ]
|
|
54
|
+
);
|
|
55
|
+
const { __unstableCreateUndoLevel, editEntityRecord } =
|
|
56
|
+
useDispatch( STORE_NAME );
|
|
57
|
+
|
|
58
|
+
const blocks = useMemo( () => {
|
|
59
|
+
if ( ! id ) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if ( editedBlocks ) {
|
|
64
|
+
return editedBlocks;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ( ! content || typeof content !== 'string' ) {
|
|
68
|
+
return EMPTY_ARRAY;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If there's an edit, cache the parsed blocks by the edit.
|
|
72
|
+
// If not, cache by the original enity record.
|
|
73
|
+
const edits = getEntityRecordEdits( kind, name, id );
|
|
74
|
+
const isUnedited = ! edits || ! Object.keys( edits ).length;
|
|
75
|
+
const cackeKey = isUnedited ? getEntityRecord( kind, name, id ) : edits;
|
|
76
|
+
let _blocks = parsedBlocksCache.get( cackeKey );
|
|
77
|
+
|
|
78
|
+
if ( ! _blocks ) {
|
|
79
|
+
_blocks = parse( content );
|
|
80
|
+
parsedBlocksCache.set( cackeKey, _blocks );
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return _blocks;
|
|
84
|
+
}, [
|
|
85
|
+
kind,
|
|
86
|
+
name,
|
|
87
|
+
id,
|
|
88
|
+
editedBlocks,
|
|
89
|
+
content,
|
|
90
|
+
getEntityRecord,
|
|
91
|
+
getEntityRecordEdits,
|
|
92
|
+
] );
|
|
93
|
+
|
|
94
|
+
const updateFootnotes = useCallback(
|
|
95
|
+
( _blocks ) => updateFootnotesFromMeta( _blocks, meta ),
|
|
96
|
+
[ meta ]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const onChange = useCallback(
|
|
100
|
+
( newBlocks, options ) => {
|
|
101
|
+
const noChange = blocks === newBlocks;
|
|
102
|
+
if ( noChange ) {
|
|
103
|
+
return __unstableCreateUndoLevel( kind, name, id );
|
|
104
|
+
}
|
|
105
|
+
const { selection, ...rest } = options;
|
|
106
|
+
|
|
107
|
+
// We create a new function here on every persistent edit
|
|
108
|
+
// to make sure the edit makes the post dirty and creates
|
|
109
|
+
// a new undo level.
|
|
110
|
+
const edits = {
|
|
111
|
+
selection,
|
|
112
|
+
content: ( { blocks: blocksForSerialization = [] } ) =>
|
|
113
|
+
__unstableSerializeAndClean( blocksForSerialization ),
|
|
114
|
+
...updateFootnotes( newBlocks ),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
editEntityRecord( kind, name, id, edits, {
|
|
118
|
+
isCached: false,
|
|
119
|
+
...rest,
|
|
120
|
+
} );
|
|
121
|
+
},
|
|
122
|
+
[
|
|
123
|
+
kind,
|
|
124
|
+
name,
|
|
125
|
+
id,
|
|
126
|
+
blocks,
|
|
127
|
+
updateFootnotes,
|
|
128
|
+
__unstableCreateUndoLevel,
|
|
129
|
+
editEntityRecord,
|
|
130
|
+
]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const onInput = useCallback(
|
|
134
|
+
( newBlocks, options ) => {
|
|
135
|
+
const { selection, ...rest } = options;
|
|
136
|
+
const footnotesChanges = updateFootnotes( newBlocks );
|
|
137
|
+
const edits = { selection, ...footnotesChanges };
|
|
138
|
+
|
|
139
|
+
editEntityRecord( kind, name, id, edits, {
|
|
140
|
+
isCached: true,
|
|
141
|
+
...rest,
|
|
142
|
+
} );
|
|
143
|
+
},
|
|
144
|
+
[ kind, name, id, updateFootnotes, editEntityRecord ]
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return [ blocks, onInput, onChange ];
|
|
148
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useContext } from '@wordpress/element';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { EntityContext } from '../entity-context';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Hook that returns the ID for the nearest
|
|
13
|
+
* provided entity of the specified type.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} kind The entity kind.
|
|
16
|
+
* @param {string} name The entity name.
|
|
17
|
+
*/
|
|
18
|
+
export default function useEntityId( kind, name ) {
|
|
19
|
+
const context = useContext( EntityContext );
|
|
20
|
+
return context?.[ kind ]?.[ name ];
|
|
21
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useCallback } from '@wordpress/element';
|
|
5
|
+
import { useDispatch, useSelect } from '@wordpress/data';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { STORE_NAME } from '../name';
|
|
11
|
+
import useEntityId from './use-entity-id';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Hook that returns the value and a setter for the
|
|
15
|
+
* specified property of the nearest provided
|
|
16
|
+
* entity of the specified type.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} kind The entity kind.
|
|
19
|
+
* @param {string} name The entity name.
|
|
20
|
+
* @param {string} prop The property name.
|
|
21
|
+
* @param {string} [_id] An entity ID to use instead of the context-provided one.
|
|
22
|
+
*
|
|
23
|
+
* @return {[*, Function, *]} An array where the first item is the
|
|
24
|
+
* property value, the second is the
|
|
25
|
+
* setter and the third is the full value
|
|
26
|
+
* object from REST API containing more
|
|
27
|
+
* information like `raw`, `rendered` and
|
|
28
|
+
* `protected` props.
|
|
29
|
+
*/
|
|
30
|
+
export default function useEntityProp( kind, name, prop, _id ) {
|
|
31
|
+
const providerId = useEntityId( kind, name );
|
|
32
|
+
const id = _id ?? providerId;
|
|
33
|
+
|
|
34
|
+
const { value, fullValue } = useSelect(
|
|
35
|
+
( select ) => {
|
|
36
|
+
const { getEntityRecord, getEditedEntityRecord } =
|
|
37
|
+
select( STORE_NAME );
|
|
38
|
+
const record = getEntityRecord( kind, name, id ); // Trigger resolver.
|
|
39
|
+
const editedRecord = getEditedEntityRecord( kind, name, id );
|
|
40
|
+
return record && editedRecord
|
|
41
|
+
? {
|
|
42
|
+
value: editedRecord[ prop ],
|
|
43
|
+
fullValue: record[ prop ],
|
|
44
|
+
}
|
|
45
|
+
: {};
|
|
46
|
+
},
|
|
47
|
+
[ kind, name, id, prop ]
|
|
48
|
+
);
|
|
49
|
+
const { editEntityRecord } = useDispatch( STORE_NAME );
|
|
50
|
+
const setValue = useCallback(
|
|
51
|
+
( newValue ) => {
|
|
52
|
+
editEntityRecord( kind, name, id, {
|
|
53
|
+
[ prop ]: newValue,
|
|
54
|
+
} );
|
|
55
|
+
},
|
|
56
|
+
[ editEntityRecord, kind, name, id, prop ]
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return [ value, setValue, fullValue ];
|
|
60
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import deprecated from '@wordpress/deprecated';
|
|
5
|
+
import warning from '@wordpress/warning';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Internal dependencies
|
|
@@ -41,20 +42,34 @@ type ResourcePermissionsResolution< IdType > = [
|
|
|
41
42
|
( IdType extends void ? SpecificResourcePermissionsResolution : {} ),
|
|
42
43
|
];
|
|
43
44
|
|
|
45
|
+
type EntityResource = { kind: string; name: string; id?: string | number };
|
|
46
|
+
|
|
47
|
+
function useResourcePermissions< IdType = void >(
|
|
48
|
+
resource: string,
|
|
49
|
+
id?: IdType
|
|
50
|
+
): ResourcePermissionsResolution< IdType >;
|
|
51
|
+
|
|
52
|
+
function useResourcePermissions< IdType = void >(
|
|
53
|
+
resource: EntityResource,
|
|
54
|
+
id?: never
|
|
55
|
+
): ResourcePermissionsResolution< IdType >;
|
|
56
|
+
|
|
44
57
|
/**
|
|
45
58
|
* Resolves resource permissions.
|
|
46
59
|
*
|
|
47
60
|
* @since 6.1.0 Introduced in WordPress core.
|
|
48
61
|
*
|
|
49
|
-
* @param resource
|
|
50
|
-
*
|
|
62
|
+
* @param resource Entity resource to check. Accepts entity object `{ kind: 'root', name: 'media', id: 1 }`
|
|
63
|
+
* or REST base as a string - `media`.
|
|
64
|
+
* @param id Optional ID of the resource to check, e.g. 10. Note: This argument is discouraged
|
|
65
|
+
* when using an entity object as a resource to check permissions and will be ignored.
|
|
51
66
|
*
|
|
52
67
|
* @example
|
|
53
68
|
* ```js
|
|
54
69
|
* import { useResourcePermissions } from '@wordpress/core-data';
|
|
55
70
|
*
|
|
56
71
|
* function PagesList() {
|
|
57
|
-
* const { canCreate, isResolving } = useResourcePermissions( '
|
|
72
|
+
* const { canCreate, isResolving } = useResourcePermissions( { kind: 'postType', name: 'page' } );
|
|
58
73
|
*
|
|
59
74
|
* if ( isResolving ) {
|
|
60
75
|
* return 'Loading ...';
|
|
@@ -82,7 +97,7 @@ type ResourcePermissionsResolution< IdType > = [
|
|
|
82
97
|
* canUpdate,
|
|
83
98
|
* canDelete,
|
|
84
99
|
* isResolving
|
|
85
|
-
* } = useResourcePermissions( '
|
|
100
|
+
* } = useResourcePermissions( { kind: 'postType', name: 'page', id: pageId } );
|
|
86
101
|
*
|
|
87
102
|
* if ( isResolving ) {
|
|
88
103
|
* return 'Loading ...';
|
|
@@ -109,15 +124,35 @@ type ResourcePermissionsResolution< IdType > = [
|
|
|
109
124
|
* @return Entity records data.
|
|
110
125
|
* @template IdType
|
|
111
126
|
*/
|
|
112
|
-
|
|
113
|
-
resource: string,
|
|
127
|
+
function useResourcePermissions< IdType = void >(
|
|
128
|
+
resource: string | EntityResource,
|
|
114
129
|
id?: IdType
|
|
115
130
|
): ResourcePermissionsResolution< IdType > {
|
|
131
|
+
// Serialize `resource` to a string that can be safely used as a React dep.
|
|
132
|
+
// We can't just pass `resource` as one of the deps, because if it is passed
|
|
133
|
+
// as an object literal, then it will be a different object on each call even
|
|
134
|
+
// if the values remain the same.
|
|
135
|
+
const isEntity = typeof resource === 'object';
|
|
136
|
+
const resourceAsString = isEntity ? JSON.stringify( resource ) : resource;
|
|
137
|
+
|
|
138
|
+
if ( isEntity && typeof id !== 'undefined' ) {
|
|
139
|
+
warning(
|
|
140
|
+
`When 'resource' is an entity object, passing 'id' as a separate argument isn't supported.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
116
144
|
return useQuerySelect(
|
|
117
145
|
( resolve ) => {
|
|
146
|
+
const hasId = isEntity ? !! resource.id : !! id;
|
|
118
147
|
const { canUser } = resolve( coreStore );
|
|
119
|
-
const create = canUser(
|
|
120
|
-
|
|
148
|
+
const create = canUser(
|
|
149
|
+
'create',
|
|
150
|
+
isEntity
|
|
151
|
+
? { kind: resource.kind, name: resource.name }
|
|
152
|
+
: resource
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if ( ! hasId ) {
|
|
121
156
|
const read = canUser( 'read', resource );
|
|
122
157
|
|
|
123
158
|
const isResolving = create.isResolving || read.isResolving;
|
|
@@ -168,10 +203,12 @@ export default function useResourcePermissions< IdType = void >(
|
|
|
168
203
|
canDelete: hasResolved && _delete.data,
|
|
169
204
|
};
|
|
170
205
|
},
|
|
171
|
-
[
|
|
206
|
+
[ resourceAsString, id ]
|
|
172
207
|
);
|
|
173
208
|
}
|
|
174
209
|
|
|
210
|
+
export default useResourcePermissions;
|
|
211
|
+
|
|
175
212
|
export function __experimentalUseResourcePermissions(
|
|
176
213
|
resource: string,
|
|
177
214
|
id?: unknown
|
package/src/resolvers.js
CHANGED
|
@@ -15,7 +15,13 @@ import apiFetch from '@wordpress/api-fetch';
|
|
|
15
15
|
*/
|
|
16
16
|
import { STORE_NAME } from './name';
|
|
17
17
|
import { getOrLoadEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
forwardResolver,
|
|
20
|
+
getNormalizedCommaSeparable,
|
|
21
|
+
getUserPermissionCacheKey,
|
|
22
|
+
getUserPermissionsFromResponse,
|
|
23
|
+
ALLOWED_RESOURCE_ACTIONS,
|
|
24
|
+
} from './utils';
|
|
19
25
|
import { getSyncProvider } from './sync';
|
|
20
26
|
import { fetchBlockPatterns } from './fetch';
|
|
21
27
|
|
|
@@ -58,12 +64,12 @@ export const getCurrentUser =
|
|
|
58
64
|
*/
|
|
59
65
|
export const getEntityRecord =
|
|
60
66
|
( kind, name, key = '', query ) =>
|
|
61
|
-
async ( { select, dispatch } ) => {
|
|
67
|
+
async ( { select, dispatch, registry } ) => {
|
|
62
68
|
const configs = await dispatch( getOrLoadEntitiesConfig( kind, name ) );
|
|
63
69
|
const entityConfig = configs.find(
|
|
64
70
|
( config ) => config.name === name && config.kind === kind
|
|
65
71
|
);
|
|
66
|
-
if ( ! entityConfig
|
|
72
|
+
if ( ! entityConfig ) {
|
|
67
73
|
return;
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -165,8 +171,29 @@ export const getEntityRecord =
|
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
173
|
|
|
168
|
-
const
|
|
169
|
-
|
|
174
|
+
const response = await apiFetch( { path, parse: false } );
|
|
175
|
+
const record = await response.json();
|
|
176
|
+
const permissions = getUserPermissionsFromResponse( response );
|
|
177
|
+
|
|
178
|
+
registry.batch( () => {
|
|
179
|
+
dispatch.receiveEntityRecords( kind, name, record, query );
|
|
180
|
+
|
|
181
|
+
for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
|
|
182
|
+
const permissionKey = getUserPermissionCacheKey(
|
|
183
|
+
action,
|
|
184
|
+
{ kind, name, id: key }
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
dispatch.receiveUserPermission(
|
|
188
|
+
permissionKey,
|
|
189
|
+
permissions[ action ]
|
|
190
|
+
);
|
|
191
|
+
dispatch.finishResolution( 'canUser', [
|
|
192
|
+
action,
|
|
193
|
+
{ kind, name, id: key },
|
|
194
|
+
] );
|
|
195
|
+
}
|
|
196
|
+
} );
|
|
170
197
|
}
|
|
171
198
|
} finally {
|
|
172
199
|
dispatch.__unstableReleaseStoreLock( lock );
|
|
@@ -198,7 +225,7 @@ export const getEntityRecords =
|
|
|
198
225
|
const entityConfig = configs.find(
|
|
199
226
|
( config ) => config.name === name && config.kind === kind
|
|
200
227
|
);
|
|
201
|
-
if ( ! entityConfig
|
|
228
|
+
if ( ! entityConfig ) {
|
|
202
229
|
return;
|
|
203
230
|
}
|
|
204
231
|
|
|
@@ -281,16 +308,10 @@ export const getEntityRecords =
|
|
|
281
308
|
.filter( ( record ) => record?.[ key ] )
|
|
282
309
|
.map( ( record ) => [ kind, name, record[ key ] ] );
|
|
283
310
|
|
|
284
|
-
dispatch(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
} );
|
|
289
|
-
dispatch( {
|
|
290
|
-
type: 'FINISH_RESOLUTIONS',
|
|
291
|
-
selectorName: 'getEntityRecord',
|
|
292
|
-
args: resolutionsArgs,
|
|
293
|
-
} );
|
|
311
|
+
dispatch.finishResolutions(
|
|
312
|
+
'getEntityRecord',
|
|
313
|
+
resolutionsArgs
|
|
314
|
+
);
|
|
294
315
|
}
|
|
295
316
|
|
|
296
317
|
dispatch.__unstableReleaseStoreLock( lock );
|
|
@@ -352,25 +373,47 @@ export const getEmbedPreview =
|
|
|
352
373
|
* Checks whether the current user can perform the given action on the given
|
|
353
374
|
* REST resource.
|
|
354
375
|
*
|
|
355
|
-
* @param {string}
|
|
356
|
-
*
|
|
357
|
-
* @param {string}
|
|
358
|
-
*
|
|
376
|
+
* @param {string} requestedAction Action to check. One of: 'create', 'read', 'update',
|
|
377
|
+
* 'delete'.
|
|
378
|
+
* @param {string|Object} resource Entity resource to check. Accepts entity object `{ kind: 'root', name: 'media', id: 1 }`
|
|
379
|
+
* or REST base as a string - `media`.
|
|
380
|
+
* @param {?string} id ID of the rest resource to check.
|
|
359
381
|
*/
|
|
360
382
|
export const canUser =
|
|
361
383
|
( requestedAction, resource, id ) =>
|
|
362
384
|
async ( { dispatch, registry } ) => {
|
|
363
|
-
|
|
385
|
+
if ( ! ALLOWED_RESOURCE_ACTIONS.includes( requestedAction ) ) {
|
|
386
|
+
throw new Error( `'${ requestedAction }' is not a valid action.` );
|
|
387
|
+
}
|
|
364
388
|
|
|
365
|
-
|
|
366
|
-
|
|
389
|
+
let resourcePath = null;
|
|
390
|
+
if ( typeof resource === 'object' ) {
|
|
391
|
+
if ( ! resource.kind || ! resource.name ) {
|
|
392
|
+
throw new Error( 'The entity resource object is not valid.' );
|
|
393
|
+
}
|
|
367
394
|
|
|
368
|
-
|
|
369
|
-
|
|
395
|
+
const configs = await dispatch(
|
|
396
|
+
getOrLoadEntitiesConfig( resource.kind, resource.name )
|
|
397
|
+
);
|
|
398
|
+
const entityConfig = configs.find(
|
|
399
|
+
( config ) =>
|
|
400
|
+
config.name === resource.name &&
|
|
401
|
+
config.kind === resource.kind
|
|
402
|
+
);
|
|
403
|
+
if ( ! entityConfig ) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
resourcePath =
|
|
408
|
+
entityConfig.baseURL + ( resource.id ? '/' + resource.id : '' );
|
|
409
|
+
} else {
|
|
410
|
+
resourcePath = `/wp/v2/${ resource }` + ( id ? '/' + id : '' );
|
|
370
411
|
}
|
|
371
412
|
|
|
413
|
+
const { hasStartedResolution } = registry.select( STORE_NAME );
|
|
414
|
+
|
|
372
415
|
// Prevent resolving the same resource twice.
|
|
373
|
-
for ( const relatedAction of
|
|
416
|
+
for ( const relatedAction of ALLOWED_RESOURCE_ACTIONS ) {
|
|
374
417
|
if ( relatedAction === requestedAction ) {
|
|
375
418
|
continue;
|
|
376
419
|
}
|
|
@@ -387,7 +430,7 @@ export const canUser =
|
|
|
387
430
|
let response;
|
|
388
431
|
try {
|
|
389
432
|
response = await apiFetch( {
|
|
390
|
-
path:
|
|
433
|
+
path: resourcePath,
|
|
391
434
|
method: 'OPTIONS',
|
|
392
435
|
parse: false,
|
|
393
436
|
} );
|
|
@@ -397,29 +440,23 @@ export const canUser =
|
|
|
397
440
|
return;
|
|
398
441
|
}
|
|
399
442
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const allowedMethods = allowHeader?.allow || allowHeader || '';
|
|
405
|
-
|
|
406
|
-
const permissions = {};
|
|
407
|
-
const methods = {
|
|
408
|
-
create: 'POST',
|
|
409
|
-
read: 'GET',
|
|
410
|
-
update: 'PUT',
|
|
411
|
-
delete: 'DELETE',
|
|
412
|
-
};
|
|
413
|
-
for ( const [ actionName, methodName ] of Object.entries( methods ) ) {
|
|
414
|
-
permissions[ actionName ] = allowedMethods.includes( methodName );
|
|
415
|
-
}
|
|
443
|
+
const permissions = getUserPermissionsFromResponse( response );
|
|
444
|
+
registry.batch( () => {
|
|
445
|
+
for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
|
|
446
|
+
const key = getUserPermissionCacheKey( action, resource, id );
|
|
416
447
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
448
|
+
dispatch.receiveUserPermission( key, permissions[ action ] );
|
|
449
|
+
|
|
450
|
+
// Mark related action resolutions as finished.
|
|
451
|
+
if ( action !== requestedAction ) {
|
|
452
|
+
dispatch.finishResolution( 'canUser', [
|
|
453
|
+
action,
|
|
454
|
+
resource,
|
|
455
|
+
id,
|
|
456
|
+
] );
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
} );
|
|
423
460
|
};
|
|
424
461
|
|
|
425
462
|
/**
|
|
@@ -433,16 +470,7 @@ export const canUser =
|
|
|
433
470
|
export const canUserEditEntityRecord =
|
|
434
471
|
( kind, name, recordId ) =>
|
|
435
472
|
async ( { dispatch } ) => {
|
|
436
|
-
|
|
437
|
-
const entityConfig = configs.find(
|
|
438
|
-
( config ) => config.name === name && config.kind === kind
|
|
439
|
-
);
|
|
440
|
-
if ( ! entityConfig ) {
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const resource = entityConfig.__unstable_rest_base;
|
|
445
|
-
await dispatch( canUser( 'update', resource, recordId ) );
|
|
473
|
+
await dispatch( canUser( 'update', { kind, name, id: recordId } ) );
|
|
446
474
|
};
|
|
447
475
|
|
|
448
476
|
/**
|
|
@@ -543,13 +571,17 @@ export const __experimentalGetCurrentGlobalStylesId =
|
|
|
543
571
|
const globalStylesURL =
|
|
544
572
|
activeThemes?.[ 0 ]?._links?.[ 'wp:user-global-styles' ]?.[ 0 ]
|
|
545
573
|
?.href;
|
|
546
|
-
if ( globalStylesURL ) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
574
|
+
if ( ! globalStylesURL ) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Regex matches the ID at the end of a URL or immediately before
|
|
579
|
+
// the query string.
|
|
580
|
+
const matches = globalStylesURL.match( /\/(\d+)(?:\?|$)/ );
|
|
581
|
+
const id = matches ? Number( matches[ 1 ] ) : null;
|
|
582
|
+
|
|
583
|
+
if ( id ) {
|
|
584
|
+
dispatch.__experimentalReceiveCurrentGlobalStylesId( id );
|
|
553
585
|
}
|
|
554
586
|
};
|
|
555
587
|
|
|
@@ -736,7 +768,7 @@ export const getRevisions =
|
|
|
736
768
|
( config ) => config.name === name && config.kind === kind
|
|
737
769
|
);
|
|
738
770
|
|
|
739
|
-
if ( ! entityConfig
|
|
771
|
+
if ( ! entityConfig ) {
|
|
740
772
|
return;
|
|
741
773
|
}
|
|
742
774
|
|
|
@@ -820,16 +852,8 @@ export const getRevisions =
|
|
|
820
852
|
record[ key ],
|
|
821
853
|
] );
|
|
822
854
|
|
|
823
|
-
dispatch(
|
|
824
|
-
|
|
825
|
-
selectorName: 'getRevision',
|
|
826
|
-
args: resolutionsArgs,
|
|
827
|
-
} );
|
|
828
|
-
dispatch( {
|
|
829
|
-
type: 'FINISH_RESOLUTIONS',
|
|
830
|
-
selectorName: 'getRevision',
|
|
831
|
-
args: resolutionsArgs,
|
|
832
|
-
} );
|
|
855
|
+
dispatch.startResolutions( 'getRevision', resolutionsArgs );
|
|
856
|
+
dispatch.finishResolutions( 'getRevision', resolutionsArgs );
|
|
833
857
|
}
|
|
834
858
|
}
|
|
835
859
|
};
|
|
@@ -861,7 +885,7 @@ export const getRevision =
|
|
|
861
885
|
( config ) => config.name === name && config.kind === kind
|
|
862
886
|
);
|
|
863
887
|
|
|
864
|
-
if ( ! entityConfig
|
|
888
|
+
if ( ! entityConfig ) {
|
|
865
889
|
return;
|
|
866
890
|
}
|
|
867
891
|
|